Skip to content

Commit 4c61e07

Browse files
ianlancetaylorgopherbot
authored andcommitted
encoding/gob: support large slices in slice decode helpers
The slice decode helpers weren't aware of partially allocated slices. Also add large slice support for []byte. Change-Id: I5044587e917508887c7721f8059d364189831693 Reviewed-on: https://go-review.googlesource.com/c/go/+/443777 Auto-Submit: Ian Lance Taylor <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> Reviewed-by: David Chase <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 4725c71 commit 4c61e07

File tree

3 files changed

+174
-5
lines changed

3 files changed

+174
-5
lines changed

src/encoding/gob/codec_test.go

+66
Original file line numberDiff line numberDiff line change
@@ -1527,3 +1527,69 @@ func TestErrorInvalidTypeId(t *testing.T) {
15271527
}
15281528
}
15291529
}
1530+
1531+
type LargeSliceByte struct {
1532+
S []byte
1533+
}
1534+
1535+
type LargeSliceInt8 struct {
1536+
S []int8
1537+
}
1538+
1539+
type StringPair struct {
1540+
A, B string
1541+
}
1542+
1543+
type LargeSliceStruct struct {
1544+
S []StringPair
1545+
}
1546+
1547+
func testEncodeDecode(t *testing.T, in, out any) {
1548+
t.Helper()
1549+
var b bytes.Buffer
1550+
err := NewEncoder(&b).Encode(in)
1551+
if err != nil {
1552+
t.Fatal("encode:", err)
1553+
}
1554+
err = NewDecoder(&b).Decode(out)
1555+
if err != nil {
1556+
t.Fatal("decode:", err)
1557+
}
1558+
if !reflect.DeepEqual(in, out) {
1559+
t.Errorf("output mismatch")
1560+
}
1561+
}
1562+
1563+
func TestLargeSlice(t *testing.T) {
1564+
t.Run("byte", func(t *testing.T) {
1565+
t.Parallel()
1566+
s := make([]byte, 10<<21)
1567+
for i := range s {
1568+
s[i] = byte(i)
1569+
}
1570+
st := &LargeSliceByte{S: s}
1571+
rt := &LargeSliceByte{}
1572+
testEncodeDecode(t, st, rt)
1573+
})
1574+
t.Run("int8", func(t *testing.T) {
1575+
t.Parallel()
1576+
s := make([]int8, 10<<21)
1577+
for i := range s {
1578+
s[i] = int8(i)
1579+
}
1580+
st := &LargeSliceInt8{S: s}
1581+
rt := &LargeSliceInt8{}
1582+
testEncodeDecode(t, st, rt)
1583+
})
1584+
t.Run("struct", func(t *testing.T) {
1585+
t.Parallel()
1586+
s := make([]StringPair, 1<<21)
1587+
for i := range s {
1588+
s[i].A = string(rune(i))
1589+
s[i].B = s[i].A
1590+
}
1591+
st := &LargeSliceStruct{S: s}
1592+
rt := &LargeSliceStruct{}
1593+
testEncodeDecode(t, st, rt)
1594+
})
1595+
}

src/encoding/gob/dec_helpers.go

+75
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/encoding/gob/decode.go

+33-5
Original file line numberDiff line numberDiff line change
@@ -370,12 +370,40 @@ func decUint8Slice(i *decInstr, state *decoderState, value reflect.Value) {
370370
errorf("bad %s slice length: %d", value.Type(), n)
371371
}
372372
if value.Cap() < n {
373-
value.Set(reflect.MakeSlice(value.Type(), n, n))
373+
safe := saferio.SliceCap((*byte)(nil), uint64(n))
374+
if safe < 0 {
375+
errorf("%s slice too big: %d elements", value.Type(), n)
376+
}
377+
value.Set(reflect.MakeSlice(value.Type(), safe, safe))
378+
ln := safe
379+
i := 0
380+
for i < n {
381+
if i >= ln {
382+
// We didn't allocate the entire slice,
383+
// due to using saferio.SliceCap.
384+
// Append a value to grow the slice.
385+
// The slice is full, so this should
386+
// bump up the capacity.
387+
value.Set(reflect.Append(value, reflect.Zero(value.Type().Elem())))
388+
}
389+
// Copy into s up to the capacity or n,
390+
// whichever is less.
391+
ln = value.Cap()
392+
if ln > n {
393+
ln = n
394+
}
395+
value.SetLen(ln)
396+
sub := value.Slice(i, ln)
397+
if _, err := state.b.Read(sub.Bytes()); err != nil {
398+
errorf("error decoding []byte at %d: %s", err, i)
399+
}
400+
i = ln
401+
}
374402
} else {
375403
value.SetLen(n)
376-
}
377-
if _, err := state.b.Read(value.Bytes()); err != nil {
378-
errorf("error decoding []byte: %s", err)
404+
if _, err := state.b.Read(value.Bytes()); err != nil {
405+
errorf("error decoding []byte: %s", err)
406+
}
379407
}
380408
}
381409

@@ -522,7 +550,7 @@ func (dec *Decoder) decodeArrayHelper(state *decoderState, value reflect.Value,
522550
if i >= ln {
523551
// This is a slice that we only partially allocated.
524552
// Grow it using append, up to length.
525-
value = reflect.Append(value, reflect.Zero(value.Type().Elem()))
553+
value.Set(reflect.Append(value, reflect.Zero(value.Type().Elem())))
526554
cp := value.Cap()
527555
if cp > length {
528556
cp = length

0 commit comments

Comments
 (0)