strs.nim 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2017 Nim contributors
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## Default new string implementation used by Nim's core.
  10. when false:
  11. # these are to be implemented or changed in the code generator.
  12. #proc rawNewStringNoInit(space: int): NimString {.compilerProc.}
  13. # seems to be unused.
  14. proc copyDeepString(src: NimString): NimString {.inline.}
  15. # ----------------- sequences ----------------------------------------------
  16. proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.}
  17. proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
  18. compilerRtl.}
  19. proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.}
  20. proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.}
  21. import allocators
  22. type
  23. NimStrPayload {.core.} = object
  24. cap: int
  25. region: Allocator
  26. data: UncheckedArray[char]
  27. NimStringV2 {.core.} = object
  28. len: int
  29. p: ptr NimStrPayload ## can be nil if len == 0.
  30. const nimStrVersion {.core.} = 2
  31. template isLiteral(s): bool = s.p == nil or s.p.region == nil
  32. template contentSize(cap): int = cap + 1 + sizeof(int) + sizeof(Allocator)
  33. template frees(s) =
  34. if not isLiteral(s):
  35. s.p.region.dealloc(s.p.region, s.p, contentSize(s.p.cap))
  36. proc `=destroy`(s: var string) =
  37. var a = cast[ptr NimStringV2](addr s)
  38. frees(a)
  39. a.len = 0
  40. a.p = nil
  41. template lose(a) =
  42. frees(a)
  43. proc `=sink`(x: var string, y: string) =
  44. var a = cast[ptr NimStringV2](addr x)
  45. var b = cast[ptr NimStringV2](unsafeAddr y)
  46. # we hope this is optimized away for not yet alive objects:
  47. if unlikely(a.p == b.p): return
  48. lose(a)
  49. a.len = b.len
  50. a.p = b.p
  51. proc `=`(x: var string, y: string) =
  52. var a = cast[ptr NimStringV2](addr x)
  53. var b = cast[ptr NimStringV2](unsafeAddr y)
  54. if unlikely(a.p == b.p): return
  55. lose(a)
  56. a.len = b.len
  57. if isLiteral(b):
  58. # we can shallow copy literals:
  59. a.p = b.p
  60. else:
  61. let region = if a.p.region != nil: a.p.region else: getLocalAllocator()
  62. # we have to allocate the 'cap' here, consider
  63. # 'let y = newStringOfCap(); var x = y'
  64. # on the other hand... These get turned into moves now.
  65. a.p = cast[ptr NimStrPayload](region.alloc(region, contentSize(b.len)))
  66. a.p.region = region
  67. a.p.cap = b.len
  68. copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1)
  69. proc resize(old: int): int {.inline.} =
  70. if old <= 0: result = 4
  71. elif old < 65536: result = old * 2
  72. else: result = old * 3 div 2 # for large arrays * 3/2 is better
  73. proc prepareAdd(s: var NimStringV2; addlen: int) {.compilerRtl.} =
  74. if isLiteral(s):
  75. let oldP = s.p
  76. # can't mutate a literal, so we need a fresh copy here:
  77. let region = getLocalAllocator()
  78. s.p = cast[ptr NimStrPayload](region.alloc(region, contentSize(s.len + addlen)))
  79. s.p.region = region
  80. s.p.cap = s.len + addlen
  81. if s.len > 0:
  82. # we are about to append, so there is no need to copy the \0 terminator:
  83. copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len)
  84. elif s.len + addlen > s.p.cap:
  85. let cap = max(s.len + addlen, resize(s.p.cap))
  86. s.p = cast[ptr NimStrPayload](s.p.region.realloc(s.p.region, s.p,
  87. oldSize = contentSize(s.p.cap),
  88. newSize = contentSize(cap)))
  89. s.p.cap = cap
  90. proc nimAddCharV1(s: var NimStringV2; c: char) {.compilerRtl.} =
  91. prepareAdd(s, 1)
  92. s.p.data[s.len] = c
  93. s.p.data[s.len+1] = '\0'
  94. inc s.len
  95. proc toNimStr(str: cstring, len: int): NimStringV2 {.compilerProc.} =
  96. if len <= 0:
  97. result = NimStringV2(len: 0, p: nil)
  98. else:
  99. let region = getLocalAllocator()
  100. var p = cast[ptr NimStrPayload](region.alloc(region, contentSize(len)))
  101. p.region = region
  102. p.cap = len
  103. if len > 0:
  104. # we are about to append, so there is no need to copy the \0 terminator:
  105. copyMem(unsafeAddr p.data[0], str, len)
  106. result = NimStringV2(len: 0, p: p)
  107. proc cstrToNimstr(str: cstring): NimStringV2 {.compilerRtl.} =
  108. if str == nil: toNimStr(str, 0)
  109. else: toNimStr(str, str.len)
  110. proc nimToCStringConv(s: NimStringV2): cstring {.compilerProc, inline.} =
  111. if s.len == 0: result = cstring""
  112. else: result = cstring(unsafeAddr s.p.data)
  113. proc appendString(dest: var NimStringV2; src: NimStringV2) {.compilerproc, inline.} =
  114. if src.len > 0:
  115. # also copy the \0 terminator:
  116. copyMem(unsafeAddr dest.p.data[dest.len], unsafeAddr src.p.data[0], src.len+1)
  117. proc appendChar(dest: var NimStringV2; c: char) {.compilerproc, inline.} =
  118. dest.p.data[dest.len] = c
  119. dest.p.data[dest.len+1] = '\0'
  120. inc dest.len
  121. proc rawNewString(space: int): NimStringV2 {.compilerProc.} =
  122. # this is also 'system.newStringOfCap'.
  123. if space <= 0:
  124. result = NimStringV2(len: 0, p: nil)
  125. else:
  126. let region = getLocalAllocator()
  127. var p = cast[ptr NimStrPayload](region.alloc(region, contentSize(space)))
  128. p.region = region
  129. p.cap = space
  130. result = NimStringV2(len: 0, p: p)
  131. proc mnewString(len: int): NimStringV2 {.compilerProc.} =
  132. if len <= 0:
  133. result = NimStringV2(len: 0, p: nil)
  134. else:
  135. let region = getLocalAllocator()
  136. var p = cast[ptr NimStrPayload](region.alloc(region, contentSize(len)))
  137. p.region = region
  138. p.cap = len
  139. result = NimStringV2(len: len, p: p)
  140. proc setLengthStrV2(s: var NimStringV2, newLen: int) {.compilerRtl.} =
  141. if newLen > s.len:
  142. prepareAdd(s, newLen - s.len)
  143. else:
  144. s.len = newLen
  145. # this also only works because the destructor
  146. # looks at s.p and not s.len