-
Notifications
You must be signed in to change notification settings - Fork 1
/
path.go
144 lines (126 loc) · 2.61 KB
/
path.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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package structology
import (
"fmt"
"github.com/viant/xunsafe"
"reflect"
"unsafe"
)
type (
path struct {
field *xunsafe.Field
kind reflect.Kind
slice *xunsafe.Slice
marker *Marker
isPtr bool
converter *converter
}
paths []*path
pathOptions struct {
indexes []int
indexPos int
withMarkerSet bool
//keys []string once we add map support
}
PathOption func(o *pathOptions)
)
func (p paths) useSlice() bool {
for _, aPath := range p {
if aPath.slice != nil {
return true
}
}
return false
}
func (o *pathOptions) index() int {
if o == nil {
return 0
}
if o.hasIndex() {
return o.nextIndex()
}
return 0
}
func (o *pathOptions) hasIndex() bool {
if o == nil {
return false
}
return o.indexPos < len(o.indexes)
}
func (o *pathOptions) nextIndex() int {
ret := o.indexes[o.indexPos]
o.indexPos++
return ret
}
func (o *pathOptions) shallSetMarker() bool {
if o == nil {
return false
}
return o.withMarkerSet
}
func newPathOptions(opts []PathOption) *pathOptions {
var result = &pathOptions{}
for _, opt := range opts {
opt(result)
}
return result
}
func (p *path) setSliceItem(holderPtr unsafe.Pointer, value interface{}, options *pathOptions) {
if p.field != nil {
holderPtr = p.field.Pointer(holderPtr)
}
idx := options.index()
p.slice.SetValueAt(holderPtr, idx, value)
}
func (p paths) upstream(ptr unsafe.Pointer, options *pathOptions) (unsafe.Pointer, *path) {
count := len(p)
if count == 1 {
return ptr, p[0]
}
for i := 0; i < count-1; i++ {
ptr = p[i].pointer(ptr, options)
}
leaf := p[count-1]
return ptr, leaf
}
func (p *path) setMarker(ptr unsafe.Pointer) error {
if !p.ensureMarker(ptr) {
return nil
}
return p.marker.Set(ptr, int(p.field.Index), true)
}
func (p *path) ensureMarker(ptr unsafe.Pointer) bool {
if p.marker == nil {
return false
}
p.marker.EnsureHolder(ptr)
return true
}
func (p *path) pointer(parent unsafe.Pointer, options *pathOptions) unsafe.Pointer {
ptr := parent
if xField := p.field; xField != nil {
ptr = xField.Pointer(ptr)
}
if p.slice != nil {
ptr = p.item(ptr, options)
}
if p.isPtr {
ptr = xunsafe.DerefPointer(ptr)
}
if options.shallSetMarker() {
_ = p.setMarker(parent)
}
return ptr
}
func (p *path) item(ptr unsafe.Pointer, options *pathOptions) unsafe.Pointer {
sliceLen := p.slice.Len(ptr)
index := options.index()
if index >= sliceLen {
panic(fmt.Sprintf("IndexOutOfRange: %v, len: %v", index, sliceLen))
}
return p.slice.PointerAt(ptr, uintptr(index))
}
func WithPathIndex(indexes ...int) PathOption {
return func(o *pathOptions) {
o.indexes = indexes
}
}