Compare commits

...

20 Commits

Author SHA1 Message Date
ringabout
112e274804 switch on 2023-12-11 14:12:55 +00:00
ringabout
f1a7bfce47 switch on 2023-12-11 14:12:47 +00:00
ringabout
68bf6c7f93 what's problem os cancelled CI 2023-12-11 11:00:02 +00:00
ringabout
7cd431804b test CI 2023-12-11 09:07:41 +00:00
ringabout
01388dc816 Merge branch 'devel' into pr_string_v3 2023-12-11 13:16:03 +08:00
ringabout
bee9baa8bf reprieve IC 2023-12-09 11:56:17 +00:00
ringabout
147621ec21 fixes a critical issue 2023-12-09 02:41:05 +00:00
ringabout
5bde480596 workaround a cstring conversion bug 2023-12-09 02:34:22 +00:00
ringabout
33f911e691 fixes C++ compilation 2023-12-08 14:13:21 +00:00
ringabout
ec03e476d5 fixes more problems 2023-12-08 08:41:16 +00:00
ringabout
4adb79f7cb a small fix 2023-12-08 07:29:36 +00:00
ringabout
1da417b818 tests nimSeqsV3 2023-12-08 07:26:32 +00:00
ringabout
7ef0f43f35 basic examples work now 2023-12-08 07:04:15 +00:00
ringabout
da277cf1b8 more fixes 2023-12-07 14:47:51 +00:00
ringabout
cb172328ba simple cases compile 2023-12-07 13:56:33 +00:00
ringabout
9af21cf719 progress 2023-12-07 12:53:06 +00:00
ringabout
f7bdec6f0d progress 2023-12-06 14:25:34 +00:00
ringabout
88c0ac44fc simple additions 2023-12-06 06:09:25 +00:00
ringabout
119cfe8bc8 some improvements 2023-12-06 02:27:05 +00:00
ringabout
ae99903236 wip: intern strings 2023-12-05 14:51:24 +00:00
16 changed files with 449 additions and 58 deletions

View File

@@ -194,11 +194,11 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareF
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
if atyp.kind in {tyVar} and not compileToCpp(p.module):
result = ("(($5) ? (($4*)(*$1)$3+($2)) : NIM_NIL)" %
[rdLoc(a), rdLoc(b), dataField(p), dest, dataFieldAccessor(p, "*" & rdLoc(a))],
[rdLoc(a), rdLoc(b), dataField(p, ty.kind == tyString), dest, dataFieldAccessor(p, "*" & rdLoc(a))],
lengthExpr)
else:
result = ("(($5) ? (($4*)$1$3+($2)) : NIM_NIL)" %
[rdLoc(a), rdLoc(b), dataField(p), dest, dataFieldAccessor(p, rdLoc(a))],
[rdLoc(a), rdLoc(b), dataField(p, ty.kind == tyString), dest, dataFieldAccessor(p, rdLoc(a))],
lengthExpr)
else:
result = ("", "")
@@ -222,7 +222,8 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
result.add x & ", " & y
else:
var a = initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n)
case skipTypes(a.t, abstractVar+{tyStatic}).kind
let typKind = skipTypes(a.t, abstractVar+{tyStatic}).kind
case typKind
of tyOpenArray, tyVarargs:
if reifiedOpenArray(n):
if a.t.kind in {tyVar, tyLent}:
@@ -240,11 +241,11 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
var t: TLoc
t.r = "(*$1)" % [a.rdLoc]
result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" %
[a.rdLoc, lenExpr(p, t), dataField(p),
[a.rdLoc, lenExpr(p, t, typKind == tyString), dataField(p, typKind == tyString),
dataFieldAccessor(p, "*" & a.rdLoc)]
else:
result.add "($4) ? ($1$3) : NIM_NIL, $2" %
[a.rdLoc, lenExpr(p, a), dataField(p), dataFieldAccessor(p, a.rdLoc)]
[a.rdLoc, lenExpr(p, a, typKind == tyString), dataField(p, typKind == tyString), dataFieldAccessor(p, a.rdLoc)]
of tyArray:
result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))]
of tyPtr, tyRef:
@@ -253,7 +254,7 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
var t: TLoc
t.r = "(*$1)" % [a.rdLoc]
result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" %
[a.rdLoc, lenExpr(p, t), dataField(p),
[a.rdLoc, lenExpr(p, t, typKind == tyString), dataField(p, typKind == tyString),
dataFieldAccessor(p, "*" & a.rdLoc)]
of tyArray:
result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, lastSon(a.t)))]

View File

@@ -309,7 +309,7 @@ proc genOpenArrayConv(p: BProc; d: TLoc; a: TLoc) =
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
linefmt(p, cpsStmts, "$1.Field0 = ($5) ? ($2$3) : NIM_NIL; $1.Field1 = $4;$n",
[rdLoc(d), a.rdLoc, dataField(p), lenExpr(p, a), dataFieldAccessor(p, a.rdLoc)])
[rdLoc(d), a.rdLoc, dataField(p, isString = true), lenExpr(p, a, isString = true), dataFieldAccessor(p, a.rdLoc)])
else:
internalError(p.config, a.lode.info, "cannot handle " & $a.t.kind)
@@ -1046,7 +1046,7 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc) =
linefmt(p, cpsStmts,
"if ($2-$1 != -1 && " &
"($1 < 0 || $1 >= $3 || $2 < 0 || $2 >= $3)){ #raiseIndexError4($1, $2, $3); ",
[rdLoc(a), rdLoc(b), lenExpr(p, arr)])
[rdLoc(a), rdLoc(b), lenExpr(p, arr, ty.kind == tyString)])
raiseInstr(p, p.s(cpsStmts))
linefmt p, cpsStmts, "}$n", []
@@ -1086,7 +1086,7 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
if optBoundsCheck in p.options:
linefmt(p, cpsStmts,
"if ($1 < 0 || $1 >= $2){ #raiseIndexError2($1,$2-1); ",
[rdCharLoc(b), lenExpr(p, a)])
[rdCharLoc(b), lenExpr(p, a, ty.kind == tyString)])
raiseInstr(p, p.s(cpsStmts))
linefmt p, cpsStmts, "}$n", []
@@ -1098,7 +1098,7 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
optSeqDestructors in p.config.globalOptions:
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
putIntoDest(p, d, n,
ropecg(p.module, "$1$3[$2]", [rdLoc(a), rdCharLoc(b), dataField(p)]), a.storage)
ropecg(p.module, "$1$3[$2]", [rdLoc(a), rdCharLoc(b), dataField(p, ty.kind == tyString)]), a.storage)
proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) =
var ty = skipTypes(n[0].typ, abstractVarRange + tyUserTypeClasses)
@@ -1199,6 +1199,7 @@ proc genEcho(p: BProc, n: PNode) =
a = initLocExpr(p, it)
if i > 0:
args.add(", ")
## TODO: fixme nimseqsv3 needs to be treated as well
case detectStrVersion(p.module)
of 2:
args.add(ropecg(p.module, "Genode::Cstring($1.p->data, $1.len)", [a.rdLoc]))
@@ -1260,7 +1261,7 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) =
if e[i + 1].kind in {nkStrLit..nkTripleStrLit}:
inc(L, e[i + 1].strVal.len)
else:
lens.add(lenExpr(p, a))
lens.add(lenExpr(p, a, isString = true))
lens.add(" + ")
appends.add(ropecg(p.module, "#appendString($1, $2);$n", [strLoc(p, tmp), rdLoc(a)]))
linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", [tmp.r, lens, L])
@@ -1300,7 +1301,7 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
if e[i + 2].kind in {nkStrLit..nkTripleStrLit}:
inc(L, e[i + 2].strVal.len)
else:
lens.add(lenExpr(p, a))
lens.add(lenExpr(p, a, isString = true))
lens.add(" + ")
appends.add(ropecg(p.module, "#appendString($1, $2);$n",
[strLoc(p, dest), rdLoc(a)]))
@@ -1754,13 +1755,14 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
addrLoc(p.config, a), genTypeInfoV1(p.module, t, e.info)]), a.storage)
of tyOpenArray, tyVarargs:
var b: TLoc = default(TLoc)
case skipTypes(a.t, abstractVarRange).kind
let typKind = skipTypes(a.t, abstractVarRange).kind
case typKind
of tyOpenArray, tyVarargs:
putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage)
of tyString, tySequence:
putIntoDest(p, b, e,
"($4) ? ($1$3) : NIM_NIL, $2" %
[rdLoc(a), lenExpr(p, a), dataField(p), dataFieldAccessor(p, a.rdLoc)],
[rdLoc(a), lenExpr(p, a, typKind == tyString), dataField(p, typKind == tyString), dataFieldAccessor(p, a.rdLoc)],
a.storage)
of tyArray:
putIntoDest(p, b, e,
@@ -1873,7 +1875,7 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
else: unaryExpr(p, e, d, "#nimCStrLen($1)")
of tyString:
var a: TLoc = initLocExpr(p, e[1])
var x = lenExpr(p, a)
var x = lenExpr(p, a, isString = true)
if op == mHigh: x = "($1-1)" % [x]
putIntoDest(p, d, e, x)
of tySequence:
@@ -2254,11 +2256,11 @@ proc genStrEquals(p: BProc, e: PNode, d: var TLoc) =
if a.kind in {nkStrLit..nkTripleStrLit} and a.strVal == "":
x = initLocExpr(p, e[2])
putIntoDest(p, d, e,
ropecg(p.module, "($1 == 0)", [lenExpr(p, x)]))
ropecg(p.module, "($1 == 0)", [lenExpr(p, x, isString = true)]))
elif b.kind in {nkStrLit..nkTripleStrLit} and b.strVal == "":
x = initLocExpr(p, e[1])
putIntoDest(p, d, e,
ropecg(p.module, "($1 == 0)", [lenExpr(p, x)]))
ropecg(p.module, "($1 == 0)", [lenExpr(p, x, isString = true)]))
else:
binaryExpr(p, e, d, "#eqStrings($1, $2)")
@@ -2303,7 +2305,11 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) =
var src: TLoc = initLocExpr(p, n[2])
linefmt(p, cpsStmts, "if ($1.p != $2.p) {", [rdLoc(a), rdLoc(src)])
genStmts(p, n[3])
linefmt(p, cpsStmts, "}$n$1.len = $2.len; $1.p = $2.p;$n", [rdLoc(a), rdLoc(src)])
let typkind = skipTypes(a.t, abstractVar+{tyStatic}).kind
if typkind == tyString and p.config.isDefined("nimSeqsV3"):
linefmt(p, cpsStmts, "}$n$1.rawlen = $2.rawlen; $1.p = $2.p;$n", [rdLoc(a), rdLoc(src)])
else:
linefmt(p, cpsStmts, "}$n$1.len = $2.len; $1.p = $2.p;$n", [rdLoc(a), rdLoc(src)])
else:
if d.k == locNone: d = getTemp(p, n.typ)
if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
@@ -2342,14 +2348,18 @@ proc genDestroy(p: BProc; n: PNode) =
case t.kind
of tyString:
var a: TLoc = initLocExpr(p, arg)
if optThreads in p.config.globalOptions:
linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" &
" #deallocShared($1.p);$n" &
"}$n", [rdLoc(a)])
if p.config.isDefined("nimSeqsV3"):
linefmt(p, cpsStmts, "#nimDestroyStrV1($1);$n",
[rdLoc(a)])
else:
linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" &
" #dealloc($1.p);$n" &
"}$n", [rdLoc(a)])
if optThreads in p.config.globalOptions:
linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" &
" #deallocShared($1.p);$n" &
"}$n", [rdLoc(a)])
else:
linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" &
" #dealloc($1.p);$n" &
"}$n", [rdLoc(a)])
of tySequence:
var a: TLoc = initLocExpr(p, arg)
linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" &
@@ -3437,7 +3447,10 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; resul
genConstObjConstr(p, n, isConst, result)
of tyString, tyCstring:
if optSeqDestructors in p.config.globalOptions and n.kind != nkNilLit and ty == tyString:
genStringLiteralV2Const(p.module, n, isConst, result)
if p.config.isDefined("nimSeqsV3"):
genStringLiteralV3Const(p.module, n, isConst, result)
else:
genStringLiteralV2Const(p.module, n, isConst, result)
else:
var d: TLoc = initLocExpr(p, n)
result.add rdLoc(d)

View File

@@ -94,6 +94,48 @@ proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Ro
pureLit = m.tmpBase & rope(id)
result.addf "{$1, (NimStrPayload*)&$2}", [rope(n.strVal.len), pureLit]
# ------ Version 3: destructor based strings and seqs -----------------------
# strings are enhanced by interned strings
proc toConstLenV3(len: int): string =
result = rope((len shl 1) or 1)
proc genStringLiteralDataOnlyV3(m: BModule, s: string; result: Rope; isConst: bool) =
# TODO: fixme: perhaps use makeCString for clarity for C
m.s[cfsStrData].addf("static $4 NIM_CHAR $1[$2] = $3;$n",
[result, rope(s.len), makeCCharArray(s),
rope(if isConst: "const" else: "")])
proc genStringLiteralV3(m: BModule; n: PNode; isConst: bool; result: var Rope) =
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
if id == m.labels:
let pureLit = getTempName(m)
genStringLiteralDataOnlyV3(m, n.strVal, pureLit, isConst)
let tmp = getTempName(m)
result.add tmp
cgsym(m, "NimStringV3")
# string literal not found in the cache:
m.s[cfsStrData].addf("static $4 NimStringV3 $1 = {$2, (NIM_CHAR*)&$3};$n",
[tmp, toConstLenV3(n.strVal.len), pureLit, rope(if isConst: "const" else: "")])
else:
let tmp = getTempName(m)
result.add tmp
m.s[cfsStrData].addf("static $4 NimStringV3 $1 = {$2, (NIM_CHAR*)&$3};$n",
[tmp, toConstLenV3(n.strVal.len), m.tmpBase & rope(id),
rope(if isConst: "const" else: "")])
proc genStringLiteralV3Const(m: BModule; n: PNode; isConst: bool; result: var Rope) =
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
var pureLit: Rope
if id == m.labels:
pureLit = getTempName(m)
cgsym(m, "NimStringV3")
# string literal not found in the cache:
genStringLiteralDataOnlyV3(m, n.strVal, pureLit, isConst)
else:
pureLit = m.tmpBase & rope(id)
result.addf "{$1, (NIM_CHAR*)&$2}", [toConstLenV3(n.strVal.len), pureLit]
# ------ Version selector ---------------------------------------------------
proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo;
@@ -104,6 +146,10 @@ proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo;
let tmp = getTempName(m)
genStringLiteralDataOnlyV2(m, s, tmp, isConst)
result.add tmp
of 3:
let tmp = getTempName(m)
genStringLiteralDataOnlyV3(m, s, tmp, isConst)
result.add tmp
else:
localError(m.config, info, "cannot determine how to produce code for string literal")
@@ -114,5 +160,6 @@ proc genStringLiteral(m: BModule; n: PNode; result: var Rope) =
case detectStrVersion(m)
of 0, 1: genStringLiteralV1(m, n, result)
of 2: genStringLiteralV2(m, n, isConst = true, result)
of 3: genStringLiteralV3(m, n, isConst = true, result)
else:
localError(m.config, n.info, "cannot determine how to produce code for string literal")

View File

@@ -311,6 +311,9 @@ proc getSimpleTypeDesc(m: BModule; typ: PType): Rope =
result = typeNameOrLiteral(m, typ, "void*")
of tyString:
case detectStrVersion(m)
of 3:
cgsym(m, "NimStringV3")
result = typeNameOrLiteral(m, typ, "NimStringV3")
of 2:
cgsym(m, "NimStrPayload")
cgsym(m, "NimStringV2")

View File

@@ -348,9 +348,12 @@ proc addRdLoc(a: TLoc; result: var Rope) =
proc lenField(p: BProc): Rope {.inline.} =
result = rope(if p.module.compileToCpp: "len" else: "Sup.len")
proc lenExpr(p: BProc; a: TLoc): Rope =
proc lenExpr(p: BProc; a: TLoc; isString = false): Rope =
if optSeqDestructors in p.config.globalOptions:
result = rdLoc(a) & ".len"
if isString and p.config.isDefined("nimSeqsV3"):
result = ropecg(p.module, "(#nimStrLenV3($1))", [rdLoc(a)])
else:
result = rdLoc(a) & ".len"
else:
result = "($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p)]
@@ -360,9 +363,13 @@ proc dataFieldAccessor(p: BProc, sym: Rope): Rope =
else:
result = sym
proc dataField(p: BProc): Rope =
proc dataField(p: BProc; isString: bool = false): Rope =
# TODO: revisit this after unify strings and seqs
if optSeqDestructors in p.config.globalOptions:
result = rope".p->data"
if isString and p.config.isDefined("nimSeqsV3"):
result = rope".p"
else:
result = rope".p->data"
else:
result = rope"->data"
@@ -479,10 +486,16 @@ proc resetLoc(p: BProc, loc: var TLoc) =
assert loc.r != ""
let atyp = skipTypes(loc.t, abstractInst)
if atyp.kind in {tyVar, tyLent}:
linefmt(p, cpsStmts, "$1->len = 0; $1->p = NIM_NIL;$n", [rdLoc(loc)])
if p.config.isDefined("nimSeqsV3") and typ.kind == tyString:
if atyp.kind in {tyVar, tyLent}:
linefmt(p, cpsStmts, "$1->rawlen = 0; $1->p = NIM_NIL;$n", [rdLoc(loc)])
else:
linefmt(p, cpsStmts, "$1.rawlen = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
else:
linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
if atyp.kind in {tyVar, tyLent}:
linefmt(p, cpsStmts, "$1->len = 0; $1->p = NIM_NIL;$n", [rdLoc(loc)])
else:
linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
elif not isComplexValueType(typ):
if containsGcRef:
var nilLoc: TLoc = initLoc(locTemp, loc.lode, OnStack)
@@ -519,8 +532,12 @@ proc resetLoc(p: BProc, loc: var TLoc) =
proc constructLoc(p: BProc, loc: var TLoc, isTemp = false) =
let typ = loc.t
if optSeqDestructors in p.config.globalOptions and skipTypes(typ, abstractInst + {tyStatic}).kind in {tyString, tySequence}:
linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
let typKind = skipTypes(typ, abstractInst + {tyStatic}).kind
if optSeqDestructors in p.config.globalOptions and typKind in {tyString, tySequence}:
if typKind == tyString and p.config.isDefined("nimSeqsV3"):
linefmt(p, cpsStmts, "$1.rawlen = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
else:
linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
elif not isComplexValueType(typ):
if containsGarbageCollectedRef(loc.t):
var nilLoc: TLoc = initLoc(locTemp, loc.lode, OnStack)

View File

@@ -165,3 +165,4 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimHasWarnStdPrefix")
defineSymbol("nimHasVtables")
defineSymbol("nimHasSeqsV3")

View File

@@ -60,6 +60,22 @@ proc makeCString*(s: string): Rope =
toCChar(s[i], result)
result.add('\"')
proc makeCCharArray*(s: string): Rope =
result = newStringOfCap(int(s.len.toFloat * 1.1) + 1)
result.add("{")
for i in 0..<s.len:
# line wrapping of string litterals in cgen'd code was a bad idea, e.g. causes: bug #16265
# It also makes reading c sources or grepping harder, for zero benefit.
# const MaxLineLength = 64
# if (i + 1) mod MaxLineLength == 0:
# res.add("\"\L\"")
if i != 0:
result.add ", "
result.add '\''
toCChar(s[i], result)
result.add '\''
result.add('}')
proc newFileInfo(fullPath: AbsoluteFile, projPath: RelativeFile): TFileInfo =
result.fullPath = fullPath
#shallow(result.fullPath)

View File

@@ -539,11 +539,11 @@ when not weirdTarget and (defined(freebsd) or defined(dragonfly) or defined(netb
when not weirdTarget and (defined(linux) or defined(solaris) or defined(bsd) or defined(aix)):
proc getApplAux(procPath: string): string =
result = newString(maxSymlinkLen)
var len = readlink(procPath, result.cstring, maxSymlinkLen)
result = newString(maxSymlinkLen+1) # TODO: fixme how to ?
var len = readlink(procPath, cast[cstring](addr result[0]), maxSymlinkLen)
if len > maxSymlinkLen:
result = newString(len+1)
len = readlink(procPath, result.cstring, len)
len = readlink(procPath, cast[cstring](addr result[0]), len)
setLen(result, len)
when not weirdTarget and defined(openbsd):

View File

@@ -378,7 +378,7 @@ proc write*(s: Stream, x: string) =
var x = x
writeData(s, addr(x), x.len)
else:
writeData(s, cstring(x), x.len)
writeData(s, addr x[0], x.len)
proc write*(s: Stream, args: varargs[string, `$`]) =
## Writes one or more strings to the the stream. No length fields or
@@ -937,11 +937,12 @@ proc peekFloat64*(s: Stream): float64 =
peek(s, result)
proc readStrPrivate(s: Stream, length: int, str: var string) =
if length == 0: return
if length > len(str): setLen(str, length)
when defined(js):
let L = readData(s, addr(str), length)
else:
let L = readData(s, cstring(str), length)
let L = readData(s, addr(str[0]), length)
if L != len(str): setLen(str, L)
proc readStr*(s: Stream, length: int, str: var string) {.since: (1, 3).} =
@@ -963,11 +964,13 @@ proc readStr*(s: Stream, length: int): string =
readStrPrivate(s, length, result)
proc peekStrPrivate(s: Stream, length: int, str: var string) =
# TODO: fixme breaking changes: cannot modify strings by cstring
if length == 0: return
if length > len(str): setLen(str, length)
when defined(js):
let L = peekData(s, addr(str), length)
else:
let L = peekData(s, cstring(str), length)
let L = peekData(s, addr str[0], length)
if L != len(str): setLen(str, L)
proc peekStr*(s: Stream, length: int, str: var string) {.since: (1, 3).} =

View File

@@ -1981,7 +1981,7 @@ func find*(s: string, sub: char, start: Natural = 0, last = -1): int {.rtl,
if length > 0:
let found = c_memchr(s[start].unsafeAddr, sub, cast[csize_t](length))
if not found.isNil:
return cast[int](found) -% cast[int](s.cstring)
return cast[int](found) -% cast[int](s[0].addr)
else:
findImpl()
@@ -2036,8 +2036,9 @@ func find*(s, sub: string, start: Natural = 0, last = -1): int {.rtl,
let subLen = sub.len
if last < 0 and start < s.len and subLen != 0:
let found = memmem(s[start].unsafeAddr, csize_t(s.len - start), sub.cstring, csize_t(subLen))
# TODO: fixme does it insinuate cstring cannot be used for addr anymore?
result = if not found.isNil:
cast[int](found) -% cast[int](s.cstring)
cast[int](found) -% cast[int](s[start].unsafeAddr)
else:
-1
else:

View File

@@ -1616,7 +1616,10 @@ when not defined(js) and defined(nimV2):
proc supportsCopyMem(t: typedesc): bool {.magic: "TypeTrait".}
when notJSnotNims and defined(nimSeqsV2):
include "system/strs_v2"
when defined(nimHasSeqsV3) and defined(nimSeqsV3):
include "system/strs_v3"
else:
include "system/strs_v2"
include "system/seqs_v2"
when not defined(js):
@@ -1680,10 +1683,15 @@ when not defined(js):
else:
result = newStringOfCap(len)
when defined(nimSeqsV2):
let s = cast[ptr NimStringV2](addr result)
if len > 0:
s.len = len
s.p.data[len] = '\0'
when defined(nimHasSeqsV3) and defined(nimSeqsV3):
let s = cast[ptr NimStringV3](addr result)
if len > 0:
setRawLen(s[], len)
else:
let s = cast[ptr NimStringV2](addr result)
if len > 0:
s.len = len
s.p.data[len] = '\0'
else:
let s = cast[NimString](result)
s.len = len

View File

@@ -62,9 +62,14 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) =
case mt.kind
of tyString:
when defined(nimSeqsV2):
var x = cast[ptr NimStringV2](dest)
var s2 = cast[ptr NimStringV2](s)[]
nimAsgnStrV2(x[], s2)
when defined(nimHasSeqsV3) and defined(nimSeqsV3):
var x = cast[ptr NimStringV3](dest)
var s2 = cast[ptr NimStringV3](s)[]
nimAsgnStrV2(x[], s2)
else:
var x = cast[ptr NimStringV2](dest)
var s2 = cast[ptr NimStringV2](s)[]
nimAsgnStrV2(x[], s2)
else:
var x = cast[PPointer](dest)
var s2 = cast[PPointer](s)[]
@@ -245,9 +250,14 @@ proc genericReset(dest: pointer, mt: PNimType) =
unsureAsgnRef(cast[PPointer](dest), nil)
of tyString:
when defined(nimSeqsV2):
var s = cast[ptr NimStringV2](dest)
frees(s[])
zeroMem(dest, mt.size)
when defined(nimHasSeqsV3) and defined(nimSeqsV3):
var s = cast[ptr NimStringV3](dest)
frees(s[])
zeroMem(dest, mt.size)
else:
var s = cast[ptr NimStringV2](dest)
frees(s[])
zeroMem(dest, mt.size)
else:
unsureAsgnRef(cast[PPointer](dest), nil)
of tySequence:

View File

@@ -91,9 +91,14 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) =
case mt.kind
of tyString:
when defined(nimSeqsV2):
var x = cast[ptr NimStringV2](dest)
var s2 = cast[ptr NimStringV2](s)[]
nimAsgnStrV2(x[], s2)
when defined(nimHasSeqsV3) and defined(nimSeqsV3):
var x = cast[ptr NimStringV3](dest)
var s2 = cast[ptr NimStringV3](s)[]
nimAsgnStrV2(x[], s2)
else:
var x = cast[ptr NimStringV2](dest)
var s2 = cast[ptr NimStringV2](s)[]
nimAsgnStrV2(x[], s2)
else:
var x = cast[PPointer](dest)
var s2 = cast[PPointer](s)[]

265
lib/system/strs_v3.nim Normal file
View File

@@ -0,0 +1,265 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2024 Nim contributors
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Default new string implementation used by Nim's core.
type
NimStrPayloadBase = object
## cap lives at the negative offset of p: p - wordSize
cap: int
NimStringV3 {.core.} = object
rawlen: int ## the lowest bit is used to indict whether it's a const or intern string
## TODO: would it be better to use distinct?
p: ptr UncheckedArray[char] ## can be nil if len == 0.
## cap lives at the negative offset of p: p - wordSize
## non-zero terminated
const nimStrVersion {.core.} = 3
# TODO: fixme what about s.p != nil?
template isLiteral(s): bool = (s.rawlen and 1) == 1
template strHead(p: pointer): pointer =
cast[pointer](cast[int](p) -% sizeof(NimStrPayloadBase))
template strCap(p: pointer): int =
cast[ptr NimStrPayloadBase](strHead(p))[].cap
template `strCap=`(p: pointer, size: int) =
cast[ptr NimStrPayloadBase](strHead(p))[].cap = size
proc nimStrLenV3(s: NimStringV3): int {.compilerRtl, inl.} = s.rawlen shr 1
template toRawLen(len: int): int = len shl 1
template incRawLen(s: var NimStringV3, value: int = 1) =
s.rawlen += value shl 1
template setRawLen(s: var NimStringV3, len: int) =
if isLiteral(s):
s.rawLen = toRawLen(len) or 1
else:
s.rawLen = toRawLen(len)
proc markIntern(s: var NimStringV3): bool =
s.rawlen = s.rawlen or 1
result = not isLiteral(s)
proc unsafeUnmarkIntern(s: var NimStringV3) =
s.rawlen = s.rawlen and (not 1) # unmark?
when compileOption("threads"):
deallocShared(strHead(s.p))
else:
dealloc(strHead(s.p))
template contentSize(cap): int = cap + sizeof(NimStrPayloadBase)
template frees(s) =
if not isLiteral(s) and s.p != nil:
when compileOption("threads"):
deallocShared(strHead(s.p))
else:
dealloc(strHead(s.p))
template allocPayload(newLen: int): ptr UncheckedArray[char] =
when compileOption("threads"):
cast[ptr UncheckedArray[char]](allocShared(contentSize(newLen)) +! sizeof(NimStrPayloadBase))
else:
cast[ptr UncheckedArray[char]](alloc(contentSize(newLen)) +! sizeof(NimStrPayloadBase))
template allocPayload0(newLen: int): ptr UncheckedArray[char] =
when compileOption("threads"):
cast[ptr UncheckedArray[char]](allocShared0(contentSize(newLen)) +! sizeof(NimStrPayloadBase))
else:
cast[ptr UncheckedArray[char]](alloc0(contentSize(newLen)) +! sizeof(NimStrPayloadBase))
template reallocPayload(p: pointer, newLen: int): ptr UncheckedArray[char] =
when compileOption("threads"):
cast[ptr UncheckedArray[char]](reallocShared(p, contentSize(newLen)) +! sizeof(NimStrPayloadBase))
else:
cast[ptr UncheckedArray[char]](realloc(p, contentSize(newLen)) +! sizeof(NimStrPayloadBase))
template reallocPayload0(p: pointer; oldLen, newLen: int): ptr UncheckedArray[char] =
when compileOption("threads"):
cast[ptr UncheckedArray[char]](reallocShared0(p, contentSize(oldLen), contentSize(newLen)) +! sizeof(NimStrPayloadBase))
else:
cast[ptr UncheckedArray[char]](realloc0(p, contentSize(oldLen), contentSize(newLen)) +! sizeof(NimStrPayloadBase))
proc resize(old: int): int {.inline.} =
if old <= 0: result = 4
elif old <= high(int16): result = old * 2
else: result = old * 3 div 2 # for large arrays * 3/2 is better
proc prepareAdd(s: var NimStringV3; addLen: int) {.compilerRtl.} =
let newLen = s.nimStrLenV3 + addLen
if isLiteral(s):
let oldP = s.p
# can't mutate a literal, so we need a fresh copy here:
s.p = allocPayload(newLen)
s.p.strCap = newLen
if s.nimStrLenV3 > 0:
# we are about to append
copyMem(unsafeAddr s.p[0], unsafeAddr oldP[0], min(s.nimStrLenV3, newLen))
else:
if s.p != nil:
let oldCap = s.p.strCap
if newLen > oldCap:
let newCap = max(newLen, resize(oldCap))
s.p = reallocPayload(strHead(s.p), newCap)
s.p.strCap = newCap
if newLen < newCap:
zeroMem(cast[pointer](addr s.p[newLen]), newCap - newLen)
else:
s.p = allocPayload(newLen)
s.p.strCap = newLen
proc nimAddCharV1(s: var NimStringV3; c: char) {.compilerRtl, inl.} =
#if (s.p == nil) or (s.len+1 > s.p.cap and not strlitFlag):
prepareAdd(s, 1)
s.p[s.nimStrLenV3] = c
incRawLen s
proc toNimStr(str: cstring, len: int): NimStringV3 {.compilerproc.} =
if len <= 0:
result = NimStringV3(rawlen: 0, p: nil)
else:
var p = allocPayload(len)
p.strCap = len
copyMem(unsafeAddr p[0], str, len)
result = NimStringV3(rawlen: toRawLen(len), p: p)
proc cstrToNimstr(str: cstring): NimStringV3 {.compilerRtl.} =
if str == nil: toNimStr(str, 0)
else: toNimStr(str, str.len)
proc nimToCStringConv(s: NimStringV3): cstring {.compilerproc, nonReloadable, inline.} =
if s.nimStrLenV3 == 0: result = cstring""
else:
## TODO: fixme: inject conversions somewhere else and be cleaned up
## but let it leak for now
let len = s.nimStrLenV3
result = cast[cstring](allocShared0(len+1))
copyMem(result, unsafeAddr s.p[0], len)
proc appendString(dest: var NimStringV3; src: NimStringV3) {.compilerproc, inline.} =
if src.nimStrLenV3 > 0:
copyMem(unsafeAddr dest.p[dest.nimStrLenV3], unsafeAddr src.p[0], src.nimStrLenV3)
incRawLen(dest, src.nimStrLenV3)
proc appendChar(dest: var NimStringV3; c: char) {.compilerproc, inline.} =
dest.p[dest.nimStrLenV3] = c
incRawLen(dest)
proc rawNewString(space: int): NimStringV3 {.compilerproc.} =
# this is also 'system.newStringOfCap'.
if space <= 0:
result = NimStringV3(rawlen: 0, p: nil)
else:
var p = allocPayload(space)
p.strCap = space
result = NimStringV3(rawlen: 0, p: p)
proc mnewString(len: int): NimStringV3 {.compilerproc.} =
if len <= 0:
result = NimStringV3(rawlen: 0, p: nil)
else:
var p = allocPayload0(len)
p.strCap = len
result = NimStringV3(rawlen: toRawLen(len), p: p)
proc setLengthStrV2(s: var NimStringV3, newLen: int) {.compilerRtl.} =
if newLen == 0:
discard "do not free the buffer here, pattern 's.setLen 0' is common for avoiding allocations"
else:
if isLiteral(s):
let oldP = s.p
s.p = allocPayload(newLen)
s.p.strCap = newLen
if s.nimStrLenV3 > 0:
copyMem(unsafeAddr s.p[0], unsafeAddr oldP[0], min(s.nimStrLenV3, newLen))
if newLen > s.nimStrLenV3:
zeroMem(cast[pointer](addr s.p[s.nimStrLenV3]), newLen - s.nimStrLenV3)
else:
zeroMem(cast[pointer](addr s.p[0]), newLen)
elif newLen > s.nimStrLenV3:
if s.p != nil:
let oldCap = s.p.strCap
if newLen > oldCap:
let newCap = max(newLen, resize(oldCap))
s.p = reallocPayload0(strHead(s.p), oldCap, newCap)
s.p.strCap = newCap
else:
s.p = allocPayload0(newLen)
s.p.strCap = newLen
setRawLen(s, newLen)
proc nimAsgnStrV2(a: var NimStringV3, b: NimStringV3) {.compilerRtl.} =
if a.p == b.p: return
if isLiteral(b):
# we can shallow copy literals:
frees(a)
a.rawlen = b.rawlen
a.p = b.p
else:
if isLiteral(a) or a.p == nil or a.p.strCap < b.nimStrLenV3:
# we have to allocate the 'cap' here, consider
# 'let y = newStringOfCap(); var x = y'
# on the other hand... These get turned into moves now.
frees(a)
a.p = allocPayload(b.nimStrLenV3)
a.p.strCap = b.nimStrLenV3
a.rawlen = b.rawlen
copyMem(unsafeAddr a.p[0], unsafeAddr b.p[0], b.nimStrLenV3)
proc nimPrepareStrMutationImpl(s: var NimStringV3) =
let oldP = s.p
# can't mutate a literal, so we need a fresh copy here:
s.p = allocPayload(s.nimStrLenV3)
s.p.strCap = s.nimStrLenV3
s.rawlen = s.rawlen and (not 1)
copyMem(unsafeAddr s.p[0], unsafeAddr oldP[0], s.nimStrLenV3)
proc nimPrepareStrMutationV2(s: var NimStringV3) {.compilerRtl, inl.} =
if isLiteral(s):
nimPrepareStrMutationImpl(s)
proc prepareMutation*(s: var string) {.inline.} =
# string literals are "copy on write", so you need to call
# `prepareMutation` before modifying the strings via `addr`.
{.cast(noSideEffect).}:
let s = unsafeAddr s
nimPrepareStrMutationV2(cast[ptr NimStringV3](s)[])
proc nimAddStrV1(s: var NimStringV3; src: NimStringV3) {.compilerRtl, inl.} =
#if (s.p == nil) or (s.len+1 > s.p.cap and not strlitFlag):
prepareAdd(s, src.nimStrLenV3)
appendString s, src
proc nimDestroyStrV1(s: NimStringV3) {.compilerRtl, inl.} =
frees(s)
proc nimStrAtLe(s: string; idx: int; ch: char): bool {.compilerRtl, inl.} =
result = idx < s.len and s[idx] <= ch
func capacity*(self: string): int {.inline.} =
## Returns the current capacity of the string. Intern strings
## don't have a capacity and 0 will be returned
# See https://github.com/nim-lang/RFCs/issues/460
runnableExamples:
var str = newStringOfCap(cap = 42)
str.add "Nim"
assert str.capacity == 42
let str = cast[ptr NimStringV3](unsafeAddr self)
if isLiteral(str):
result = 0
else:
result = if str.p != nil: str.p.strCap else: 0

View File

@@ -45,3 +45,4 @@ switch("define", "nimPreviewNonVarDestructor")
switch("warningAserror", "UnnamedBreak")
switch("legacy", "verboseTypeMismatch")
switch("experimental", "vtables")
switch("define", "nimSeqsV3")

View File

@@ -1 +1 @@
--mm:refc
--mm:refc