funcgrowslice(oldPtr unsafe.Pointer, newLen, oldCap, num int, et *_type) slice { oldLen := newLen - num if raceenabled { callerpc := getcallerpc() racereadrangepc(oldPtr, uintptr(oldLen*int(et.Size_)), callerpc, abi.FuncPCABIInternal(growslice)) } if msanenabled { msanread(oldPtr, uintptr(oldLen*int(et.Size_))) } if asanenabled { asanread(oldPtr, uintptr(oldLen*int(et.Size_))) }
if newLen < 0 { panic(errorString("growslice: len out of range")) }
if et.Size_ == 0 { // append should not create a slice with nil pointer but non-zero len. // We assume that append doesn't need to preserve oldPtr in this case. return slice{unsafe.Pointer(&zerobase), newLen, newLen} }
newcap := nextslicecap(newLen, oldCap)
var overflow bool var lenmem, newlenmem, capmem uintptr // Specialize for common values of et.Size. // For 1 we don't need any division/multiplication. // For goarch.PtrSize, compiler will optimize division/multiplication into a shift by a constant. // For powers of 2, use a variable shift. noscan := !et.Pointers() switch { case et.Size_ == 1: lenmem = uintptr(oldLen) newlenmem = uintptr(newLen) capmem = roundupsize(uintptr(newcap), noscan) overflow = uintptr(newcap) > maxAlloc newcap = int(capmem) case et.Size_ == goarch.PtrSize: lenmem = uintptr(oldLen) * goarch.PtrSize newlenmem = uintptr(newLen) * goarch.PtrSize capmem = roundupsize(uintptr(newcap)*goarch.PtrSize, noscan) overflow = uintptr(newcap) > maxAlloc/goarch.PtrSize newcap = int(capmem / goarch.PtrSize) case isPowerOfTwo(et.Size_): var shift uintptr if goarch.PtrSize == 8 { // Mask shift for better code generation. shift = uintptr(sys.TrailingZeros64(uint64(et.Size_))) & 63 } else { shift = uintptr(sys.TrailingZeros32(uint32(et.Size_))) & 31 } lenmem = uintptr(oldLen) << shift newlenmem = uintptr(newLen) << shift capmem = roundupsize(uintptr(newcap)<<shift, noscan) overflow = uintptr(newcap) > (maxAlloc >> shift) newcap = int(capmem >> shift) capmem = uintptr(newcap) << shift default: lenmem = uintptr(oldLen) * et.Size_ newlenmem = uintptr(newLen) * et.Size_ capmem, overflow = math.MulUintptr(et.Size_, uintptr(newcap)) capmem = roundupsize(capmem, noscan) newcap = int(capmem / et.Size_) capmem = uintptr(newcap) * et.Size_ }
// The check of overflow in addition to capmem > maxAlloc is needed // to prevent an overflow which can be used to trigger a segfault // on 32bit architectures with this example program: // // type T [1<<27 + 1]int64 // // var d T // var s []T // // func main() { // s = append(s, d, d, d, d) // print(len(s), "\n") // } if overflow || capmem > maxAlloc { panic(errorString("growslice: len out of range")) }
var p unsafe.Pointer if !et.Pointers() { 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. p = mallocgc(capmem, et, true) if lenmem > 0 && writeBarrier.enabled { // Only shade the pointers in oldPtr since we know the destination slice p // only contains nil pointers because it has been cleared during alloc. // // It's safe to pass a type to this function as an optimization because // from and to only ever refer to memory representing whole values of // type et. See the comment on bulkBarrierPreWrite. bulkBarrierPreWriteSrcOnly(uintptr(p), uintptr(oldPtr), lenmem-et.Size_+et.PtrBytes, et) } } memmove(p, oldPtr, lenmem)
return slice{p, newLen, newcap} }
oldPtr unsafe.Pointer:指向当前切片的底层数组的指针。
newLen int:扩展后的切片的目标长度。
oldCap int:当前切片的容量。
num int:额外需要的空间量,通常用于处理切片扩容时的新数据量。
et *_type:元素的类型,用来确定切片元素的大小和是否是指针类型(如 int、*struct)。
const threshold = 256 if oldCap < threshold { return doublecap } for { // Transition from growing 2x for small slices // to growing 1.25x for large slices. This formula // gives a smooth-ish transition between the two. newcap += (newcap + 3*threshold) >> 2
// We need to check `newcap >= newLen` and whether `newcap` overflowed. // newLen is guaranteed to be larger than zero, hence // when newcap overflows then `uint(newcap) > uint(newLen)`. // This allows to check for both with the same comparison. ifuint(newcap) >= uint(newLen) { break } }
// Set newcap to the requested cap when // the newcap calculation overflowed. if newcap <= 0 { return newLen } return newcap }