strs.nim 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  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. allocator: 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.allocator == nil
  32. template contentSize(cap): int = cap + 1 + sizeof(int) + sizeof(Allocator)
  33. template frees(s) =
  34. if not isLiteral(s):
  35. s.p.allocator.dealloc(s.p.allocator, s.p, contentSize(s.p.cap))
  36. when not defined(nimV2):
  37. proc `=destroy`(s: var string) =
  38. var a = cast[ptr NimStringV2](addr s)
  39. frees(a)
  40. a.len = 0
  41. a.p = nil
  42. proc `=sink`(x: var string, y: string) =
  43. var a = cast[ptr NimStringV2](addr x)
  44. var b = cast[ptr NimStringV2](unsafeAddr y)
  45. # we hope this is optimized away for not yet alive objects:
  46. if unlikely(a.p == b.p): return
  47. frees(a)
  48. a.len = b.len
  49. a.p = b.p
  50. proc `=`(x: var string, y: string) =
  51. var a = cast[ptr NimStringV2](addr x)
  52. var b = cast[ptr NimStringV2](unsafeAddr y)
  53. if unlikely(a.p == b.p): return
  54. frees(a)
  55. a.len = b.len
  56. if isLiteral(b):
  57. # we can shallow copy literals:
  58. a.p = b.p
  59. else:
  60. let allocator = if a.p != nil and a.p.allocator != nil: a.p.allocator else: getLocalAllocator()
  61. # we have to allocate the 'cap' here, consider
  62. # 'let y = newStringOfCap(); var x = y'
  63. # on the other hand... These get turned into moves now.
  64. a.p = cast[ptr NimStrPayload](allocator.alloc(allocator, contentSize(b.len)))
  65. a.p.allocator = allocator
  66. a.p.cap = b.len
  67. copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1)
  68. proc resize(old: int): int {.inline.} =
  69. if old <= 0: result = 4
  70. elif old < 65536: result = old * 2
  71. else: result = old * 3 div 2 # for large arrays * 3/2 is better
  72. proc prepareAdd(s: var NimStringV2; addlen: int) {.compilerRtl.} =
  73. if isLiteral(s) and addlen > 0:
  74. let oldP = s.p
  75. # can't mutate a literal, so we need a fresh copy here:
  76. let allocator = getLocalAllocator()
  77. s.p = cast[ptr NimStrPayload](allocator.alloc(allocator, contentSize(s.len + addlen)))
  78. s.p.allocator = allocator
  79. s.p.cap = s.len + addlen
  80. if s.len > 0:
  81. # we are about to append, so there is no need to copy the \0 terminator:
  82. copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len)
  83. elif s.len + addlen > s.p.cap:
  84. let cap = max(s.len + addlen, resize(s.p.cap))
  85. s.p = cast[ptr NimStrPayload](s.p.allocator.realloc(s.p.allocator, s.p,
  86. oldSize = contentSize(s.p.cap),
  87. newSize = contentSize(cap)))
  88. s.p.cap = cap
  89. proc nimAddCharV1(s: var NimStringV2; c: char) {.compilerRtl.} =
  90. prepareAdd(s, 1)
  91. s.p.data[s.len] = c
  92. s.p.data[s.len+1] = '\0'
  93. inc s.len
  94. proc toNimStr(str: cstring, len: int): NimStringV2 {.compilerProc.} =
  95. if len <= 0:
  96. result = NimStringV2(len: 0, p: nil)
  97. else:
  98. let allocator = getLocalAllocator()
  99. var p = cast[ptr NimStrPayload](allocator.alloc(allocator, contentSize(len)))
  100. p.allocator = allocator
  101. p.cap = len
  102. if len > 0:
  103. # we are about to append, so there is no need to copy the \0 terminator:
  104. copyMem(unsafeAddr p.data[0], str, len)
  105. result = NimStringV2(len: len, p: p)
  106. proc cstrToNimstr(str: cstring): NimStringV2 {.compilerRtl.} =
  107. if str == nil: toNimStr(str, 0)
  108. else: toNimStr(str, str.len)
  109. proc nimToCStringConv(s: NimStringV2): cstring {.compilerProc, nonReloadable, inline.} =
  110. if s.len == 0: result = cstring""
  111. else: result = cstring(unsafeAddr s.p.data)
  112. proc appendString(dest: var NimStringV2; src: NimStringV2) {.compilerproc, inline.} =
  113. if src.len > 0:
  114. # also copy the \0 terminator:
  115. copyMem(unsafeAddr dest.p.data[dest.len], unsafeAddr src.p.data[0], src.len+1)
  116. inc dest.len, src.len
  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 allocator = getLocalAllocator()
  127. var p = cast[ptr NimStrPayload](allocator.alloc(allocator, contentSize(space)))
  128. p.allocator = allocator
  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 allocator = getLocalAllocator()
  136. var p = cast[ptr NimStrPayload](allocator.alloc(allocator, contentSize(len)))
  137. p.allocator = allocator
  138. p.cap = len
  139. result = NimStringV2(len: len, p: p)
  140. proc setLengthStrV2(s: var NimStringV2, newLen: int) {.compilerRtl.} =
  141. if newLen == 0:
  142. frees(s)
  143. s.p = nil
  144. elif newLen > s.len or isLiteral(s):
  145. prepareAdd(s, newLen - s.len)
  146. s.len = newLen
  147. proc nimAsgnStrV2(a: var NimStringV2, b: NimStringV2) {.compilerRtl.} =
  148. if a.p == b.p: return
  149. if isLiteral(b):
  150. # we can shallow copy literals:
  151. frees(a)
  152. a.len = b.len
  153. a.p = b.p
  154. else:
  155. if isLiteral(a) or a.p.cap < b.len:
  156. let allocator = if a.p != nil and a.p.allocator != nil: a.p.allocator else: getLocalAllocator()
  157. # we have to allocate the 'cap' here, consider
  158. # 'let y = newStringOfCap(); var x = y'
  159. # on the other hand... These get turned into moves now.
  160. frees(a)
  161. a.p = cast[ptr NimStrPayload](allocator.alloc(allocator, contentSize(b.len)))
  162. a.p.allocator = allocator
  163. a.p.cap = b.len
  164. a.len = b.len
  165. copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1)