This repository has been archived on 2022-08-10. You can view files and clone it, but cannot push or open issues or pull requests.
chez-openbsd/s/newhash.ss

1258 lines
50 KiB
Scheme
Raw Normal View History

2022-07-29 15:12:07 +02:00
;;; 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))))))
)