diff --git a/internal/danger/danger.go b/internal/danger/danger.go index e38e113..e6a633c 100644 --- a/internal/danger/danger.go +++ b/internal/danger/danger.go @@ -63,3 +63,14 @@ func Stride(ptr unsafe.Pointer, size uintptr, offset int) unsafe.Pointer { // https://github.com/golang/go/issues/40481 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 +} diff --git a/internal/danger/slices.go b/internal/danger/slices.go new file mode 100644 index 0000000..8bce8ef --- /dev/null +++ b/internal/danger/slices.go @@ -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, + } +} diff --git a/internal/danger/slices_optimized.go b/internal/danger/slices_optimized.go new file mode 100644 index 0000000..3d79685 --- /dev/null +++ b/internal/danger/slices_optimized.go @@ -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 +} diff --git a/unmarshaler.go b/unmarshaler.go index 2385111..49f6a97 100644 --- a/unmarshaler.go +++ b/unmarshaler.go @@ -622,37 +622,42 @@ func (d *decoder) handleValue(value *ast.Node, v reflect.Value) error { } func (d *decoder) unmarshalArrayFastSliceInterface(array *ast.Node, v reflect.Value) error { - ifaceType := v.Type().Elem() + vt := v.Type() - sp := (*[]interface{})(unsafe.Pointer(v.UnsafeAddr())) - s := *sp + elemType := vt.Elem() + elemSize := elemType.Size() - if s == nil { - s = make([]interface{}, 0, 16) - } else { - s = s[:0] - } + sp := (*danger.Slice)(unsafe.Pointer(v.UnsafeAddr())) - var x interface{} + sp.Len = 0 it := array.Children() for it.Next() { n := it.Node() - idx := len(s) - s = append(s, x) + idx := sp.Len - datap := unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(&s)).Data) - elemP := danger.Stride(datap, unsafe.Sizeof(x), idx) - elem := reflect.NewAt(ifaceType, elemP).Elem() + if sp.Len == sp.Cap { + c := sp.Cap + if c == 0 { + c = 10 + } 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) if err != nil { return err } - } - *sp = s + sp.Len++ + } return nil }