@@ -48,18 +48,30 @@ else:
4848 cast [NimString ](newObjNoInit (addr (strDesc), size))
4949
5050proc 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
5759proc 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
6269proc 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+
94112proc cstrToNimstr (str: cstring ): NimString {.compilerRtl .} =
95113 if str == nil : NimString (nil )
96114 else : toNimStr (str, str.len)
97115
98116proc 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-
117128proc 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
141152proc 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
195187proc 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
215203proc 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