From 3c7a97674be546bac0f49c829367894136e5c3e0 Mon Sep 17 00:00:00 2001 From: araq Date: Sun, 8 Mar 2026 20:26:41 +0100 Subject: [PATCH] now with newStringUninit for SSO --- lib/system.nim | 31 ++++++++++++++++++------------- lib/system/strs_v3.nim | 27 +++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index bf40b333cf..5c23bf7bc8 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1747,16 +1747,21 @@ when not defined(js): else: {.error: "The type T cannot contain managed memory or have destructors".} - when not defined(nimsso): - proc newStringUninit*(len: Natural): string {.noSideEffect.} = - ## Returns a new string of length `len` but with uninitialized - ## content. One needs to fill the string character after character - ## with the index operator `s[i]`. - ## - ## This procedure exists only for optimization purposes; - ## the same effect can be achieved with the `&` operator or with `add`. - when nimvm: - result = newString(len) + when defined(nimsso) and not declared(newStringUninitWasDeclared): + proc newStringUninitImpl(len: Natural): string {.noSideEffect, inline.} + + proc newStringUninit*(len: Natural): string {.noSideEffect.} = + ## Returns a new string of length `len` but with uninitialized + ## content. One needs to fill the string character after character + ## with the index operator `s[i]`. + ## + ## This procedure exists only for optimization purposes; + ## the same effect can be achieved with the `&` operator or with `add`. + when nimvm: + result = newString(len) + else: + when defined(nimsso): + result = newStringUninitImpl(len) else: result = newStringOfCap(len) {.cast(noSideEffect).}: @@ -1769,9 +1774,6 @@ when not defined(js): let s = cast[NimString](result) s.len = len s.data[len] = '\0' -else: - proc newStringUninit*(len: Natural): string {. - magic: "NewString", importc: "mnewString", noSideEffect.} {.pop.} @@ -3176,3 +3178,6 @@ when hostOS == "standalone": # ssymbols being duplicated. proc nimPanic(s: string) {.exportc, noreturn.} = panic(s) proc nimRawoutput(s: string) {.exportc.} = rawoutput(s) + +when not declared(newStringUninitWasDeclared): + proc newStringUninitImpl(len: Natural): string {.noSideEffect, inline.} = discard diff --git a/lib/system/strs_v3.nim b/lib/system/strs_v3.nim index 1475bad738..05fbd7a077 100644 --- a/lib/system/strs_v3.nim +++ b/lib/system/strs_v3.nim @@ -509,6 +509,33 @@ proc nimStrData(s: var SmallString): ptr UncheckedArray[char] {.compilerproc, in if slen > PayloadSize: cast[ptr UncheckedArray[char]](addr s.more.data[0]) else: cast[ptr UncheckedArray[char]](addr s.payload[0]) +const + newStringUninitWasDeclared = true + +proc newStringUninitImpl(len: Natural): string {.noSideEffect, inline.} = + ## Returns a new string of length `len` but with uninitialized content. + ## One needs to fill the string character after character + ## with the index operator `s[i]`. + ## + ## This procedure exists only for optimization purposes; + ## the same effect can be achieved with the `&` operator or with `add`. + when nimvm: + result = newString(len) + else: + result = newStringOfCap(len) # rawNewString: alloc (not alloc0) for long strings + {.cast(noSideEffect).}: + if len > 0: + let s = cast[ptr SmallString](addr result) + if len <= PayloadSize: + s.slen = byte(len) + # Null-terminate; bytes [0..len-1] left uninitialized for caller to fill. + cast[ptr UncheckedArray[char]](addr s.payload[0])[len] = '\0' + else: + # rawNewString allocated with alloc (not alloc0), so data[0..len-1] is + # intentionally uninitialized. Caller fills it and calls completeStore. + s.more.fullLen = len + s.more.data[len] = '\0' + proc completeStore(s: var SmallString) {.compilerproc, inline.} = ## Must be called after bulk data has been written directly into the string buffer ## via a raw pointer obtained from `nimStrData`/`nimStrAtMutV3` (e.g. `readBuffer`,