123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- #
- #
- # Nim's Runtime Library
- # (c) Copyright 2017 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: int
- NimStrPayload {.core.} = object
- cap: int
- data: UncheckedArray[char]
- NimStringV2 {.core.} = object
- len: int
- p: ptr NimStrPayload ## can be nil if len == 0.
- const nimStrVersion {.core.} = 2
- template isLiteral(s): bool = (s.p == nil) or (s.p.cap and strlitFlag) == strlitFlag
- template contentSize(cap): int = cap + 1 + sizeof(NimStrPayloadBase)
- template frees(s) =
- if not isLiteral(s):
- when compileOption("threads"):
- deallocShared(s.p)
- else:
- dealloc(s.p)
- 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 NimStringV2; addlen: int) {.compilerRtl.} =
- let newLen = s.len + addlen
- if isLiteral(s):
- let oldP = s.p
- # can't mutate a literal, so we need a fresh copy here:
- when compileOption("threads"):
- s.p = cast[ptr NimStrPayload](allocShared0(contentSize(newLen)))
- else:
- s.p = cast[ptr NimStrPayload](alloc0(contentSize(newLen)))
- s.p.cap = newLen
- if s.len > 0:
- # we are about to append, so there is no need to copy the \0 terminator:
- copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], min(s.len, newLen))
- else:
- let oldCap = s.p.cap and not strlitFlag
- if newLen > oldCap:
- let newCap = max(newLen, resize(oldCap))
- when compileOption("threads"):
- s.p = cast[ptr NimStrPayload](reallocShared0(s.p, contentSize(oldCap), contentSize(newCap)))
- else:
- s.p = cast[ptr NimStrPayload](realloc0(s.p, contentSize(oldCap), contentSize(newCap)))
- s.p.cap = newCap
- proc nimAddCharV1(s: var NimStringV2; c: char) {.compilerRtl, inline.} =
- #if (s.p == nil) or (s.len+1 > s.p.cap and not strlitFlag):
- prepareAdd(s, 1)
- s.p.data[s.len] = c
- s.p.data[s.len+1] = '\0'
- inc s.len
- proc toNimStr(str: cstring, len: int): NimStringV2 {.compilerproc.} =
- if len <= 0:
- result = NimStringV2(len: 0, p: nil)
- else:
- when compileOption("threads"):
- var p = cast[ptr NimStrPayload](allocShared0(contentSize(len)))
- else:
- var p = cast[ptr NimStrPayload](alloc0(contentSize(len)))
- p.cap = len
- if len > 0:
- # we are about to append, so there is no need to copy the \0 terminator:
- copyMem(unsafeAddr p.data[0], str, len)
- result = NimStringV2(len: len, p: p)
- proc cstrToNimstr(str: cstring): NimStringV2 {.compilerRtl.} =
- if str == nil: toNimStr(str, 0)
- else: toNimStr(str, str.len)
- proc nimToCStringConv(s: NimStringV2): cstring {.compilerproc, nonReloadable, inline.} =
- if s.len == 0: result = cstring""
- else: result = cast[cstring](unsafeAddr s.p.data)
- proc appendString(dest: var NimStringV2; src: NimStringV2) {.compilerproc, inline.} =
- if src.len > 0:
- # also copy the \0 terminator:
- copyMem(unsafeAddr dest.p.data[dest.len], unsafeAddr src.p.data[0], src.len+1)
- inc dest.len, src.len
- proc appendChar(dest: var NimStringV2; c: char) {.compilerproc, inline.} =
- dest.p.data[dest.len] = c
- dest.p.data[dest.len+1] = '\0'
- inc dest.len
- proc rawNewString(space: int): NimStringV2 {.compilerproc.} =
- # this is also 'system.newStringOfCap'.
- if space <= 0:
- result = NimStringV2(len: 0, p: nil)
- else:
- when compileOption("threads"):
- var p = cast[ptr NimStrPayload](allocShared0(contentSize(space)))
- else:
- var p = cast[ptr NimStrPayload](alloc0(contentSize(space)))
- p.cap = space
- result = NimStringV2(len: 0, p: p)
- proc mnewString(len: int): NimStringV2 {.compilerproc.} =
- if len <= 0:
- result = NimStringV2(len: 0, p: nil)
- else:
- when compileOption("threads"):
- var p = cast[ptr NimStrPayload](allocShared0(contentSize(len)))
- else:
- var p = cast[ptr NimStrPayload](alloc0(contentSize(len)))
- p.cap = len
- result = NimStringV2(len: len, p: p)
- proc setLengthStrV2(s: var NimStringV2, newLen: int) {.compilerRtl.} =
- if newLen == 0:
- discard "do not free the buffer here, pattern 's.setLen 0' is common for avoiding allocations"
- else:
- if newLen > s.len or isLiteral(s):
- prepareAdd(s, newLen - s.len)
- s.p.data[newLen] = '\0'
- s.len = newLen
- proc nimAsgnStrV2(a: var NimStringV2, b: NimStringV2) {.compilerRtl.} =
- if a.p == b.p: return
- if isLiteral(b):
- # we can shallow copy literals:
- frees(a)
- a.len = b.len
- a.p = b.p
- else:
- if isLiteral(a) or (a.p.cap and not strlitFlag) < b.len:
- # 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)
- when compileOption("threads"):
- a.p = cast[ptr NimStrPayload](allocShared0(contentSize(b.len)))
- else:
- a.p = cast[ptr NimStrPayload](alloc0(contentSize(b.len)))
- a.p.cap = b.len
- a.len = b.len
- copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1)
- proc nimPrepareStrMutationImpl(s: var NimStringV2) =
- let oldP = s.p
- # can't mutate a literal, so we need a fresh copy here:
- when compileOption("threads"):
- s.p = cast[ptr NimStrPayload](allocShared0(contentSize(s.len)))
- else:
- s.p = cast[ptr NimStrPayload](alloc0(contentSize(s.len)))
- s.p.cap = s.len
- copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len+1)
- proc nimPrepareStrMutationV2(s: var NimStringV2) {.compilerRtl, inline.} =
- if s.p != nil and (s.p.cap and strlitFlag) == strlitFlag:
- 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 NimStringV2](s)[])
- template capacityImpl(str: NimStringV2): int =
- if str.p != nil: str.p.cap else: 0
- func capacity*(self: string): int {.inline.} =
- ## Returns the current capacity of the string.
- # See https://github.com/nim-lang/RFCs/issues/460
- runnableExamples:
- var str = newStringOfCap(cap = 42)
- str.add "Nim"
- assert str.capacity == 42
- {.cast(noSideEffect).}:
- let str = unsafeAddr self
- result = capacityImpl(cast[ptr NimStringV2](str)[])
|