1258 lines
50 KiB
Scheme
1258 lines
50 KiB
Scheme
;;; newhash.ss
|
|
;;; Copyright 1984-2017 Cisco Systems, Inc.
|
|
;;;
|
|
;;; Licensed under the Apache License, Version 2.0 (the "License");
|
|
;;; you may not use this file except in compliance with the License.
|
|
;;; You may obtain a copy of the License at
|
|
;;;
|
|
;;; http://www.apache.org/licenses/LICENSE-2.0
|
|
;;;
|
|
;;; Unless required by applicable law or agreed to in writing, software
|
|
;;; distributed under the License is distributed on an "AS IS" BASIS,
|
|
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
;;; See the License for the specific language governing permissions and
|
|
;;; limitations under the License.
|
|
|
|
#|
|
|
Documentation notes:
|
|
- hashtable-copy can create immutable weak eq hashtables. an immutable weak
|
|
hashtable is immutable in the sense that it cannot be modified by
|
|
hashtable-set! or hashtable-update!, but the disappearance of key, val
|
|
pairs can be detected with hashtable-size, hashtable-keys, and
|
|
hashtable-entries.
|
|
- symbols are collectable, so weak hash tables should not be used to create
|
|
permanent associations with symbols as keys
|
|
|#
|
|
|
|
#|
|
|
; csv7:
|
|
(define make-hash-table) ; weakflag
|
|
(define hash-table?) ; x
|
|
(define put-hash-table!) ; hashtable key obj
|
|
(define get-hash-table) ; hashtable key default
|
|
(define remove-hash-table!) ; hashtable key
|
|
(define hash-table-map) ; hashtable proc
|
|
(define hash-table-for-each) ; hashtable proc
|
|
|
|
;;; r6rs:
|
|
(define make-eq-hashtable) ; [k], k >= 0
|
|
(define make-eqv-hashtable) ; [k], k >= 0
|
|
(define make-hashtable) ; hashproc equivproc [k], k >= 0
|
|
(define hashtable?) ; x
|
|
(define hashtable-size) ; hashtable
|
|
(define hashtable-ref) ; hashtable key default
|
|
(define hashtable-set!) ; hashtable key obj
|
|
(define hashtable-delete!) ; hashtable key
|
|
(define hashtable-contains?) ; hashtable key
|
|
(define hashtable-update!) ; hashtable key proc default
|
|
(define hashtable-copy) ; hashtable [mutableflag]
|
|
(define hashtable-clear!) ; hashtable [k], k >= 0
|
|
(define hashtable-keys) ; hashtable
|
|
(define hashtable-entries) ; hashtable
|
|
(define hashtable-cells) ; hashtable
|
|
(define hashtable-equivalence-function) ; hashtable
|
|
(define hashtable-hash-function) ; hashtable
|
|
(define hashtable-mutable?) ; hashtable
|
|
(define equal-hash) ; obj
|
|
(define string-hash) ; string
|
|
(define string-ci-hash) ; string
|
|
(define symbol-hash) ; symbol
|
|
|
|
;;; other generic hash operators
|
|
(define hashtable-cell)
|
|
(define hashtable-weak?) ; hashtable
|
|
(define hashtable-ephemeron?) ; hashtable
|
|
|
|
;;; eq-hashtable operators
|
|
(define make-weak-eq-hashtable) ; [k], k >= 0
|
|
(define eq-hashtable-ref) ; eq-hashtable key default
|
|
(define eq-hashtable-contains?) ; eq-hashtable key
|
|
(define eq-hashtable-set!) ; eq-hashtable key obj
|
|
(define eq-hashtable-update!) ; eq-hashtable key proc default
|
|
(define eq-hashtable-cell) ; eq-hashtable key default
|
|
(define eq-hashtable-delete!) ; eq-hashtable key
|
|
(define eq-hashtable-weak?) ; eq-hashtable
|
|
(define eq-hashtable-ephemeron?) ; eq-hashtable
|
|
|
|
;;; eq-hashtable operators
|
|
(define make-symbol-hashtable) ; [k], k >= 0
|
|
(define symbol-hashtable-ref) ; symbol-hashtable key default
|
|
(define symbol-hashtable-contains?) ; symbol-hashtable key
|
|
(define symbol-hashtable-set!) ; symbol-hashtable key obj
|
|
(define symbol-hashtable-update!) ; symbol-hashtable key proc default
|
|
(define symbol-hashtable-cell) ; symbol-hashtable key default
|
|
(define symbol-hashtable-delete!) ; symbol-hashtable key
|
|
|
|
;;; eqv-hashtable operators
|
|
(define make-weak-eqv-hashtable) ; [k], k >= 0
|
|
|
|
;;; unsafe eq-hashtable operators
|
|
(define $make-eq-hashtable) ; fxminlen subtype, fxminlen = 2^n, n >= 0
|
|
(define $eq-hashtable-keys) ; eq-hashtable
|
|
(define $eq-hashtable-values) ; eq-hashtable
|
|
(define $eq-hashtable-entries) ; eq-hashtable
|
|
(define $eq-hashtable-cells) ; eq-hashtable
|
|
(define $eq-hashtable-copy) ; eq-hashtable [mutableflag]
|
|
(define $eq-hashtable-clear!) ; eq-hashtable [fxminlen]
|
|
|
|
;;; inspection
|
|
(define $hashtable-veclen)
|
|
(define $hashtable-report)
|
|
|#
|
|
|
|
(let ()
|
|
(include "hashtable-types.ss")
|
|
|
|
(define do-hash
|
|
(lambda (hash x mask who)
|
|
; NB: the hash function should return a nonnegative exact integer.
|
|
; NB: we check only that it returns an exact integer, i.e., extend the semantics to
|
|
; NB: allow negative exact integers.
|
|
(let ([i (hash x)])
|
|
(cond
|
|
[(fixnum? i) (fxlogand i mask)]
|
|
[(bignum? i) (logand i mask)]
|
|
[else ($oops who "invalid hash-function ~s return value ~s for ~s" hash i x)]))))
|
|
|
|
(define size->minlen
|
|
(lambda (who k)
|
|
(define maxbits (fx- (fixnum-width) 4))
|
|
(cond
|
|
[(and (fixnum? k) (fx>= k 0))
|
|
(fxmax 8 (fxsll 1 (fxmin maxbits (fxlength (fx- k 1)))))]
|
|
[(and (bignum? k) (>= k 0)) (fxsll 1 maxbits)]
|
|
[else ($oops who "invalid size argument ~s" k)])))
|
|
|
|
(define $gen-hashtable-ref
|
|
(lambda (h x v who)
|
|
(let ([vec (ht-vec h)] [equiv? (gen-ht-equiv? h)])
|
|
(let loop ([b (vector-ref vec (do-hash (gen-ht-hash h) x (fx- (vector-length vec) 1) who))])
|
|
(if (null? b)
|
|
v
|
|
(let ([a (car b)])
|
|
(if (equiv? (car a) x) (cdr a) (loop (cdr b)))))))))
|
|
|
|
(define $gen-hashtable-contains?
|
|
(lambda (h x who)
|
|
(let ([vec (ht-vec h)] [equiv? (gen-ht-equiv? h)])
|
|
(let loop ([b (vector-ref vec (do-hash (gen-ht-hash h) x (fx- (vector-length vec) 1) who))])
|
|
(and (not (null? b))
|
|
(or (equiv? (caar b) x)
|
|
(loop (cdr b))))))))
|
|
|
|
(module ($gen-hashtable-set! $gen-hashtable-update! $gen-hashtable-cell $gen-hashtable-delete!)
|
|
(define-syntax incr-size!
|
|
(syntax-rules ()
|
|
[(_ h vec who)
|
|
(let ([size (fx+ (ht-size h) 1)] [n (vector-length vec)])
|
|
(ht-size-set! h size)
|
|
(when (and (fx> size n) (fx< n (fxsrl (most-positive-fixnum) 1)))
|
|
(adjust! h vec (fxsll n 1) who)))]))
|
|
|
|
(define-syntax decr-size!
|
|
(syntax-rules ()
|
|
[(_ h vec who)
|
|
(let ([size (fx- (ht-size h) 1)] [n (vector-length vec)])
|
|
(ht-size-set! h size)
|
|
(when (and (fx< size (fxsrl n 2)) (fx> n (ht-minlen h)))
|
|
(adjust! h vec (fxsrl n 1) who)))]))
|
|
|
|
(define adjust!
|
|
(lambda (h vec1 n2 who)
|
|
(let ([vec2 (make-vector n2 '())]
|
|
[mask2 (fx- n2 1)]
|
|
[hash (gen-ht-hash h)])
|
|
(vector-for-each
|
|
(lambda (b)
|
|
(for-each
|
|
(lambda (a)
|
|
(let ([hc (do-hash hash (car a) mask2 who)])
|
|
(vector-set! vec2 hc (cons a (vector-ref vec2 hc)))))
|
|
b))
|
|
vec1)
|
|
(ht-vec-set! h vec2))))
|
|
|
|
(define $gen-hashtable-set!
|
|
(lambda (h x v who)
|
|
(let ([vec (ht-vec h)] [equiv? (gen-ht-equiv? h)])
|
|
(let ([idx (do-hash (gen-ht-hash h) x (fx- (vector-length vec) 1) who)])
|
|
(let ([bucket (vector-ref vec idx)])
|
|
(let loop ([b bucket])
|
|
(if (null? b)
|
|
(begin
|
|
(vector-set! vec idx (cons (cons x v) bucket))
|
|
(incr-size! h vec who))
|
|
(let ([a (car b)])
|
|
(if (equiv? (car a) x) (set-cdr! a v) (loop (cdr b)))))))))))
|
|
|
|
(define $gen-hashtable-update!
|
|
(lambda (h x p v who)
|
|
(let ([vec (ht-vec h)] [equiv? (gen-ht-equiv? h)])
|
|
(let ([idx (do-hash (gen-ht-hash h) x (fx- (vector-length vec) 1) who)])
|
|
(let ([bucket (vector-ref vec idx)])
|
|
(let loop ([b bucket])
|
|
(if (null? b)
|
|
(begin
|
|
(vector-set! vec idx (cons (cons x (p v)) bucket))
|
|
(incr-size! h vec who))
|
|
(let ([a (car b)])
|
|
(if (equiv? (car a) x)
|
|
(set-cdr! a (p (cdr a)))
|
|
(loop (cdr b)))))))))))
|
|
|
|
(define $gen-hashtable-cell
|
|
(lambda (h x v who)
|
|
(let ([vec (ht-vec h)] [equiv? (gen-ht-equiv? h)])
|
|
(let ([idx (do-hash (gen-ht-hash h) x (fx- (vector-length vec) 1) who)])
|
|
(let ([bucket (vector-ref vec idx)])
|
|
(let loop ([b bucket])
|
|
(if (null? b)
|
|
(let ([a (cons x v)])
|
|
(vector-set! vec idx (cons a bucket))
|
|
(incr-size! h vec who)
|
|
a)
|
|
(let ([a (car b)])
|
|
(if (equiv? (car a) x)
|
|
a
|
|
(loop (cdr b)))))))))))
|
|
|
|
(define $gen-hashtable-delete!
|
|
(lambda (h x who)
|
|
(let ([vec (ht-vec h)] [equiv? (gen-ht-equiv? h)])
|
|
(let ([idx (do-hash (gen-ht-hash h) x (fx- (vector-length vec) 1) who)])
|
|
(let loop ([b (vector-ref vec idx)] [p #f])
|
|
(unless (null? b)
|
|
(let ([a (car b)])
|
|
(if (equiv? (car a) x)
|
|
(begin
|
|
(if p (set-cdr! p (cdr b)) (vector-set! vec idx (cdr b)))
|
|
(decr-size! h vec who))
|
|
(loop (cdr b) b))))))))))
|
|
|
|
(module ($gen-hashtable-copy $symbol-hashtable-copy)
|
|
(define copy-hashtable-vector
|
|
(lambda (h)
|
|
(let* ([vec1 (ht-vec h)]
|
|
[n (vector-length vec1)]
|
|
[vec2 (make-vector n '())])
|
|
(do ([i 0 (fx+ i 1)])
|
|
((fx= i n))
|
|
(vector-set! vec2 i
|
|
(map (lambda (a) (cons (car a) (cdr a)))
|
|
(vector-ref vec1 i))))
|
|
vec2)))
|
|
|
|
(define $gen-hashtable-copy
|
|
(lambda (h mutable?)
|
|
(make-gen-ht 'generic mutable? (copy-hashtable-vector h) (ht-minlen h) (ht-size h)
|
|
(gen-ht-hash h) (gen-ht-equiv? h))))
|
|
|
|
(define $symbol-hashtable-copy
|
|
(lambda (h mutable?)
|
|
(make-symbol-ht 'symbol mutable? (copy-hashtable-vector h) (ht-minlen h) (ht-size h)
|
|
(symbol-ht-equiv? h)))))
|
|
|
|
(define $ht-hashtable-clear!
|
|
(lambda (h minlen)
|
|
(ht-vec-set! h (make-vector minlen '()))
|
|
(ht-minlen-set! h minlen)
|
|
(ht-size-set! h 0)))
|
|
|
|
(define $ht-hashtable-keys
|
|
(lambda (h max-sz)
|
|
(let ([size (fxmin max-sz (ht-size h))])
|
|
(let ([keys (make-vector size)]
|
|
[vec (ht-vec h)])
|
|
(let ([n (vector-length vec)])
|
|
(let f ([i 0] [ikey 0])
|
|
(unless (or (fx= i n) (fx= ikey size))
|
|
(let g ([b (vector-ref vec i)] [ikey ikey])
|
|
(if (or (null? b) (fx= ikey size))
|
|
(f (fx+ i 1) ikey)
|
|
(begin
|
|
(vector-set! keys ikey (caar b))
|
|
(g (cdr b) (fx+ ikey 1))))))))
|
|
keys))))
|
|
|
|
(define $ht-hashtable-values
|
|
(lambda (h max-sz)
|
|
(let ([size (fxmin max-sz (ht-size h))])
|
|
(let ([vals (make-vector size)]
|
|
[vec (ht-vec h)])
|
|
(let ([n (vector-length vec)])
|
|
(let f ([i 0] [ival 0])
|
|
(unless (or (fx= i n) (fx= ival size))
|
|
(let g ([b (vector-ref vec i)] [ival ival])
|
|
(if (or (null? b) (fx= ival size))
|
|
(f (fx+ i 1) ival)
|
|
(begin
|
|
(vector-set! vals ival (cdar b))
|
|
(g (cdr b) (fx+ ival 1))))))))
|
|
vals))))
|
|
|
|
(define $ht-hashtable-entries
|
|
(lambda (h max-sz)
|
|
(let ([size (fxmin max-sz (ht-size h))])
|
|
(let ([keys (make-vector size)]
|
|
[vals (make-vector size)]
|
|
[vec (ht-vec h)])
|
|
(let ([n (vector-length vec)])
|
|
(let f ([i 0] [ikey 0])
|
|
(unless (or (fx= i n) (fx= ikey size))
|
|
(let g ([b (vector-ref vec i)] [ikey ikey])
|
|
(if (or (null? b) (fx= ikey size))
|
|
(f (fx+ i 1) ikey)
|
|
(let ([a (car b)])
|
|
(vector-set! keys ikey (car a))
|
|
(vector-set! vals ikey (cdr a))
|
|
(g (cdr b) (fx+ ikey 1))))))))
|
|
(values keys vals)))))
|
|
|
|
(define $ht-hashtable-cells
|
|
(lambda (h max-sz)
|
|
(let ([size (fxmin max-sz (ht-size h))])
|
|
(let ([cells (make-vector size)]
|
|
[vec (ht-vec h)])
|
|
(let ([n (vector-length vec)])
|
|
(let f ([i 0] [icell 0])
|
|
(unless (or (fx= i n) (fx= icell size))
|
|
(let g ([b (vector-ref vec i)] [icell icell])
|
|
(if (or (null? b) (fx= icell size))
|
|
(f (fx+ i 1) icell)
|
|
(let ([a (car b)])
|
|
(vector-set! cells icell a)
|
|
(g (cdr b) (fx+ icell 1))))))))
|
|
cells))))
|
|
|
|
(define eqv-generic?
|
|
(lambda (x)
|
|
; all numbers except fixnums must go through generic hashtable
|
|
(or (flonum? x) (bignum? x) (ratnum? x) ($exactnum? x) ($inexactnum? x))))
|
|
|
|
(define $eqv-hashtable-ref
|
|
(lambda (h x v who)
|
|
(if (eqv-generic? x)
|
|
($gen-hashtable-ref (eqv-ht-genht h) x v who)
|
|
(#3%eq-hashtable-ref (eqv-ht-eqht h) x v))))
|
|
|
|
(define $eqv-hashtable-contains?
|
|
(lambda (h x who)
|
|
(if (eqv-generic? x)
|
|
($gen-hashtable-contains? (eqv-ht-genht h) x who)
|
|
(#3%eq-hashtable-contains? (eqv-ht-eqht h) x))))
|
|
|
|
(define $eqv-hashtable-set!
|
|
(lambda (h x v who)
|
|
(if (eqv-generic? x)
|
|
($gen-hashtable-set! (eqv-ht-genht h) x v who)
|
|
(#3%eq-hashtable-set! (eqv-ht-eqht h) x v))))
|
|
|
|
(define $eqv-hashtable-update!
|
|
(lambda (h x p v who)
|
|
(if (eqv-generic? x)
|
|
($gen-hashtable-update! (eqv-ht-genht h) x p v who)
|
|
(#3%eq-hashtable-update! (eqv-ht-eqht h) x p v))))
|
|
|
|
(define $eqv-hashtable-cell
|
|
(lambda (h x v who)
|
|
(if (eqv-generic? x)
|
|
($gen-hashtable-cell (eqv-ht-genht h) x v who)
|
|
(#3%eq-hashtable-cell (eqv-ht-eqht h) x v))))
|
|
|
|
(define $eqv-hashtable-delete!
|
|
(lambda (h x who)
|
|
(if (eqv-generic? x)
|
|
($gen-hashtable-delete! (eqv-ht-genht h) x who)
|
|
(#3%eq-hashtable-delete! (eqv-ht-eqht h) x))))
|
|
|
|
(define $eqv-hashtable-copy
|
|
(lambda (h mutable?)
|
|
(make-eqv-ht 'eqv mutable?
|
|
($eq-hashtable-copy (eqv-ht-eqht h) mutable?)
|
|
($gen-hashtable-copy (eqv-ht-genht h) mutable?))))
|
|
|
|
(module ($eqv-hashtable-keys $eqv-hashtable-values $eqv-hashtable-entries $eqv-hashtable-cells)
|
|
(define vector-append
|
|
(lambda (v1 v2)
|
|
(let ([n1 (vector-length v1)] [n2 (vector-length v2)])
|
|
(if (fx= n1 0)
|
|
v2
|
|
(if (fx= n2 0)
|
|
v1
|
|
(let ([v (make-vector (fx+ n1 n2))])
|
|
(do ([i 0 (fx+ i 1)])
|
|
((fx= i n1))
|
|
(vector-set! v i (vector-ref v1 i)))
|
|
(do ([i 0 (fx+ i 1)] [j n1 (fx+ j 1)])
|
|
((fx= i n2))
|
|
(vector-set! v j (vector-ref v2 i)))
|
|
v))))))
|
|
(define $eqv-hashtable-keys
|
|
(lambda (h max-sz)
|
|
(let* ([keys1 ($eq-hashtable-keys (eqv-ht-eqht h) max-sz)]
|
|
[keys2 ($ht-hashtable-keys (eqv-ht-genht h) (fx- max-sz (vector-length keys1)))])
|
|
(vector-append keys1 keys2))))
|
|
(define $eqv-hashtable-values
|
|
(lambda (h max-sz)
|
|
(let* ([vals1 ($eq-hashtable-values (eqv-ht-eqht h) max-sz)]
|
|
[vals2 ($ht-hashtable-values (eqv-ht-genht h) (fx- max-sz (vector-length vals1)))])
|
|
(vector-append vals1 vals2))))
|
|
(define $eqv-hashtable-entries
|
|
(lambda (h max-sz)
|
|
(let*-values ([(keys1 vals1) ($eq-hashtable-entries (eqv-ht-eqht h) max-sz)]
|
|
[(keys2 vals2) ($ht-hashtable-entries (eqv-ht-genht h) (fx- max-sz (vector-length keys1)))])
|
|
(values
|
|
(vector-append keys1 keys2)
|
|
(vector-append vals1 vals2)))))
|
|
(define $eqv-hashtable-cells
|
|
(lambda (h max-sz)
|
|
(let* ([cells1 ($eq-hashtable-cells (eqv-ht-eqht h) max-sz)]
|
|
[cells2 ($ht-hashtable-cells (eqv-ht-genht h) (fx- max-sz (vector-length cells1)))])
|
|
(vector-append cells1 cells2)))))
|
|
|
|
(define number-hash
|
|
(lambda (z)
|
|
(cond
|
|
[(fixnum? z) (if (fx< z 0) (fxnot z) z)]
|
|
[(flonum? z) ($flhash z)]
|
|
[(bignum? z) (modulo z (most-positive-fixnum))]
|
|
[(ratnum? z) (number-hash (+ (* (numerator z) 5) (denominator z)))]
|
|
[else (logxor (lognot (number-hash (real-part z))) (number-hash (imag-part z)))])))
|
|
|
|
(set! $make-eq-hashtable ; assumes minlen is a power of two >= 1
|
|
(lambda (minlen subtype)
|
|
(make-eq-ht 'eq #t ($make-eqhash-vector minlen) minlen 0 subtype)))
|
|
|
|
(set-who! $hashtable-veclen
|
|
(lambda (h)
|
|
(unless (xht? h) ($oops who "~s is not a hashtable" h))
|
|
(case (xht-type h)
|
|
[(eqv) (values (vector-length (ht-vec (eqv-ht-eqht h))) (vector-length (ht-vec (eqv-ht-genht h))))]
|
|
[else (vector-length (ht-vec h))])))
|
|
|
|
(set-who! $ht-veclen
|
|
(lambda (h)
|
|
(unless (ht? h) ($oops who "~s is not an ht" h))
|
|
(vector-length (ht-vec h))))
|
|
|
|
(set-who! $ht-minlen
|
|
(lambda (h)
|
|
(unless (ht? h) ($oops who "~s is not an ht" h))
|
|
(ht-minlen h)))
|
|
|
|
(let ()
|
|
(define report
|
|
(lambda (h bucket-length)
|
|
(define (rnd n) (/ (round (* (inexact n) 100)) 100))
|
|
(let ([vec (ht-vec h)])
|
|
(let ([n (vector-length vec)])
|
|
(let f ([i 0] [cnt 0] [m 0] [ss 0])
|
|
(if (= i n)
|
|
(let ([mean (/ cnt n)])
|
|
(printf
|
|
"size, count, max, mean, std = ~s, ~s, ~s, ~s, ~s~%"
|
|
n cnt m (rnd mean)
|
|
(rnd (* (sqrt (- (/ ss n) (* mean mean)))))))
|
|
(let ([k (bucket-length (vector-ref vec i))])
|
|
(f (+ i 1) (+ cnt k) (max k m) (+ ss (* k k))))))))))
|
|
(define eq-bucket-length
|
|
(lambda (b)
|
|
(if (fixnum? b) 0 (fx1+ (eq-bucket-length ($tlc-next b))))))
|
|
(set-who! $hashtable-report
|
|
(lambda (h)
|
|
(unless (xht? h) ($oops who "~s is not a hashtable" h))
|
|
(case (xht-type h)
|
|
[(eq) (report h eq-bucket-length)]
|
|
[(eqv)
|
|
(report (eqv-ht-eqht h) eq-bucket-length)
|
|
(report (eqv-ht-genht h) length)]
|
|
[else (report h length)]))))
|
|
|
|
; csv7 interface
|
|
(set! make-hash-table
|
|
(case-lambda
|
|
[() ($make-eq-hashtable (constant hashtable-default-size) (constant eq-hashtable-subtype-normal))]
|
|
[(weak?) ($make-eq-hashtable (constant hashtable-default-size)
|
|
(if weak?
|
|
(constant eq-hashtable-subtype-weak)
|
|
(constant eq-hashtable-subtype-normal)))]))
|
|
|
|
(set! hash-table?
|
|
(lambda (x)
|
|
(eq-ht? x)))
|
|
|
|
(set-who! put-hash-table!
|
|
(lambda (h x v)
|
|
(unless (eq-ht? h) ($oops who "~s is not an eq hashtable" h))
|
|
(unless (xht-mutable? h) ($oops who "~s is not mutable" h))
|
|
(#3%eq-hashtable-set! h x v)))
|
|
|
|
(set-who! get-hash-table
|
|
(lambda (h x d)
|
|
(unless (eq-ht? h) ($oops who "~s is not an eq hashtable" h))
|
|
(#3%eq-hashtable-ref h x d)))
|
|
|
|
(set-who! remove-hash-table!
|
|
(lambda (h x)
|
|
(unless (eq-ht? h) ($oops who "~s is not an eq hashtable" h))
|
|
(unless (xht-mutable? h) ($oops who "~s is not mutable" h))
|
|
(#3%eq-hashtable-delete! h x)))
|
|
|
|
(set-who! hash-table-map
|
|
(lambda (h p)
|
|
(unless (eq-ht? h) ($oops who "~s is not an eq hashtable" h))
|
|
(unless (procedure? p) ($oops who "~s is not a procedure" p))
|
|
(let-values ([(keys vals) ($eq-hashtable-entries h (most-positive-fixnum))])
|
|
(let f ([i (vector-length keys)] [ls '()])
|
|
(if (fx= i 0)
|
|
ls
|
|
(let ([i (fx- i 1)])
|
|
(f i (cons (p (vector-ref keys i) (vector-ref vals i)) ls))))))))
|
|
|
|
(set-who! hash-table-for-each
|
|
(lambda (h p)
|
|
(unless (eq-ht? h) ($oops who "~s is not an eq hashtable" h))
|
|
(unless (procedure? p) ($oops who "~s is not a procedure" p))
|
|
(let-values ([(keys vals) ($eq-hashtable-entries h (most-positive-fixnum))])
|
|
(vector-for-each p keys vals))))
|
|
|
|
(set-who! make-eq-hashtable
|
|
(case-lambda
|
|
[() ($make-eq-hashtable (constant hashtable-default-size) (constant eq-hashtable-subtype-normal))]
|
|
[(k) ($make-eq-hashtable (size->minlen who k) (constant eq-hashtable-subtype-normal))]))
|
|
|
|
(set-who! make-weak-eq-hashtable
|
|
(case-lambda
|
|
[() ($make-eq-hashtable (constant hashtable-default-size) (constant eq-hashtable-subtype-weak))]
|
|
[(k) ($make-eq-hashtable (size->minlen who k) (constant eq-hashtable-subtype-weak))]))
|
|
|
|
(set-who! make-ephemeron-eq-hashtable
|
|
(case-lambda
|
|
[() ($make-eq-hashtable (constant hashtable-default-size) (constant eq-hashtable-subtype-ephemeron))]
|
|
[(k) ($make-eq-hashtable (size->minlen who k) (constant eq-hashtable-subtype-ephemeron))]))
|
|
|
|
(let ()
|
|
(define $make-hashtable
|
|
(lambda (minlen hash equiv?)
|
|
(if (and (eq? hash symbol-hash)
|
|
(or (eq? equiv? eq?)
|
|
(eq? equiv? symbol=?)
|
|
(eq? equiv? eqv?)
|
|
(eq? equiv? equal?)))
|
|
(make-symbol-ht 'symbol #t (make-vector minlen '()) minlen 0 equiv?)
|
|
(make-gen-ht 'generic #t (make-vector minlen '()) minlen 0 hash equiv?))))
|
|
(define $make-eqv-hashtable
|
|
(lambda (minlen subtype)
|
|
(make-eqv-ht 'eqv #t
|
|
($make-eq-hashtable minlen subtype)
|
|
($make-hashtable minlen number-hash eqv?))))
|
|
(set-who! make-hashtable
|
|
(case-lambda
|
|
[(hash equiv?)
|
|
(unless (procedure? hash) ($oops who "~s is not a procedure" hash))
|
|
(unless (procedure? equiv?) ($oops who "~s is not a procedure" equiv?))
|
|
($make-hashtable (constant hashtable-default-size) hash equiv?)]
|
|
[(hash equiv? k)
|
|
(unless (procedure? hash) ($oops who "~s is not a procedure" hash))
|
|
(unless (procedure? equiv?) ($oops who "~s is not a procedure" equiv?))
|
|
($make-hashtable (size->minlen who k) hash equiv?)]))
|
|
(set-who! make-eqv-hashtable
|
|
(case-lambda
|
|
[() ($make-eqv-hashtable (constant hashtable-default-size) (constant eq-hashtable-subtype-normal))]
|
|
[(k) ($make-eqv-hashtable (size->minlen who k) (constant eq-hashtable-subtype-normal))]))
|
|
(set-who! make-weak-eqv-hashtable
|
|
(case-lambda
|
|
[() ($make-eqv-hashtable (constant hashtable-default-size) (constant eq-hashtable-subtype-weak))]
|
|
[(k) ($make-eqv-hashtable (size->minlen who k) (constant eq-hashtable-subtype-weak))]))
|
|
(set-who! make-ephemeron-eqv-hashtable
|
|
(case-lambda
|
|
[() ($make-eqv-hashtable (constant hashtable-default-size) (constant eq-hashtable-subtype-ephemeron))]
|
|
[(k) ($make-eqv-hashtable (size->minlen who k) (constant eq-hashtable-subtype-ephemeron))])))
|
|
|
|
(set! eq-hashtable-ref
|
|
(lambda (h x v)
|
|
(unless (eq-ht? h)
|
|
($oops 'eq-hashtable-ref "~s is not an eq hashtable" h))
|
|
(#3%eq-hashtable-ref h x v)))
|
|
|
|
(set! eq-hashtable-contains?
|
|
(lambda (h x)
|
|
(unless (eq-ht? h)
|
|
($oops 'eq-hashtable-contains? "~s is not an eq hashtable" h))
|
|
(#3%eq-hashtable-contains? h x)))
|
|
|
|
(set! eq-hashtable-set!
|
|
(lambda (h x v)
|
|
(unless (eq-ht? h)
|
|
($oops 'eq-hashtable-set! "~s is not an eq hashtable" h))
|
|
(unless (xht-mutable? h)
|
|
($oops 'eq-hashtable-set! "~s is not mutable" h))
|
|
(#3%eq-hashtable-set! h x v)))
|
|
|
|
(set! eq-hashtable-update!
|
|
(lambda (h x p v)
|
|
(unless (eq-ht? h)
|
|
($oops 'eq-hashtable-update! "~s is not an eq hashtable" h))
|
|
(unless (xht-mutable? h)
|
|
($oops 'eq-hashtable-update! "~s is not mutable" h))
|
|
(unless (procedure? p)
|
|
($oops 'eq-hashtable-update! "~s is not a procedure" p))
|
|
(#3%eq-hashtable-update! h x p v)))
|
|
|
|
(set! eq-hashtable-cell
|
|
(lambda (h x v)
|
|
(unless (eq-ht? h)
|
|
($oops 'eq-hashtable-cell "~s is not an eq hashtable" h))
|
|
(#3%eq-hashtable-cell h x v)))
|
|
|
|
(set! eq-hashtable-delete!
|
|
(lambda (h x)
|
|
(unless (eq-ht? h)
|
|
($oops 'eq-hashtable-delete! "~s is not an eq hashtable" h))
|
|
(unless (xht-mutable? h)
|
|
($oops 'eq-hashtable-delete! "~s is not mutable" h))
|
|
(#3%eq-hashtable-delete! h x)))
|
|
|
|
(set-who! eq-hashtable-weak?
|
|
(lambda (h)
|
|
(unless (eq-ht? h) ($oops who "~s is not an eq hashtable" h))
|
|
(eq? (constant eq-hashtable-subtype-weak) (eq-ht-subtype h))))
|
|
|
|
(set-who! eq-hashtable-ephemeron?
|
|
(lambda (h)
|
|
(unless (eq-ht? h) ($oops who "~s is not an eq hashtable" h))
|
|
(eq? (constant eq-hashtable-subtype-ephemeron) (eq-ht-subtype h))))
|
|
|
|
(set-who! hashtable-weak?
|
|
(lambda (h)
|
|
(unless (xht? h) ($oops who "~s is not a hashtable" h))
|
|
(case (xht-type h)
|
|
[(eq) (eq? (constant eq-hashtable-subtype-weak) (eq-ht-subtype h))]
|
|
[(eqv) (eq? (constant eq-hashtable-subtype-weak) (eq-ht-subtype (eqv-ht-eqht h)))]
|
|
[else #f])))
|
|
|
|
(set-who! hashtable-ephemeron?
|
|
(lambda (h)
|
|
(unless (xht? h) ($oops who "~s is not a hashtable" h))
|
|
(case (xht-type h)
|
|
[(eq) (eq? (constant eq-hashtable-subtype-ephemeron) (eq-ht-subtype h))]
|
|
[(eqv) (eq? (constant eq-hashtable-subtype-ephemeron) (eq-ht-subtype (eqv-ht-eqht h)))]
|
|
[else #f])))
|
|
|
|
(set-who! symbol-hashtable-ref
|
|
(lambda (h x v)
|
|
(unless (symbol-ht? h) ($oops who "~s is not a symbol hashtable" h))
|
|
(unless (symbol? x) ($oops who "~s is not a symbol" x))
|
|
(#3%symbol-hashtable-ref h x v)))
|
|
|
|
(set-who! symbol-hashtable-contains?
|
|
(lambda (h x)
|
|
(unless (symbol-ht? h) ($oops who "~s is not a symbol hashtable" h))
|
|
(unless (symbol? x) ($oops who "~s is not a symbol" x))
|
|
(#3%symbol-hashtable-contains? h x)))
|
|
|
|
(set-who! symbol-hashtable-set!
|
|
(lambda (h x v)
|
|
(unless (symbol-ht? h) ($oops who "~s is not a symbol hashtable" h))
|
|
(unless (symbol? x) ($oops who "~s is not a symbol" x))
|
|
(unless (xht-mutable? h) ($oops who "~s is not mutable" h))
|
|
(#3%symbol-hashtable-set! h x v)))
|
|
|
|
(set-who! symbol-hashtable-update!
|
|
(lambda (h x p v)
|
|
(unless (symbol-ht? h) ($oops who "~s is not a symbol hashtable" h))
|
|
(unless (symbol? x) ($oops who "~s is not a symbol" x))
|
|
(unless (xht-mutable? h) ($oops who "~s is not mutable" h))
|
|
(unless (procedure? p)
|
|
($oops who "~s is not a procedure" p))
|
|
(#3%symbol-hashtable-update! h x p v)))
|
|
|
|
(set-who! symbol-hashtable-cell
|
|
(lambda (h x v)
|
|
(unless (symbol-ht? h) ($oops who "~s is not a symbol hashtable" h))
|
|
(unless (symbol? x) ($oops who "~s is not a symbol" x))
|
|
(#3%symbol-hashtable-cell h x v)))
|
|
|
|
(set-who! symbol-hashtable-delete!
|
|
(lambda (h x)
|
|
(unless (symbol-ht? h) ($oops who "~s is not a symbol hashtable" h))
|
|
(unless (symbol? x) ($oops who "~s is not a symbol" x))
|
|
(unless (xht-mutable? h) ($oops who "~s is not mutable" h))
|
|
(#3%symbol-hashtable-delete! h x)))
|
|
|
|
(set-who! hashtable-ref
|
|
(lambda (h x v)
|
|
(unless (xht? h)
|
|
($oops who "~s is not a hashtable" h))
|
|
(case (xht-type h)
|
|
[(eq) (#3%eq-hashtable-ref h x v)]
|
|
[(symbol)
|
|
(unless (symbol? x) ($oops 'symbol-hash "~s is not a symbol" x))
|
|
(#3%symbol-hashtable-ref h x v)]
|
|
[(eqv) ($eqv-hashtable-ref h x v who)]
|
|
[else ($gen-hashtable-ref h x v who)])))
|
|
|
|
(set-who! hashtable-contains?
|
|
(lambda (h x)
|
|
(unless (xht? h)
|
|
($oops who "~s is not a hashtable" h))
|
|
(case (xht-type h)
|
|
[(eq) (#3%eq-hashtable-contains? h x)]
|
|
[(symbol)
|
|
(unless (symbol? x) ($oops 'symbol-hash "~s is not a symbol" x))
|
|
(#3%symbol-hashtable-contains? h x)]
|
|
[(eqv) ($eqv-hashtable-contains? h x who)]
|
|
[else ($gen-hashtable-contains? h x who)])))
|
|
|
|
(set-who! hashtable-set!
|
|
(lambda (h x v)
|
|
(unless (xht? h)
|
|
($oops who "~s is not a hashtable" h))
|
|
(unless (xht-mutable? h)
|
|
($oops who "~s is not mutable" h))
|
|
(case (xht-type h)
|
|
[(eq) (#3%eq-hashtable-set! h x v)]
|
|
[(symbol)
|
|
(unless (symbol? x) ($oops 'symbol-hash "~s is not a symbol" x))
|
|
(#3%symbol-hashtable-set! h x v)]
|
|
[(eqv) ($eqv-hashtable-set! h x v who)]
|
|
[else ($gen-hashtable-set! h x v who)])))
|
|
|
|
(set-who! hashtable-update!
|
|
(lambda (h x p v)
|
|
(unless (xht? h)
|
|
($oops who "~s is not a hashtable" h))
|
|
(unless (xht-mutable? h)
|
|
($oops who "~s is not mutable" h))
|
|
(unless (procedure? p)
|
|
($oops who "~s is not a procedure" p))
|
|
(case (xht-type h)
|
|
[(eq) (#3%eq-hashtable-update! h x p v)]
|
|
[(symbol)
|
|
(unless (symbol? x) ($oops 'symbol-hash "~s is not a symbol" x))
|
|
(#3%symbol-hashtable-update! h x p v)]
|
|
[(eqv) ($eqv-hashtable-update! h x p v who)]
|
|
[else ($gen-hashtable-update! h x p v who)])))
|
|
|
|
(set-who! hashtable-cell
|
|
(lambda (h x v)
|
|
(unless (xht? h)
|
|
($oops who "~s is not a hashtable" h))
|
|
(case (xht-type h)
|
|
[(eq) (#3%eq-hashtable-cell h x v)]
|
|
[(symbol)
|
|
(unless (symbol? x) ($oops 'symbol-hash "~s is not a symbol" x))
|
|
(#3%symbol-hashtable-cell h x v)]
|
|
[(eqv) ($eqv-hashtable-cell h x v who)]
|
|
[else ($gen-hashtable-cell h x v who)])))
|
|
|
|
(set-who! hashtable-delete!
|
|
(lambda (h x)
|
|
(unless (xht? h)
|
|
($oops who "~s is not a hashtable" h))
|
|
(unless (xht-mutable? h)
|
|
($oops who "~s is not mutable" h))
|
|
(case (xht-type h)
|
|
[(eq) (#3%eq-hashtable-delete! h x)]
|
|
[(symbol)
|
|
(unless (symbol? x) ($oops 'symbol-hash "~s is not a symbol" x))
|
|
(#3%symbol-hashtable-delete! h x)]
|
|
[(eqv) ($eqv-hashtable-delete! h x who)]
|
|
[else ($gen-hashtable-delete! h x who)])))
|
|
|
|
(set! hashtable-copy
|
|
(rec hashtable-copy
|
|
(case-lambda
|
|
[(h) (hashtable-copy h #f)]
|
|
[(h mutable?)
|
|
(unless (xht? h)
|
|
($oops 'hashtable-copy "~s is not a hashtable" h))
|
|
(case (xht-type h)
|
|
[(eq) ($eq-hashtable-copy h (and mutable? #t))]
|
|
[(symbol) ($symbol-hashtable-copy h (and mutable? #t))]
|
|
[(eqv) ($eqv-hashtable-copy h (and mutable? #t))]
|
|
[else ($gen-hashtable-copy h (and mutable? #t))])])))
|
|
|
|
(set-who! hashtable-clear!
|
|
(let ()
|
|
(case-lambda
|
|
[(h)
|
|
(unless (xht? h)
|
|
($oops who "~s is not a hashtable" h))
|
|
(unless (xht-mutable? h)
|
|
($oops who "~s is not mutable" h))
|
|
(case (xht-type h)
|
|
[(eq) ($eq-hashtable-clear! h (ht-minlen h))]
|
|
[(eqv)
|
|
(let ([h (eqv-ht-eqht h)]) ($eq-hashtable-clear! h (ht-minlen h)))
|
|
(let ([h (eqv-ht-genht h)]) ($ht-hashtable-clear! h (ht-minlen h)))]
|
|
[else ($ht-hashtable-clear! h (ht-minlen h))])]
|
|
[(h k)
|
|
(unless (xht? h)
|
|
($oops who "~s is not a hashtable" h))
|
|
(unless (xht-mutable? h)
|
|
($oops who "~s is not mutable" h))
|
|
(let ([minlen (size->minlen who k)])
|
|
(case (xht-type h)
|
|
[(eq) ($eq-hashtable-clear! h minlen)]
|
|
[(eqv)
|
|
($eq-hashtable-clear! (eqv-ht-eqht h) minlen)
|
|
($ht-hashtable-clear! (eqv-ht-genht h) minlen)]
|
|
[else ($ht-hashtable-clear! h minlen)]))])))
|
|
|
|
(let ()
|
|
(define (invalid-length who max-sz)
|
|
($oops who "~s is not a valid length" max-sz))
|
|
(define (invalid-table who h)
|
|
($oops who "~s is not a hashtable" h))
|
|
|
|
(define-syntax hashtable-content-dispatch
|
|
(syntax-rules ()
|
|
[(_ who $eq-hashtable-content $eqv-hashtable-content $ht-hashtable-content)
|
|
(let ()
|
|
(define (dispatch h max-sz)
|
|
(unless (xht? h) (invalid-table who h))
|
|
(case (xht-type h)
|
|
[(eq) ($eq-hashtable-content h max-sz)]
|
|
[(eqv) ($eqv-hashtable-content h max-sz)]
|
|
[else ($ht-hashtable-content h max-sz)]))
|
|
(case-lambda
|
|
[(h max-sz)
|
|
(cond
|
|
[(fixnum? max-sz)
|
|
(unless (fx>= max-sz 0) (invalid-length who max-sz))
|
|
(dispatch h max-sz)]
|
|
[(bignum? max-sz)
|
|
(unless (>= max-sz 0) (invalid-length who max-sz))
|
|
(dispatch h (most-positive-fixnum))]
|
|
[else (invalid-length who max-sz)])]
|
|
[(h) (dispatch h (most-positive-fixnum))]))]))
|
|
|
|
(set-who! hashtable-keys
|
|
(hashtable-content-dispatch who
|
|
$eq-hashtable-keys
|
|
$eqv-hashtable-keys
|
|
$ht-hashtable-keys))
|
|
|
|
(set-who! #(r6rs: hashtable-keys)
|
|
(lambda (h)
|
|
(unless (xht? h) (invalid-table who h))
|
|
(case (xht-type h)
|
|
[(eq) ($eq-hashtable-keys h (most-positive-fixnum))]
|
|
[(eqv) ($eqv-hashtable-keys h (most-positive-fixnum))]
|
|
[else ($ht-hashtable-keys h (most-positive-fixnum))])))
|
|
|
|
(set-who! hashtable-values
|
|
(hashtable-content-dispatch who
|
|
$eq-hashtable-values
|
|
$eqv-hashtable-values
|
|
$ht-hashtable-values))
|
|
|
|
(set-who! hashtable-entries
|
|
(hashtable-content-dispatch who
|
|
$eq-hashtable-entries
|
|
$eqv-hashtable-entries
|
|
$ht-hashtable-entries))
|
|
|
|
(set-who! #(r6rs: hashtable-entries)
|
|
(lambda (h)
|
|
(unless (xht? h) (invalid-table who h))
|
|
(case (xht-type h)
|
|
[(eq) ($eq-hashtable-entries h (most-positive-fixnum))]
|
|
[(eqv) ($eqv-hashtable-entries h (most-positive-fixnum))]
|
|
[else ($ht-hashtable-entries h (most-positive-fixnum))])))
|
|
|
|
(set-who! hashtable-cells
|
|
(hashtable-content-dispatch who
|
|
$eq-hashtable-cells
|
|
$eqv-hashtable-cells
|
|
$ht-hashtable-cells)))
|
|
|
|
(set! hashtable-size
|
|
(lambda (h)
|
|
(unless (xht? h) ($oops 'hashtable-size "~s is not a hashtable" h))
|
|
(if (eq? (xht-type h) 'eqv)
|
|
(fx+ (ht-size (eqv-ht-eqht h))
|
|
(ht-size (eqv-ht-genht h)))
|
|
(ht-size h))))
|
|
|
|
(set! hashtable-mutable?
|
|
(lambda (h)
|
|
(unless (xht? h)
|
|
($oops 'hashtable-mutable? "~s is not a hashtable" h))
|
|
(xht-mutable? h)))
|
|
|
|
(set! hashtable?
|
|
(lambda (x)
|
|
(xht? x)))
|
|
|
|
(set! eq-hashtable?
|
|
(lambda (x)
|
|
(eq-ht? x)))
|
|
|
|
(set! symbol-hashtable?
|
|
(lambda (x)
|
|
(symbol-ht? x)))
|
|
|
|
(set-who! $hashtable-size->minlen
|
|
(lambda (k)
|
|
(size->minlen who k)))
|
|
|
|
(set-who! hashtable-hash-function
|
|
(lambda (h)
|
|
(unless (xht? h) ($oops who "~s is not an eq hashtable" h))
|
|
(case (xht-type h)
|
|
[(eq eqv) #f]
|
|
[(symbol) symbol-hash]
|
|
[else (gen-ht-hash h)])))
|
|
|
|
(set-who! hashtable-equivalence-function
|
|
(lambda (h)
|
|
(unless (xht? h) ($oops who "~s is not an eq hashtable" h))
|
|
(case (xht-type h)
|
|
[(eq) eq?]
|
|
[(symbol) (symbol-ht-equiv? h)]
|
|
[(eqv) eqv?]
|
|
[else (gen-ht-equiv? h)])))
|
|
|
|
(let ()
|
|
(define (hcabs hc) (if (fx< hc 0) (fxnot hc) hc))
|
|
|
|
(define (update hc k)
|
|
(fxlogxor (#3%fx+ (#3%fxsll hc 2) hc) k))
|
|
|
|
(define bytevector-hash
|
|
(lambda (bv)
|
|
(define (bvupdate hc bv i)
|
|
(update hc (bytevector-u8-ref bv i)))
|
|
(let ([n (bytevector-length bv)])
|
|
(if (fx<= n 16)
|
|
(do ([i 0 (fx+ i 1)] [hc 440697712 (bvupdate hc bv i)])
|
|
((fx= i n) (hcabs hc)))
|
|
(do ([i 0 (fx+ i 1)]
|
|
[hc 440697712 (bvupdate hc bv i)])
|
|
((fx= i 5)
|
|
(do ([i (fx- n 5) (fx+ i 1)]
|
|
[hc hc (bvupdate hc bv i)])
|
|
((fx= i n)
|
|
(let ([stride (fxsrl n 4)])
|
|
(do ([i 5 (fx+ i stride)]
|
|
[hc hc (bvupdate hc bv i)])
|
|
((fx>= i n) (hcabs hc))))))))))))
|
|
|
|
(set-who! string-hash
|
|
(lambda (s)
|
|
(define (strupdate hc s i)
|
|
(update hc (char->integer (string-ref s i))))
|
|
(unless (string? s) ($oops who "~s is not a string" s))
|
|
(let ([n (string-length s)])
|
|
(if (fx<= n 16)
|
|
(do ([i 0 (fx+ i 1)] [hc 523658599 (strupdate hc s i)])
|
|
((fx= i n) (hcabs hc)))
|
|
(do ([i 0 (fx+ i 1)]
|
|
[hc 523658599 (strupdate hc s i)])
|
|
((fx= i 5)
|
|
(do ([i (fx- n 5) (fx+ i 1)]
|
|
[hc hc (strupdate hc s i)])
|
|
((fx= i n)
|
|
(let ([stride (fxsrl n 4)])
|
|
(do ([i 5 (fx+ i stride)]
|
|
[hc hc (strupdate hc s i)])
|
|
((fx>= i n) (hcabs hc))))))))))))
|
|
|
|
(set-who! string-ci-hash
|
|
(lambda (s)
|
|
(define (charupdate hc c) (update hc (char->integer c)))
|
|
(unless (string? s) ($oops who "~s is not a string" s))
|
|
(let ([n (string-length s)])
|
|
(let f ([i 0] [hc 523658599])
|
|
(if (fx= i n)
|
|
(hcabs hc)
|
|
(let g ([c* ($string-char-foldcase (string-ref s i))] [hc hc])
|
|
(if (char? c*)
|
|
(f (fx+ i 1) (charupdate hc c*))
|
|
(g (cdr c*) (charupdate hc (car c*))))))))))
|
|
|
|
(set-who! symbol-hash
|
|
(lambda (x)
|
|
(unless (symbol? x) ($oops who "~s is not a symbol" x))
|
|
(or ($symbol-hash x)
|
|
(and (gensym? x) (begin (gensym->unique-string x) ($symbol-hash x)))
|
|
($oops who "symbol hash is not set for ~s" x))))
|
|
|
|
(set-who! equal-hash
|
|
(lambda (x)
|
|
(define (f x hc i)
|
|
(let ([i (fx- i 1)])
|
|
(cond
|
|
[(fx<= i 0) (values hc 0)]
|
|
[(pair? x)
|
|
(let ([i/2 (fxsrl (fx+ i 1) 1)])
|
|
(let-values ([(hc i^) (f (car x) (update hc 119001092) i/2)])
|
|
(f (cdr x) hc (fx+ (fx- i i/2) i^))))]
|
|
[(vector? x)
|
|
(let ([n (vector-length x)] [hc (update hc 513566316)])
|
|
(if (fx= n 0)
|
|
(values hc i)
|
|
(let g ([j 0] [hc hc] [i i])
|
|
(if (or (fx= j n) (fx= i 0))
|
|
(values hc i)
|
|
(let ([i/2 (fxsrl (fx+ i 1) 1)])
|
|
(let-values ([(hc i^) (f (vector-ref x j) hc i/2)])
|
|
(g (fx+ j 1) hc (fx+ (fx- i i/2) i^))))))))]
|
|
[(null? x) (values (update hc 496904691) i)]
|
|
[(box? x) (f (unbox x) (update hc 410225874) i)]
|
|
[(symbol? x) (values (update hc (symbol-hash x)) i)]
|
|
[(string? x) (values (update hc (string-hash x)) i)]
|
|
[(number? x) (values (update hc (number-hash x)) i)]
|
|
[(bytevector? x) (values (update hc (bytevector-hash x)) i)]
|
|
[(boolean? x) (values (update hc (if x 336200167 307585980)) i)]
|
|
[(char? x) (values (update hc (char->integer x)) i)]
|
|
[(and ($record? x) ($record-hash-procedure x))
|
|
=> (lambda (rec-hash)
|
|
(let ([new-i i])
|
|
(let ([sub-hc (rec-hash
|
|
x
|
|
(lambda (v)
|
|
(if (fx<= new-i 0)
|
|
0
|
|
(let-values ([(sub-hc sub-i) (f v 0 i)])
|
|
(set! new-i sub-i)
|
|
sub-hc))))])
|
|
(let ([hc (update hc (if (fixnum? sub-hc)
|
|
sub-hc
|
|
(modulo (abs sub-hc) (greatest-fixnum))))])
|
|
(values hc new-i)))))]
|
|
[else (values (update hc 120634730) i)])))
|
|
(let-values ([(hc i) (f x 523658599 64)])
|
|
(hcabs hc)))))
|
|
|
|
(record-writer (type-descriptor hashtable)
|
|
(lambda (x p wr)
|
|
(display "#<hashtable>" p)))
|
|
|
|
(record-writer (type-descriptor eq-ht)
|
|
(lambda (x p wr)
|
|
(display "#<eq hashtable>" p)))
|
|
|
|
(record-writer (type-descriptor eqv-ht)
|
|
(lambda (x p wr)
|
|
(display "#<eqv hashtable>" p)))
|
|
)
|
|
|
|
;;; eq hashtable operations must be compiled with
|
|
;;; generate-interrupt-trap #f and optimize-level 3
|
|
;;; so they can't be interrupted by a collection
|
|
;;; see also library routines in library.ss
|
|
(eval-when (compile)
|
|
(generate-interrupt-trap #f)
|
|
(optimize-level 3))
|
|
|
|
(let ()
|
|
(include "hashtable-types.ss")
|
|
|
|
(set! $eq-hashtable-keys
|
|
(lambda (h max-sz)
|
|
(let ([vec (ht-vec h)] [size (fxmin max-sz (ht-size h))])
|
|
(let ([n (vector-length vec)] [keys (make-vector size)])
|
|
(let outer ([i 0] [j 0])
|
|
(if (or (fx= i n) (fx= j size))
|
|
keys
|
|
(let inner ([b (vector-ref vec i)] [j j])
|
|
(if (or (fixnum? b) (fx= j size))
|
|
(outer (fx+ i 1) j)
|
|
(let ([keyval ($tlc-keyval b)])
|
|
(vector-set! keys j (car keyval))
|
|
(inner ($tlc-next b) (fx+ j 1)))))))))))
|
|
|
|
(set! $eq-hashtable-values
|
|
(lambda (h max-sz)
|
|
(let ([vec (ht-vec h)] [size (fxmin max-sz (ht-size h))])
|
|
(let ([n (vector-length vec)] [vals (make-vector size)])
|
|
(let outer ([i 0] [j 0])
|
|
(if (or (fx= i n) (fx= j size))
|
|
vals
|
|
(let inner ([b (vector-ref vec i)] [j j])
|
|
(if (or (fixnum? b) (fx= j size))
|
|
(outer (fx+ i 1) j)
|
|
(let ([keyval ($tlc-keyval b)])
|
|
(vector-set! vals j (cdr keyval))
|
|
(inner ($tlc-next b) (fx+ j 1)))))))))))
|
|
|
|
(set! $eq-hashtable-entries
|
|
(lambda (h max-sz)
|
|
(let ([vec (ht-vec h)] [size (fxmin max-sz (ht-size h))])
|
|
(let ([n (vector-length vec)]
|
|
[keys (make-vector size)]
|
|
[vals (make-vector size)])
|
|
(let outer ([i 0] [j 0])
|
|
(if (or (fx= i n) (fx= j size))
|
|
(values keys vals)
|
|
(let inner ([b (vector-ref vec i)] [j j])
|
|
(if (or (fixnum? b) (fx= j size))
|
|
(outer (fx+ i 1) j)
|
|
(let ([keyval ($tlc-keyval b)])
|
|
(vector-set! keys j (car keyval))
|
|
(vector-set! vals j (cdr keyval))
|
|
(inner ($tlc-next b) (fx+ j 1)))))))))))
|
|
|
|
(set! $eq-hashtable-cells
|
|
(lambda (h max-sz)
|
|
(let ([vec (ht-vec h)] [size (fxmin max-sz (ht-size h))])
|
|
(let ([n (vector-length vec)] [cells (make-vector size)])
|
|
(let outer ([i 0] [j 0])
|
|
(if (or (fx= i n) (fx= j size))
|
|
cells
|
|
(let inner ([b (vector-ref vec i)] [j j])
|
|
(if (or (fixnum? b) (fx= j size))
|
|
(outer (fx+ i 1) j)
|
|
(let ([keyval ($tlc-keyval b)])
|
|
(vector-set! cells j keyval)
|
|
(inner ($tlc-next b) (fx+ j 1)))))))))))
|
|
|
|
(set! $eq-hashtable-copy
|
|
(lambda (h1 mutable?)
|
|
(let ([subtype (eq-ht-subtype h1)])
|
|
(let* ([vec1 (ht-vec h1)]
|
|
[n (vector-length vec1)]
|
|
[vec2 ($make-eqhash-vector n)]
|
|
[h2 (make-eq-ht 'eq mutable? vec2 (ht-minlen h1) (ht-size h1) subtype)])
|
|
(let outer ([i 0])
|
|
(if (fx= i n)
|
|
h2
|
|
(begin
|
|
(vector-set! vec2 i
|
|
(let inner ([b (vector-ref vec1 i)])
|
|
(if (fixnum? b)
|
|
b
|
|
($make-tlc h2
|
|
(let* ([keyval ($tlc-keyval b)] [key (car keyval)] [val (cdr keyval)])
|
|
(cond
|
|
[(eq? subtype (constant eq-hashtable-subtype-normal)) (cons key val)]
|
|
[(eq? subtype (constant eq-hashtable-subtype-weak)) (weak-cons key val)]
|
|
[else (ephemeron-cons key val)]))
|
|
(inner ($tlc-next b))))))
|
|
(outer (fx+ i 1)))))
|
|
h2))))
|
|
|
|
(set! $eq-hashtable-clear!
|
|
(lambda (h minlen)
|
|
(let* ([vec (ht-vec h)] [n (vector-length vec)])
|
|
(do ([i 0 (fx+ i 1)])
|
|
((fx= i n))
|
|
(let loop ([b (vector-ref vec i)])
|
|
(if (fixnum? b)
|
|
(vector-set! vec i i)
|
|
(let ([next ($tlc-next b)])
|
|
($set-tlc-next! b #f)
|
|
(loop next)))))
|
|
(ht-size-set! h 0)
|
|
(unless (fx= n minlen)
|
|
(ht-vec-set! h ($make-eqhash-vector minlen))))))
|
|
|
|
(let ()
|
|
;; An equal/hash mapping contains an equal or hash procedure (or #f)
|
|
;; plus the rtd where the procedure was installed. It also has a weak
|
|
;; list of uids for child rtds that have inherited the setting, in
|
|
;; case the rtd's setting changes.
|
|
(define-record-type equal/hash
|
|
(fields maybe-proc rtd (mutable inheritors))
|
|
(nongenerative)
|
|
(sealed #t)
|
|
(protocol
|
|
(lambda (new)
|
|
(lambda (maybe-proc rtd)
|
|
(new maybe-proc rtd '())))))
|
|
|
|
(let ()
|
|
(define (get-equal/hash who rtd key)
|
|
(unless (record-type-descriptor? rtd)
|
|
($oops who "~s is not a record-type descriptor" rtd))
|
|
(let ([e/h ($sgetprop (record-type-uid rtd) key #f)])
|
|
(and e/h
|
|
(eq? (equal/hash-rtd e/h) rtd)
|
|
(equal/hash-maybe-proc e/h))))
|
|
(define (set-equal/hash! who rtd key proc)
|
|
(unless (record-type-descriptor? rtd)
|
|
($oops who "~s is not a record-type descriptor" rtd))
|
|
(unless (or (not proc) (procedure? proc))
|
|
($oops who "~s is not a procedure or #f" proc))
|
|
(with-tc-mutex
|
|
(let* ([uid (record-type-uid rtd)]
|
|
[old-e/h ($sgetprop uid key #f)])
|
|
;; Remove the old record from anywhere that it's inherited,
|
|
;; and a later lookup will re-inherit:
|
|
(when old-e/h
|
|
(for-each
|
|
(lambda (uid)
|
|
(unless (bwp-object? uid)
|
|
(when (eq? ($sgetprop uid key #f) old-e/h)
|
|
($sremprop uid key))))
|
|
(equal/hash-inheritors old-e/h)))
|
|
(if proc
|
|
($sputprop uid key (make-equal/hash proc rtd))
|
|
($sremprop uid key)))))
|
|
(set-who! record-type-equal-procedure
|
|
(case-lambda
|
|
[(rtd) (get-equal/hash who rtd 'equal-proc)]
|
|
[(rtd equal-proc) (set-equal/hash! who rtd 'equal-proc equal-proc)]))
|
|
(set-who! record-type-hash-procedure
|
|
(case-lambda
|
|
[(rtd) (get-equal/hash who rtd 'hash-proc)]
|
|
[(rtd hash-proc) (set-equal/hash! who rtd 'hash-proc hash-proc)])))
|
|
|
|
(let ()
|
|
;; Gets an `equal/hash` record for the given rtd, finding
|
|
;; it from a parent rtd and caching if necessary:
|
|
(define (lookup-equal/hash record key)
|
|
(let* ([rtd ($record-type-descriptor record)] [uid (record-type-uid rtd)])
|
|
; Get out quick w/o mutex if equal/hash record is present
|
|
(or ($sgetprop uid key #f)
|
|
(with-tc-mutex
|
|
(let f ([uid uid] [rtd rtd])
|
|
;; Double-check first time around to avoid a race
|
|
(or ($sgetprop uid key #f)
|
|
(let ([parent-rtd (record-type-parent rtd)])
|
|
(if parent-rtd
|
|
;; Cache parent's value, and register as an inheritor:
|
|
(let ([e/h (f (record-type-uid parent-rtd) parent-rtd)])
|
|
(equal/hash-inheritors-set! e/h (weak-cons uid (equal/hash-inheritors e/h)))
|
|
($sputprop uid key e/h)
|
|
e/h)
|
|
;; Cache an empty `equal/hash` record:
|
|
(let ([e/h (make-equal/hash #f rtd)])
|
|
($sputprop uid key e/h)
|
|
e/h)))))))))
|
|
(let ()
|
|
(define (lookup-equal-procedure record1 record2)
|
|
(let ([e/h (lookup-equal/hash record1 'equal-proc)])
|
|
(let ([proc (equal/hash-maybe-proc e/h)])
|
|
(if proc
|
|
(and
|
|
(eq? (equal/hash-rtd (lookup-equal/hash record2 'equal-proc)) (equal/hash-rtd e/h))
|
|
proc)
|
|
(let ([default-proc (default-record-equal-procedure)])
|
|
(and default-proc
|
|
(not (equal/hash-maybe-proc (lookup-equal/hash record2 'equal-proc)))
|
|
default-proc))))))
|
|
(set-who! $record-equal-procedure
|
|
(lambda (record1 record2)
|
|
(lookup-equal-procedure record1 record2)))
|
|
(set-who! record-equal-procedure
|
|
(lambda (record1 record2)
|
|
(unless ($record? record1) ($oops who "~s is not a record" record1))
|
|
(unless ($record? record2) ($oops who "~s is not a record" record2))
|
|
(lookup-equal-procedure record1 record2))))
|
|
(let ()
|
|
(define (lookup-hash-procedure record)
|
|
(or (equal/hash-maybe-proc (lookup-equal/hash record 'hash-proc))
|
|
(default-record-hash-procedure)))
|
|
(set-who! $record-hash-procedure
|
|
(lambda (record)
|
|
(lookup-hash-procedure record)))
|
|
(set-who! record-hash-procedure
|
|
(lambda (record)
|
|
(unless ($record? record) ($oops who "~s is not a record" record))
|
|
(lookup-hash-procedure record))))))
|
|
)
|