Use global cache to unmarshal all slice types

This commit is contained in:
Thomas Pelletier
2021-11-11 10:16:29 -05:00
parent 6430ee0bfa
commit 12244064bb
2 changed files with 55 additions and 35 deletions
-1
View File
@@ -321,7 +321,6 @@ type benchmarkDoc struct {
Key1 []int64 Key1 []int64
Key2 []string Key2 []string
Key3 [][]int64 Key3 [][]int64
// TODO: Key4 not supported by go-toml's Unmarshal
Key4 []interface{} Key4 []interface{}
Key5 []int64 Key5 []int64
Key6 []int64 Key6 []int64
+55 -34
View File
@@ -621,45 +621,71 @@ func (d *decoder) handleValue(value *ast.Node, v reflect.Value) error {
} }
} }
func (d *decoder) unmarshalArrayFastSliceInterface(array *ast.Node, v reflect.Value) error { type unmarshalArrayFn func(d *decoder, array *ast.Node, v reflect.Value) error
vt := v.Type()
var globalUnmarshalArrayFnCache atomic.Value // map[danger.TypeID]unmarshalArrayFn
func unmarshalArrayFnFor(vt reflect.Type) unmarshalArrayFn {
tid := danger.MakeTypeID(vt)
cache, _ := globalUnmarshalArrayFnCache.Load().(map[danger.TypeID]unmarshalArrayFn)
fn, ok := cache[tid]
if ok {
return fn
}
elemType := vt.Elem() elemType := vt.Elem()
elemSize := elemType.Size() elemSize := elemType.Size()
sp := (*danger.Slice)(unsafe.Pointer(v.UnsafeAddr())) fn = func(d *decoder, array *ast.Node, v reflect.Value) error {
sp := (*danger.Slice)(unsafe.Pointer(v.UnsafeAddr()))
sp.Len = 0 sp.Len = 0
it := array.Children() it := array.Children()
for it.Next() { for it.Next() {
n := it.Node() n := it.Node()
idx := sp.Len idx := sp.Len
if sp.Len == sp.Cap { if sp.Len == sp.Cap {
c := sp.Cap c := sp.Cap
if c == 0 { if c == 0 {
c = 10 c = 16
} else { } else {
c *= 2 c *= 2
}
*sp = danger.ExtendSlice(vt, sp, c)
} }
*sp = danger.ExtendSlice(vt, sp, c)
datap := unsafe.Pointer(sp.Data)
elemp := danger.Stride(datap, elemSize, idx)
elem := reflect.NewAt(elemType, elemp).Elem()
err := d.handleValue(n, elem)
if err != nil {
return err
}
sp.Len++
} }
datap := unsafe.Pointer(sp.Data) if sp.Data == nil {
elemp := danger.Stride(datap, elemSize, idx) *sp = danger.ExtendSlice(vt, sp, 0)
elem := reflect.NewAt(elemType, elemp).Elem()
err := d.handleValue(n, elem)
if err != nil {
return err
} }
sp.Len++ return nil
} }
return nil newCache := make(map[danger.TypeID]unmarshalArrayFn, len(cache)+1)
newCache[tid] = fn
for k, v := range cache {
newCache[k] = v
}
globalUnmarshalArrayFnCache.Store(newCache)
return fn
} }
func (d *decoder) unmarshalArray(array *ast.Node, v reflect.Value) error { func (d *decoder) unmarshalArray(array *ast.Node, v reflect.Value) error {
@@ -667,14 +693,8 @@ func (d *decoder) unmarshalArray(array *ast.Node, v reflect.Value) error {
switch v.Kind() { switch v.Kind() {
case reflect.Slice: case reflect.Slice:
vt = v.Type() vt = v.Type()
if vt == sliceInterfaceType { fn := unmarshalArrayFnFor(vt)
return d.unmarshalArrayFastSliceInterface(array, v) return fn(d, array, v)
}
if v.IsNil() {
v.Set(reflect.MakeSlice(v.Type(), 0, 16))
} else {
v.SetLen(0)
}
case reflect.Array: case reflect.Array:
vt = v.Type() vt = v.Type()
// arrays are always initialized // arrays are always initialized
@@ -1126,9 +1146,10 @@ var globalFieldPathsCache atomic.Value // map[danger.TypeID]fieldPathsMap
func structField(v reflect.Value, name string) (reflect.Value, bool) { func structField(v reflect.Value, name string) (reflect.Value, bool) {
t := v.Type() t := v.Type()
tid := danger.MakeTypeID(t)
cache, _ := globalFieldPathsCache.Load().(map[danger.TypeID]fieldPathsMap) cache, _ := globalFieldPathsCache.Load().(map[danger.TypeID]fieldPathsMap)
fieldPaths, ok := cache[danger.MakeTypeID(t)] fieldPaths, ok := cache[tid]
if !ok { if !ok {
fieldPaths = map[string][]int{} fieldPaths = map[string][]int{}
@@ -1140,7 +1161,7 @@ func structField(v reflect.Value, name string) (reflect.Value, bool) {
}) })
newCache := make(map[danger.TypeID]fieldPathsMap, len(cache)+1) newCache := make(map[danger.TypeID]fieldPathsMap, len(cache)+1)
newCache[danger.MakeTypeID(t)] = fieldPaths newCache[tid] = fieldPaths
for k, v := range cache { for k, v := range cache {
newCache[k] = v newCache[k] = v
} }