more progress on destructor based strings

This commit is contained in:
Andreas Rumpf
2018-07-13 21:15:47 +02:00
parent 5b59852406
commit 74bf316619
16 changed files with 279 additions and 244 deletions

View File

@@ -14,28 +14,15 @@ when false:
#proc rawNewStringNoInit(space: int): NimString {.compilerProc.}
# seems to be unused.
proc rawNewString(space: int): NimString {.compilerProc.}
proc mnewString(len: int): NimString {.compilerProc.}
proc copyStrLast(s: NimString, start, last: int): NimString {.compilerProc.}
proc nimToCStringConv(s: NimString): cstring {.compilerProc, inline.}
proc copyStr(s: NimString, start: int): NimString {.compilerProc.}
proc toNimStr(str: cstring, len: int): NimString {.compilerProc.}
proc cstrToNimstr(str: cstring): NimString {.compilerRtl.}
proc copyString(src: NimString): NimString {.compilerRtl.}
proc copyStringRC1(src: NimString): NimString {.compilerRtl.}
proc copyDeepString(src: NimString): NimString {.inline.}
proc addChar(s: NimString, c: char): NimString
proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.}
proc appendString(dest, src: NimString) {.compilerproc, inline.}
proc appendChar(dest: NimString, c: char) {.compilerproc, inline.}
proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.}
# ----------------- sequences ----------------------------------------------
proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.} =
proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.}
proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
compilerRtl.}
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.}
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.}
import allocators
@@ -45,35 +32,36 @@ type
region: Allocator
data: UncheckedArray[char]
NimString {.core.} = object
NimStringV2 {.core.} = object
len: int
p: ptr StrContent ## invariant. Never nil
p: ptr StrContent ## can be nil if len == 0.
const nimStrVersion {.core.} = 2
template isLiteral(s): bool = s.len == 0 or s.p.region == nil
template isLiteral(s): bool = s.p == nil or s.p.region == nil
template contentSize(cap): int = cap + 1 + sizeof(int) + sizeof(Allocator)
template frees(s) =
if not isLiteral(s):
s.p.region.dealloc(s.p, contentSize(s.p.cap))
s.p.region.dealloc(s.p.region, s.p, contentSize(s.p.cap))
proc `=destroy`(s: var NimString) =
proc `=destroy`(s: var NimStringV2) =
frees(s)
s.len = 0
s.p = nil
template lose(a) =
frees(a)
proc `=sink`(a: var NimString, b: NimString) =
proc `=sink`(a: var NimStringV2, b: NimStringV2) =
# we hope this is optimized away for not yet alive objects:
if unlikely(a.p == b.p): return
lose(a)
a.len = b.len
a.p = b.p
proc `=`(a: var NimString; b: NimString) =
proc `=`(a: var NimStringV2; b: NimStringV2) =
if unlikely(a.p == b.p): return
lose(a)
a.len = b.len
@@ -85,7 +73,7 @@ proc `=`(a: var NimString; b: NimString) =
# we have to allocate the 'cap' here, consider
# 'let y = newStringOfCap(); var x = y'
# on the other hand... These get turned into moves now.
a.p = cast[ptr StrContent](region.alloc(contentSize(b.len)))
a.p = cast[ptr StrContent](region.alloc(region, contentSize(b.len)))
a.p.region = region
a.p.cap = b.len
copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1)
@@ -95,12 +83,12 @@ proc resize(old: int): int {.inline.} =
elif old < 65536: result = old * 2
else: result = old * 3 div 2 # for large arrays * 3/2 is better
proc prepareAdd(s: var NimString; addlen: int) =
proc prepareAdd(s: var NimStringV2; addlen: int) {.compilerRtl.} =
if isLiteral(s):
let oldP = s.p
# can't mutate a literal, so we need a fresh copy here:
let region = getLocalAllocator()
s.p = cast[ptr StrContent](region.alloc(contentSize(s.len + addlen)))
s.p = cast[ptr StrContent](region.alloc(region, contentSize(s.len + addlen)))
s.p.region = region
s.p.cap = s.len + addlen
if s.len > 0:
@@ -108,61 +96,65 @@ proc prepareAdd(s: var NimString; addlen: int) =
copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len)
elif s.len + addlen > s.p.cap:
let cap = max(s.len + addlen, resize(s.p.cap))
s.p = s.p.region.realloc(s.p, oldSize = contentSize(s.p.cap), newSize = contentSize(cap))
s.p = cast[ptr StrContent](s.p.region.realloc(s.p.region, s.p,
oldSize = contentSize(s.p.cap),
newSize = contentSize(cap)))
s.p.cap = cap
proc nimAddCharV1(s: var NimString; c: char) {.compilerRtl.} =
proc nimAddCharV1(s: var NimStringV2; c: char) {.compilerRtl.} =
prepareAdd(s, 1)
s.p.data[s.len] = c
s.p.data[s.len+1] = '\0'
inc s.len
proc ensure(s: var string; newLen: int) =
let old = s.cap
if newLen >= old:
s.cap = max((old * 3) shr 1, newLen)
if s.cap > 0:
s.data = cast[type(s.data)](realloc(s.data, old + 1, s.cap + 1))
proc toNimStr(str: cstring, len: int): NimStringV2 {.compilerProc.} =
if len <= 0:
result = NimStringV2(len: 0, p: nil)
else:
let region = getLocalAllocator()
var p = cast[ptr StrContent](region.alloc(region, contentSize(len)))
p.region = region
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)
result = NimStringV2(len: 0, p: p)
proc add*(s: var string; y: string) =
if y.len != 0:
let newLen = s.len + y.len
ensure(s, newLen)
copyMem(addr s.data[len], y.data, y.data.len + 1)
s.len = newLen
proc cstrToNimstr(str: cstring): NimStringV2 {.compilerRtl.} =
if str == nil: toNimStr(str, 0)
else: toNimStr(str, str.len)
proc newString*(len: int): string =
result.len = len
result.cap = len
if len > 0:
result.data = alloc0(len+1)
proc nimToCStringConv(s: NimStringV2): cstring {.compilerProc, inline.} =
if s.len == 0: result = cstring""
else: result = cstring(unsafeAddr s.p.data)
converter toCString(x: string): cstring {.core, inline.} =
if x.len == 0: cstring"" else: cast[cstring](x.data)
proc appendString(dest: var NimStringV2; src: NimStringV2) {.compilerproc, inline.} =
if src.len > 0:
# also copy the \0 terminator:
copyMem(unsafeAddr dest.p.data[dest.len], unsafeAddr src.p.data[0], src.len+1)
proc newStringOfCap*(cap: int): string =
result.len = 0
result.cap = cap
if cap > 0:
result.data = alloc(cap+1)
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
proc `&`*(a, b: string): string =
let sum = a.len + b.len
result = newStringOfCap(sum)
result.len = sum
copyMem(addr result.data[0], a.data, a.len)
copyMem(addr result.data[a.len], b.data, b.len)
if sum > 0:
result.data[sum] = '\0'
proc concat(x: openArray[string]): string {.core.} =
## used be the code generator to optimize 'x & y & z ...'
var sum = 0
for i in 0 ..< x.len: inc(sum, x[i].len)
result = newStringOfCap(sum)
sum = 0
for i in 0 ..< x.len:
let L = x[i].len
copyMem(addr result.data[sum], x[i].data, L)
inc(sum, L)
proc rawNewString(space: int): NimStringV2 {.compilerProc.} =
# this is also 'system.newStringOfCap'.
if space <= 0:
result = NimStringV2(len: 0, p: nil)
else:
let region = getLocalAllocator()
var p = cast[ptr StrContent](region.alloc(region, contentSize(space)))
p.region = region
p.cap = space
result = NimStringV2(len: 0, p: p)
proc mnewString(len: int): NimStringV2 {.compilerProc.} =
if len <= 0:
result = NimStringV2(len: 0, p: nil)
else:
let region = getLocalAllocator()
var p = cast[ptr StrContent](region.alloc(region, contentSize(len)))
p.region = region
p.cap = len
result = NimStringV2(len: len, p: p)

View File

@@ -211,6 +211,7 @@ proc new*(T: typedesc): auto =
new(r)
return r
const ThisIsSystem = true
proc internalNew*[T](a: var ref T) {.magic: "New", noSideEffect.}
## leaked implementation detail. Do not use.
@@ -426,8 +427,9 @@ when not defined(JS) and not defined(gcDestructors):
NimString = ptr NimStringDesc
when not defined(JS) and not defined(nimscript):
template space(s: PGenericSeq): int {.dirty.} =
s.reserved and not (seqShallowFlag or strlitFlag)
when not defined(gcDestructors):
template space(s: PGenericSeq): int {.dirty.} =
s.reserved and not (seqShallowFlag or strlitFlag)
include "system/hti"
type
@@ -730,7 +732,8 @@ proc newSeqOfCap*[T](cap: Natural): seq[T] {.
## ``cap``.
discard
when not defined(JS):
when not defined(JS) and not defined(gcDestructors):
# XXX enable this for --gc:destructors
proc newSeqUninitialized*[T: SomeNumber](len: Natural): seq[T] =
## creates a new sequence of type ``seq[T]`` with length ``len``.
##
@@ -1502,11 +1505,11 @@ const hasAlloc = (hostOS != "standalone" or not defined(nogc)) and not defined(n
when not defined(JS) and not defined(nimscript) and hostOS != "standalone":
include "system/cgprocs"
when not defined(JS) and not defined(nimscript) and hasAlloc:
when not defined(JS) and not defined(nimscript) and hasAlloc and not defined(gcDestructors):
proc addChar(s: NimString, c: char): NimString {.compilerProc, benign.}
proc add *[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.}
proc add *[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} =
proc add*[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.}
proc add*[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} =
## Generic proc for adding a data item `y` to a container `x`.
## For containers that have an order, `add` means *append*. New generic
## containers should also call their adding proc `add` for consistency.
@@ -2829,6 +2832,58 @@ else:
if x < 0: -x else: x
{.pop.}
when not defined(JS):
proc likely_proc(val: bool): bool {.importc: "likely", nodecl, nosideeffect.}
proc unlikely_proc(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.}
template likely*(val: bool): bool =
## Hints the optimizer that `val` is likely going to be true.
##
## You can use this template to decorate a branch condition. On certain
## platforms this can help the processor predict better which branch is
## going to be run. Example:
##
## .. code-block:: nim
## for value in inputValues:
## if likely(value <= 100):
## process(value)
## else:
## echo "Value too big!"
##
## On backends without branch prediction (JS and the nimscript VM), this
## template will not affect code execution.
when nimvm:
val
else:
when defined(JS):
val
else:
likely_proc(val)
template unlikely*(val: bool): bool =
## Hints the optimizer that `val` is likely going to be false.
##
## You can use this proc to decorate a branch condition. On certain
## platforms this can help the processor predict better which branch is
## going to be run. Example:
##
## .. code-block:: nim
## for value in inputValues:
## if unlikely(value > 100):
## echo "Value too big!"
## else:
## process(value)
##
## On backends without branch prediction (JS and the nimscript VM), this
## template will not affect code execution.
when nimvm:
val
else:
when defined(JS):
val
else:
unlikely_proc(val)
type
FileSeekPos* = enum ## Position relative to which seek should happen
# The values are ordered so that they match with stdio
@@ -2862,10 +2917,11 @@ when not defined(JS): #and not defined(nimscript):
when declared(nimGC_setStackBottom):
nimGC_setStackBottom(locals)
{.push profiler: off.}
var
strDesc = TNimType(size: sizeof(string), kind: tyString, flags: {ntfAcyclic})
{.pop.}
when not defined(gcDestructors):
{.push profiler: off.}
var
strDesc = TNimType(size: sizeof(string), kind: tyString, flags: {ntfAcyclic})
{.pop.}
# ----------------- IO Part ------------------------------------------------
@@ -3302,8 +3358,9 @@ when not defined(JS): #and not defined(nimscript):
while f.readLine(res): yield res
when not defined(nimscript) and hasAlloc:
include "system/assign"
include "system/repr"
when not defined(gcDestructors):
include "system/assign"
include "system/repr"
when hostOS != "standalone" and not defined(nimscript):
proc getCurrentException*(): ref Exception {.compilerRtl, inl, benign.} =
@@ -3410,58 +3467,6 @@ proc quit*(errormsg: string, errorcode = QuitFailure) {.noReturn.} =
{.pop.} # checks
{.pop.} # hints
when not defined(JS):
proc likely_proc(val: bool): bool {.importc: "likely", nodecl, nosideeffect.}
proc unlikely_proc(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.}
template likely*(val: bool): bool =
## Hints the optimizer that `val` is likely going to be true.
##
## You can use this template to decorate a branch condition. On certain
## platforms this can help the processor predict better which branch is
## going to be run. Example:
##
## .. code-block:: nim
## for value in inputValues:
## if likely(value <= 100):
## process(value)
## else:
## echo "Value too big!"
##
## On backends without branch prediction (JS and the nimscript VM), this
## template will not affect code execution.
when nimvm:
val
else:
when defined(JS):
val
else:
likely_proc(val)
template unlikely*(val: bool): bool =
## Hints the optimizer that `val` is likely going to be false.
##
## You can use this proc to decorate a branch condition. On certain
## platforms this can help the processor predict better which branch is
## going to be run. Example:
##
## .. code-block:: nim
## for value in inputValues:
## if unlikely(value > 100):
## echo "Value too big!"
## else:
## process(value)
##
## On backends without branch prediction (JS and the nimscript VM), this
## template will not affect code execution.
when nimvm:
val
else:
when defined(JS):
val
else:
unlikely_proc(val)
proc `/`*(x, y: int): float {.inline, noSideEffect.} =
## integer division that results in a float.
result = toFloat(x) / toFloat(y)
@@ -4090,6 +4095,21 @@ template once*(body: untyped): untyped =
{.pop.} #{.push warning[GcMem]: off, warning[Uninit]: off.}
proc substr*(s: string, first, last: int): string =
let L = max(min(last, high(s)) - first + 1, 0)
result = newString(L)
for i in 0 .. L-1:
result[i] = s[i+first]
proc substr*(s: string, first = 0): string =
## copies a slice of `s` into a new string and returns this new
## string. The bounds `first` and `last` denote the indices of
## the first and last characters that shall be copied. If ``last``
## is omitted, it is treated as ``high(s)``. If ``last >= s.len``, ``s.len``
## is used instead: This means ``substr`` can also be used to `cut`:idx:
## or `limit`:idx: a string's length.
result = substr(s, first, high(s))
when defined(nimconfig):
include "system/nimscript"
@@ -4164,21 +4184,6 @@ when not defined(js):
proc toOpenArrayByte*(x: string; first, last: int): openarray[byte] {.
magic: "Slice".}
proc substr*(s: string, first, last: int): string =
let L = max(min(last, high(s)) - first + 1, 0)
result = newString(L)
for i in 0 .. L-1:
result[i] = s[i+first]
proc substr*(s: string, first = 0): string =
## copies a slice of `s` into a new string and returns this new
## string. The bounds `first` and `last` denote the indices of
## the first and last characters that shall be copied. If ``last``
## is omitted, it is treated as ``high(s)``. If ``last >= s.len``, ``s.len``
## is used instead: This means ``substr`` can also be used to `cut`:idx:
## or `limit`:idx: a string's length.
result = substr(s, first, high(s))
type
ForLoopStmt* {.compilerProc.} = object ## special type that marks a macro
## as a `for-loop macro`:idx:

View File

@@ -202,11 +202,6 @@ proc objectInit(dest: pointer, typ: PNimType) =
# ---------------------- assign zero -----------------------------------------
proc nimDestroyRange[T](r: T) {.compilerProc.} =
# internal proc used for destroying sequences and arrays
mixin `=destroy`
for i in countup(0, r.len - 1): `=destroy`(r[i])
proc genericReset(dest: pointer, mt: PNimType) {.compilerProc, benign.}
proc genericResetAux(dest: pointer, n: ptr TNimNode) =
var d = cast[ByteAddress](dest)

View File

@@ -12,7 +12,6 @@
type
LibHandle = pointer # private type
ProcAddr = pointer # library loading and loading of procs:
{.deprecated: [TLibHandle: LibHandle, TProcAddr: ProcAddr].}
proc nimLoadLibrary(path: string): LibHandle {.compilerproc.}
proc nimUnloadLibrary(lib: LibHandle) {.compilerproc.}

View File

@@ -264,12 +264,13 @@ proc forAllChildren(cell: PCell, op: WalkOp) =
of tyRef, tyOptAsRef: # common case
forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
of tySequence:
var d = cast[ByteAddress](cellToUsr(cell))
var s = cast[PGenericSeq](d)
if s != nil:
for i in 0..s.len-1:
forAllChildrenAux(cast[pointer](d +% i *% cell.typ.base.size +%
GenericSeqSize), cell.typ.base, op)
when not defined(gcDestructors):
var d = cast[ByteAddress](cellToUsr(cell))
var s = cast[PGenericSeq](d)
if s != nil:
for i in 0..s.len-1:
forAllChildrenAux(cast[pointer](d +% i *% cell.typ.base.size +%
GenericSeqSize), cell.typ.base, op)
else: discard
proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
@@ -310,53 +311,54 @@ proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
result = rawNewObj(typ, size, gch)
when defined(memProfiler): nimProfile(size)
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
# `newObj` already uses locks, so no need for them here.
let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
result = newObj(typ, size)
cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).reserved = len
when defined(memProfiler): nimProfile(size)
proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
result = rawNewObj(typ, size, gch)
zeroMem(result, size)
when defined(memProfiler): nimProfile(size)
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
result = newObj(typ, size)
cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).reserved = len
when defined(memProfiler): nimProfile(size)
when not defined(gcDestructors):
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
# `newObj` already uses locks, so no need for them here.
let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
result = newObj(typ, size)
cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).reserved = len
when defined(memProfiler): nimProfile(size)
proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
acquire(gch)
collectCT(gch, newsize + sizeof(Cell))
var ol = usrToCell(old)
sysAssert(ol.typ != nil, "growObj: 1")
gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2")
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
result = newObj(typ, size)
cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).reserved = len
when defined(memProfiler): nimProfile(size)
var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
var elemSize = 1
if ol.typ.kind != tyString: elemSize = ol.typ.base.size
incTypeSize ol.typ, newsize
proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
acquire(gch)
collectCT(gch, newsize + sizeof(Cell))
var ol = usrToCell(old)
sysAssert(ol.typ != nil, "growObj: 1")
gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2")
var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
copyMem(res, ol, oldsize + sizeof(Cell))
zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)),
newsize-oldsize)
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
when withBitvectors: incl(gch.allocated, res)
when useCellIds:
inc gch.idGenerator
res.id = gch.idGenerator
release(gch)
result = cellToUsr(res)
when defined(memProfiler): nimProfile(newsize-oldsize)
var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
var elemSize = 1
if ol.typ.kind != tyString: elemSize = ol.typ.base.size
incTypeSize ol.typ, newsize
proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
result = growObj(old, newsize, gch)
var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
copyMem(res, ol, oldsize + sizeof(Cell))
zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)),
newsize-oldsize)
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
when withBitvectors: incl(gch.allocated, res)
when useCellIds:
inc gch.idGenerator
res.id = gch.idGenerator
release(gch)
result = cellToUsr(res)
when defined(memProfiler): nimProfile(newsize-oldsize)
proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
result = growObj(old, newsize, gch)
{.push profiler:off.}

View File

@@ -7,7 +7,7 @@
# distribution, for details about the copyright.
#
when declared(NimString):
when declared(ThisIsSystem):
# we are in system module:
{.pragma: codegenType, compilerproc.}
else:

View File

@@ -31,8 +31,6 @@ type
JSRef = ref RootObj # Fake type.
{.deprecated: [TSafePoint: SafePoint, TCallFrame: CallFrame].}
var
framePtr {.importc, nodecl, volatile.}: PCallFrame
excHandler {.importc, nodecl, volatile.}: int = 0
@@ -506,7 +504,7 @@ proc chckNilDisp(p: pointer) {.compilerproc.} =
if p == nil:
sysFatal(NilAccessError, "cannot dispatch; dispatcher is nil")
type NimString = string # hack for hti.nim
const ThisIsSystem = true # for hti.nim
include "system/hti"
proc isFatPointer(ti: PNimType): bool =

View File

@@ -554,7 +554,7 @@ else:
else:
include "system/gc"
when not declared(nimNewSeqOfCap):
when not declared(nimNewSeqOfCap) and not defined(gcDestructors):
proc nimNewSeqOfCap(typ: PNimType, cap: int): pointer {.compilerproc.} =
when defined(gcRegions):
let s = mulInt(cap, typ.base.size) # newStr already adds GenericSeqSize

View File

@@ -148,7 +148,7 @@ proc readLine(f: File, line: var TaintedString): bool =
if line.string.isNil:
line = TaintedString(newStringOfCap(80))
else:
when not defined(nimscript):
when not defined(nimscript) and not defined(gcDestructors):
sp = cint(cast[PGenericSeq](line.string).space)
line.string.setLen(sp)
while true:

View File

@@ -10,13 +10,12 @@
# Nim support for C/C++'s `wide strings`:idx:. This is part of the system
# module! Do not import it directly!
when not declared(NimString):
when not declared(ThisIsSystem):
{.error: "You must not import this module explicitly".}
type
Utf16Char* = distinct int16
WideCString* = ref UncheckedArray[Utf16Char]
{.deprecated: [TUtf16Char: Utf16Char].}
proc len*(w: WideCString): int =
## returns the length of a widestring. This traverses the whole string to