Skip to content

Commit b539adf

Browse files
authored
std: sysstr cleanup, add docs (#25180)
- Removed redundant `len` and `reserved` sets already performed by prior `rawNewStringNoInit` calls. - Reuse `appendChar` - Removed never used `newOwnedString` - Added internal `toOwnedCopy` - Documents differences in impls of internal procs used for `system.string.setLen`: + `strs_v2.setLengthStrV2`: - does not set the terminating zero byte when new length is 0 - does not handle negative new length + `sysstr.setLengthStr`: - sets the terminating zero byte when new length is 0 - bounds negative new length to 0
1 parent 01c0840 commit b539adf

File tree

2 files changed

+68
-57
lines changed

2 files changed

+68
-57
lines changed

lib/system/strs_v2.nim

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ proc mnewString(len: int): NimStringV2 {.compilerproc.} =
141141
result = NimStringV2(len: len, p: p)
142142

143143
proc setLengthStrV2(s: var NimStringV2, newLen: int) {.compilerRtl.} =
144+
## Sets the `s` length to `newLen` zeroing memory on growth.
145+
## Terminating zero at `s[newLen]` for cstring compatibility is set
146+
## on length change, **excluding** `newLen == 0`.
147+
## Negative `newLen` is **not** bound to zero.
144148
if newLen == 0:
145149
discard "do not free the buffer here, pattern 's.setLen 0' is common for avoiding allocations"
146150
else:

lib/system/sysstr.nim

Lines changed: 64 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -48,18 +48,30 @@ else:
4848
cast[NimString](newObjNoInit(addr(strDesc), size))
4949

5050
proc rawNewStringNoInit(space: int): NimString =
51+
## Returns a newly-allocated NimString with `reserved` set.
52+
## .. warning:: `len` and the terminating null-byte are not set!
5153
let s = max(space, 7)
5254
result = allocStrNoInit(sizeof(TGenericSeq) + s + 1)
5355
result.reserved = s
5456
when defined(gogc):
5557
result.elemSize = 1
5658

5759
proc rawNewString(space: int): NimString {.compilerproc.} =
60+
## Returns a newly-allocated and *not* zeroed NimString
61+
## with everything required set:
62+
## - `reserved`
63+
## - `len` (0)
64+
## - terminating null-byte
5865
result = rawNewStringNoInit(space)
5966
result.len = 0
6067
result.data[0] = '\0'
6168

6269
proc mnewString(len: int): NimString {.compilerproc.} =
70+
## Returns a newly-allocated and zeroed NimString
71+
## with everything required set:
72+
## - `reserved`
73+
## - `len`
74+
## - terminating null-byte
6375
result = rawNewStringNoInit(len)
6476
result.len = len
6577
zeroMem(addr result.data[0], len + 1)
@@ -91,29 +103,28 @@ proc toNimStr(str: cstring, len: int): NimString {.compilerproc.} =
91103
copyMem(addr(result.data), str, len)
92104
result.data[len] = '\0'
93105

106+
proc toOwnedCopy(src: NimString): NimString {.inline.} =
107+
## Expects `src` to be not nil and initialized (len and terminating zero set)
108+
result = rawNewStringNoInit(src.len)
109+
result.len = src.len
110+
copyMem(addr(result.data), addr(src.data), src.len + 1)
111+
94112
proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} =
95113
if str == nil: NimString(nil)
96114
else: toNimStr(str, str.len)
97115

98116
proc copyString(src: NimString): NimString {.compilerRtl.} =
117+
## Expects `src` to be initialized (len and terminating zero set)
99118
if src != nil:
100119
if (src.reserved and seqShallowFlag) != 0:
101120
result = src
102121
else:
103-
result = rawNewStringNoInit(src.len)
104-
result.len = src.len
105-
copyMem(addr(result.data), addr(src.data), src.len + 1)
122+
result = toOwnedCopy(src)
106123
sysAssert((seqShallowFlag and result.reserved) == 0, "copyString")
107124
when defined(nimShallowStrings):
108125
if (src.reserved and strlitFlag) != 0:
109126
result.reserved = (result.reserved and not strlitFlag) or seqShallowFlag
110127

111-
proc newOwnedString(src: NimString; n: int): NimString =
112-
result = rawNewStringNoInit(n)
113-
result.len = n
114-
copyMem(addr(result.data), addr(src.data), n)
115-
result.data[n] = '\0'
116-
117128
proc copyStringRC1(src: NimString): NimString {.compilerRtl.} =
118129
if src != nil:
119130
if (src.reserved and seqShallowFlag) != 0:
@@ -129,39 +140,20 @@ proc copyStringRC1(src: NimString): NimString {.compilerRtl.} =
129140
result.reserved = s
130141
when defined(gogc):
131142
result.elemSize = 1
143+
result.len = src.len
144+
copyMem(addr(result.data), addr(src.data), src.len + 1)
132145
else:
133-
result = rawNewStringNoInit(src.len)
134-
result.len = src.len
135-
copyMem(addr(result.data), addr(src.data), src.len + 1)
146+
result = toOwnedCopy(src)
136147
sysAssert((seqShallowFlag and result.reserved) == 0, "copyStringRC1")
137148
when defined(nimShallowStrings):
138149
if (src.reserved and strlitFlag) != 0:
139150
result.reserved = (result.reserved and not strlitFlag) or seqShallowFlag
140151

141152
proc copyDeepString(src: NimString): NimString {.inline.} =
142153
if src != nil:
143-
result = rawNewStringNoInit(src.len)
144-
result.len = src.len
145-
copyMem(addr(result.data), addr(src.data), src.len + 1)
154+
result = toOwnedCopy(src)
146155

147-
proc addChar(s: NimString, c: char): NimString =
148-
# is compilerproc!
149-
if s == nil:
150-
result = rawNewStringNoInit(1)
151-
result.len = 0
152-
else:
153-
result = s
154-
if result.len >= result.space:
155-
let r = resize(result.space)
156-
result = rawNewStringNoInit(r)
157-
result.len = s.len
158-
copyMem(addr result.data[0], unsafeAddr(s.data[0]), s.len+1)
159-
result.reserved = r
160-
result.data[result.len] = c
161-
result.data[result.len+1] = '\0'
162-
inc(result.len)
163-
164-
# These routines should be used like following:
156+
# The following resize- and append- routines should be used like following:
165157
# <Nim code>
166158
# s &= "Hello " & name & ", how do you feel?"
167159
#
@@ -193,46 +185,61 @@ proc addChar(s: NimString, c: char): NimString =
193185
# s = rawNewString(0);
194186

195187
proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} =
188+
## Prepares `dest` for appending up to `addlen` new bytes.
189+
## .. warning:: Does not update `len`!
196190
if dest == nil:
197-
result = rawNewString(addlen)
198-
elif dest.len + addlen <= dest.space:
191+
return rawNewString(addlen)
192+
let futureLen = dest.len + addlen
193+
if futureLen <= dest.space:
199194
result = dest
200195
else: # slow path:
201-
let sp = max(resize(dest.space), dest.len + addlen)
196+
# growth strategy: next `resize` step or exact `futureLen` if jumping over
197+
let sp = max(resize(dest.space), futureLen)
202198
result = rawNewStringNoInit(sp)
203199
result.len = dest.len
204-
copyMem(addr result.data[0], unsafeAddr(dest.data[0]), dest.len+1)
205-
result.reserved = sp
206-
#result = rawNewString(sp)
207-
#copyMem(result, dest, dest.len + sizeof(TGenericSeq))
208-
# DO NOT UPDATE LEN YET: dest.len = newLen
209-
210-
proc appendString(dest, src: NimString) {.compilerproc, inline.} =
211-
if src != nil:
212-
copyMem(addr(dest.data[dest.len]), addr(src.data), src.len + 1)
213-
inc(dest.len, src.len)
200+
# newFutureLen > space => addlen is never zero, copy terminating null anyway
201+
copyMem(addr(result.data), addr(dest.data), dest.len + 1)
214202

215203
proc appendChar(dest: NimString, c: char) {.compilerproc, inline.} =
216204
dest.data[dest.len] = c
217205
dest.data[dest.len+1] = '\0'
218206
inc(dest.len)
219207

220-
proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} =
221-
let n = max(newLen, 0)
208+
proc addChar(s: NimString, c: char): NimString =
209+
# is compilerproc! used in `ccgexprs.nim`
222210
if s == nil:
223-
if n == 0:
224-
return s
225-
else:
226-
result = mnewString(n)
227-
elif n <= s.space:
211+
result = rawNewStringNoInit(1)
212+
result.len = 0
213+
else:
228214
result = s
215+
if s.len >= s.space: # len.inc would overflow (`>` just in case)
216+
let sp = resize(s.space)
217+
result = rawNewStringNoInit(sp)
218+
copyMem(addr(result.data), addr(s.data), s.len)
219+
result.len = s.len
220+
result.appendChar(c)
221+
222+
proc appendString(dest, src: NimString) {.compilerproc, inline.} =
223+
## Raw, does not prepare `dest` space for copying
224+
if src != nil:
225+
copyMem(addr(dest.data[dest.len]), addr(src.data), src.len + 1)
226+
inc(dest.len, src.len)
227+
228+
proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} =
229+
## Sets the `s` length to `newLen` zeroing memory on growth.
230+
## Terminating zero at `s[newLen]` for cstring compatibility is set
231+
## on any length change, including `newLen == 0`.
232+
## Negative `newLen` is bound to zero.
233+
let n = max(newLen, 0)
234+
if s == nil: # early return check
235+
return if n == 0: s else: mnewString(n) # sets everything required
236+
if n <= s.space:
237+
result = s # len and null-byte still need updating
229238
else:
230239
let sp = max(resize(s.space), n)
231-
result = rawNewStringNoInit(sp)
232-
result.len = s.len
233-
copyMem(addr result.data[0], unsafeAddr(s.data[0]), s.len)
240+
result = rawNewStringNoInit(sp) # len and null-byte not set
241+
copyMem(addr(result.data), addr(s.data), s.len)
234242
zeroMem(addr result.data[s.len], n - s.len)
235-
result.reserved = sp
236243
result.len = n
237244
result.data[n] = '\0'
238245

0 commit comments

Comments
 (0)