diff options
| author | Joe Tsai <joetsai@digital-static.net> | 2022-03-02 13:01:48 -0800 |
|---|---|---|
| committer | Joseph Tsai <joetsai@digital-static.net> | 2022-10-15 17:02:11 +0000 |
| commit | 61f0409c31cad8729d7982425d353d7b2ea80534 (patch) | |
| tree | 017883773a9694862745945370b7ff6630526d9e /src/runtime/slice.go | |
| parent | 9ddb8ea73724d717a9bbf44be7d585ba5587504f (diff) | |
| download | go-61f0409c31cad8729d7982425d353d7b2ea80534.tar.xz | |
reflect: add Value.Grow
The Grow method is like the proposed slices.Grow function
in that it ensures that the slice has enough capacity to append
n elements without allocating.
The implementation of Grow is a thin wrapper over runtime.growslice.
This also changes Append and AppendSlice to use growslice under the hood.
Fixes #48000
Change-Id: I992a58584a2ff1448c1c2bc0877fe76073609111
Reviewed-on: https://go-review.googlesource.com/c/go/+/389635
Run-TryBot: Joseph Tsai <joetsai@digital-static.net>
Reviewed-by: Keith Randall <khr@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Diffstat (limited to 'src/runtime/slice.go')
| -rw-r--r-- | src/runtime/slice.go | 39 |
1 files changed, 31 insertions, 8 deletions
diff --git a/src/runtime/slice.go b/src/runtime/slice.go index 284ee1f484..134d14f1a0 100644 --- a/src/runtime/slice.go +++ b/src/runtime/slice.go @@ -126,16 +126,18 @@ func mulUintptr(a, b uintptr) (uintptr, bool) { // growslice allocates new backing store for a slice. // // arguments: -// oldPtr = pointer to the slice's backing array -// newLen = new length (= oldLen + num) -// oldCap = original slice's capacity. -// num = number of elements being added -// et = element type +// +// oldPtr = pointer to the slice's backing array +// newLen = new length (= oldLen + num) +// oldCap = original slice's capacity. +// num = number of elements being added +// et = element type // // return values: -// newPtr = pointer to the new backing store -// newLen = same value as the argument -// newCap = capacity of the new backing store +// +// newPtr = pointer to the new backing store +// newLen = same value as the argument +// newCap = capacity of the new backing store // // Requires that uint(newLen) > uint(oldCap). // Assumes the original slice length is newLen - num @@ -264,6 +266,8 @@ func growslice(oldPtr unsafe.Pointer, newLen, oldCap, num int, et *_type) slice p = mallocgc(capmem, nil, false) // The append() that calls growslice is going to overwrite from oldLen to newLen. // Only clear the part that will not be overwritten. + // The reflect_growslice() that calls growslice will manually clear + // the region not cleared here. memclrNoHeapPointers(add(p, newlenmem), capmem-newlenmem) } else { // Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan uninitialized memory. @@ -279,6 +283,25 @@ func growslice(oldPtr unsafe.Pointer, newLen, oldCap, num int, et *_type) slice return slice{p, newLen, newcap} } +//go:linkname reflect_growslice reflect.growslice +func reflect_growslice(et *_type, old slice, num int) slice { + // Semantically equivalent to slices.Grow, except that the caller + // is responsible for ensuring that old.len+num > old.cap. + num -= old.cap - old.len // preserve memory of old[old.len:old.cap] + new := growslice(old.array, old.cap+num, old.cap, num, et) + // growslice does not zero out new[old.cap:new.len] since it assumes that + // the memory will be overwritten by an append() that called growslice. + // Since the caller of reflect_growslice is not append(), + // zero out this region before returning the slice to the reflect package. + if et.ptrdata == 0 { + oldcapmem := uintptr(old.cap) * et.size + newlenmem := uintptr(new.len) * et.size + memclrNoHeapPointers(add(new.array, oldcapmem), newlenmem-oldcapmem) + } + new.len = old.len // preserve the old length + return new +} + func isPowerOfTwo(x uintptr) bool { return x&(x-1) == 0 } |
