seqs_v2.nim 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  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 prepareSeqAddUninit(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {.
  76. noSideEffect, tags: [], raises: [], compilerRtl.} =
  77. {.noSideEffect.}:
  78. let headerSize = align(sizeof(NimSeqPayloadBase), elemAlign)
  79. if addlen <= 0:
  80. result = p
  81. elif p == nil:
  82. result = newSeqPayloadUninit(len+addlen, elemSize, elemAlign)
  83. else:
  84. # Note: this means we cannot support things that have internal pointers as
  85. # they get reallocated here. This needs to be documented clearly.
  86. var p = cast[ptr NimSeqPayloadBase](p)
  87. let oldCap = p.cap and not strlitFlag
  88. let newCap = max(resize(oldCap), len+addlen)
  89. if (p.cap and strlitFlag) == strlitFlag:
  90. var q = cast[ptr NimSeqPayloadBase](alignedAlloc(headerSize + elemSize * newCap, elemAlign))
  91. copyMem(q +! headerSize, p +! headerSize, len * elemSize)
  92. q.cap = newCap
  93. result = q
  94. else:
  95. let oldSize = headerSize + elemSize * oldCap
  96. let newSize = headerSize + elemSize * newCap
  97. var q = cast[ptr NimSeqPayloadBase](alignedRealloc(p, oldSize, newSize, elemAlign))
  98. q.cap = newCap
  99. result = q
  100. proc shrink*[T](x: var seq[T]; newLen: Natural) {.tags: [], raises: [].} =
  101. when nimvm:
  102. {.cast(tags: []).}:
  103. setLen(x, newLen)
  104. else:
  105. #sysAssert newLen <= x.len, "invalid newLen parameter for 'shrink'"
  106. when not supportsCopyMem(T):
  107. for i in countdown(x.len - 1, newLen):
  108. reset x[i]
  109. # XXX This is wrong for const seqs that were moved into 'x'!
  110. {.noSideEffect.}:
  111. cast[ptr NimSeqV2[T]](addr x).len = newLen
  112. proc grow*[T](x: var seq[T]; newLen: Natural; value: T) {.nodestroy.} =
  113. let oldLen = x.len
  114. #sysAssert newLen >= x.len, "invalid newLen parameter for 'grow'"
  115. if newLen <= oldLen: return
  116. var xu = cast[ptr NimSeqV2[T]](addr x)
  117. if xu.p == nil or (xu.p.cap and not strlitFlag) < newLen:
  118. xu.p = cast[typeof(xu.p)](prepareSeqAddUninit(oldLen, xu.p, newLen - oldLen, sizeof(T), alignof(T)))
  119. xu.len = newLen
  120. for i in oldLen .. newLen-1:
  121. wasMoved(xu.p.data[i])
  122. `=copy`(xu.p.data[i], value)
  123. proc add*[T](x: var seq[T]; y: sink T) {.magic: "AppendSeqElem", noSideEffect, nodestroy.} =
  124. ## Generic proc for adding a data item `y` to a container `x`.
  125. ##
  126. ## For containers that have an order, `add` means *append*. New generic
  127. ## containers should also call their adding proc `add` for consistency.
  128. ## Generic code becomes much easier to write if the Nim naming scheme is
  129. ## respected.
  130. {.cast(noSideEffect).}:
  131. let oldLen = x.len
  132. var xu = cast[ptr NimSeqV2[T]](addr x)
  133. if xu.p == nil or (xu.p.cap and not strlitFlag) < oldLen+1:
  134. xu.p = cast[typeof(xu.p)](prepareSeqAddUninit(oldLen, xu.p, 1, sizeof(T), alignof(T)))
  135. xu.len = oldLen+1
  136. # .nodestroy means `xu.p.data[oldLen] = value` is compiled into a
  137. # copyMem(). This is fine as know by construction that
  138. # in `xu.p.data[oldLen]` there is nothing to destroy.
  139. # We also save the `wasMoved + destroy` pair for the sink parameter.
  140. xu.p.data[oldLen] = y
  141. proc setLen[T](s: var seq[T], newlen: Natural) {.nodestroy.} =
  142. {.noSideEffect.}:
  143. if newlen < s.len:
  144. shrink(s, newlen)
  145. else:
  146. let oldLen = s.len
  147. if newlen <= oldLen: return
  148. var xu = cast[ptr NimSeqV2[T]](addr s)
  149. if xu.p == nil or (xu.p.cap and not strlitFlag) < newlen:
  150. xu.p = cast[typeof(xu.p)](prepareSeqAddUninit(oldLen, xu.p, newlen - oldLen, sizeof(T), alignof(T)))
  151. xu.len = newlen
  152. for i in oldLen..<newlen:
  153. xu.p.data[i] = default(T)
  154. proc newSeq[T](s: var seq[T], len: Natural) =
  155. shrink(s, 0)
  156. setLen(s, len)
  157. proc sameSeqPayload(x: pointer, y: pointer): bool {.compilerRtl, inl.} =
  158. result = cast[ptr NimRawSeq](x)[].p == cast[ptr NimRawSeq](y)[].p
  159. func capacity*[T](self: seq[T]): int {.inline.} =
  160. ## Returns the current capacity of the seq.
  161. # See https://github.com/nim-lang/RFCs/issues/460
  162. runnableExamples:
  163. var lst = newSeqOfCap[string](cap = 42)
  164. lst.add "Nim"
  165. assert lst.capacity == 42
  166. let sek = cast[ptr NimSeqV2[T]](unsafeAddr self)
  167. result = if sek.p != nil: sek.p.cap and not strlitFlag else: 0
  168. func setLenUninit*[T](s: var seq[T], newlen: Natural) {.nodestroy.} =
  169. ## Sets the length of seq `s` to `newlen`. `T` may be any sequence type.
  170. ## New slots will not be initialized.
  171. ##
  172. ## If the current length is greater than the new length,
  173. ## `s` will be truncated.
  174. ## ```nim
  175. ## var x = @[10, 20]
  176. ## x.setLenUninit(5)
  177. ## x[4] = 50
  178. ## assert x[4] == 50
  179. ## x.setLenUninit(1)
  180. ## assert x == @[10]
  181. ## ```
  182. {.noSideEffect.}:
  183. if newlen < s.len:
  184. shrink(s, newlen)
  185. else:
  186. let oldLen = s.len
  187. if newlen <= oldLen: return
  188. var xu = cast[ptr NimSeqV2[T]](addr s)
  189. if xu.p == nil or (xu.p.cap and not strlitFlag) < newlen:
  190. xu.p = cast[typeof(xu.p)](prepareSeqAddUninit(oldLen, xu.p, newlen - oldLen, sizeof(T), alignof(T)))
  191. xu.len = newlen
  192. {.pop.} # See https://github.com/nim-lang/Nim/issues/21401