Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dc72d75f3e | |||
| f77775b59e | |||
| b52f6c9823 | |||
| 12244064bb | |||
| 6430ee0bfa | |||
| cf530eba46 |
@@ -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
|
||||||
|
|||||||
@@ -63,3 +63,14 @@ func Stride(ptr unsafe.Pointer, size uintptr, offset int) unsafe.Pointer {
|
|||||||
// https://github.com/golang/go/issues/40481
|
// https://github.com/golang/go/issues/40481
|
||||||
return unsafe.Pointer(uintptr(ptr) + uintptr(int(size)*offset))
|
return unsafe.Pointer(uintptr(ptr) + uintptr(int(size)*offset))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Slice struct {
|
||||||
|
Data unsafe.Pointer
|
||||||
|
Len int
|
||||||
|
Cap int
|
||||||
|
}
|
||||||
|
|
||||||
|
type iface struct {
|
||||||
|
typ unsafe.Pointer
|
||||||
|
ptr unsafe.Pointer
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
//go:build go1.18
|
||||||
|
// +build go1.18
|
||||||
|
|
||||||
|
package danger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExtendSlice(t reflect.Type, s *Slice, n int) Slice {
|
||||||
|
arrayType := reflect.ArrayOf(n, t.Elem())
|
||||||
|
arrayData := reflect.New(arrayType)
|
||||||
|
reflect.Copy(arrayData.Elem(), reflect.NewAt(t, unsafe.Pointer(s)).Elem())
|
||||||
|
return Slice{
|
||||||
|
Data: unsafe.Pointer(arrayData.Pointer()),
|
||||||
|
Len: s.Len,
|
||||||
|
Cap: n,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
//go:build !go1.18
|
||||||
|
// +build !go1.18
|
||||||
|
|
||||||
|
package danger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:linkname unsafe_NewArray reflect.unsafe_NewArray
|
||||||
|
func unsafe_NewArray(rtype unsafe.Pointer, length int) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname typedslicecopy reflect.typedslicecopy
|
||||||
|
//go:noescape
|
||||||
|
func typedslicecopy(elemType unsafe.Pointer, dst, src Slice) int
|
||||||
|
|
||||||
|
func ExtendSlice(t reflect.Type, s *Slice, n int) Slice {
|
||||||
|
elemTypeRef := t.Elem()
|
||||||
|
elemTypePtr := ((*iface)(unsafe.Pointer(&elemTypeRef))).ptr
|
||||||
|
|
||||||
|
d := Slice{
|
||||||
|
Data: unsafe_NewArray(elemTypePtr, n),
|
||||||
|
Len: s.Len,
|
||||||
|
Cap: n,
|
||||||
|
}
|
||||||
|
|
||||||
|
typedslicecopy(elemTypePtr, d, *s)
|
||||||
|
return d
|
||||||
|
}
|
||||||
+149
-48
@@ -11,6 +11,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/pelletier/go-toml/v2/internal/ast"
|
"github.com/pelletier/go-toml/v2/internal/ast"
|
||||||
"github.com/pelletier/go-toml/v2/internal/danger"
|
"github.com/pelletier/go-toml/v2/internal/danger"
|
||||||
@@ -620,62 +621,128 @@ func (d *decoder) handleValue(value *ast.Node, v reflect.Value) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) unmarshalArray(array *ast.Node, v reflect.Value) error {
|
type unmarshalArrayFn func(d *decoder, array *ast.Node, v reflect.Value) error
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Slice:
|
var globalUnmarshalArrayFnCache atomic.Value // map[danger.TypeID]unmarshalArrayFn
|
||||||
if v.IsNil() {
|
|
||||||
v.Set(reflect.MakeSlice(v.Type(), 0, 16))
|
func unmarshalArrayFnForSlice(vt reflect.Type) unmarshalArrayFn {
|
||||||
} else {
|
tid := danger.MakeTypeID(vt)
|
||||||
v.SetLen(0)
|
|
||||||
}
|
cache, _ := globalUnmarshalArrayFnCache.Load().(map[danger.TypeID]unmarshalArrayFn)
|
||||||
case reflect.Array:
|
fn, ok := cache[tid]
|
||||||
// arrays are always initialized
|
|
||||||
case reflect.Interface:
|
if ok {
|
||||||
elem := v.Elem()
|
return fn
|
||||||
if !elem.IsValid() {
|
|
||||||
elem = reflect.New(sliceInterfaceType).Elem()
|
|
||||||
elem.Set(reflect.MakeSlice(sliceInterfaceType, 0, 16))
|
|
||||||
} else if elem.Kind() == reflect.Slice {
|
|
||||||
if elem.Type() != sliceInterfaceType {
|
|
||||||
elem = reflect.New(sliceInterfaceType).Elem()
|
|
||||||
elem.Set(reflect.MakeSlice(sliceInterfaceType, 0, 16))
|
|
||||||
} else if !elem.CanSet() {
|
|
||||||
nelem := reflect.New(sliceInterfaceType).Elem()
|
|
||||||
nelem.Set(reflect.MakeSlice(sliceInterfaceType, elem.Len(), elem.Cap()))
|
|
||||||
reflect.Copy(nelem, elem)
|
|
||||||
elem = nelem
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err := d.unmarshalArray(array, elem)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v.Set(elem)
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
// TODO: use newDecodeError, but first the parser needs to fill
|
|
||||||
// array.Data.
|
|
||||||
return fmt.Errorf("toml: cannot store array in Go type %s", v.Kind())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
elemType := v.Type().Elem()
|
elemType := vt.Elem()
|
||||||
|
elemSize := elemType.Size()
|
||||||
|
|
||||||
it := array.Children()
|
fn = func(d *decoder, array *ast.Node, v reflect.Value) error {
|
||||||
idx := 0
|
sp := (*danger.Slice)(unsafe.Pointer(v.UnsafeAddr()))
|
||||||
for it.Next() {
|
|
||||||
n := it.Node()
|
|
||||||
|
|
||||||
// TODO: optimize
|
sp.Len = 0
|
||||||
if v.Kind() == reflect.Slice {
|
|
||||||
elem := reflect.New(elemType).Elem()
|
it := array.Children()
|
||||||
|
for it.Next() {
|
||||||
|
n := it.Node()
|
||||||
|
|
||||||
|
idx := sp.Len
|
||||||
|
|
||||||
|
if sp.Len == sp.Cap {
|
||||||
|
c := sp.Cap
|
||||||
|
if c == 0 {
|
||||||
|
c = 16
|
||||||
|
} else {
|
||||||
|
c *= 2
|
||||||
|
}
|
||||||
|
*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)
|
err := d.handleValue(n, elem)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
v.Set(reflect.Append(v, elem))
|
sp.Len++
|
||||||
} else { // array
|
}
|
||||||
|
|
||||||
|
if sp.Data == nil {
|
||||||
|
*sp = danger.ExtendSlice(vt, sp, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 unmarshalArraySliceInterface(d *decoder, array *ast.Node, v reflect.Value) error {
|
||||||
|
sp := (*danger.Slice)(unsafe.Pointer(v.UnsafeAddr()))
|
||||||
|
|
||||||
|
sp.Len = 0
|
||||||
|
|
||||||
|
var x interface{}
|
||||||
|
|
||||||
|
it := array.Children()
|
||||||
|
for it.Next() {
|
||||||
|
n := it.Node()
|
||||||
|
|
||||||
|
idx := sp.Len
|
||||||
|
|
||||||
|
if sp.Len == sp.Cap {
|
||||||
|
c := sp.Cap
|
||||||
|
if c == 0 {
|
||||||
|
c = 16
|
||||||
|
} else {
|
||||||
|
c *= 2
|
||||||
|
}
|
||||||
|
*sp = danger.ExtendSlice(sliceInterfaceType, sp, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
datap := unsafe.Pointer(sp.Data)
|
||||||
|
elemp := danger.Stride(datap, unsafe.Sizeof(x), idx)
|
||||||
|
elem := reflect.NewAt(sliceInterfaceType.Elem(), elemp).Elem()
|
||||||
|
|
||||||
|
err := d.handleValue(n, elem)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sp.Len++
|
||||||
|
}
|
||||||
|
|
||||||
|
if sp.Data == nil {
|
||||||
|
*sp = danger.ExtendSlice(sliceInterfaceType, sp, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *decoder) unmarshalArray(array *ast.Node, v reflect.Value) error {
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Slice:
|
||||||
|
fn := unmarshalArrayFnForSlice(v.Type())
|
||||||
|
return fn(d, array, v)
|
||||||
|
case reflect.Array:
|
||||||
|
// arrays are always initialized
|
||||||
|
|
||||||
|
it := array.Children()
|
||||||
|
idx := 0
|
||||||
|
for it.Next() {
|
||||||
|
n := it.Node()
|
||||||
|
|
||||||
if idx >= v.Len() {
|
if idx >= v.Len() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -686,6 +753,39 @@ func (d *decoder) unmarshalArray(array *ast.Node, v reflect.Value) error {
|
|||||||
}
|
}
|
||||||
idx++
|
idx++
|
||||||
}
|
}
|
||||||
|
case reflect.Interface:
|
||||||
|
elemIsSliceInterface := false
|
||||||
|
elem := v.Elem()
|
||||||
|
if !elem.IsValid() {
|
||||||
|
s := make([]interface{}, 0, 16)
|
||||||
|
elem = reflect.ValueOf(&s).Elem()
|
||||||
|
elemIsSliceInterface = true
|
||||||
|
} else if elem.Kind() == reflect.Slice {
|
||||||
|
if elem.Type() != sliceInterfaceType {
|
||||||
|
s := make([]interface{}, 0, 16)
|
||||||
|
elem = reflect.ValueOf(&s).Elem()
|
||||||
|
} else if !elem.CanSet() {
|
||||||
|
s := make([]interface{}, elem.Len(), elem.Cap())
|
||||||
|
nelem := reflect.ValueOf(&s).Elem()
|
||||||
|
reflect.Copy(nelem, elem)
|
||||||
|
elem = nelem
|
||||||
|
}
|
||||||
|
elemIsSliceInterface = true
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if elemIsSliceInterface {
|
||||||
|
err = unmarshalArraySliceInterface(d, array, elem)
|
||||||
|
} else {
|
||||||
|
err = d.unmarshalArray(array, elem)
|
||||||
|
}
|
||||||
|
|
||||||
|
v.Set(elem)
|
||||||
|
return err
|
||||||
|
default:
|
||||||
|
// TODO: use newDecodeError, but first the parser needs to fill
|
||||||
|
// array.Data.
|
||||||
|
return fmt.Errorf("toml: cannot store array in Go type %s", v.Kind())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -1078,9 +1178,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{}
|
||||||
@@ -1092,7 +1193,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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user