sharedstrings.nim 4.2 KB

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