seqs_v2.nim 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  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. proc supportsCopyMem(t: typedesc): bool {.magic: "TypeTrait".}
  12. ## Default seq implementation used by Nim's core.
  13. type
  14. NimSeqPayloadBase = object
  15. cap: int
  16. NimSeqPayload[T] = object
  17. cap: int
  18. data: UncheckedArray[T]
  19. NimSeqV2*[T] = object
  20. len: int
  21. p: ptr NimSeqPayload[T]
  22. const nimSeqVersion {.core.} = 2
  23. # XXX make code memory safe for overflows in '*'
  24. proc newSeqPayload(cap, elemSize: int): pointer {.compilerRtl, raises: [].} =
  25. # we have to use type erasure here as Nim does not support generic
  26. # compilerProcs. Oh well, this will all be inlined anyway.
  27. if cap > 0:
  28. var p = cast[ptr NimSeqPayloadBase](allocShared0(cap * elemSize + sizeof(NimSeqPayloadBase)))
  29. p.cap = cap
  30. result = p
  31. else:
  32. result = nil
  33. proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize: int): pointer {.
  34. noSideEffect, raises: [].} =
  35. {.noSideEffect.}:
  36. template `+!`(p: pointer, s: int): pointer =
  37. cast[pointer](cast[int](p) +% s)
  38. const headerSize = sizeof(NimSeqPayloadBase)
  39. if addlen <= 0:
  40. result = p
  41. elif p == nil:
  42. result = newSeqPayload(len+addlen, elemSize)
  43. else:
  44. # Note: this means we cannot support things that have internal pointers as
  45. # they get reallocated here. This needs to be documented clearly.
  46. var p = cast[ptr NimSeqPayloadBase](p)
  47. let oldCap = p.cap and not strlitFlag
  48. let newCap = max(resize(oldCap), len+addlen)
  49. if (p.cap and strlitFlag) == strlitFlag:
  50. var q = cast[ptr NimSeqPayloadBase](allocShared0(headerSize + elemSize * newCap))
  51. copyMem(q +! headerSize, p +! headerSize, len * elemSize)
  52. q.cap = newCap
  53. result = q
  54. else:
  55. let oldSize = headerSize + elemSize * oldCap
  56. let newSize = headerSize + elemSize * newCap
  57. var q = cast[ptr NimSeqPayloadBase](reallocShared0(p, oldSize, newSize))
  58. q.cap = newCap
  59. result = q
  60. proc shrink*[T](x: var seq[T]; newLen: Natural) =
  61. when nimvm:
  62. setLen(x, newLen)
  63. else:
  64. mixin `=destroy`
  65. sysAssert newLen <= x.len, "invalid newLen parameter for 'shrink'"
  66. when not supportsCopyMem(T):
  67. for i in countdown(x.len - 1, newLen):
  68. `=destroy`(x[i])
  69. # XXX This is wrong for const seqs that were moved into 'x'!
  70. cast[ptr NimSeqV2[T]](addr x).len = newLen
  71. proc grow*[T](x: var seq[T]; newLen: Natural; value: T) =
  72. let oldLen = x.len
  73. if newLen <= oldLen: return
  74. var xu = cast[ptr NimSeqV2[T]](addr x)
  75. if xu.p == nil or xu.p.cap < newLen:
  76. xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, newLen - oldLen, sizeof(T)))
  77. xu.len = newLen
  78. for i in oldLen .. newLen-1:
  79. xu.p.data[i] = value
  80. proc add*[T](x: var seq[T]; value: sink T) {.magic: "AppendSeqElem", noSideEffect, nodestroy.} =
  81. ## Generic proc for adding a data item `y` to a container `x`.
  82. ##
  83. ## For containers that have an order, `add` means *append*. New generic
  84. ## containers should also call their adding proc `add` for consistency.
  85. ## Generic code becomes much easier to write if the Nim naming scheme is
  86. ## respected.
  87. let oldLen = x.len
  88. var xu = cast[ptr NimSeqV2[T]](addr x)
  89. if xu.p == nil or xu.p.cap < oldLen+1:
  90. xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, 1, sizeof(T)))
  91. xu.len = oldLen+1
  92. # .nodestroy means `xu.p.data[oldLen] = value` is compiled into a
  93. # copyMem(). This is fine as know by construction that
  94. # in `xu.p.data[oldLen]` there is nothing to destroy.
  95. # We also save the `wasMoved + destroy` pair for the sink parameter.
  96. xu.p.data[oldLen] = value
  97. proc setLen[T](s: var seq[T], newlen: Natural) =
  98. {.noSideEffect.}:
  99. if newlen < s.len:
  100. shrink(s, newlen)
  101. else:
  102. let oldLen = s.len
  103. if newlen <= oldLen: return
  104. var xu = cast[ptr NimSeqV2[T]](addr s)
  105. if xu.p == nil or xu.p.cap < newlen:
  106. xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, newlen - oldLen, sizeof(T)))
  107. xu.len = newlen