diff --git a/lib/system/strs_v2.nim b/lib/system/strs_v2.nim index 14db5d7e64..b64684e298 100644 --- a/lib/system/strs_v2.nim +++ b/lib/system/strs_v2.nim @@ -34,53 +34,72 @@ template frees(s) = else: dealloc(s.p) +template allocPayload(newLen: int): ptr NimStrPayload = + when compileOption("threads"): + cast[ptr NimStrPayload](allocShared(contentSize(newLen))) + else: + cast[ptr NimStrPayload](alloc(contentSize(newLen))) + +template allocPayload0(newLen: int): ptr NimStrPayload = + when compileOption("threads"): + cast[ptr NimStrPayload](allocShared0(contentSize(newLen))) + else: + cast[ptr NimStrPayload](alloc0(contentSize(newLen))) + +template reallocPayload(p: pointer, newLen: int): ptr NimStrPayload = + when compileOption("threads"): + cast[ptr NimStrPayload](reallocShared(p, contentSize(newLen))) + else: + cast[ptr NimStrPayload](realloc(p, contentSize(newLen))) + +template reallocPayload0(p: pointer; oldLen, newLen: int): ptr NimStrPayload = + when compileOption("threads"): + cast[ptr NimStrPayload](reallocShared0(p, contentSize(oldLen), contentSize(newLen))) + else: + cast[ptr NimStrPayload](realloc0(p, contentSize(oldLen), contentSize(newLen))) + proc resize(old: int): int {.inline.} = if old <= 0: result = 4 elif old < 65536: result = old * 2 else: result = old * 3 div 2 # for large arrays * 3/2 is better -proc prepareAdd(s: var NimStringV2; addlen: int) {.compilerRtl.} = - let newLen = s.len + addlen +proc prepareAdd(s: var NimStringV2; addLen: int) {.compilerRtl.} = + let newLen = s.len + addLen if isLiteral(s): let oldP = s.p # can't mutate a literal, so we need a fresh copy here: - when compileOption("threads"): - s.p = cast[ptr NimStrPayload](allocShared0(contentSize(newLen))) - else: - s.p = cast[ptr NimStrPayload](alloc0(contentSize(newLen))) + s.p = allocPayload(newLen) s.p.cap = newLen if s.len > 0: # we are about to append, so there is no need to copy the \0 terminator: copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], min(s.len, newLen)) + elif oldP == nil: + # In the case of `newString(0) & ""`, since `src.len == 0`, `appendString` + # will not set the `\0` terminator, so we set it here. + s.p.data[0] = '\0' else: let oldCap = s.p.cap and not strlitFlag if newLen > oldCap: let newCap = max(newLen, resize(oldCap)) - when compileOption("threads"): - s.p = cast[ptr NimStrPayload](reallocShared0(s.p, contentSize(oldCap), contentSize(newCap))) - else: - s.p = cast[ptr NimStrPayload](realloc0(s.p, contentSize(oldCap), contentSize(newCap))) + s.p = reallocPayload(s.p, newCap) s.p.cap = newCap + if newLen < newCap: + zeroMem(cast[pointer](addr s.p.data[newLen+1]), newCap - newLen) proc nimAddCharV1(s: var NimStringV2; c: char) {.compilerRtl, inline.} = #if (s.p == nil) or (s.len+1 > s.p.cap and not strlitFlag): prepareAdd(s, 1) s.p.data[s.len] = c - s.p.data[s.len+1] = '\0' inc s.len + s.p.data[s.len] = '\0' proc toNimStr(str: cstring, len: int): NimStringV2 {.compilerproc.} = if len <= 0: result = NimStringV2(len: 0, p: nil) else: - when compileOption("threads"): - var p = cast[ptr NimStrPayload](allocShared0(contentSize(len))) - else: - var p = cast[ptr NimStrPayload](alloc0(contentSize(len))) + var p = allocPayload(len) p.cap = len - if len > 0: - # we are about to append, so there is no need to copy the \0 terminator: - copyMem(unsafeAddr p.data[0], str, len) + copyMem(unsafeAddr p.data[0], str, len+1) result = NimStringV2(len: len, p: p) proc cstrToNimstr(str: cstring): NimStringV2 {.compilerRtl.} = @@ -99,18 +118,15 @@ proc appendString(dest: var NimStringV2; src: NimStringV2) {.compilerproc, inlin proc appendChar(dest: var NimStringV2; c: char) {.compilerproc, inline.} = dest.p.data[dest.len] = c - dest.p.data[dest.len+1] = '\0' inc dest.len + dest.p.data[dest.len] = '\0' proc rawNewString(space: int): NimStringV2 {.compilerproc.} = # this is also 'system.newStringOfCap'. if space <= 0: result = NimStringV2(len: 0, p: nil) else: - when compileOption("threads"): - var p = cast[ptr NimStrPayload](allocShared(contentSize(space))) - else: - var p = cast[ptr NimStrPayload](alloc(contentSize(space))) + var p = allocPayload(space) p.cap = space p.data[0] = '\0' result = NimStringV2(len: 0, p: p) @@ -119,10 +135,7 @@ proc mnewString(len: int): NimStringV2 {.compilerproc.} = if len <= 0: result = NimStringV2(len: 0, p: nil) else: - when compileOption("threads"): - var p = cast[ptr NimStrPayload](allocShared0(contentSize(len))) - else: - var p = cast[ptr NimStrPayload](alloc0(contentSize(len))) + var p = allocPayload0(len) p.cap = len result = NimStringV2(len: len, p: p) @@ -130,8 +143,25 @@ proc setLengthStrV2(s: var NimStringV2, newLen: int) {.compilerRtl.} = if newLen == 0: discard "do not free the buffer here, pattern 's.setLen 0' is common for avoiding allocations" else: - if newLen > s.len or isLiteral(s): - prepareAdd(s, newLen - s.len) + 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)) + if newLen > s.len: + zeroMem(cast[pointer](addr s.p.data[s.len]), newLen - s.len + 1) + else: + s.p.data[newLen] = '\0' + else: + zeroMem(cast[pointer](addr s.p.data[0]), newLen + 1) + 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 @@ -148,10 +178,7 @@ proc nimAsgnStrV2(a: var NimStringV2, b: NimStringV2) {.compilerRtl.} = # 'let y = newStringOfCap(); var x = y' # on the other hand... These get turned into moves now. frees(a) - when compileOption("threads"): - a.p = cast[ptr NimStrPayload](allocShared0(contentSize(b.len))) - else: - a.p = cast[ptr NimStrPayload](alloc0(contentSize(b.len))) + a.p = allocPayload(b.len) a.p.cap = b.len a.len = b.len copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1) @@ -159,10 +186,7 @@ proc nimAsgnStrV2(a: var NimStringV2, b: NimStringV2) {.compilerRtl.} = proc nimPrepareStrMutationImpl(s: var NimStringV2) = let oldP = s.p # can't mutate a literal, so we need a fresh copy here: - when compileOption("threads"): - s.p = cast[ptr NimStrPayload](allocShared0(contentSize(s.len))) - else: - s.p = cast[ptr NimStrPayload](alloc0(contentSize(s.len))) + s.p = allocPayload(s.len) s.p.cap = s.len copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len+1)