}
}
}
+
+type LargeSliceByte struct {
+ S []byte
+}
+
+type LargeSliceInt8 struct {
+ S []int8
+}
+
+type StringPair struct {
+ A, B string
+}
+
+type LargeSliceStruct struct {
+ S []StringPair
+}
+
+func testEncodeDecode(t *testing.T, in, out any) {
+ t.Helper()
+ var b bytes.Buffer
+ err := NewEncoder(&b).Encode(in)
+ if err != nil {
+ t.Fatal("encode:", err)
+ }
+ err = NewDecoder(&b).Decode(out)
+ if err != nil {
+ t.Fatal("decode:", err)
+ }
+ if !reflect.DeepEqual(in, out) {
+ t.Errorf("output mismatch")
+ }
+}
+
+func TestLargeSlice(t *testing.T) {
+ t.Run("byte", func(t *testing.T) {
+ t.Parallel()
+ s := make([]byte, 10<<21)
+ for i := range s {
+ s[i] = byte(i)
+ }
+ st := &LargeSliceByte{S: s}
+ rt := &LargeSliceByte{}
+ testEncodeDecode(t, st, rt)
+ })
+ t.Run("int8", func(t *testing.T) {
+ t.Parallel()
+ s := make([]int8, 10<<21)
+ for i := range s {
+ s[i] = int8(i)
+ }
+ st := &LargeSliceInt8{S: s}
+ rt := &LargeSliceInt8{}
+ testEncodeDecode(t, st, rt)
+ })
+ t.Run("struct", func(t *testing.T) {
+ t.Parallel()
+ s := make([]StringPair, 1<<21)
+ for i := range s {
+ s[i].A = string(rune(i))
+ s[i].B = s[i].A
+ }
+ st := &LargeSliceStruct{S: s}
+ rt := &LargeSliceStruct{}
+ testEncodeDecode(t, st, rt)
+ })
+}
if state.b.Len() == 0 {
errorf("decoding bool array or slice: length exceeds input size (%d elements)", length)
}
+ if i >= len(slice) {
+ // This is a slice that we only partially allocated.
+ growSlice(v, &slice, length)
+ }
slice[i] = state.decodeUint() != 0
}
return true
if state.b.Len() == 0 {
errorf("decoding complex64 array or slice: length exceeds input size (%d elements)", length)
}
+ if i >= len(slice) {
+ // This is a slice that we only partially allocated.
+ growSlice(v, &slice, length)
+ }
real := float32FromBits(state.decodeUint(), ovfl)
imag := float32FromBits(state.decodeUint(), ovfl)
slice[i] = complex(float32(real), float32(imag))
if state.b.Len() == 0 {
errorf("decoding complex128 array or slice: length exceeds input size (%d elements)", length)
}
+ if i >= len(slice) {
+ // This is a slice that we only partially allocated.
+ growSlice(v, &slice, length)
+ }
real := float64FromBits(state.decodeUint())
imag := float64FromBits(state.decodeUint())
slice[i] = complex(real, imag)
if state.b.Len() == 0 {
errorf("decoding float32 array or slice: length exceeds input size (%d elements)", length)
}
+ if i >= len(slice) {
+ // This is a slice that we only partially allocated.
+ growSlice(v, &slice, length)
+ }
slice[i] = float32(float32FromBits(state.decodeUint(), ovfl))
}
return true
if state.b.Len() == 0 {
errorf("decoding float64 array or slice: length exceeds input size (%d elements)", length)
}
+ if i >= len(slice) {
+ // This is a slice that we only partially allocated.
+ growSlice(v, &slice, length)
+ }
slice[i] = float64FromBits(state.decodeUint())
}
return true
if state.b.Len() == 0 {
errorf("decoding int array or slice: length exceeds input size (%d elements)", length)
}
+ if i >= len(slice) {
+ // This is a slice that we only partially allocated.
+ growSlice(v, &slice, length)
+ }
x := state.decodeInt()
// MinInt and MaxInt
if x < ^int64(^uint(0)>>1) || int64(^uint(0)>>1) < x {
if state.b.Len() == 0 {
errorf("decoding int16 array or slice: length exceeds input size (%d elements)", length)
}
+ if i >= len(slice) {
+ // This is a slice that we only partially allocated.
+ growSlice(v, &slice, length)
+ }
x := state.decodeInt()
if x < math.MinInt16 || math.MaxInt16 < x {
error_(ovfl)
if state.b.Len() == 0 {
errorf("decoding int32 array or slice: length exceeds input size (%d elements)", length)
}
+ if i >= len(slice) {
+ // This is a slice that we only partially allocated.
+ growSlice(v, &slice, length)
+ }
x := state.decodeInt()
if x < math.MinInt32 || math.MaxInt32 < x {
error_(ovfl)
if state.b.Len() == 0 {
errorf("decoding int64 array or slice: length exceeds input size (%d elements)", length)
}
+ if i >= len(slice) {
+ // This is a slice that we only partially allocated.
+ growSlice(v, &slice, length)
+ }
slice[i] = state.decodeInt()
}
return true
if state.b.Len() == 0 {
errorf("decoding int8 array or slice: length exceeds input size (%d elements)", length)
}
+ if i >= len(slice) {
+ // This is a slice that we only partially allocated.
+ growSlice(v, &slice, length)
+ }
x := state.decodeInt()
if x < math.MinInt8 || math.MaxInt8 < x {
error_(ovfl)
if state.b.Len() == 0 {
errorf("decoding uint array or slice: length exceeds input size (%d elements)", length)
}
+ if i >= len(slice) {
+ // This is a slice that we only partially allocated.
+ growSlice(v, &slice, length)
+ }
x := state.decodeUint()
/*TODO if math.MaxUint32 < x {
error_(ovfl)
if state.b.Len() == 0 {
errorf("decoding uint16 array or slice: length exceeds input size (%d elements)", length)
}
+ if i >= len(slice) {
+ // This is a slice that we only partially allocated.
+ growSlice(v, &slice, length)
+ }
x := state.decodeUint()
if math.MaxUint16 < x {
error_(ovfl)
if state.b.Len() == 0 {
errorf("decoding uint32 array or slice: length exceeds input size (%d elements)", length)
}
+ if i >= len(slice) {
+ // This is a slice that we only partially allocated.
+ growSlice(v, &slice, length)
+ }
x := state.decodeUint()
if math.MaxUint32 < x {
error_(ovfl)
if state.b.Len() == 0 {
errorf("decoding uint64 array or slice: length exceeds input size (%d elements)", length)
}
+ if i >= len(slice) {
+ // This is a slice that we only partially allocated.
+ growSlice(v, &slice, length)
+ }
slice[i] = state.decodeUint()
}
return true
if state.b.Len() == 0 {
errorf("decoding uintptr array or slice: length exceeds input size (%d elements)", length)
}
+ if i >= len(slice) {
+ // This is a slice that we only partially allocated.
+ growSlice(v, &slice, length)
+ }
x := state.decodeUint()
if uint64(^uintptr(0)) < x {
error_(ovfl)
}
return true
}
+
+// growSlice is called for a slice that we only partially allocated,
+// to grow it up to length.
+func growSlice[E any](v reflect.Value, ps *[]E, length int) {
+ var zero E
+ s := *ps
+ s = append(s, zero)
+ cp := cap(s)
+ if cp > length {
+ cp = length
+ }
+ s = s[:cp]
+ v.Set(reflect.ValueOf(s))
+ *ps = s
+}
errorf("bad %s slice length: %d", value.Type(), n)
}
if value.Cap() < n {
- value.Set(reflect.MakeSlice(value.Type(), n, n))
+ safe := saferio.SliceCap((*byte)(nil), uint64(n))
+ if safe < 0 {
+ errorf("%s slice too big: %d elements", value.Type(), n)
+ }
+ value.Set(reflect.MakeSlice(value.Type(), safe, safe))
+ ln := safe
+ i := 0
+ for i < n {
+ if i >= ln {
+ // We didn't allocate the entire slice,
+ // due to using saferio.SliceCap.
+ // Append a value to grow the slice.
+ // The slice is full, so this should
+ // bump up the capacity.
+ value.Set(reflect.Append(value, reflect.Zero(value.Type().Elem())))
+ }
+ // Copy into s up to the capacity or n,
+ // whichever is less.
+ ln = value.Cap()
+ if ln > n {
+ ln = n
+ }
+ value.SetLen(ln)
+ sub := value.Slice(i, ln)
+ if _, err := state.b.Read(sub.Bytes()); err != nil {
+ errorf("error decoding []byte at %d: %s", err, i)
+ }
+ i = ln
+ }
} else {
value.SetLen(n)
- }
- if _, err := state.b.Read(value.Bytes()); err != nil {
- errorf("error decoding []byte: %s", err)
+ if _, err := state.b.Read(value.Bytes()); err != nil {
+ errorf("error decoding []byte: %s", err)
+ }
}
}
if i >= ln {
// This is a slice that we only partially allocated.
// Grow it using append, up to length.
- value = reflect.Append(value, reflect.Zero(value.Type().Elem()))
+ value.Set(reflect.Append(value, reflect.Zero(value.Type().Elem())))
cp := value.Cap()
if cp > length {
cp = length