var data struct {
a []byte
}
- instr := &decInstr{decUint8Array, 6, 0, 0, ovfl}
+ instr := &decInstr{decUint8Slice, 6, 0, 0, ovfl}
state := newDecodeStateFromData(bytesResult)
execDec("bytes", instr, state, t, unsafe.Pointer(&data))
if string(data.a) != "hello" {
*(*complex128)(p) = complex(real, imag)
}
-// decUint8Array decodes byte array and stores through p a slice header
+// decUint8Slice decodes a byte slice and stores through p a slice header
// describing the data.
-// uint8 arrays are encoded as an unsigned count followed by the raw bytes.
-func decUint8Array(i *decInstr, state *decoderState, p unsafe.Pointer) {
+// uint8 slices are encoded as an unsigned count followed by the raw bytes.
+func decUint8Slice(i *decInstr, state *decoderState, p unsafe.Pointer) {
if i.indir > 0 {
if *(*unsafe.Pointer)(p) == nil {
*(*unsafe.Pointer)(p) = unsafe.Pointer(new([]uint8))
}
p = *(*unsafe.Pointer)(p)
}
- b := make([]uint8, state.decodeUint())
- state.b.Read(b)
- *(*[]uint8)(p) = b
+ n := int(state.decodeUint())
+ if n < 0 {
+ errorf("negative length decoding []byte")
+ }
+ slice := (*[]uint8)(p)
+ if cap(*slice) < n {
+ *slice = make([]uint8, n)
+ } else {
+ *slice = (*slice)[0:n]
+ }
+ if _, err := state.b.Read(*slice); err != nil {
+ errorf("error decoding []byte: %s", err)
+ }
}
// decString decodes byte array and stores through p a string header
}
p = *(*uintptr)(up)
}
- // Allocate storage for the slice elements, that is, the underlying array.
+ // Allocate storage for the slice elements, that is, the underlying array,
+ // if the existing slice does not have the capacity.
// Always write a header at p.
hdrp := (*reflect.SliceHeader)(unsafe.Pointer(p))
- hdrp.Data = uintptr(unsafe.NewArray(atyp.Elem(), n))
+ if hdrp.Cap < n {
+ hdrp.Data = uintptr(unsafe.NewArray(atyp.Elem(), n))
+ hdrp.Cap = n
+ }
hdrp.Len = n
- hdrp.Cap = n
dec.decodeArrayHelper(state, hdrp.Data, elemOp, elemWid, n, elemIndir, ovfl)
}
case reflect.Slice:
name = "element of " + name
if t.Elem().Kind() == reflect.Uint8 {
- op = decUint8Array
+ op = decUint8Slice
break
}
var elemId typeId
operation will fail.
Structs, arrays and slices are also supported. Strings and arrays of bytes are
-supported with a special, efficient representation (see below).
+supported with a special, efficient representation (see below). When a slice is
+decoded, if the existing slice has capacity the slice will be extended in place;
+if not, a new array is allocated. Regardless, the length of the resuling slice
+reports the number of elements decoded.
Functions and channels cannot be sent in a gob. Attempting
to encode a value that contains one will fail.
enc := NewEncoder(buf)
err := enc.Encode(m)
if err != nil {
- t.Errorf("gob.Encode map: %s", err)
+ t.Errorf("encode map: %s", err)
+ }
+}
+
+func TestSliceReusesMemory(t *testing.T) {
+ buf := bytes.NewBuffer(nil)
+ // Bytes
+ {
+ x := []byte("abcd")
+ enc := NewEncoder(buf)
+ err := enc.Encode(x)
+ if err != nil {
+ t.Errorf("bytes: encode: %s", err)
+ }
+ // Decode into y, which is big enough.
+ y := []byte("ABCDE")
+ addr := &y[0]
+ dec := NewDecoder(buf)
+ err = dec.Decode(&y)
+ if err != nil {
+ t.Fatal("bytes: decode:", err)
+ }
+ if !bytes.Equal(x, y) {
+ t.Errorf("bytes: expected %q got %q\n", x, y)
+ }
+ if addr != &y[0] {
+ t.Errorf("bytes: unnecessary reallocation")
+ }
+ }
+ // general slice
+ {
+ x := []int("abcd")
+ enc := NewEncoder(buf)
+ err := enc.Encode(x)
+ if err != nil {
+ t.Errorf("ints: encode: %s", err)
+ }
+ // Decode into y, which is big enough.
+ y := []int("ABCDE")
+ addr := &y[0]
+ dec := NewDecoder(buf)
+ err = dec.Decode(&y)
+ if err != nil {
+ t.Fatal("ints: decode:", err)
+ }
+ if !reflect.DeepEqual(x, y) {
+ t.Errorf("ints: expected %q got %q\n", x, y)
+ }
+ if addr != &y[0] {
+ t.Errorf("ints: unnecessary reallocation")
+ }
}
}