diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index 9ecdffb669..c879558dd0 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -264,7 +264,7 @@ proc setLengthStrUninit(s: var string, newlen: Natural) {.nodestroy.} = str.data[n] = '\0' str.len = n s = cast[string](str) - elif n < s.len: + elif n != s.len: str.data[n] = '\0' str.len = n else: return diff --git a/tests/stdlib/tstring.nim b/tests/stdlib/tstring.nim index 724eef4314..fad3865085 100644 --- a/tests/stdlib/tstring.nim +++ b/tests/stdlib/tstring.nim @@ -176,6 +176,13 @@ proc main() = result.add char(ord('a') + i mod 26) proc checkSetLenUninit(oldLen, newLen: int; cmpAfter = -1) = + ## Verifies `setLenUninit`: + ## - preserves the existing prefix + ## - updates the string length + ## - keeps internal null termination valid for both shrink and growth + ## + ## `cmpAfter` is used for layouts where trailing zeroed padding affects + ## string comparison semantics after the resize. var s = makeStr(oldLen) let prefixLen = min(oldLen, newLen) let prefix = makeStr(prefixLen) @@ -203,9 +210,18 @@ proc main() = block setLenUninit: # Shared baseline for both SSO and V2: noop, shrink, grow. - checkSetLenUninit(numbers.len, numbers.len) - checkSetLenUninit(numbers.len, 5) - checkSetLenUninit(numbers.len, 11) + checkSetLenUninit(10, 10) + checkSetLenUninit(10, 5) + checkSetLenUninit(10, 11) + + block growingWithinBiggerCapacity: + # Strings can reserve spare capacity even for short strings. + # Growing within that capacity must still update len and the trailing zero. + var s = newStringOfCap(10) + s.add("abc") + s.setLenUninit(6) + s.checkStrInternals(6) + doAssert s[0..2] == "abc" when hasNativeSso: const