From 9af21cf7197f257ab13b949dafdedb5cf9fae8c1 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 7 Dec 2023 12:53:06 +0000 Subject: [PATCH] progress --- compiler/ccgcalls.nim | 13 +++--- compiler/ccgexprs.nim | 28 +++++++----- compiler/cgen.nim | 13 ++++-- compiler/condsyms.nim | 1 + lib/system.nim | 21 ++++----- lib/system/assign.nim | 34 ++++++++------- lib/system/deepcopy.nim | 17 ++++---- lib/system/strs_v3.nim | 97 +++++++++++++++++++++-------------------- 8 files changed, 123 insertions(+), 101 deletions(-) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index ad84be3f9e..39dce8f5fb 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -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), 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), 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), dataField(p, typKind == tyString), dataFieldAccessor(p, "*" & a.rdLoc)] of tyArray: result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, lastSon(a.t)))] diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index fad963af41..c0ba474afa 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -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), dataFieldAccessor(p, a.rdLoc)]) else: internalError(p.config, a.lode.info, "cannot handle " & $a.t.kind) @@ -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])) @@ -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), dataField(p, typKind == tyString), dataFieldAccessor(p, a.rdLoc)], a.storage) of tyArray: putIntoDest(p, b, e, @@ -2342,14 +2344,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" & diff --git a/compiler/cgen.nim b/compiler/cgen.nim index d22a6bdc24..58b3a03640 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -350,7 +350,10 @@ proc lenField(p: BProc): Rope {.inline.} = proc lenExpr(p: BProc; a: TLoc): Rope = if optSeqDestructors in p.config.globalOptions: - result = rdLoc(a) & ".len" + if 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" diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 5865e5310e..4a0e2463a7 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -165,3 +165,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasWarnStdPrefix") defineSymbol("nimHasVtables") + defineSymbol("nimHasSeqsV3") diff --git a/lib/system.nim b/lib/system.nim index 10481768aa..cbce2c06de 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1616,7 +1616,7 @@ when not defined(js) and defined(nimV2): proc supportsCopyMem(t: typedesc): bool {.magic: "TypeTrait".} when notJSnotNims and defined(nimSeqsV2): - when defined(nimSeqsV3): + when defined(nimHasSeqsV3) and defined(nimSeqsV3): include "system/strs_v3" else: include "system/strs_v2" @@ -1682,15 +1682,16 @@ when not defined(js): result = newString(len) else: result = newStringOfCap(len) - when defined(nimSeqsV3): - let s = cast[ptr NimStringV3](addr result) - if len > 0: - s.len = len - elif defined(nimSeqsV2): - let s = cast[ptr NimStringV2](addr result) - if len > 0: - s.len = len - s.p.data[len] = '\0' + when defined(nimSeqsV2): + 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 diff --git a/lib/system/assign.nim b/lib/system/assign.nim index 8be9c324d5..e94d304ddb 100644 --- a/lib/system/assign.nim +++ b/lib/system/assign.nim @@ -61,14 +61,15 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) = sysAssert(mt != nil, "genericAssignAux 2") case mt.kind of tyString: - when defined(nimSeqsV3): - var x = cast[ptr NimStringV3](dest) - var s2 = cast[ptr NimStringV3](s)[] - nimAsgnStrV2(x[], s2) - elif defined(nimSeqsV2): - var x = cast[ptr NimStringV2](dest) - var s2 = cast[ptr NimStringV2](s)[] - nimAsgnStrV2(x[], s2) + when defined(nimSeqsV2): + 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)[] @@ -248,14 +249,15 @@ proc genericReset(dest: pointer, mt: PNimType) = of tyRef: unsureAsgnRef(cast[PPointer](dest), nil) of tyString: - when defined(nimSeqsV3): - var s = cast[ptr NimStringV3](dest) - frees(s[]) - zeroMem(dest, mt.size) - elif defined(nimSeqsV2): - var s = cast[ptr NimStringV2](dest) - frees(s[]) - zeroMem(dest, mt.size) + when defined(nimSeqsV2): + 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: diff --git a/lib/system/deepcopy.nim b/lib/system/deepcopy.nim index 040e3265db..1b0331b719 100644 --- a/lib/system/deepcopy.nim +++ b/lib/system/deepcopy.nim @@ -90,14 +90,15 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) = sysAssert(mt != nil, "genericDeepCopyAux 2") case mt.kind of tyString: - when defined(nimSeqsV3): - var x = cast[ptr NimStringV3](dest) - var s2 = cast[ptr NimStringV3](s)[] - nimAsgnStrV2(x[], s2) - elif defined(nimSeqsV2): - var x = cast[ptr NimStringV2](dest) - var s2 = cast[ptr NimStringV2](s)[] - nimAsgnStrV2(x[], s2) + when defined(nimSeqsV2): + 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)[] diff --git a/lib/system/strs_v3.nim b/lib/system/strs_v3.nim index 2951bfefe6..94ec71b741 100644 --- a/lib/system/strs_v3.nim +++ b/lib/system/strs_v3.nim @@ -25,22 +25,25 @@ const nimStrVersion {.core.} = 3 template isLiteral(s): bool = (s.rawlen and 1) == 1 -template head(p: pointer): pointer = +template strHead(p: pointer): pointer = cast[pointer](cast[int](p) -% sizeof(NimStrPayloadBase)) -template cap(p: pointer): int = - cast[ptr NimStrPayloadBase](head(p))[].cap +template strCap(p: pointer): int = + cast[ptr NimStrPayloadBase](strHead(p))[].cap -template `cap=`(p: pointer, size: int) = - cast[ptr NimStrPayloadBase](head(p))[].cap = size +template `strCap=`(p: pointer, size: int) = + cast[ptr NimStrPayloadBase](strHead(p))[].cap = size -template len(s: NimStringV3): int = s.rawlen shr 1 +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 = s.rawlen + value shl 1 +template setRawLen(s: var NimStringV3, len: int) = + s.rawLen = toRawLen(len) + proc markIntern(s: var NimStringV3): bool = s.rawlen = s.rawlen or 1 result = not isLiteral(s) @@ -48,18 +51,18 @@ proc markIntern(s: var NimStringV3): bool = proc unsafeUnmarkIntern(s: var NimStringV3) = s.rawlen = s.rawlen and (not 1) # unmark? when compileOption("threads"): - deallocShared(head(s.p)) + deallocShared(strHead(s.p)) else: - dealloc(head(s.p)) + dealloc(strHead(s.p)) template contentSize(cap): int = cap + sizeof(NimStrPayloadBase) template frees(s) = if not isLiteral(s): when compileOption("threads"): - deallocShared(head(s.p)) + deallocShared(strHead(s.p)) else: - dealloc(head(s.p)) + dealloc(strHead(s.p)) template allocPayload(newLen: int): ptr UncheckedArray[char] = when compileOption("threads"): @@ -91,28 +94,28 @@ proc resize(old: int): int {.inline.} = else: result = old * 3 div 2 # for large arrays * 3/2 is better proc prepareAdd(s: var NimStringV3; addLen: int) {.compilerRtl.} = - let newLen = s.len + addLen + 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.cap = newLen - if s.len > 0: + s.p.strCap = newLen + if s.nimStrLenV3 > 0: # we are about to append - copyMem(unsafeAddr s.p[0], unsafeAddr oldP[0], min(s.len, newLen)) + copyMem(unsafeAddr s.p[0], unsafeAddr oldP[0], min(s.nimStrLenV3, newLen)) else: - let oldCap = s.p.cap + let oldCap = s.p.strCap if newLen > oldCap: let newCap = max(newLen, resize(oldCap)) - s.p = reallocPayload(head(s.p), newCap) - s.p.cap = newCap + s.p = reallocPayload(strHead(s.p), newCap) + s.p.strCap = newCap if newLen < newCap: zeroMem(cast[pointer](addr s.p[newLen]), newCap - 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.len] = c + s.p[s.nimStrLenV3] = c incRawLen s proc toNimStr(str: cstring, len: int): NimStringV3 {.compilerproc.} = @@ -120,7 +123,7 @@ proc toNimStr(str: cstring, len: int): NimStringV3 {.compilerproc.} = result = NimStringV3(rawlen: 0, p: nil) else: var p = allocPayload(len) - p.cap = len + p.strCap = len copyMem(unsafeAddr p[0], str, len) result = NimStringV3(rawlen: toRawLen(len), p: p) @@ -129,20 +132,20 @@ proc cstrToNimstr(str: cstring): NimStringV3 {.compilerRtl.} = else: toNimStr(str, str.len) proc nimToCStringConv(s: NimStringV3): cstring {.compilerproc, nonReloadable, inline.} = - if s.len == 0: result = cstring"" + if s.nimStrLenV3 == 0: result = cstring"" else: ## TODO: fixme: inject conversions somewhere else and be cleaned up ## but let it leak for now - result = cast[cstring](allocPayload0(s.len+1)) - copyMem(result, unsafeAddr s.p[0], s.len) + result = cast[cstring](allocPayload0(s.nimStrLenV3+1)) + copyMem(result, unsafeAddr s.p[0], s.nimStrLenV3) proc appendString(dest: var NimStringV3; src: NimStringV3) {.compilerproc, inline.} = - if src.len > 0: - copyMem(unsafeAddr dest.p[dest.len], unsafeAddr src.p[0], src.len) - incRawLen(dest, src.len) + 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.len] = c + dest.p[dest.nimStrLenV3] = c incRawLen(dest) proc rawNewString(space: int): NimStringV3 {.compilerproc.} = @@ -151,7 +154,7 @@ proc rawNewString(space: int): NimStringV3 {.compilerproc.} = result = NimStringV3(rawlen: 0, p: nil) else: var p = allocPayload(space) - p.cap = space + p.strCap = space result = NimStringV3(rawlen: 0, p: p) proc mnewString(len: int): NimStringV3 {.compilerproc.} = @@ -159,7 +162,7 @@ proc mnewString(len: int): NimStringV3 {.compilerproc.} = result = NimStringV3(rawlen: 0, p: nil) else: var p = allocPayload0(len) - p.cap = len + p.strCap = len result = NimStringV3(rawlen: toRawLen(len), p: p) proc setLengthStrV2(s: var NimStringV3, newLen: int) {.compilerRtl.} = @@ -169,21 +172,21 @@ proc setLengthStrV2(s: var NimStringV3, newLen: int) {.compilerRtl.} = if isLiteral(s): let oldP = s.p s.p = allocPayload(newLen) - s.p.cap = newLen - if s.len > 0: - copyMem(unsafeAddr s.p[0], unsafeAddr oldP[0], min(s.len, newLen)) - if newLen > s.len: - zeroMem(cast[pointer](addr s.p[s.len]), newLen - s.len) + 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.len: - let oldCap = s.p.cap + elif newLen > s.nimStrLenV3: + let oldCap = s.p.strCap if newLen > oldCap: let newCap = max(newLen, resize(oldCap)) - s.p = reallocPayload0(head(s.p), oldCap, newCap) - s.p.cap = newCap + s.p = reallocPayload0(strHead(s.p), oldCap, newCap) + s.p.strCap = newCap - s.rawlen = toRawLen(newLen) + setRawLen(s, newLen) proc nimAsgnStrV2(a: var NimStringV3, b: NimStringV3) {.compilerRtl.} = if a.p == b.p: return @@ -193,22 +196,22 @@ proc nimAsgnStrV2(a: var NimStringV3, b: NimStringV3) {.compilerRtl.} = a.rawlen = b.rawlen a.p = b.p else: - if isLiteral(a) or a.p.cap < b.len: + if isLiteral(a) 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.len) - a.p.cap = b.len + a.p = allocPayload(b.nimStrLenV3) + a.p.strCap = b.nimStrLenV3 a.rawlen = b.rawlen - copyMem(unsafeAddr a.p[0], unsafeAddr b.p[0], b.len) + 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.len) - s.p.cap = s.len - copyMem(unsafeAddr s.p[0], unsafeAddr oldP[0], s.len) + s.p = allocPayload(s.nimStrLenV3) + s.p.strCap = s.nimStrLenV3 + copyMem(unsafeAddr s.p[0], unsafeAddr oldP[0], s.nimStrLenV3) proc nimPrepareStrMutationV2(s: var NimStringV3) {.compilerRtl, inl.} = if isLiteral(s): @@ -223,7 +226,7 @@ proc prepareMutation*(s: var string) {.inline.} = 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.len) + prepareAdd(s, src.nimStrLenV3) appendString s, src proc nimDestroyStrV1(s: NimStringV3) {.compilerRtl, inl.} = @@ -245,4 +248,4 @@ func capacity*(self: string): int {.inline.} = if isLiteral(str): result = 0 else: - result = if str.p != nil: str.p.cap else: 0 + result = if str.p != nil: str.p.strCap else: 0