github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/runtime/slice.go (about) 1 package runtime 2 3 // This file implements compiler builtins for slices: append() and copy(). 4 5 import ( 6 "unsafe" 7 ) 8 9 // Builtin append(src, elements...) function: append elements to src and return 10 // the modified (possibly expanded) slice. 11 func sliceAppend(srcBuf, elemsBuf unsafe.Pointer, srcLen, srcCap, elemsLen uintptr, elemSize uintptr) (unsafe.Pointer, uintptr, uintptr) { 12 if elemsLen == 0 { 13 // Nothing to append, return the input slice. 14 return srcBuf, srcLen, srcCap 15 } 16 17 if srcLen+elemsLen > srcCap { 18 // Slice does not fit, allocate a new buffer that's large enough. 19 srcCap = srcCap * 2 20 if srcCap == 0 { // e.g. zero slice 21 srcCap = 1 22 } 23 for srcLen+elemsLen > srcCap { 24 // This algorithm may be made more memory-efficient: don't multiply 25 // by two but by 1.5 or something. As far as I can see, that's 26 // allowed by the Go language specification (but may be observed by 27 // programs). 28 srcCap *= 2 29 } 30 buf := alloc(srcCap*elemSize, nil) 31 32 // Copy the old slice to the new slice. 33 if srcLen != 0 { 34 memmove(buf, srcBuf, srcLen*elemSize) 35 } 36 srcBuf = buf 37 } 38 39 // The slice fits (after possibly allocating a new one), append it in-place. 40 memmove(unsafe.Add(srcBuf, srcLen*elemSize), elemsBuf, elemsLen*elemSize) 41 return srcBuf, srcLen + elemsLen, srcCap 42 } 43 44 // Builtin copy(dst, src) function: copy bytes from dst to src. 45 func sliceCopy(dst, src unsafe.Pointer, dstLen, srcLen uintptr, elemSize uintptr) int { 46 // n = min(srcLen, dstLen) 47 n := srcLen 48 if n > dstLen { 49 n = dstLen 50 } 51 memmove(dst, src, n*elemSize) 52 return int(n) 53 } 54 55 // sliceGrow returns a new slice with space for at least newCap elements 56 func sliceGrow(oldBuf unsafe.Pointer, oldLen, oldCap, newCap, elemSize uintptr) (unsafe.Pointer, uintptr, uintptr) { 57 58 // TODO(dgryski): sliceGrow() and sliceAppend() should be refactored to share the base growth code. 59 60 if oldCap >= newCap { 61 // No need to grow, return the input slice. 62 return oldBuf, oldLen, oldCap 63 } 64 65 // allow nil slice 66 if oldCap == 0 { 67 oldCap++ 68 } 69 70 // grow capacity 71 for oldCap < newCap { 72 oldCap *= 2 73 } 74 75 buf := alloc(oldCap*elemSize, nil) 76 if oldLen > 0 { 77 // copy any data to new slice 78 memmove(buf, oldBuf, oldLen*elemSize) 79 } 80 81 return buf, oldLen, oldCap 82 }