sharedstrings.nim 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  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. ## Shared string support for Nim.
  10. type
  11. UncheckedCharArray = UncheckedArray[char]
  12. import system/helpers2
  13. type
  14. Buffer = ptr object
  15. refcount: int
  16. capacity, realLen: int
  17. data: UncheckedCharArray
  18. SharedString* = object ## A string that can be shared. Slicing is O(1).
  19. buffer: Buffer
  20. first, len: int
  21. proc decRef(b: Buffer) {.inline.} =
  22. if atomicDec(b.refcount) <= 0:
  23. deallocShared(b)
  24. proc incRef(b: Buffer) {.inline.} =
  25. atomicInc(b.refcount)
  26. {.experimental.}
  27. proc `=destroy`*(s: SharedString) =
  28. #echo "destroyed"
  29. if not s.buffer.isNil:
  30. decRef(s.buffer)
  31. when false:
  32. proc `=`*(dest: var SharedString; src: SharedString) =
  33. incRef(src.buffer)
  34. if not dest.buffer.isNil:
  35. decRef(dest.buffer)
  36. dest.buffer = src.buffer
  37. dest.first = src.first
  38. dest.len = src.len
  39. proc len*(s: SharedString): int = s.len
  40. proc `[]`*(s: SharedString; i: Natural): char =
  41. if i < s.len: result = s.buffer.data[i+s.first]
  42. else: raise newException(IndexError, formatErrorIndexBound(i, s.len-1))
  43. proc `[]=`*(s: var SharedString; i: Natural; value: char) =
  44. if i < s.len: s.buffer.data[i+s.first] = value
  45. else: raise newException(IndexError, formatErrorIndexBound(i, s.len-1))
  46. proc `[]`*(s: SharedString; ab: HSlice[int, int]): SharedString =
  47. #incRef(src.buffer)
  48. if ab.a < s.len:
  49. result.buffer = s.buffer
  50. result.first = ab.a
  51. result.len = min(s.len, ab.b - ab.a + 1)
  52. # else: produce empty string ;-)
  53. proc newBuffer(cap, len: int): Buffer =
  54. assert cap >= len
  55. result = cast[Buffer](allocShared0(sizeof(int)*3 + cap))
  56. result.refcount = 0
  57. result.capacity = cap
  58. result.realLen = len
  59. proc newSharedString*(len: Natural): SharedString =
  60. if len != 0:
  61. # optimization: Don't have an underlying buffer when 'len == 0'
  62. result.buffer = newBuffer(len, len)
  63. result.first = 0
  64. result.len = len
  65. proc newSharedString*(s: string): SharedString =
  66. let len = s.len
  67. if len != 0:
  68. # optimization: Don't have an underlying buffer when 'len == 0'
  69. result.buffer = newBuffer(len, len)
  70. copyMem(addr result.buffer.data[0], cstring(s), s.len)
  71. result.first = 0
  72. result.len = len
  73. when declared(atomicLoadN):
  74. template load(x): untyped = atomicLoadN(addr x, ATOMIC_SEQ_CST)
  75. else:
  76. # XXX Fixme
  77. template load(x): untyped = x
  78. proc add*(s: var SharedString; t: cstring; len: Natural) =
  79. if len == 0: return
  80. let newLen = s.len + len
  81. if s.buffer.isNil:
  82. s.buffer = newBuffer(len, len)
  83. copyMem(addr s.buffer.data[0], t, len)
  84. s.len = len
  85. elif newLen >= s.buffer.capacity or s.first != 0 or
  86. s.len != s.buffer.realLen or load(s.buffer.refcount) > 1:
  87. let oldBuf = s.buffer
  88. s.buffer = newBuffer(max(s.buffer.capacity * 3 div 2, newLen), newLen)
  89. copyMem(addr s.buffer.data[0], addr oldBuf.data[s.first], s.len)
  90. copyMem(addr s.buffer.data[s.len], t, len)
  91. decRef(oldBuf)
  92. else:
  93. copyMem(addr s.buffer.data[s.len], t, len)
  94. s.buffer.realLen += len
  95. s.len += len
  96. proc add*(s: var SharedString; t: string) =
  97. s.add(t.cstring, t.len)
  98. proc rawData*(s: var SharedString): pointer =
  99. if s.buffer.isNil: result = nil
  100. else: result = addr s.buffer.data[s.first]
  101. proc add*(s: var SharedString; t: SharedString) =
  102. if t.buffer.isNil: return
  103. s.add(cast[cstring](addr s.buffer.data[s.first]), t.len)
  104. proc `$`*(s: SharedString): string =
  105. result = newString(s.len)
  106. if s.len > 0:
  107. copyMem(addr result[0], addr s.buffer.data[s.first], s.len)
  108. proc `==`*(s: SharedString; t: string): bool =
  109. if s.buffer.isNil: result = t.len == 0
  110. else: result = t.len == s.len and equalMem(addr s.buffer.data[s.first],
  111. cstring(t), t.len)
  112. proc `==`*(s, t: SharedString): bool =
  113. if s.buffer.isNil: result = t.len == 0
  114. else: result = t.len == s.len and equalMem(addr s.buffer.data[s.first],
  115. addr t.buffer.data[t.first], t.len)
  116. iterator items*(s: SharedString): char =
  117. let buf = s.buffer.data
  118. let x = s.first
  119. if buf != nil:
  120. for i in 0..<s.len:
  121. yield buf[i+x]
  122. import hashes
  123. proc hash*(s: SharedString): THash =
  124. var h: THash = 0
  125. for x in s: h = h !& x.hash
  126. result = !$h