strs_v2.nim 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  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. type
  11. NimStrPayloadBase = object
  12. cap: int
  13. NimStrPayload {.core.} = object
  14. cap: int
  15. data: UncheckedArray[char]
  16. NimStringV2 {.core.} = object
  17. len: int
  18. p: ptr NimStrPayload ## can be nil if len == 0.
  19. const nimStrVersion {.core.} = 2
  20. template isLiteral(s): bool = (s.p == nil) or (s.p.cap and strlitFlag) == strlitFlag
  21. template contentSize(cap): int = cap + 1 + sizeof(NimStrPayloadBase)
  22. template frees(s) =
  23. if not isLiteral(s):
  24. deallocShared(s.p)
  25. proc resize(old: int): int {.inline.} =
  26. if old <= 0: result = 4
  27. elif old < 65536: result = old * 2
  28. else: result = old * 3 div 2 # for large arrays * 3/2 is better
  29. proc prepareAdd(s: var NimStringV2; addlen: int) {.compilerRtl.} =
  30. if isLiteral(s):
  31. let oldP = s.p
  32. # can't mutate a literal, so we need a fresh copy here:
  33. s.p = cast[ptr NimStrPayload](allocShared0(contentSize(s.len + addlen)))
  34. s.p.cap = s.len + addlen
  35. if s.len > 0:
  36. # we are about to append, so there is no need to copy the \0 terminator:
  37. copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len)
  38. else:
  39. let oldCap = s.p.cap and not strlitFlag
  40. if s.len + addlen > oldCap:
  41. let newCap = max(s.len + addlen, resize(oldCap))
  42. s.p = cast[ptr NimStrPayload](reallocShared0(s.p, contentSize(oldCap), contentSize(newCap)))
  43. s.p.cap = newCap
  44. proc nimAddCharV1(s: var NimStringV2; c: char) {.compilerRtl.} =
  45. prepareAdd(s, 1)
  46. s.p.data[s.len] = c
  47. s.p.data[s.len+1] = '\0'
  48. inc s.len
  49. proc toNimStr(str: cstring, len: int): NimStringV2 {.compilerproc.} =
  50. if len <= 0:
  51. result = NimStringV2(len: 0, p: nil)
  52. else:
  53. var p = cast[ptr NimStrPayload](allocShared0(contentSize(len)))
  54. p.cap = len
  55. if len > 0:
  56. # we are about to append, so there is no need to copy the \0 terminator:
  57. copyMem(unsafeAddr p.data[0], str, len)
  58. result = NimStringV2(len: len, p: p)
  59. proc cstrToNimstr(str: cstring): NimStringV2 {.compilerRtl.} =
  60. if str == nil: toNimStr(str, 0)
  61. else: toNimStr(str, str.len)
  62. proc nimToCStringConv(s: NimStringV2): cstring {.compilerproc, nonReloadable, inline.} =
  63. if s.len == 0: result = cstring""
  64. else: result = cstring(unsafeAddr s.p.data)
  65. proc appendString(dest: var NimStringV2; src: NimStringV2) {.compilerproc, inline.} =
  66. if src.len > 0:
  67. # also copy the \0 terminator:
  68. copyMem(unsafeAddr dest.p.data[dest.len], unsafeAddr src.p.data[0], src.len+1)
  69. inc dest.len, src.len
  70. proc appendChar(dest: var NimStringV2; c: char) {.compilerproc, inline.} =
  71. dest.p.data[dest.len] = c
  72. dest.p.data[dest.len+1] = '\0'
  73. inc dest.len
  74. proc rawNewString(space: int): NimStringV2 {.compilerproc.} =
  75. # this is also 'system.newStringOfCap'.
  76. if space <= 0:
  77. result = NimStringV2(len: 0, p: nil)
  78. else:
  79. var p = cast[ptr NimStrPayload](allocShared0(contentSize(space)))
  80. p.cap = space
  81. result = NimStringV2(len: 0, p: p)
  82. proc mnewString(len: int): NimStringV2 {.compilerproc.} =
  83. if len <= 0:
  84. result = NimStringV2(len: 0, p: nil)
  85. else:
  86. var p = cast[ptr NimStrPayload](allocShared0(contentSize(len)))
  87. p.cap = len
  88. result = NimStringV2(len: len, p: p)
  89. proc setLengthStrV2(s: var NimStringV2, newLen: int) {.compilerRtl.} =
  90. if newLen == 0:
  91. frees(s)
  92. s.p = nil
  93. else:
  94. if newLen > s.len or isLiteral(s):
  95. prepareAdd(s, newLen - s.len)
  96. s.p.data[newLen] = '\0'
  97. s.len = newLen
  98. proc nimAsgnStrV2(a: var NimStringV2, b: NimStringV2) {.compilerRtl.} =
  99. if a.p == b.p: return
  100. if isLiteral(b):
  101. # we can shallow copy literals:
  102. frees(a)
  103. a.len = b.len
  104. a.p = b.p
  105. else:
  106. if isLiteral(a) or (a.p.cap and not strlitFlag) < b.len:
  107. # we have to allocate the 'cap' here, consider
  108. # 'let y = newStringOfCap(); var x = y'
  109. # on the other hand... These get turned into moves now.
  110. frees(a)
  111. a.p = cast[ptr NimStrPayload](allocShared0(contentSize(b.len)))
  112. a.p.cap = b.len
  113. a.len = b.len
  114. copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1)
  115. proc nimPrepareStrMutationV2(s: var NimStringV2) {.compilerRtl.} =
  116. if s.p != nil and (s.p.cap and strlitFlag) == strlitFlag:
  117. let oldP = s.p
  118. # can't mutate a literal, so we need a fresh copy here:
  119. s.p = cast[ptr NimStrPayload](allocShared0(contentSize(s.len)))
  120. s.p.cap = s.len
  121. copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len+1)