-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathslice.go
93 lines (86 loc) · 2.68 KB
/
slice.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package set
import (
"reflect"
)
// SliceValue wraps around a slice []T and facilitates element creation and appending.
type SliceValue struct {
// Top is the original values passed to Slice.
Top reflect.Value
// V represents []T.
V reflect.Value
// ElemType and ElemEndType describe the type of elements in the slice.
//
// ElemType!=reflect.Ptr means ElemType equals ElemEndType; they describe the same type.
// ElemType==reflect.Ptr means ElemEndType is the type at the end of the pointer chain.
ElemType reflect.Type
ElemEndType reflect.Type
}
// Slice expects its argument to be a *[]T or a pointer chain ending in []T.
//
// As a convenience Slice will also accept a reflect.Value as long as it represents
// a writable []T value.
func Slice(v interface{}) (SliceValue, error) {
var s SliceValue
var pv reflect.Value
//
// Allow reflect.Value as an argument.
switch sw := v.(type) {
case reflect.Value:
pv = sw
default:
pv = reflect.ValueOf(v)
}
//
// Incoming type must be *[]T or any length ****[]T
pt := pv.Type()
if pt.Kind() != reflect.Ptr {
return s, pkgerr{Err: ErrInvalidSlice, CallSite: "Slice", Context: "expected pointer to slice; got " + pt.String()}
}
for pt.Kind() == reflect.Ptr {
pt = pt.Elem()
}
if pt.Kind() != reflect.Slice {
return s, pkgerr{Err: ErrInvalidSlice, CallSite: "Slice", Context: "expected pointer to slice; got " + pv.Type().String()}
}
//
// Type is valid so instantiate pointer chain to final []T.
top := pv
for pv.Kind() == reflect.Ptr {
// Update our pointer reference each iteration. We want our ptr reference
// to be the **last** pointer in the chain such that slicePtr.ptr.Elem() is the []T.
//
if pv.IsNil() {
if !pv.CanSet() {
return s, pkgerr{Err: ErrReadOnly, CallSite: "Slice", Context: "can not set " + pv.Type().String()}
}
pv.Set(reflect.New(pv.Type().Elem()))
}
pv = pv.Elem()
}
//
elemType := pv.Type().Elem()
endElemType := elemType
for ; endElemType.Kind() == reflect.Ptr; endElemType = endElemType.Elem() {
}
s = SliceValue{
Top: top,
V: pv,
ElemType: elemType,
ElemEndType: endElemType,
}
return s, nil
}
// Append appends an element created by the Elem method.
//
// If the slice is []T then Elem returns a *T. Append automatically dereferences
// the incoming value so that a T is appended as expected.
func (s *SliceValue) Append(elem reflect.Value) {
s.V.Set(reflect.Append(s.V, elem.Elem()))
}
// Elem returns a newly allocated slice element.
//
// If the slice is []T then Elem returns a *T. This is so the created element
// can be passed directly into function wanting to populate T.
func (s SliceValue) Elem() reflect.Value {
return reflect.New(s.ElemType)
}