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  }