tlc-table.scm 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. ; Part of Scheme 48 1.9. See file COPYING for notices and license.
  2. ; Authors: Marcus Crestani, Robert Ransom, Harald Glab-Phlak
  3. ;; transport link cells hash table
  4. ;; Ghuloum, Dybvig 2007
  5. ;; TODO
  6. ;; - shall the hashtable grow/shrink over time?
  7. ;; record type for table
  8. (define-record-type tlc-table :tlc-table
  9. (really-make-tlc-table buckets-size buckets hash-function
  10. equivalence-function tconc
  11. count loc)
  12. tlc-table?
  13. (buckets-size tlc-table-buckets-size set-tlc-table-buckets-size!)
  14. (buckets tlc-table-buckets set-tlc-table-buckets!)
  15. (hash-function tlc-table-hash-function) ; hash function
  16. (equivalence-function tlc-table-equivalence-function) ; equivalence function
  17. (tconc tlc-table-tconc) ; to track the links that need rehashing
  18. (count tlc-table-count set-tlc-table-count!) ; number of elements in table
  19. (loc tlc-table-loc set-tlc-table-loc!)) ; doubly-linked list with all TLCs
  20. ;; record type for value
  21. (define-record-type tlc-value :tlc-value
  22. (make-tlc-value value prev-tlc next-tlc)
  23. tlc-value?
  24. (value tlc-value-value set-tlc-value-value!)
  25. (prev-tlc tlc-value-prev-tlc set-tlc-value-prev-tlc!)
  26. (next-tlc tlc-value-next-tlc set-tlc-value-next-tlc!))
  27. ;; minimal size of a tlc table
  28. (define *tlc-table-min-size* 1)
  29. ;; default size if no size is given
  30. (define *tlc-table-default-init-size* 20)
  31. ;; initialize buckets
  32. (define (tlc-table-initialize-buckets! buckets)
  33. (let fill-with-index! ((vector buckets)
  34. (n (- (vector-length buckets) 1)))
  35. (if (>= n 0)
  36. (begin
  37. (vector-set! vector n n)
  38. (fill-with-index! vector (- n 1))))))
  39. ;; smart constructor
  40. (define (make-tlc-table-internally size hash-function
  41. equiv-function use-tconc-queue?)
  42. (let* ((size (max size *tlc-table-min-size*))
  43. (buckets (make-vector size))
  44. (tconc (and use-tconc-queue? (make-tconc-queue))))
  45. (tlc-table-initialize-buckets! buckets)
  46. (really-make-tlc-table size buckets hash-function equiv-function tconc 0 #f)))
  47. ;; default hash functions
  48. (define (tlc-table-default-eq-hash-function object)
  49. (memory-status (enum memory-status-option pointer-hash) object))
  50. (define (tlc-table-default-eqv-hash-function x)
  51. (let recur ((x x)
  52. (budget 16))
  53. (cond
  54. ((number? x)
  55. (number-hash x)) ; imported from general-tables
  56. ((string? x)
  57. (table-string-hash x)) ; imported from general-tables
  58. ((symbol? x)
  59. (table-symbol-hash x)) ; imported from general-tables
  60. (else
  61. (memory-status (enum memory-status-option pointer-hash) x)))))
  62. ;; adjust results of hash function to table size
  63. (define (tlc-table-hash-value size value)
  64. (let ((v (remainder value size)))
  65. (if (< v 0)
  66. (- v)
  67. v)))
  68. (define (tlc-table-calculate-hash table object)
  69. (tlc-table-hash-value (tlc-table-buckets-size table)
  70. ((tlc-table-hash-function table) object)))
  71. ;; access link chains
  72. (define (set-tlc-table-entry! table index link)
  73. (vector-set! (tlc-table-buckets table) index link))
  74. (define (tlc-table-entry table index)
  75. (vector-ref (tlc-table-buckets table) index))
  76. ;; insert links
  77. (define (tlc-table-insert-link table link)
  78. (let* ((key (transport-link-cell-key link))
  79. (index (tlc-table-calculate-hash table
  80. (transport-link-cell-key link))))
  81. (set-transport-link-cell-next! link (tlc-table-entry table index))
  82. (set-tlc-table-entry! table index link)))
  83. (define (tlc-table-add table key value)
  84. (let* ((tlc-value (make-tlc-value value #f (tlc-table-loc table)))
  85. (link (make-transport-link-cell key tlc-value
  86. (tlc-table-tconc table) #f)))
  87. (tlc-table-insert-link table link)
  88. (if (tlc-table-loc table)
  89. (set-tlc-value-prev-tlc! (transport-link-cell-value
  90. (tlc-table-loc table)) link))
  91. (set-tlc-table-loc! table link)
  92. (set-tlc-table-count! table (+ (tlc-table-count table) 1))))
  93. ;; get index of link chain
  94. (define (tlc-table-index-of-link x)
  95. (if (number? x)
  96. x
  97. (tlc-table-index-of-link (transport-link-cell-next x))))
  98. ;; delete links
  99. (define (tlc-table-delete-link table link)
  100. (let* ((index (tlc-table-index-of-link link))
  101. (chain (tlc-table-entry table index)))
  102. (letrec ((delete-loop
  103. (lambda (chain)
  104. (and (transport-link-cell? chain)
  105. (let ((x (transport-link-cell-next chain)))
  106. (if (transport-link-cell? x)
  107. (if (eq? x link)
  108. (set-transport-link-cell-next!
  109. chain (transport-link-cell-next x))
  110. (delete-loop x))
  111. (set-transport-link-cell-next! chain x)))))))
  112. (if (eq? chain link)
  113. (set-tlc-table-entry! table index
  114. (transport-link-cell-next link))
  115. (delete-loop chain))
  116. (let* ((tlc-value (transport-link-cell-value link))
  117. (prev-tlc (tlc-value-prev-tlc tlc-value))
  118. (next-tlc (tlc-value-next-tlc tlc-value)))
  119. (if prev-tlc
  120. (set-tlc-value-next-tlc! (transport-link-cell-value prev-tlc) next-tlc)
  121. (set-tlc-table-loc! table next-tlc))
  122. (if next-tlc
  123. (set-tlc-value-prev-tlc! (transport-link-cell-value next-tlc) prev-tlc)))
  124. (set-tlc-table-count! table (- (tlc-table-count table) 1)))))
  125. ;; lookup
  126. (define (tlc-table-direct-lookup table key)
  127. (let ((index (tlc-table-calculate-hash table key)))
  128. (let lookup ((x (tlc-table-entry table index)))
  129. (and (transport-link-cell? x)
  130. (if ((tlc-table-equivalence-function table) (transport-link-cell-key x) key)
  131. x
  132. (lookup (transport-link-cell-next x)))))))
  133. (define (tlc-table-rehash-link table link)
  134. (tlc-table-delete-link table link)
  135. (tlc-table-add table
  136. (transport-link-cell-key link)
  137. (tlc-value-value (transport-link-cell-value link))))
  138. (define (tlc-table-rehash-lookup table key)
  139. (let ((tconc (tlc-table-tconc table)))
  140. (let tconc-dequeue-loop ()
  141. (and tconc (not (tconc-queue-empty? tconc))
  142. (let ((link (tconc-queue-dequeue! tconc)))
  143. (tlc-table-rehash-link table link)
  144. (if ((tlc-table-equivalence-function table) (transport-link-cell-key link) key)
  145. link
  146. (tconc-dequeue-loop)))))))
  147. (define (tlc-table-lookup-link table key)
  148. (or (tlc-table-direct-lookup table key)
  149. (tlc-table-rehash-lookup table key)))
  150. (define (tlc-table-rehash-and-clean-tconc-queue table key)
  151. (let ((tconc (tlc-table-tconc table)))
  152. (let tconc-dequeue-loop ()
  153. (and tconc (not (tconc-queue-empty? tconc))
  154. (let ((link (tconc-queue-dequeue! tconc)))
  155. (if ((tlc-table-equivalence-function table) (transport-link-cell-key link) key)
  156. link
  157. (begin
  158. (tlc-table-rehash-link table link)
  159. (tconc-dequeue-loop))))))))
  160. ;; DELETING FROM A TLC TABLE IS DIFFICULT:
  161. ;; There are rare occasions where a link is enqueued to the tconc
  162. ;; queue during garbage collection that is hashed into the same bucket
  163. ;; as before. So, strictly speaking, there is no need for the link to
  164. ;; go into the tconc queue because a direct lookup finds it anyway.
  165. ;; But if the user really wants to delete a link, we have to make sure
  166. ;; that it is removed from the tconc queue so that a later lookup will
  167. ;; not resurrect the link. Thus, if the tlc's tconc field is #f, the
  168. ;; tlc is in the tconc queue and we first walk the tconc queue and
  169. ;; rehash all the links until we find the link we want to delete.
  170. ;;
  171. ;; This may make the removal of an tlc-table entry very expensive,
  172. ;; because worst case all links in the tconc queue are rehashed
  173. ;; whenever the user deletes an element from the tlc table.
  174. ;;
  175. ;; In even more rare circumstances, a deleted link may resurrect this
  176. ;; way: If a garbage collection happens during the deletion of a link
  177. ;; (i.e. while traversing a bucket's link list), the collector may
  178. ;; enqueue the link to the tconc queue just before the link is
  179. ;; deleted from the link list. To prevent this from happening, we set
  180. ;; the link's tconc field to #f, so that the collector will not try to
  181. ;; enqueue it.
  182. ;;
  183. ;; For non-deleting lookups it does not matter if the link is still in
  184. ;; the tconc. At some point in time, the link will be rehashed to the
  185. ;; same bucket as it was before. This is unneeded but way cheaper
  186. ;; than checking and acting to prevent such a situation.
  187. (define (tlc-table-lookup-link-for-deletion table key)
  188. (let ((link (tlc-table-direct-lookup table key)))
  189. (if (and link (tlc-table-tconc table) (transport-link-cell-tconc link))
  190. (begin
  191. (set-transport-link-cell-tconc! link #f)
  192. link)
  193. (tlc-table-rehash-and-clean-tconc-queue table key))))
  194. ;; exported functions below
  195. ;; constructors
  196. (define (make-non-default-tlc-table hash-function equiv size use-tconc-queue?)
  197. (make-tlc-table-internally size hash-function equiv use-tconc-queue?))
  198. (define make-eq-tlc-table
  199. (opt-lambda ((size *tlc-table-default-init-size*))
  200. (make-non-default-tlc-table tlc-table-default-eq-hash-function eq? size #t)))
  201. (define make-tlc-table make-eq-tlc-table)
  202. (define make-eqv-tlc-table
  203. (opt-lambda ((size *tlc-table-default-init-size*))
  204. (make-non-default-tlc-table tlc-table-default-eqv-hash-function eqv? size #t)))
  205. ;; size
  206. (define tlc-table-size tlc-table-count)
  207. ;; lookup
  208. (define (tlc-table-ref table key not-found)
  209. (let ((x (tlc-table-lookup-link table key)))
  210. (if x
  211. (tlc-value-value (transport-link-cell-value x))
  212. not-found)))
  213. ;; set
  214. (define (assert-immutable table origin)
  215. (and (immutable? table)
  216. (assertion-violation origin
  217. "immutable argument"
  218. table)))
  219. (define (tlc-table-set! table key value)
  220. (assert-immutable table 'tlc-table-set!)
  221. (let ((x (tlc-table-lookup-link table key)))
  222. (if x
  223. (let ((tlc-value (transport-link-cell-value x)))
  224. (set-tlc-value-value! tlc-value value))
  225. (tlc-table-add table key value))))
  226. ;; delete
  227. (define (tlc-table-delete! table key not-found)
  228. (assert-immutable table 'tlc-table-delete!)
  229. (let ((x (tlc-table-lookup-link-for-deletion table key)))
  230. (if x
  231. (tlc-table-delete-link table x)
  232. not-found)))
  233. ;; contains?
  234. (define (tlc-table-contains? table key)
  235. (and (tlc-table-lookup-link table key) #t))
  236. ;; update
  237. (define (tlc-table-update! table key proc not-found)
  238. (assert-immutable table 'tlc-table-update!)
  239. (let ((x (tlc-table-lookup-link table key)))
  240. (if x
  241. (let ((tlc-value (transport-link-cell-value x)))
  242. (set-tlc-value-value!
  243. tlc-value
  244. (proc (tlc-value-value tlc-value))))
  245. not-found)))
  246. ;; copy
  247. (define (make-tlc-table-immutable! table)
  248. (and (tlc-table-tconc table)
  249. (assertion-violation 'make-tlc-table-immutable!
  250. "tlc tables that need tconc queues cannot be made immutable"))
  251. (let loop ((tlc (tlc-table-loc table)))
  252. (if tlc
  253. (begin
  254. (make-immutable! (transport-link-cell-value tlc))
  255. (make-immutable! (transport-link-cell-key tlc))
  256. (make-immutable! tlc)
  257. (loop (tlc-value-next-tlc (transport-link-cell-value tlc))))))
  258. (make-immutable! (tlc-table-buckets table))
  259. (make-immutable! table))
  260. (define tlc-table-copy
  261. (opt-lambda
  262. (table (mutable? #f))
  263. (and (not mutable?) (tlc-table-tconc table)
  264. (assertion-violation 'tlc-table-copy
  265. "tlc tables that need tconc queues cannot be copied to immutable tables"))
  266. (let ((hash-function (tlc-table-hash-function table))
  267. (equiv (tlc-table-equivalence-function table))
  268. (size (tlc-table-count table))
  269. (use-tconc-queue? (tlc-table-tconc table)))
  270. (let ((copy (make-non-default-tlc-table hash-function equiv size use-tconc-queue?)))
  271. (let loop ((tlc (tlc-table-loc table)))
  272. (if tlc
  273. (begin
  274. (tlc-table-add copy
  275. (transport-link-cell-key tlc)
  276. (tlc-value-value (transport-link-cell-value tlc)))
  277. (loop (tlc-value-next-tlc (transport-link-cell-value tlc))))))
  278. (and (not mutable?) (make-tlc-table-immutable! copy))
  279. copy))))
  280. ;; clear
  281. (define (tlc-table-clear! table)
  282. (assert-immutable table 'tlc-table-clear!)
  283. (tlc-table-initialize-buckets! (tlc-table-buckets table))
  284. (if (tlc-table-tconc table)
  285. (tconc-queue-clear! (tlc-table-tconc table)))
  286. (set-tlc-table-count! table 0)
  287. (set-tlc-table-loc! table #f))
  288. ;; resize
  289. ;; at the moment if and really only if the table is empty
  290. (define (tlc-table-resize! table size)
  291. (assert-immutable table 'tlc-table-resize!)
  292. (let ((capacity (vector-length (tlc-table-buckets table)))
  293. (buckets (tlc-table-buckets table))
  294. (count (tlc-table-count table)))
  295. (if (eq? count 0)
  296. (begin (set-tlc-table-buckets! table (make-vector size))
  297. (set-tlc-table-buckets-size! table size)
  298. (tlc-table-initialize-buckets! (tlc-table-buckets table))
  299. (set-tlc-table-loc! table #f) #t)
  300. #f)))
  301. ;; keys
  302. (define (tlc-table-keys table)
  303. (let ((keys (make-vector (tlc-table-count table))))
  304. (let loop ((tlc (tlc-table-loc table))
  305. (count 0))
  306. (if tlc
  307. (begin
  308. (vector-set! keys count (transport-link-cell-key tlc))
  309. (loop
  310. (tlc-value-next-tlc (transport-link-cell-value tlc))
  311. (+ count 1))))
  312. keys)))
  313. ;; keys & values
  314. (define (tlc-table-entries table)
  315. (let ((keys (make-vector (tlc-table-count table)))
  316. (vals (make-vector (tlc-table-count table))))
  317. (let loop ((tlc (tlc-table-loc table))
  318. (count 0))
  319. (if tlc
  320. (begin
  321. (vector-set! keys count (transport-link-cell-key tlc))
  322. (vector-set! vals count (tlc-value-value
  323. (transport-link-cell-value tlc)))
  324. (loop
  325. (tlc-value-next-tlc (transport-link-cell-value tlc))
  326. (+ count 1))))
  327. (values keys vals))))
  328. ;; utility function to detect eq? and eqv? tables (so their hash
  329. ;; functions can be hidden from R6RS code)
  330. (define (tlc-table-has-tconc-queue? table)
  331. (and (tlc-table-tconc table)
  332. #t))
  333. ;; exported hash functions
  334. (define (string-ci-hash value)
  335. (let ((converted (string-foldcase value))) ;; get sring-fold... from r6rs impl
  336. (string-hash converted)))
  337. (define (equal-hash object)
  338. (datum-hash object))
  339. ;; debugging
  340. (define (tlc-table-distribution table)
  341. (let loop-table ((n (- (tlc-table-buckets-size table) 1))
  342. (distribution '()))
  343. (let ((count
  344. (let loop-chain ((x (tlc-table-entry table n))
  345. (count 0))
  346. (if (transport-link-cell? x)
  347. (loop-chain (transport-link-cell-next x) (+ count 1))
  348. count)))
  349. (count-tconc
  350. (let ((tconc (tlc-table-tconc table)))
  351. (and tconc (pair? tconc)
  352. (let loop-tconc ((x (car tconc))
  353. (count 0))
  354. (if (or (eq? x (cdr tconc))
  355. (not (pair? x)))
  356. count
  357. (loop-tconc (cdr x) (+ count 1)))))))
  358. (count-loc
  359. (let loop ((tlc (tlc-table-loc table))
  360. (count 0))
  361. (if tlc
  362. (loop (tlc-value-next-tlc (transport-link-cell-value tlc))
  363. (+ count 1))
  364. count))))
  365. (if (> n 0)
  366. (loop-table (- n 1) (cons (cons n count) distribution))
  367. (list (cons 'tconc count-tconc)
  368. (cons 'count (tlc-table-count table))
  369. (cons 'loc count-loc)
  370. (cons 'buckets
  371. (cons (cons n count) distribution)))))))