deepcopy.nim 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2015 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. const
  10. TableSize = when sizeof(int) <= 2: 0xff else: 0xff_ffff
  11. type
  12. PtrTable = ptr object
  13. counter, max: int
  14. data: array[TableSize, (pointer, pointer)]
  15. template hashPtr(key: pointer): int = cast[int](key) shr 8
  16. template allocPtrTable: untyped =
  17. cast[PtrTable](alloc0(sizeof(int)*2 + sizeof(pointer)*2*cap))
  18. proc rehash(t: PtrTable): PtrTable =
  19. let cap = (t.max+1) * 2
  20. result = allocPtrTable()
  21. result.counter = t.counter
  22. result.max = cap-1
  23. for i in 0..t.max:
  24. let k = t.data[i][0]
  25. if k != nil:
  26. var h = hashPtr(k)
  27. while result.data[h and result.max][0] != nil: inc h
  28. result.data[h and result.max] = t.data[i]
  29. dealloc t
  30. proc initPtrTable(): PtrTable =
  31. const cap = 32
  32. result = allocPtrTable()
  33. result.counter = 0
  34. result.max = cap-1
  35. template deinit(t: PtrTable) = dealloc(t)
  36. proc get(t: PtrTable; key: pointer): pointer =
  37. var h = hashPtr(key)
  38. while true:
  39. let k = t.data[h and t.max][0]
  40. if k == nil: break
  41. if k == key:
  42. return t.data[h and t.max][1]
  43. inc h
  44. proc put(t: var PtrTable; key, val: pointer) =
  45. if (t.max+1) * 2 < t.counter * 3: t = rehash(t)
  46. var h = hashPtr(key)
  47. while t.data[h and t.max][0] != nil: inc h
  48. t.data[h and t.max] = (key, val)
  49. inc t.counter
  50. proc genericDeepCopyAux(dest, src: pointer, mt: PNimType;
  51. tab: var PtrTable) {.benign.}
  52. proc genericDeepCopyAux(dest, src: pointer, n: ptr TNimNode;
  53. tab: var PtrTable) {.benign.} =
  54. var
  55. d = cast[int](dest)
  56. s = cast[int](src)
  57. case n.kind
  58. of nkSlot:
  59. genericDeepCopyAux(cast[pointer](d +% n.offset),
  60. cast[pointer](s +% n.offset), n.typ, tab)
  61. of nkList:
  62. for i in 0..n.len-1:
  63. genericDeepCopyAux(dest, src, n.sons[i], tab)
  64. of nkCase:
  65. var dd = selectBranch(dest, n)
  66. var m = selectBranch(src, n)
  67. # reset if different branches are in use; note different branches also
  68. # imply that's not self-assignment (``x = x``)!
  69. if m != dd and dd != nil:
  70. genericResetAux(dest, dd)
  71. copyMem(cast[pointer](d +% n.offset), cast[pointer](s +% n.offset),
  72. n.typ.size)
  73. if m != nil:
  74. genericDeepCopyAux(dest, src, m, tab)
  75. of nkNone: sysAssert(false, "genericDeepCopyAux")
  76. proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) =
  77. var
  78. d = cast[int](dest)
  79. s = cast[int](src)
  80. sysAssert(mt != nil, "genericDeepCopyAux 2")
  81. case mt.kind
  82. of tyString:
  83. when defined(nimSeqsV2):
  84. var x = cast[ptr NimStringV2](dest)
  85. var s2 = cast[ptr NimStringV2](s)[]
  86. nimAsgnStrV2(x[], s2)
  87. else:
  88. var x = cast[PPointer](dest)
  89. var s2 = cast[PPointer](s)[]
  90. if s2 == nil:
  91. unsureAsgnRef(x, s2)
  92. else:
  93. unsureAsgnRef(x, copyDeepString(cast[NimString](s2)))
  94. of tySequence:
  95. when defined(nimSeqsV2):
  96. deepSeqAssignImpl(genericDeepCopyAux, tab)
  97. else:
  98. var s2 = cast[PPointer](src)[]
  99. var seq = cast[PGenericSeq](s2)
  100. var x = cast[PPointer](dest)
  101. if s2 == nil:
  102. unsureAsgnRef(x, s2)
  103. return
  104. sysAssert(dest != nil, "genericDeepCopyAux 3")
  105. unsureAsgnRef(x, newSeq(mt, seq.len))
  106. var dst = cast[int](cast[PPointer](dest)[])
  107. for i in 0..seq.len-1:
  108. genericDeepCopyAux(
  109. cast[pointer](dst +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size),
  110. cast[pointer](cast[int](s2) +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size),
  111. mt.base, tab)
  112. of tyObject:
  113. # we need to copy m_type field for tyObject, as it could be empty for
  114. # sequence reallocations:
  115. if mt.base != nil:
  116. genericDeepCopyAux(dest, src, mt.base, tab)
  117. else:
  118. var pint = cast[ptr PNimType](dest)
  119. pint[] = cast[ptr PNimType](src)[]
  120. genericDeepCopyAux(dest, src, mt.node, tab)
  121. of tyTuple:
  122. genericDeepCopyAux(dest, src, mt.node, tab)
  123. of tyArray, tyArrayConstr:
  124. for i in 0..(mt.size div mt.base.size)-1:
  125. genericDeepCopyAux(cast[pointer](d +% i *% mt.base.size),
  126. cast[pointer](s +% i *% mt.base.size), mt.base, tab)
  127. of tyRef:
  128. let s2 = cast[PPointer](src)[]
  129. if s2 == nil:
  130. unsureAsgnRef(cast[PPointer](dest), s2)
  131. elif mt.base.deepcopy != nil:
  132. let z = mt.base.deepcopy(s2)
  133. when defined(nimSeqsV2):
  134. cast[PPointer](dest)[] = z
  135. else:
  136. unsureAsgnRef(cast[PPointer](dest), z)
  137. else:
  138. let z = tab.get(s2)
  139. if z == nil:
  140. when declared(usrToCell):
  141. let x = usrToCell(s2)
  142. let realType = x.typ
  143. let z = newObj(realType, realType.base.size)
  144. unsureAsgnRef(cast[PPointer](dest), z)
  145. tab.put(s2, z)
  146. genericDeepCopyAux(z, s2, realType.base, tab)
  147. else:
  148. when false:
  149. # addition check disabled
  150. let x = usrToCell(s2)
  151. let realType = x.typ
  152. sysAssert realType == mt, " types do differ"
  153. when defined(nimSeqsV2):
  154. let typ = if mt.base.kind == tyObject: cast[PNimType](cast[ptr PNimTypeV2](s2)[].typeInfoV1)
  155. else: mt.base
  156. let z = nimNewObj(typ.size, typ.align)
  157. cast[PPointer](dest)[] = z
  158. else:
  159. # this version should work for any other GC:
  160. let typ = if mt.base.kind == tyObject: cast[ptr PNimType](s2)[] else: mt.base
  161. let z = newObj(mt, typ.size)
  162. unsureAsgnRef(cast[PPointer](dest), z)
  163. tab.put(s2, z)
  164. genericDeepCopyAux(z, s2, typ, tab)
  165. else:
  166. unsureAsgnRef(cast[PPointer](dest), z)
  167. of tyPtr:
  168. # no cycle check here, but also not really required
  169. let s2 = cast[PPointer](src)[]
  170. if s2 != nil and mt.base.deepcopy != nil:
  171. cast[PPointer](dest)[] = mt.base.deepcopy(s2)
  172. else:
  173. cast[PPointer](dest)[] = s2
  174. else:
  175. copyMem(dest, src, mt.size)
  176. proc genericDeepCopy(dest, src: pointer, mt: PNimType) {.compilerproc.} =
  177. when not defined(nimSeqsV2): GC_disable()
  178. var tab = initPtrTable()
  179. genericDeepCopyAux(dest, src, mt, tab)
  180. deinit tab
  181. when not defined(nimSeqsV2): GC_enable()
  182. proc genericSeqDeepCopy(dest, src: pointer, mt: PNimType) {.compilerproc.} =
  183. # also invoked for 'string'
  184. var src = src
  185. genericDeepCopy(dest, addr(src), mt)
  186. proc genericDeepCopyOpenArray(dest, src: pointer, len: int,
  187. mt: PNimType) {.compilerproc.} =
  188. var
  189. d = cast[int](dest)
  190. s = cast[int](src)
  191. for i in 0..len-1:
  192. genericDeepCopy(cast[pointer](d +% i *% mt.base.size),
  193. cast[pointer](s +% i *% mt.base.size), mt.base)