mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-31 19:03:42 +00:00
Compare commits
20 Commits
v2.2.0
...
pr_string_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
112e274804 | ||
|
|
f1a7bfce47 | ||
|
|
68bf6c7f93 | ||
|
|
7cd431804b | ||
|
|
01388dc816 | ||
|
|
bee9baa8bf | ||
|
|
147621ec21 | ||
|
|
5bde480596 | ||
|
|
33f911e691 | ||
|
|
ec03e476d5 | ||
|
|
4adb79f7cb | ||
|
|
1da417b818 | ||
|
|
7ef0f43f35 | ||
|
|
da277cf1b8 | ||
|
|
cb172328ba | ||
|
|
9af21cf719 | ||
|
|
f7bdec6f0d | ||
|
|
88c0ac44fc | ||
|
|
119cfe8bc8 | ||
|
|
ae99903236 |
@@ -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)))]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -165,3 +165,4 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimHasWarnStdPrefix")
|
||||
|
||||
defineSymbol("nimHasVtables")
|
||||
defineSymbol("nimHasSeqsV3")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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).} =
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
265
lib/system/strs_v3.nim
Normal 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
|
||||
@@ -45,3 +45,4 @@ switch("define", "nimPreviewNonVarDestructor")
|
||||
switch("warningAserror", "UnnamedBreak")
|
||||
switch("legacy", "verboseTypeMismatch")
|
||||
switch("experimental", "vtables")
|
||||
switch("define", "nimSeqsV3")
|
||||
|
||||
@@ -1 +1 @@
|
||||
--mm:refc
|
||||
--mm:refc
|
||||
Reference in New Issue
Block a user