diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index a1fffa5d95..9cf7c01fed 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -259,7 +259,7 @@ proc readDataStr*(s: Stream, buffer: var string, slice: Slice[int]): int = result = s.readDataStrImpl(s, buffer, slice) else: # fallback - result = s.readData(beginStore(buffer, slice.b + 1 - slice.a, slice.a), slice.b + 1 - slice.a) + result = s.readData(beginStore(buffer, slice.b + 1, slice.a), slice.b + 1 - slice.a) endStore(buffer) template jsOrVmBlock(caseJsOrVm, caseElse: untyped): untyped = @@ -1226,7 +1226,7 @@ else: # after 1.3 or JS not defined jsOrVmBlock: buffer[slice.a.. s.data.len: - setLen(s.data, s.pos + bufLen) when defined(js): + if s.pos + bufLen > s.data.len: + setLen(s.data, s.pos + bufLen) try: s.data[s.pos..= start + ensuredLen` (e.g. via `newString` or `setLen`). +proc beginStore*(s: var string; newLen: int; start = 0): ptr UncheckedArray[char] {.inline, noSideEffect, raises: [], tags: [].} = + ## Sets s.len to `newLen` (new bytes are uninitialized), ensures unique + ## ownership, and returns a pointer to s[start] for bulk writing. ## Call `endStore(s)` afterwards to sync the inline cache. + ## To keep the current length, pass `s.len`. {.cast(noSideEffect).}: let ss = cast[ptr SmallString](addr s) let slen = ssLen(ss[]) - if slen > PayloadSize: - ensureUniqueLong(ss[], ss[].more.fullLen, ss[].more.fullLen) + let curLen = if slen > PayloadSize: ss[].more.fullLen else: slen + if newLen <= PayloadSize and slen <= PayloadSize: + # Stay inline/medium. + if newLen != curLen: + setSSLen(ss[], newLen) + result = cast[ptr UncheckedArray[char]](cast[uint](inlinePtr(ss[])) + uint(start)) + elif slen <= PayloadSize: + # Inline/medium → long. + let newCap = resize(newLen) + let p = cast[ptr LongString](alloc(LongStringDataOffset + newCap + 1)) + p.rc = 1 + p.fullLen = newLen + p.capImpl = newCap + copyMem(addr p.data[0], inlinePtr(ss[]), curLen) + p.data[newLen] = '\0' + ss[].more = p + setSSLen(ss[], HeapSlen) result = cast[ptr UncheckedArray[char]](addr ss[].more.data[start]) else: - result = cast[ptr UncheckedArray[char]](cast[uint](inlinePtr(ss[])) + uint(start)) + # Already long: resize within heap (no transition back to inline). + ensureUniqueLong(ss[], curLen, newLen) + ss[].more.data[newLen] = '\0' + result = cast[ptr UncheckedArray[char]](addr ss[].more.data[start]) proc endStore*(s: var string) {.inline, noSideEffect, raises: [], tags: [].} = ## Syncs the inline cache after bulk writes via `beginStore`. No-op for short/medium strings.