diff --git a/unmarshaler.go b/unmarshaler.go index 1307a337..9c7ef526 100644 --- a/unmarshaler.go +++ b/unmarshaler.go @@ -688,21 +688,78 @@ func unmarshalArrayFnForSlice(vt reflect.Type) unmarshalArrayFn { 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 { - var vt reflect.Type switch v.Kind() { case reflect.Slice: - vt = v.Type() - fn := unmarshalArrayFnForSlice(vt) + fn := unmarshalArrayFnForSlice(v.Type()) return fn(d, array, v) case reflect.Array: - vt = v.Type() // arrays are always initialized + + it := array.Children() + idx := 0 + for it.Next() { + n := it.Node() + + if idx >= v.Len() { + return nil + } + elem := v.Index(idx) + err := d.handleValue(n, elem) + if err != nil { + return err + } + 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) @@ -713,49 +770,24 @@ func (d *decoder) unmarshalArray(array *ast.Node, v reflect.Value) error { reflect.Copy(nelem, elem) elem = nelem } + elemIsSliceInterface = true } - err := d.unmarshalArray(array, elem) - if err != nil { - return err + + var err error + if elemIsSliceInterface { + err = unmarshalArraySliceInterface(d, array, elem) + } else { + err = d.unmarshalArray(array, elem) } + v.Set(elem) - return nil + 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()) } - elemType := vt.Elem() - - it := array.Children() - idx := 0 - for it.Next() { - n := it.Node() - - // TODO: optimize - if v.Kind() == reflect.Slice { - elem := reflect.New(elemType).Elem() - - err := d.handleValue(n, elem) - if err != nil { - return err - } - - v.Set(reflect.Append(v, elem)) - } else { // array - if idx >= v.Len() { - return nil - } - elem := v.Index(idx) - err := d.handleValue(n, elem) - if err != nil { - return err - } - idx++ - } - } - return nil }