Merge branch 'devel' into pr_unit_str

This commit is contained in:
ringabout
2026-04-15 12:24:31 +08:00
committed by GitHub
9 changed files with 272 additions and 27 deletions

View File

@@ -2418,6 +2418,33 @@ when notJSnotNims and hasAlloc:
when not defined(nimV2):
include "system/repr"
func setLenUninit*(s: var string, newlen: Natural) {.nodestroy.} =
## Sets the length of string `s` to `newlen`.
## New slots will not be initialized.
##
## If the new length is smaller than the new length,
## `s` will be truncated.
let n = max(newLen, 0)
when nimvm:
s.setLen(n)
else:
when notJSnotNims:
when defined(nimSeqsV2):
{.noSideEffect.}:
let str = unsafeAddr s
when defined(nimsso):
setLengthStrV3Uninit(cast[ptr SmallString](str)[], newlen)
else:
setLengthStrV2Uninit(cast[ptr NimStringV2](str)[], newlen)
else:
{.noSideEffect.}:
when hasAlloc:
setLengthStrUninit(s, newlen)
else:
s.setLen(n)
else: s.setLen(n)
when notJSnotNims and hasThreadSupport and hostOS != "standalone":
when not defined(nimPreviewSlimSystem):
include "system/channels_builtin"

View File

@@ -158,6 +158,26 @@ proc setLengthStrV2(s: var NimStringV2, newLen: int) {.compilerRtl.} =
s.p.data[newLen] = '\0'
s.len = newLen
proc setLengthStrV2Uninit(s: var NimStringV2, newLen: int) =
if newLen == 0:
discard "do not free the buffer here, pattern 's.setLen 0' is common for avoiding allocations"
else:
if isLiteral(s):
let oldP = s.p
s.p = allocPayload(newLen)
s.p.cap = newLen
if s.len > 0:
copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], min(s.len, newLen))
s.p.data[newLen] = '\0'
elif newLen > s.len:
let oldCap = s.p.cap and not strlitFlag
if newLen > oldCap:
let newCap = max(newLen, resize(oldCap))
s.p = reallocPayload0(s.p, oldCap, newCap)
s.p.cap = newCap
s.p.data[newLen] = '\0'
s.len = newLen
proc nimAsgnStrV2(a: var NimStringV2, b: NimStringV2) {.compilerRtl.} =
if a.p == b.p and a.len == b.len: return
if isLiteral(b):

View File

@@ -496,12 +496,16 @@ proc mnewString(len: int): SmallString {.compilerproc.} =
result.more = p
setSSLen(result, HeapSlen)
proc setLengthStrV2(s: var SmallString; newLen: int) {.compilerRtl.} =
## Sets the length of s to newLen, zeroing new bytes on growth.
proc setLengthStr(s: var SmallString; newLen: int; zeroing: bool) =
# Shared implementation for setLengthStrV2 (zeroing) and setLengthStrV3Uninit
# Difference between the two modes:
# - inline/medium -> long growth: alloc0 (zeroing) vs alloc (uninit)
# - long -> long growth: zeroMem the new tail (zeroing) or skip it (uninit)
let slen = ssLen(s)
let curLen = if slen > PayloadSize: s.more.fullLen else: slen
if newLen == curLen: return
if newLen <= 0:
# Pattern 's.setLen 0' is common for avoiding allocations; do NOT free the buffer.
if slen > PayloadSize:
if slen == HeapSlen and s.more.rc == 1:
s.more.fullLen = 0
@@ -517,7 +521,11 @@ proc setLengthStrV2(s: var SmallString; newLen: int) {.compilerRtl.} =
if newLen <= PayloadSize:
let inl = inlinePtr(s)
if newLen > curLen:
zeroMem(addr inl[curLen], newLen - curLen)
# Grow within inline/medium
# Bytes above newLen already zero by the SWAR invariant,
# so setSSLen is sufficient.
if zeroing:
zeroMem(addr inl[curLen], newLen - curLen)
inl[newLen] = '\0'
setSSLen(s, newLen)
else:
@@ -542,18 +550,23 @@ proc setLengthStrV2(s: var SmallString; newLen: int) {.compilerRtl.} =
else:
# grow into long
let newCap = resize(newLen)
let p = cast[ptr LongString](alloc0(LongStringDataOffset + newCap + 1))
let p = if zeroing:
# bytes [curLen..newLen] and p.data[newLen] zeroed by alloc0
cast[ptr LongString](alloc0(LongStringDataOffset + newCap + 1))
else:
let p = cast[ptr LongString](alloc(LongStringDataOffset + newCap + 1))
p.data[newLen] = '\0'
p
p.rc = 1
p.fullLen = newLen
p.capImpl = newCap
copyMem(addr p.data[0], inlinePtr(s), curLen)
# bytes [curLen..newLen] zeroed by alloc0; p.data[newLen] = '\0' by alloc0
s.more = p
setSSLen(s, HeapSlen)
else:
# currently long
if newLen <= PayloadSize:
# shrink back to inline
# shrink back to inline/medium
let old = s.more
let inl = inlinePtr(s)
copyMem(inl, addr old.data[0], newLen)
@@ -574,11 +587,19 @@ proc setLengthStrV2(s: var SmallString; newLen: int) {.compilerRtl.} =
else:
setSSLen(s, newLen)
else:
ensureUniqueLong(s, curLen, newLen)
# long -> long
ensureUniqueLong(s, curLen, newLen) # sets fullLen = newLen
if newLen > curLen:
zeroMem(addr s.more.data[curLen], newLen - curLen)
s.more.data[newLen] = '\0'
s.more.fullLen = newLen
proc setLengthStrV2(s: var SmallString; newLen: int) {.compilerRtl.} =
## Sets the length of `s` to `newLen`, zeroing new bytes on growth.
setLengthStr(s, newLen, zeroing = true)
proc setLengthStrV3Uninit(s: var SmallString; newLen: int) {.compilerRtl.} =
## Sets the length of `s` to `newLen`, NOT zeroing new bytes on growth.
setLengthStr(s, newLen, zeroing = false)
proc nimAsgnStrV2(a: var SmallString; b: SmallString) {.compilerRtl, inline.} =
if ssLen(b) <= PayloadSize:

View File

@@ -244,6 +244,31 @@ proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} =
result.len = n
result.data[n] = '\0'
proc setLengthStrUninit(s: var string, newlen: Natural) {.nodestroy.} =
## Sets the `s` length to `newlen` without zeroing memory on growth.
## Terminating zero for cstring compatibility is set.
var str = cast[NimString](s)
let n = max(newLen, 0)
if str == nil:
if n == 0: return
else:
str = rawNewStringNoInit(n)
str.data[n] = '\0'
str.len = n
s = cast[string](str)
else:
if n > str.space:
let sp = max(resize(str.space), n)
str = rawNewStringNoInit(sp)
copyMem(addr str.data[0], unsafeAddr s[0], s.len)
str.data[n] = '\0'
str.len = n
s = cast[string](str)
elif n < s.len:
str.data[n] = '\0'
str.len = n
else: return
# ----------------- sequences ----------------------------------------------
proc incrSeq(seq: PGenericSeq, elemSize, elemAlign: int): PGenericSeq {.compilerproc.} =