ikarus/src/ikarus.hash-tables.guardian...

274 lines
7.5 KiB
Scheme

(library (ikarus hash-tables)
(export hash-table? make-hash-table get-hash-table put-hash-table!)
(import
(ikarus system $vectors)
(ikarus system $fx)
(except (ikarus) hash-table? make-hash-table
get-hash-table put-hash-table!))
(define-syntax inthash
(syntax-rules ()
[(_ x) x]))
(define-record ht (g v count threashold rehashed))
(define-record lk (key val next))
(define make-transport-guardian
(lambda ()
(define loop
(lambda (m g)
(and m
(let ([x (car m)])
(if (bwp-object? x)
(loop (g) g)
(begin (g m) x))))))
(let ([g (make-guardian)])
(case-lambda
[(x) (g (weak-cons x #f))]
[() (loop (g) g)]))))
(define initial-size 8)
;;; assq-like lookup
(define direct-lookup
(lambda (x b)
(if (fixnum? b)
#f
(if (eq? x (lk-key b))
b
(direct-lookup x (lk-next b))))))
(define rehash-lookup
(lambda (h g x)
(cond
[(g) =>
(lambda (b)
(re-add! h b)
(if (eq? x (lk-key b))
b
(rehash-lookup h g x)))]
[else #f])))
(define get-bucket-index
(lambda (b)
(let ([next (lk-next b)])
(if (fixnum? next)
next
(get-bucket-index next)))))
(define replace!
(lambda (lb x y)
(let ([n (lk-next lb)])
(cond
[(eq? n x)
(set-lk-next! lb y)]
[else
(replace! n x y)]))))
(define re-add!
(lambda (h b)
(let ([vec (ht-v h)]
[next (lk-next b)])
;;; first remove it from its old place
(set-ht-rehashed! h (fx+ (ht-rehashed h) 1))
(let ([idx
(if (fixnum? next)
next
(get-bucket-index next))])
(let ([fst ($vector-ref vec idx)])
(cond
[(eq? fst b)
($vector-set! vec idx next)]
[else
(replace! fst b next)])))
(let ([k (lk-key b)])
(let ([ih (inthash (pointer-value k))])
(let ([idx ($fxlogand ih ($fx- ($vector-length vec) 1))])
(let ([n ($vector-ref vec idx)])
(set-lk-next! b n)
($vector-set! vec idx b))))))))
(define get-hash
(lambda (h x v)
(let ([pv (pointer-value x)]
[vec (ht-v h)])
(let ([ih (inthash pv)])
(let ([idx ($fxlogand ih ($fx- ($vector-length vec) 1))])
(let ([b ($vector-ref vec idx)])
(cond
[(or (direct-lookup x b) (rehash-lookup h (ht-g h) x))
=>
(lambda (b)
(lk-val b))]
[else v])))))))
(define put-hash!
(lambda (h x v)
(let ([pv (pointer-value x)]
[vec (ht-v h)])
(let ([ih (inthash pv)])
(let ([idx ($fxlogand ih ($fx- ($vector-length vec) 1))])
(let ([b ($vector-ref vec idx)])
(cond
[(or (direct-lookup x b) (rehash-lookup h (ht-g h) x))
=>
(lambda (b)
(set-lk-val! b v))]
[else
(let ([bucket (make-lk x v ($vector-ref vec idx))])
((ht-g h) bucket)
(if ($fx= (pointer-value x) pv)
($vector-set! vec idx bucket)
(let* ([ih (inthash (pointer-value x))]
[idx
($fxlogand ih ($fx- ($vector-length vec) 1))])
(set-lk-next! bucket ($vector-ref vec idx))
($vector-set! vec idx bucket))))
(let ([ct (ht-count h)])
(set-ht-count! h ($fx+ 1 ct))
(when ($fx> ct ($vector-length vec))
(enlarge-table h)))])))))))
(define insert-b
(lambda (b vec mask)
(let* ([x (lk-key b)]
[pv (pointer-value x)]
[ih (inthash pv)]
[idx ($fxlogand ih mask)]
[next (lk-next b)])
(set-lk-next! b ($vector-ref vec idx))
($vector-set! vec idx b)
(unless (fixnum? next)
(insert-b next vec mask)))))
(define move-all
(lambda (vec1 i n vec2 mask)
(unless ($fx= i n)
(let ([b ($vector-ref vec1 i)])
(unless (fixnum? b)
(insert-b b vec2 mask))
(move-all vec1 ($fx+ 1 i) n vec2 mask)))))
(define enlarge-table
(lambda (h)
(let* ([vec1 (ht-v h)]
[n1 ($vector-length vec1)]
[n2 ($fxsll n1 1)]
[vec2 (make-base-vec n2)])
(move-all vec1 0 n1 vec2 ($fx- n2 1))
(set-ht-v! h vec2))))
(define make-base-vec
(lambda (n)
(init-vec (make-vector n) 0 n)))
(define init-vec
(lambda (v i n)
(if ($fx= i n)
v
(begin
($vector-set! v i i)
(init-vec v ($fx+ 1 i) n)))))
;;; public interface
(define hash-table?
(lambda (x) (ht? x)))
(define make-hash-table
(lambda ()
(make-ht (make-transport-guardian)
(init-vec (make-vector initial-size) 0 initial-size)
0
initial-size
0)))
(define get-hash-table
(lambda (h x v)
(if (ht? h)
(get-hash h x v)
(error 'get-hash-table "~s is not a hash table" h))))
(define put-hash-table!
(lambda (h x v)
(if (ht? h)
(put-hash! h x v)
(error 'put-hash-table! "~s is not a hash table" h))))
(define hasht-rehash-count
(lambda (h)
(if (ht? h)
(ht-rehashed h)
(error 'hasht-rehash-count "~s is not a hash table" h))))
(define hasht-reset-count!
(lambda (h)
(if (ht? h)
(set-ht-rehashed! h 0)
(error 'hasht-rehash-count "~s is not a hash table" h))))
)
#!eof
(import ght)
(define (test1)
(printf "test1 ...\n")
(let ([ls (let f ([i 100000] [ac '()])
(cond
[(fx= i 0) ac]
[else (f (fx- i 1) (cons (cons i i) ac))]))])
(let ([ht (make-hash-table)])
(for-each (lambda (x) (put-hash-table! ht x x)) ls)
(let f ([i 1000])
(unless (fx= i 0)
(collect)
(f (fx- i 1))))
(for-each
(lambda (x)
(unless (eq? x (get-hash-table ht x #f))
(error 'test1 "failed")))
ls)))
(printf "passed test1\n"))
(define (test2)
(printf "test2 ...\n")
(let ([ls (let f ([i 10000] [ac '()])
(cond
[(fx= i 0) ac]
[else (f (fx- i 1) (cons (cons i i) ac))]))])
(let ([ht (make-hash-table)])
(for-each (lambda (x) (put-hash-table! ht x x)) ls)
(for-each
(lambda (x)
(collect)
(unless (eq? x (get-hash-table ht x #f))
(error 'test2 "failed")))
ls)))
(printf "passed test2\n"))
(define (test3)
(printf "test3 ...\n")
(let ([ls (let f ([i 10000] [ac '()])
(cond
[(fx= i 0) ac]
[else (f (fx- i 1) (cons (cons i i) ac))]))])
(let ([ht (make-hash-table)])
(for-each (lambda (x)
(collect)
(put-hash-table! ht x x))
ls)
(for-each
(lambda (x)
(unless (eq? x (get-hash-table ht x #f))
(error 'test3 "failed")))
ls)))
(printf "passed test3\n"))
(define (test-all)
(test1)
(test2)
(test3))