seqs.nim 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  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 allocators for us.
  11. proc supportsCopyMem(t: typedesc): bool {.magic: "TypeTrait".}
  12. ## Default seq implementation used by Nim's core.
  13. type
  14. NimSeqPayload {.core.}[T] = object
  15. cap: int
  16. region: Allocator
  17. data: UncheckedArray[T]
  18. NimSeqV2*[T] = object
  19. len: int
  20. p: ptr NimSeqPayload[T]
  21. const nimSeqVersion {.core.} = 2
  22. template payloadSize(cap): int = cap * sizeof(T) + sizeof(int) + sizeof(Allocator)
  23. # XXX make code memory safe for overflows in '*'
  24. when false:
  25. # this is currently not part of Nim's type bound operators and so it's
  26. # built into the tracing proc generation just like before.
  27. proc `=trace`[T](s: NimSeqV2[T]) =
  28. for i in 0 ..< s.len: `=trace`(s.data[i])
  29. proc `=destroy`[T](s: var seq[T]) =
  30. var x = cast[ptr NimSeqV2[T]](addr s)
  31. var p = x.p
  32. if p != nil:
  33. when not supportsCopyMem(T):
  34. for i in 0..<x.len: `=destroy`(p.data[i])
  35. p.region.dealloc(p.region, p, payloadSize(p.cap))
  36. x.p = nil
  37. x.len = 0
  38. proc `=`[T](x: var seq[T]; y: seq[T]) =
  39. var a = cast[ptr NimSeqV2[T]](addr x)
  40. var b = cast[ptr NimSeqV2[T]](unsafeAddr y)
  41. if a.p == b.p: return
  42. `=destroy`(a)
  43. a.len = b.len
  44. if b.p != nil:
  45. a.p = cast[type(a.p)](alloc(payloadSize(a.len)))
  46. when supportsCopyMem(T):
  47. if a.len > 0:
  48. copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], a.len * sizeof(T))
  49. else:
  50. for i in 0..<a.len:
  51. a.p.data[i] = b.p.data[i]
  52. proc `=sink`[T](x: var seq[T]; y: seq[T]) =
  53. var a = cast[ptr NimSeqV2[T]](addr x)
  54. var b = cast[ptr NimSeqV2[T]](unsafeAddr y)
  55. if a.p != nil and a.p != b.p:
  56. `=destroy`(a)
  57. a.len = b.len
  58. a.p = b.p
  59. when false:
  60. proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.}
  61. proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
  62. compilerRtl.}
  63. proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.}
  64. type
  65. PayloadBase = object
  66. cap: int
  67. region: Allocator
  68. proc newSeqPayload(cap, elemSize: int): pointer {.compilerRtl.} =
  69. # we have to use type erasure here as Nim does not support generic
  70. # compilerProcs. Oh well, this will all be inlined anyway.
  71. if cap <= 0:
  72. let region = getLocalAllocator()
  73. var p = cast[ptr PayloadBase](region.alloc(region, cap * elemSize + sizeof(int) + sizeof(Allocator)))
  74. p.region = region
  75. p.cap = cap
  76. result = p
  77. else:
  78. result = nil
  79. proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize: int): pointer {.compilerRtl.} =
  80. if len+addlen <= len:
  81. result = p
  82. elif p == nil:
  83. result = newSeqPayload(len+addlen, elemSize)
  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 PayloadBase](p)
  88. let region = if p.region == nil: getLocalAllocator() else: p.region
  89. let cap = max(resize(p.cap), len+addlen)
  90. var q = cast[ptr PayloadBase](region.realloc(region, p,
  91. sizeof(int) + sizeof(Allocator) + elemSize * p.cap,
  92. sizeof(int) + sizeof(Allocator) + elemSize * cap))
  93. q.region = region
  94. q.cap = cap
  95. result = q
  96. proc shrink*[T](x: var seq[T]; newLen: Natural) =
  97. sysAssert newLen <= x.len, "invalid newLen parameter for 'shrink'"
  98. when not supportsCopyMem(T):
  99. for i in countdown(x.len - 1, newLen - 1):
  100. `=destroy`(x[i])
  101. cast[ptr NimSeqV2[T]](addr x).len = newLen
  102. proc grow*[T](x: var seq[T]; newLen: Natural; value: T) =
  103. let oldLen = x.len
  104. if newLen <= oldLen: return
  105. var xu = cast[ptr NimSeqV2[T]](addr x)
  106. xu.p = prepareSeqAdd(oldLen, xu.p, newLen - oldLen, sizeof(T))
  107. xu.len = newLen
  108. for i in oldLen .. newLen-1:
  109. x.data[i] = value
  110. proc setLen[T](s: var seq[T], newlen: Natural) =
  111. if newlen < s.len:
  112. shrink(s, newLen)
  113. else:
  114. var v: T # get the default value of 'v'
  115. grow(s, newLen, v)
  116. when false:
  117. proc resize[T](s: var NimSeqV2[T]) =
  118. let old = s.cap
  119. if old == 0: s.cap = 8
  120. else: s.cap = (s.cap * 3) shr 1
  121. s.data = cast[type(s.data)](realloc(s.data, old * sizeof(T), s.cap * sizeof(T)))
  122. proc reserveSlot[T](x: var NimSeqV2[T]): ptr T =
  123. if x.len >= x.cap: resize(x)
  124. result = addr(x.data[x.len])
  125. inc x.len
  126. template add*[T](x: var NimSeqV2[T]; y: T) =
  127. reserveSlot(x)[] = y
  128. template `[]`*[T](x: NimSeqV2[T]; i: Natural): T =
  129. assert i < x.len
  130. x.data[i]
  131. template `[]=`*[T](x: NimSeqV2[T]; i: Natural; y: T) =
  132. assert i < x.len
  133. x.data[i] = y
  134. proc `@`*[T](elems: openArray[T]): NimSeqV2[T] =
  135. result.cap = elems.len
  136. result.len = elems.len
  137. result.data = cast[type(result.data)](alloc(result.cap * sizeof(T)))
  138. when supportsCopyMem(T):
  139. copyMem(result.data, unsafeAddr(elems[0]), result.cap * sizeof(T))
  140. else:
  141. for i in 0..<result.len:
  142. result.data[i] = elems[i]