seqs_v2.nim 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  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. # import std/typetraits
  10. # strs already imported allocateds for us.
  11. # Some optimizations here may be not to empty-seq-initialize some symbols, then StrictNotNil complains.
  12. {.push warning[StrictNotNil]: off.} # See https://github.com/nim-lang/Nim/issues/21401
  13. ## Default seq implementation used by Nim's core.
  14. type
  15. NimSeqPayloadBase = object
  16. cap: int
  17. NimSeqPayload[T] = object
  18. cap: int
  19. data: UncheckedArray[T]
  20. NimSeqV2*[T] = object # \
  21. # if you change this implementation, also change seqs_v2_reimpl.nim!
  22. len: int
  23. p: ptr NimSeqPayload[T]
  24. NimRawSeq = object
  25. len: int
  26. p: pointer
  27. const nimSeqVersion {.core.} = 2
  28. # XXX make code memory safe for overflows in '*'
  29. proc newSeqPayload(cap, elemSize, elemAlign: int): pointer {.compilerRtl, raises: [].} =
  30. # we have to use type erasure here as Nim does not support generic
  31. # compilerProcs. Oh well, this will all be inlined anyway.
  32. if cap > 0:
  33. var p = cast[ptr NimSeqPayloadBase](alignedAlloc0(align(sizeof(NimSeqPayloadBase), elemAlign) + cap * elemSize, elemAlign))
  34. p.cap = cap
  35. result = p
  36. else:
  37. result = nil
  38. proc newSeqPayloadUninit(cap, elemSize, elemAlign: int): pointer {.compilerRtl, raises: [].} =
  39. # Used in `newSeqOfCap()`.
  40. if cap > 0:
  41. var p = cast[ptr NimSeqPayloadBase](alignedAlloc(align(sizeof(NimSeqPayloadBase), elemAlign) + cap * elemSize, elemAlign))
  42. p.cap = cap
  43. result = p
  44. else:
  45. result = nil
  46. template `+!`(p: pointer, s: int): pointer =
  47. cast[pointer](cast[int](p) +% s)
  48. template `-!`(p: pointer, s: int): pointer =
  49. cast[pointer](cast[int](p) -% s)
  50. proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {.
  51. noSideEffect, tags: [], raises: [], compilerRtl.} =
  52. {.noSideEffect.}:
  53. let headerSize = align(sizeof(NimSeqPayloadBase), elemAlign)
  54. if addlen <= 0:
  55. result = p
  56. elif p == nil:
  57. result = newSeqPayload(len+addlen, elemSize, elemAlign)
  58. else:
  59. # Note: this means we cannot support things that have internal pointers as
  60. # they get reallocated here. This needs to be documented clearly.
  61. var p = cast[ptr NimSeqPayloadBase](p)
  62. let oldCap = p.cap and not strlitFlag
  63. let newCap = max(resize(oldCap), len+addlen)
  64. var q: ptr NimSeqPayloadBase
  65. if (p.cap and strlitFlag) == strlitFlag:
  66. q = cast[ptr NimSeqPayloadBase](alignedAlloc(headerSize + elemSize * newCap, elemAlign))
  67. copyMem(q +! headerSize, p +! headerSize, len * elemSize)
  68. else:
  69. let oldSize = headerSize + elemSize * oldCap
  70. let newSize = headerSize + elemSize * newCap
  71. q = cast[ptr NimSeqPayloadBase](alignedRealloc(p, oldSize, newSize, elemAlign))
  72. zeroMem(q +! headerSize +! len * elemSize, addlen * elemSize)
  73. q.cap = newCap
  74. result = q
  75. proc zeroNewElements(len: int; q: pointer; addlen, elemSize, elemAlign: int) {.
  76. noSideEffect, tags: [], raises: [], compilerRtl.} =
  77. {.noSideEffect.}:
  78. let headerSize = align(sizeof(NimSeqPayloadBase), elemAlign)
  79. zeroMem(q +! headerSize +! len * elemSize, addlen * elemSize)
  80. proc prepareSeqAddUninit(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {.
  81. noSideEffect, tags: [], raises: [], compilerRtl.} =
  82. {.noSideEffect.}:
  83. let headerSize = align(sizeof(NimSeqPayloadBase), elemAlign)
  84. if addlen <= 0:
  85. result = p
  86. elif p == nil:
  87. result = newSeqPayloadUninit(len+addlen, elemSize, elemAlign)
  88. else:
  89. # Note: this means we cannot support things that have internal pointers as
  90. # they get reallocated here. This needs to be documented clearly.
  91. var p = cast[ptr NimSeqPayloadBase](p)
  92. let oldCap = p.cap and not strlitFlag
  93. let newCap = max(resize(oldCap), len+addlen)
  94. if (p.cap and strlitFlag) == strlitFlag:
  95. var q = cast[ptr NimSeqPayloadBase](alignedAlloc(headerSize + elemSize * newCap, elemAlign))
  96. copyMem(q +! headerSize, p +! headerSize, len * elemSize)
  97. q.cap = newCap
  98. result = q
  99. else:
  100. let oldSize = headerSize + elemSize * oldCap
  101. let newSize = headerSize + elemSize * newCap
  102. var q = cast[ptr NimSeqPayloadBase](alignedRealloc(p, oldSize, newSize, elemAlign))
  103. q.cap = newCap
  104. result = q
  105. proc shrink*[T](x: var seq[T]; newLen: Natural) {.tags: [], raises: [].} =
  106. when nimvm:
  107. {.cast(tags: []).}:
  108. setLen(x, newLen)
  109. else:
  110. #sysAssert newLen <= x.len, "invalid newLen parameter for 'shrink'"
  111. when not supportsCopyMem(T):
  112. for i in countdown(x.len - 1, newLen):
  113. reset x[i]
  114. # XXX This is wrong for const seqs that were moved into 'x'!
  115. {.noSideEffect.}:
  116. cast[ptr NimSeqV2[T]](addr x).len = newLen
  117. proc grow*[T](x: var seq[T]; newLen: Natural; value: T) {.nodestroy.} =
  118. let oldLen = x.len
  119. #sysAssert newLen >= x.len, "invalid newLen parameter for 'grow'"
  120. if newLen <= oldLen: return
  121. var xu = cast[ptr NimSeqV2[T]](addr x)
  122. if xu.p == nil or (xu.p.cap and not strlitFlag) < newLen:
  123. xu.p = cast[typeof(xu.p)](prepareSeqAddUninit(oldLen, xu.p, newLen - oldLen, sizeof(T), alignof(T)))
  124. xu.len = newLen
  125. for i in oldLen .. newLen-1:
  126. wasMoved(xu.p.data[i])
  127. `=copy`(xu.p.data[i], value)
  128. proc add*[T](x: var seq[T]; y: sink T) {.magic: "AppendSeqElem", noSideEffect, nodestroy.} =
  129. ## Generic proc for adding a data item `y` to a container `x`.
  130. ##
  131. ## For containers that have an order, `add` means *append*. New generic
  132. ## containers should also call their adding proc `add` for consistency.
  133. ## Generic code becomes much easier to write if the Nim naming scheme is
  134. ## respected.
  135. {.cast(noSideEffect).}:
  136. let oldLen = x.len
  137. var xu = cast[ptr NimSeqV2[T]](addr x)
  138. if xu.p == nil or (xu.p.cap and not strlitFlag) < oldLen+1:
  139. xu.p = cast[typeof(xu.p)](prepareSeqAddUninit(oldLen, xu.p, 1, sizeof(T), alignof(T)))
  140. xu.len = oldLen+1
  141. # .nodestroy means `xu.p.data[oldLen] = value` is compiled into a
  142. # copyMem(). This is fine as know by construction that
  143. # in `xu.p.data[oldLen]` there is nothing to destroy.
  144. # We also save the `wasMoved + destroy` pair for the sink parameter.
  145. xu.p.data[oldLen] = y
  146. proc setLen[T](s: var seq[T], newlen: Natural) {.nodestroy.} =
  147. {.noSideEffect.}:
  148. if newlen < s.len:
  149. shrink(s, newlen)
  150. else:
  151. let oldLen = s.len
  152. if newlen <= oldLen: return
  153. var xu = cast[ptr NimSeqV2[T]](addr s)
  154. if xu.p == nil or (xu.p.cap and not strlitFlag) < newlen:
  155. xu.p = cast[typeof(xu.p)](prepareSeqAddUninit(oldLen, xu.p, newlen - oldLen, sizeof(T), alignof(T)))
  156. xu.len = newlen
  157. for i in oldLen..<newlen:
  158. xu.p.data[i] = default(T)
  159. proc newSeq[T](s: var seq[T], len: Natural) =
  160. shrink(s, 0)
  161. setLen(s, len)
  162. proc sameSeqPayload(x: pointer, y: pointer): bool {.compilerRtl, inl.} =
  163. result = cast[ptr NimRawSeq](x)[].p == cast[ptr NimRawSeq](y)[].p
  164. func capacity*[T](self: seq[T]): int {.inline.} =
  165. ## Returns the current capacity of the seq.
  166. # See https://github.com/nim-lang/RFCs/issues/460
  167. runnableExamples:
  168. var lst = newSeqOfCap[string](cap = 42)
  169. lst.add "Nim"
  170. assert lst.capacity == 42
  171. let sek = cast[ptr NimSeqV2[T]](unsafeAddr self)
  172. result = if sek.p != nil: sek.p.cap and not strlitFlag else: 0
  173. func setLenUninit*[T](s: var seq[T], newlen: Natural) {.nodestroy.} =
  174. ## Sets the length of seq `s` to `newlen`. `T` may be any sequence type.
  175. ## New slots will not be initialized.
  176. ##
  177. ## If the current length is greater than the new length,
  178. ## `s` will be truncated.
  179. ## ```nim
  180. ## var x = @[10, 20]
  181. ## x.setLenUninit(5)
  182. ## x[4] = 50
  183. ## assert x[4] == 50
  184. ## x.setLenUninit(1)
  185. ## assert x == @[10]
  186. ## ```
  187. {.noSideEffect.}:
  188. if newlen < s.len:
  189. shrink(s, newlen)
  190. else:
  191. let oldLen = s.len
  192. if newlen <= oldLen: return
  193. var xu = cast[ptr NimSeqV2[T]](addr s)
  194. if xu.p == nil or (xu.p.cap and not strlitFlag) < newlen:
  195. xu.p = cast[typeof(xu.p)](prepareSeqAddUninit(oldLen, xu.p, newlen - oldLen, sizeof(T), alignof(T)))
  196. xu.len = newlen
  197. {.pop.} # See https://github.com/nim-lang/Nim/issues/21401