seqs_v2.nim 7.3 KB

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