Generic slice unmarshal fn

This commit is contained in:
Thomas Pelletier
2021-11-11 09:50:06 -05:00
parent cf530eba46
commit 6430ee0bfa
4 changed files with 82 additions and 16 deletions
+11
View File
@@ -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
}
+20
View File
@@ -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,
}
}
+30
View File
@@ -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
}
+21 -16
View File
@@ -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 { func (d *decoder) unmarshalArrayFastSliceInterface(array *ast.Node, v reflect.Value) error {
ifaceType := v.Type().Elem() vt := v.Type()
sp := (*[]interface{})(unsafe.Pointer(v.UnsafeAddr())) elemType := vt.Elem()
s := *sp elemSize := elemType.Size()
if s == nil { sp := (*danger.Slice)(unsafe.Pointer(v.UnsafeAddr()))
s = make([]interface{}, 0, 16)
} else {
s = s[:0]
}
var x interface{} sp.Len = 0
it := array.Children() it := array.Children()
for it.Next() { for it.Next() {
n := it.Node() n := it.Node()
idx := len(s) idx := sp.Len
s = append(s, x)
datap := unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(&s)).Data) if sp.Len == sp.Cap {
elemP := danger.Stride(datap, unsafe.Sizeof(x), idx) c := sp.Cap
elem := reflect.NewAt(ifaceType, elemP).Elem() 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) err := d.handleValue(n, elem)
if err != nil { if err != nil {
return err return err
} }
}
*sp = s sp.Len++
}
return nil return nil
} }