Created
March 15, 2019 12:18
-
-
Save Araq/4249e3719eb6e0ff293ed5180646378a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# | |
# Nim's Runtime Library | |
# (c) Copyright 2012 Andreas Rumpf | |
# | |
# See the file "copying.txt", included in this | |
# distribution, for details about the copyright. | |
# | |
# string & sequence handling procedures needed by the code generator | |
# strings are dynamically resized, have a length field | |
# and are zero-terminated, so they can be casted to C | |
# strings easily | |
# we don't use refcounts because that's a behaviour | |
# the programmer may not want | |
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 | |
when declared(allocAtomic): | |
template allocStr(size: untyped): untyped = | |
cast[NimString](allocAtomic(size)) | |
template allocStrNoInit(size: untyped): untyped = | |
cast[NimString](boehmAllocAtomic(size)) | |
elif defined(gcRegions): | |
template allocStr(size: untyped): untyped = | |
cast[NimString](newStr(addr(strDesc), size, true)) | |
template allocStrNoInit(size: untyped): untyped = | |
cast[NimString](newStr(addr(strDesc), size, false)) | |
else: | |
template allocStr(size: untyped): untyped = | |
cast[NimString](newObj(addr(strDesc), size)) | |
template allocStrNoInit(size: untyped): untyped = | |
cast[NimString](newObjNoInit(addr(strDesc), size)) | |
proc rawNewStringNoInit(space: int): NimString {.compilerProc.} = | |
var s = space | |
if s < 7: s = 7 | |
result = allocStrNoInit(sizeof(TGenericSeq) + s + 1) | |
result.reserved = s | |
result.len = 0 | |
when defined(gogc): | |
result.elemSize = 1 | |
proc rawNewString(space: int): NimString {.compilerProc.} = | |
var s = space | |
if s < 7: s = 7 | |
result = allocStr(sizeof(TGenericSeq) + s + 1) | |
result.reserved = s | |
result.len = 0 | |
when defined(gogc): | |
result.elemSize = 1 | |
proc mnewString(len: int): NimString {.compilerProc.} = | |
result = rawNewString(len) | |
result.len = len | |
proc copyStrLast(s: NimString, start, last: int): NimString {.compilerProc.} = | |
# This is not used by most recent versions of the compiler anymore, but | |
# required for bootstrapping purposes. | |
let start = max(start, 0) | |
if s == nil: return nil | |
let len = min(last, s.len-1) - start + 1 | |
if len > 0: | |
result = rawNewStringNoInit(len) | |
result.len = len | |
copyMem(addr(result.data), addr(s.data[start]), len) | |
result.data[len] = '\0' | |
else: | |
result = rawNewString(len) | |
proc copyStr(s: NimString, start: int): NimString {.compilerProc.} = | |
# This is not used by most recent versions of the compiler anymore, but | |
# required for bootstrapping purposes. | |
if s == nil: return nil | |
result = copyStrLast(s, start, s.len-1) | |
proc nimToCStringConv(s: NimString): cstring {.compilerProc, nonReloadable, inline.} = | |
if s == nil or s.len == 0: result = cstring"" | |
else: result = cstring(addr s.data) | |
proc toNimStr(str: cstring, len: int): NimString {.compilerProc.} = | |
result = rawNewStringNoInit(len) | |
result.len = len | |
copyMem(addr(result.data), str, len + 1) | |
proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} = | |
if str == nil: NimString(nil) | |
else: toNimStr(str, str.len) | |
proc copyString(src: NimString): NimString {.compilerRtl.} = | |
if src != nil: | |
if (src.reserved and seqShallowFlag) != 0: | |
result = src | |
else: | |
result = rawNewStringNoInit(src.len) | |
result.len = src.len | |
copyMem(addr(result.data), addr(src.data), src.len + 1) | |
sysAssert((seqShallowFlag and result.reserved) == 0, "copyString") | |
when defined(nimShallowStrings): | |
if (src.reserved and strlitFlag) != 0: | |
result.reserved = (result.reserved and not strlitFlag) or seqShallowFlag | |
proc newOwnedString(src: NimString; n: int): NimString = | |
result = rawNewStringNoInit(n) | |
result.len = n | |
copyMem(addr(result.data), addr(src.data), n) | |
result.data[n] = '\0' | |
proc copyStringRC1(src: NimString): NimString {.compilerRtl.} = | |
if src != nil: | |
if (src.reserved and seqShallowFlag) != 0: | |
result = src | |
when declared(newObjRC1) and not defined(gcRegions): | |
incRef(usrToCell(result)) | |
else: | |
when declared(newObjRC1) and not defined(gcRegions): | |
var s = src.len | |
if s < 7: s = 7 | |
result = cast[NimString](newObjRC1(addr(strDesc), sizeof(TGenericSeq) + | |
s+1)) | |
result.reserved = s | |
when defined(gogc): | |
result.elemSize = 1 | |
else: | |
result = rawNewStringNoInit(src.len) | |
result.len = src.len | |
copyMem(addr(result.data), addr(src.data), src.len + 1) | |
sysAssert((seqShallowFlag and result.reserved) == 0, "copyStringRC1") | |
when defined(nimShallowStrings): | |
if (src.reserved and strlitFlag) != 0: | |
result.reserved = (result.reserved and not strlitFlag) or seqShallowFlag | |
proc copyDeepString(src: NimString): NimString {.inline.} = | |
if src != nil: | |
result = rawNewStringNoInit(src.len) | |
result.len = src.len | |
copyMem(addr(result.data), addr(src.data), src.len + 1) | |
proc addChar(s: NimString, c: char): NimString = | |
# is compilerproc! | |
if s == nil: | |
result = rawNewStringNoInit(1) | |
result.len = 0 | |
else: | |
result = s | |
if result.len >= result.space: | |
let r = resize(result.space) | |
when defined(nimIncrSeqV3): | |
result = rawNewStringNoInit(r) | |
result.len = s.len | |
copyMem(addr result.data[0], unsafeAddr(s.data[0]), s.len+1) | |
else: | |
result = cast[NimString](growObj(result, | |
sizeof(TGenericSeq) + r + 1)) | |
result.reserved = r | |
result.data[result.len] = c | |
result.data[result.len+1] = '\0' | |
inc(result.len) | |
# These routines should be used like following: | |
# <Nim code> | |
# s &= "Hello " & name & ", how do you feel?" | |
# | |
# <generated C code> | |
# { | |
# s = resizeString(s, 6 + name->len + 17); | |
# appendString(s, strLit1); | |
# appendString(s, strLit2); | |
# appendString(s, strLit3); | |
# } | |
# | |
# <Nim code> | |
# s = "Hello " & name & ", how do you feel?" | |
# | |
# <generated C code> | |
# { | |
# string tmp0; | |
# tmp0 = rawNewString(6 + name->len + 17); | |
# appendString(s, strLit1); | |
# appendString(s, strLit2); | |
# appendString(s, strLit3); | |
# s = tmp0; | |
# } | |
# | |
# <Nim code> | |
# s = "" | |
# | |
# <generated C code> | |
# s = rawNewString(0); | |
proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} = | |
if dest == nil: | |
result = rawNewStringNoInit(addlen) | |
elif dest.len + addlen <= dest.space: | |
result = dest | |
else: # slow path: | |
let sp = max(resize(dest.space), dest.len + addlen) | |
when defined(nimIncrSeqV3): | |
result = rawNewStringNoInit(sp) | |
result.len = dest.len | |
copyMem(addr result.data[0], unsafeAddr(dest.data[0]), dest.len+1) | |
else: | |
result = cast[NimString](growObj(dest, sizeof(TGenericSeq) + sp + 1)) | |
result.reserved = sp | |
#result = rawNewString(sp) | |
#copyMem(result, dest, dest.len + sizeof(TGenericSeq)) | |
# DO NOT UPDATE LEN YET: dest.len = newLen | |
proc appendString(dest, src: NimString) {.compilerproc, inline.} = | |
if src != nil: | |
copyMem(addr(dest.data[dest.len]), addr(src.data), src.len + 1) | |
inc(dest.len, src.len) | |
proc appendChar(dest: NimString, c: char) {.compilerproc, inline.} = | |
dest.data[dest.len] = c | |
dest.data[dest.len+1] = '\0' | |
inc(dest.len) | |
proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} = | |
let n = max(newLen, 0) | |
if s == nil: | |
result = mnewString(newLen) | |
elif n <= s.space: | |
result = s | |
else: | |
let sp = max(resize(s.space), newLen) | |
when defined(nimIncrSeqV3): | |
result = rawNewStringNoInit(sp) | |
result.len = s.len | |
copyMem(addr result.data[0], unsafeAddr(s.data[0]), s.len+1) | |
zeroMem(addr result.data[s.len], newLen - s.len) | |
result.reserved = sp | |
else: | |
result = resizeString(s, n) | |
result.len = n | |
result.data[n] = '\0' | |
# ----------------- sequences ---------------------------------------------- | |
proc incrSeq(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} = | |
# increments the length by one: | |
# this is needed for supporting ``add``; | |
# | |
# add(seq, x) generates: | |
# seq = incrSeq(seq, sizeof(x)); | |
# seq[seq->len-1] = x; | |
result = seq | |
if result.len >= result.space: | |
let r = resize(result.space) | |
result = cast[PGenericSeq](growObj(result, elemSize * r + | |
GenericSeqSize)) | |
result.reserved = r | |
inc(result.len) | |
proc incrSeqV2(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} = | |
# incrSeq version 2 | |
result = seq | |
if result.len >= result.space: | |
let r = resize(result.space) | |
result = cast[PGenericSeq](growObj(result, elemSize * r + | |
GenericSeqSize)) | |
result.reserved = r | |
template `+!`(p: pointer, s: int): pointer = | |
cast[pointer](cast[int](p) +% s) | |
proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.} = | |
if s == nil: | |
result = cast[PGenericSeq](newSeq(typ, 1)) | |
result.len = 0 | |
else: | |
result = s | |
if result.len >= result.space: | |
let r = resize(result.space) | |
when defined(nimIncrSeqV3): | |
result = cast[PGenericSeq](newSeq(typ, r)) | |
result.len = s.len | |
copyMem(result +! GenericSeqSize, s +! GenericSeqSize, s.len * typ.base.size) | |
# since we steal the content from 's', it's crucial to set s's len to 0. | |
s.len = 0 | |
else: | |
result = cast[PGenericSeq](growObj(result, typ.base.size * r + | |
GenericSeqSize)) | |
result.reserved = r | |
proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {. | |
compilerRtl, inl.} = | |
result = seq | |
if result.space < newLen: | |
let r = max(resize(result.space), newLen) | |
result = cast[PGenericSeq](growObj(result, elemSize * r + | |
GenericSeqSize)) | |
result.reserved = r | |
elif newLen < result.len: | |
# we need to decref here, otherwise the GC leaks! | |
when not defined(boehmGC) and not defined(nogc) and | |
not defined(gcMarkAndSweep) and not defined(gogc) and | |
not defined(gcRegions): | |
when false: # compileOption("gc", "v2"): | |
for i in newLen..result.len-1: | |
let len0 = gch.tempStack.len | |
forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +% | |
GenericSeqSize +% (i*%elemSize)), | |
extGetCellType(result).base, waPush) | |
let len1 = gch.tempStack.len | |
for i in len0 ..< len1: | |
doDecRef(gch.tempStack.d[i], LocalHeap, MaybeCyclic) | |
gch.tempStack.len = len0 | |
else: | |
if ntfNoRefs notin extGetCellType(result).base.flags: | |
for i in newLen..result.len-1: | |
forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +% | |
GenericSeqSize +% (i*%elemSize)), | |
extGetCellType(result).base, waZctDecRef) | |
# XXX: zeroing out the memory can still result in crashes if a wiped-out | |
# cell is aliased by another pointer (ie proc parameter or a let variable). | |
# This is a tough problem, because even if we don't zeroMem here, in the | |
# presence of user defined destructors, the user will expect the cell to be | |
# "destroyed" thus creating the same problem. We can destoy the cell in the | |
# finalizer of the sequence, but this makes destruction non-deterministic. | |
zeroMem(cast[pointer](cast[ByteAddress](result) +% GenericSeqSize +% | |
(newLen*%elemSize)), (result.len-%newLen) *% elemSize) | |
result.len = newLen | |
proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {. | |
compilerRtl.} = | |
sysAssert typ.kind == tySequence, "setLengthSeqV2: type is not a seq" | |
if s == nil: | |
result = cast[PGenericSeq](newSeq(typ, newLen)) | |
else: | |
when defined(nimIncrSeqV3): | |
let elemSize = typ.base.size | |
if s.space < newLen: | |
let r = max(resize(s.space), newLen) | |
result = cast[PGenericSeq](newSeq(typ, r)) | |
copyMem(result +! GenericSeqSize, s +! GenericSeqSize, s.len * elemSize) | |
# since we steal the content from 's', it's crucial to set s's len to 0. | |
s.len = 0 | |
elif newLen < s.len: | |
result = s | |
# we need to decref here, otherwise the GC leaks! | |
when not defined(boehmGC) and not defined(nogc) and | |
not defined(gcMarkAndSweep) and not defined(gogc) and | |
not defined(gcRegions): | |
if ntfNoRefs notin typ.base.flags: | |
for i in newLen..result.len-1: | |
forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +% | |
GenericSeqSize +% (i*%elemSize)), | |
extGetCellType(result).base, waZctDecRef) | |
# XXX: zeroing out the memory can still result in crashes if a wiped-out | |
# cell is aliased by another pointer (ie proc parameter or a let variable). | |
# This is a tough problem, because even if we don't zeroMem here, in the | |
# presence of user defined destructors, the user will expect the cell to be | |
# "destroyed" thus creating the same problem. We can destoy the cell in the | |
# finalizer of the sequence, but this makes destruction non-deterministic. | |
zeroMem(cast[pointer](cast[ByteAddress](result) +% GenericSeqSize +% | |
(newLen*%elemSize)), (result.len-%newLen) *% elemSize) | |
else: | |
result = s | |
result.len = newLen | |
else: | |
result = setLengthSeq(s, typ.base.size, newLen) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment