racket/mats/hash.ms
Matthew Flatt 211fe4cbd7 tests and docs for ephemerons
original commit: 2ea7dcdfca1dea2c89c51c7e9ccd692ba673ba22
2017-05-24 09:38:58 -06:00

3069 lines
108 KiB
Scheme

;;; hash.ms
;;; 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.
(mat old-hash-table
(error? (get-hash-table '((a . b)) 'a #f))
(error? (put-hash-table! (list (cons 'a 'b)) 'a 'b))
(error? (remove-hash-table! (list (cons 'a 'b)) 'a))
(error? (hash-table-map '((a . b)) cons))
(error? (hash-table-for-each '((a . b)) cons))
(begin
(define $h-ht (make-hash-table))
(hash-table? $h-ht))
(not (hash-table? 3))
(not (hash-table? '$h-ht))
(null? (hash-table-map $h-ht list))
(eq? (let ([n 0])
(hash-table-for-each $h-ht (lambda (x y) (set! n (+ n 1))))
n)
0)
(equal?
(begin
(put-hash-table! $h-ht 'ham 'spam)
(hash-table-map $h-ht list))
'((ham spam)))
(error? ; wrong number of args
(hash-table-map $h-ht (lambda (x) x)))
(error? ; wrong number of args
(hash-table-for-each $h-ht (lambda (x) x)))
((lambda (x y) (or (equal? x y) (equal? x (reverse y))))
(begin
(put-hash-table! $h-ht 'cram 'sham)
(hash-table-map $h-ht list))
'((ham spam) (cram sham)))
((lambda (x y) (or (equal? x y) (equal? x (reverse y))))
(begin
(put-hash-table! $h-ht 'ham 'jam)
(hash-table-map $h-ht list))
'((ham jam) (cram sham)))
(eq? (get-hash-table $h-ht 'ham #f) 'jam)
(eq? (get-hash-table $h-ht 'cram #f) 'sham)
(eq? (get-hash-table $h-ht 'sham #f) #f)
(equal? (get-hash-table $h-ht 'jam "rats") "rats")
(eq? (let ([n 0])
(hash-table-for-each $h-ht (lambda (x y) (set! n (+ n 1))))
n)
2)
((lambda (x y) (or (equal? x y) (equal? x (reverse y))))
(let ([keys '()] [vals '()])
(hash-table-for-each $h-ht
(lambda (k v)
(set! keys (cons k keys))
(set! vals (cons v vals))))
(map cons vals keys))
'((jam . ham) (sham . cram)))
(eq? (collect (collect-maximum-generation)) (void))
((lambda (x y) (or (equal? x y) (equal? x (reverse y))))
(let ([keys '()] [vals '()])
(hash-table-for-each $h-ht
(lambda (k v)
(set! keys (cons k keys))
(set! vals (cons v vals))))
(map cons vals keys))
'((jam . ham) (sham . cram)))
(eq? (begin
(remove-hash-table! $h-ht 'ham)
(get-hash-table $h-ht 'ham 'gone!))
'gone!)
(equal?
(hash-table-map $h-ht list)
'((cram sham)))
(eq? (collect (collect-maximum-generation)) (void))
(equal?
(hash-table-map $h-ht list)
'((cram sham)))
(eq? (begin
(remove-hash-table! $h-ht 'ham)
(get-hash-table $h-ht 'ham 'gone!))
'gone!)
(equal?
(hash-table-map $h-ht list)
'((cram sham)))
(eq? (begin
(remove-hash-table! $h-ht 'sham)
(get-hash-table $h-ht 'ham 'never-there!))
'never-there!)
(equal?
(hash-table-map $h-ht list)
'((cram sham)))
(eq? (begin
(remove-hash-table! $h-ht 'cram)
(get-hash-table $h-ht 'cram 'gone-too!))
'gone-too!)
(null? (hash-table-map $h-ht list))
; fasling out eq hash tables
(equal?
(let ([x (cons 'y '!)])
(define ht (make-hash-table))
(put-hash-table! ht x 'because)
(put-hash-table! ht 'foo "foo")
(let ([p (open-file-output-port "testfile.ss" (file-options replace))])
(fasl-write (list x ht) p)
(close-port p))
(let-values ([(x2 ht2)
(apply values
(call-with-port
(open-file-input-port "testfile.ss")
fasl-read))])
(list
(get-hash-table ht2 x2 #f)
(get-hash-table ht2 'foo #f))))
'(because "foo"))
; weak hash table tests
(begin
(define $h-ht (make-hash-table #t))
(hash-table? $h-ht))
(null?
(begin
(put-hash-table! $h-ht (string #\a) 'yea!)
(collect (collect-maximum-generation))
(hash-table-map $h-ht cons)))
(eq? (let ([n 0])
(hash-table-for-each $h-ht (lambda (x y) (set! n (+ n 1))))
n)
0)
(let ([s (string #\a)])
(put-hash-table! $h-ht s 666)
(equal? (get-hash-table $h-ht s #f) 666))
(null?
(begin
(collect (collect-maximum-generation))
(hash-table-map $h-ht cons)))
; make sure that nonweak hash tables are nonweak (explicit #f arg)
(begin
(define $h-ht (make-hash-table #f))
(hash-table? $h-ht))
(equal?
(begin
(put-hash-table! $h-ht (string #\a) "bc")
(collect (collect-maximum-generation))
(hash-table-map $h-ht string-append))
'("abc"))
; make sure that nonweak hash tables are nonweak (implicit #f arg)
(begin
(define $h-ht (make-hash-table))
(hash-table? $h-ht))
(equal?
(begin
(put-hash-table! $h-ht (string #\a) "bc")
(collect (collect-maximum-generation))
(hash-table-map $h-ht string-append))
'("abc"))
; stress tests
(let () ; nonweak
(define pick
(lambda (ls)
(list-ref ls (random (length ls)))))
(define ht (make-hash-table))
(let* ([ls (remq '|| (oblist))] [n 50000])
(let f ([i 0] [keep '()] [drop '()])
(if (= i n)
(and (= (length (hash-table-map ht (lambda (x y) x)))
(- n (length drop)))
(andmap (lambda (k)
(string=?
(symbol->string (get-hash-table ht k #f))
(cond
[(string? k) k]
[(pair? k) (car k)]
[(vector? k) (vector-ref k 0)])))
keep)
(andmap (lambda (k) (eq? (get-hash-table ht k 'no) 'no))
drop))
(let* ([x (pick ls)] [s (string-copy (symbol->string x))])
(let ([k (case (pick '(string pair vector))
[(string) s]
[(pair) (list s)]
[(vector) (vector s)])])
(put-hash-table! ht k x)
(let ([keep (if (= (modulo i 5) 0) (cons k keep) keep)])
(if (= (modulo i 17) 5)
(let ([k (pick keep)])
(remove-hash-table! ht k)
(let ([drop (cons k drop)])
(when (= (random 5) 3)
(remove-hash-table! ht (pick drop)))
(f (+ i 1) (remq k keep) drop)))
(f (+ i 1) keep drop)))))))))
(let () ; weak
(define pick
(lambda (ls)
(list-ref ls (random (length ls)))))
(define ht (make-hash-table #t))
(let* ([ls (remq '|| (oblist))] [n 50000])
(let f ([i 0] [keep '()] [drop '()])
(if (= i n)
(and (<= (length (hash-table-map ht (lambda (x y) x)))
(- n (length drop)))
(begin
(collect (collect-maximum-generation))
(= (length (hash-table-map ht (lambda (x y) x)))
(length keep)))
(andmap (lambda (k)
(string=?
(symbol->string (get-hash-table ht k #f))
(cond
[(string? k) k]
[(pair? k) (car k)]
[(vector? k) (vector-ref k 0)])))
keep)
(andmap (lambda (k) (eq? (get-hash-table ht k 'no) 'no))
drop))
(let* ([x (pick ls)] [s (string-copy (symbol->string x))])
(let ([k (case (pick '(string pair vector))
[(string) s]
[(pair) (list s)]
[(vector) (vector s)])])
(put-hash-table! ht k x)
(let ([keep (if (= (modulo i 5) 0) (cons k keep) keep)])
(if (= (modulo i 17) 5)
(let ([k (pick keep)])
(remove-hash-table! ht k)
(let ([drop (cons k drop)])
(when (= (random 5) 3)
(remove-hash-table! ht (pick drop)))
(f (+ i 1) (remq k keep) drop)))
(f (+ i 1) keep drop)))))))))
)
(mat tlc
(critical-section
(let ()
(define ht (make-eq-hashtable))
(define keyval '(a . b))
(define next 0)
(define tlc (#%$make-tlc ht keyval next))
(define tlc2 (#%$make-tlc ht keyval next))
(and
(#%$tlc? tlc)
(not (#%$tlc? keyval))
(eq? (#%$tlc-ht tlc) ht)
(eq? (#%$tlc-keyval tlc) keyval)
(eqv? (#%$tlc-next tlc) next)
(begin
(#%$set-tlc-next! tlc tlc2)
(eq? (#%$tlc-next tlc) tlc2)))))
)
(define $vector-andmap
(lambda (p . v*)
(apply andmap p (map vector->list v*))))
(define $vector-append
(lambda v*
(list->vector (apply append (map vector->list v*)))))
(define $vector-member?
(lambda (x v)
(let ([n (vector-length v)])
(let f ([i 0])
(and (not (fx= i n))
(or (equal? (vector-ref v i) x)
(f (fx+ i 1))))))))
(define same-elements?
(lambda (v1 v2)
(let ([n (vector-length v1)])
(define (each-in? v1 v2)
(let f ([i 0])
(or (fx= i n)
(and ($vector-member? (vector-ref v1 i) v2)
(f (fx+ i 1))))))
(and (fx= (vector-length v2) n)
(each-in? v1 v2)
(each-in? v2 v1)))))
(define $equal-entries?
(lambda (keys1 vals1 keys2 vals2)
(and
(same-elements? keys1 keys2)
(same-elements? vals1 vals2))))
(define-syntax equal-entries?
(syntax-rules ()
[(_ e1 e2 e3)
(let-values ([(keys1 vals1) e1])
($equal-entries? keys1 vals1 e2 e3))]))
(mat hashtable-arguments
; make-eq-hashtable
(error? ; wrong argument count
(make-eq-hashtable 3 #t))
(error? ; invalid size
(make-eq-hashtable -1))
(error? ; invalid size
(make-eq-hashtable #t))
(error? ; invalid size
(make-eq-hashtable #f))
; make-hashtable
(error? ; wrong argument count
(make-hashtable))
(error? ; wrong argument count
(make-hashtable equal-hash))
(error? ; wrong argument count
(make-hashtable equal-hash equal? 45 53))
(error? ; not a procedure
(make-hashtable 'a equal? 45))
(error? ; not a procedure
(make-hashtable equal-hash 'a 45))
(error? ; invalid size
(make-hashtable equal-hash equal? 'a))
(error? ; invalid size
(make-hashtable equal-hash equal? -45))
(error? ; invalid size
(make-hashtable equal-hash equal? 45.0))
; make-eqv-hashtable
(error? ; wrong argument count
(make-eqv-hashtable 3 #t))
(error? ; invalid size
(make-eqv-hashtable -1))
(error? ; invalid size
(make-eqv-hashtable #t))
(error? ; invalid size
(make-eqv-hashtable #f))
(begin
(define $ht (make-eq-hashtable))
(define $imht (hashtable-copy $ht))
(define $ht2 (make-eq-hashtable 50))
(and (hashtable? $ht)
(eq-hashtable? $ht)
(hashtable-mutable? $ht)
(not (hashtable-weak? $ht))
(not (eq-hashtable-weak? $ht))
(hashtable? $imht)
(eq-hashtable? $imht)
(not (hashtable-mutable? $imht))
(not (hashtable-weak? $imht))
(not (eq-hashtable-weak? $imht))
(hashtable? $ht2)
(eq-hashtable? $ht2)
(hashtable-mutable? $ht2)
(not (hashtable-weak? $ht2))
(not (eq-hashtable-weak? $ht2))))
(not (hashtable? 3))
(not (hashtable? (make-vector 3)))
(not (eq-hashtable? 3))
(not (eq-hashtable? (make-vector 3)))
; hashtable?
(error? ; wrong argument count
(hashtable?))
(error? ; wrong argument count
(hashtable? $ht 3))
(error? ; wrong argument count
(eq-hashtable?))
(error? ; wrong argument count
(eq-hashtable? $ht 3))
; hashtable-mutable?
(error? ; not a hashtable
(hashtable-mutable? (make-vector 3)))
(error? ; wrong argument count
(hashtable-mutable?))
(error? ; wrong argument count
(hashtable-mutable? $ht 3))
; hashtable-size
(error? ; wrong argument count
(hashtable-size))
(error? ; wrong argument count
(hashtable-size $ht 3))
(error? ; not a hashtable
(hashtable-size 'hello))
; hashtable-ref
(error? ; wrong argument count
(hashtable-ref))
(error? ; wrong argument count
(hashtable-ref $ht))
(error? ; wrong argument count
(hashtable-ref $ht 'a))
(error? ; wrong argument count
(hashtable-ref $ht 'a 'b 'c))
(error? ; not a hashtable
(hashtable-ref '(hash . table) 'a 'b))
; hashtable-contains?
(error? ; wrong argument count
(hashtable-contains?))
(error? ; wrong argument count
(hashtable-contains? $ht))
(error? ; wrong argument count
(hashtable-contains? $ht 'a 'b))
(error? ; not a hashtable
(hashtable-contains? '(hash . table) 'a))
; hashtable-set!
(error? ; wrong argument count
(hashtable-set!))
(error? ; wrong argument count
(hashtable-set! $ht))
(error? ; wrong argument count
(hashtable-set! $ht 'a))
(error? ; wrong argument count
(hashtable-set! $ht 'a 'b 'c))
(error? ; not a hashtable
(hashtable-set! '(hash . table) 'a 'b))
(error? ; hashtable not mutable
(hashtable-set! $imht 'a 'b))
; hashtable-update!
(error? ; wrong argument count
(hashtable-update!))
(error? ; wrong argument count
(hashtable-update! $ht))
(error? ; wrong argument count
(hashtable-update! $ht 'a values))
(error? ; wrong argument count
(hashtable-update! $ht 'a values 'c 'd))
(error? ; not a hashtable
(hashtable-update! '(hash . table) 'a values 'b))
(error? ; hashtable not mutable
(hashtable-update! $imht 'a values 'b))
(error? ; not a procedure
(hashtable-update! $ht 'a "not a procedure" 'b))
; hashtable-cell
(error? ; wrong argument count
(hashtable-cell))
(error? ; wrong argument count
(hashtable-cell $ht))
(error? ; wrong argument count
(hashtable-cell $ht 'a))
(error? ; wrong argument count
(hashtable-cell $ht 'a 'b 'c))
(error? ; not a hashtable
(hashtable-cell '(hash . table) 'a 'b))
; hashtable-delete!
(error? ; wrong argument count
(hashtable-delete!))
(error? ; wrong argument count
(hashtable-delete! $ht))
(error? ; wrong argument count
(hashtable-delete! $ht 'a 'b))
(error? ; not a hashtable
(hashtable-delete! '(hash . table) 'a))
(error? ; hashtable not mutable
(hashtable-delete! $imht 'a))
; hashtable-copy
(error? ; wrong argument count
(hashtable-copy))
(error? ; wrong argument count
(hashtable-copy $ht #t 17))
(error? ; not a hashtable
(hashtable-copy '(hash . table) #t))
; hashtable-clear!
(error? ; wrong argument count
(hashtable-clear!))
(error? ; wrong argument count
(hashtable-clear! $ht 17 'foo))
(error? ; not a hashtable
(hashtable-clear! '(hash . table)))
(error? ; not a hashtable
(hashtable-clear! '(hash . table) 17))
(error? ; hashtable not mutable
(hashtable-clear! $imht))
(error? ; hashtable not mutable
(hashtable-clear! $imht 32))
(error? ; invalid size
(hashtable-clear! $ht #t))
; hashtable-keys
(error? ; wrong argument count
(hashtable-keys))
(error? ; wrong argument count
(hashtable-keys $ht 72))
(error? ; not a hashtable
(hashtable-keys '(hash . table)))
; hashtable-values
(error? ; wrong argument count
(hashtable-values))
(error? ; wrong argument count
(hashtable-values $ht 72))
(error? ; not a hashtable
(hashtable-values '(hash . table)))
; hashtable-entries
(error? ; wrong argument count
(hashtable-entries))
(error? ; wrong argument count
(hashtable-entries $ht 72))
(error? ; not a hashtable
(hashtable-entries '(hash . table)))
; hashtable-hash-function
(error? ; wrong argument count
(hashtable-hash-function))
(error? ; wrong argument count
(hashtable-hash-function $ht $ht))
(error? ; not a hsshtable
(hashtable-hash-function '(hash . table)))
; hashtable-equivalence-function
(error? ; wrong argument count
(hashtable-equivalence-function))
(error? ; wrong argument count
(hashtable-equivalence-function $ht $ht))
(error? ; not a hsshtable
(hashtable-equivalence-function '(hash . table)))
; hashtable-weak?
(error? ; wrong argument count
(hashtable-weak?))
(error? ; wrong argument count
(hashtable-weak? $ht 3))
(error? ; not a hashtable
(hashtable-weak? '(hash . table)))
)
(mat hash-return-value
; hashtable-ref
(error? ; invalid hash-function return value
(let ([ht (make-hashtable (lambda (x) "oops") equal?)])
(hashtable-ref ht 'any #f)))
#;(error? ; invalid hash-function return value
(let ([ht (make-hashtable (lambda (x) -7) equal?)])
(hashtable-ref ht 'any #f)))
(error? ; invalid hash-function return value
(let ([ht (make-hashtable (lambda (x) 3.5) equal?)])
(hashtable-ref ht 'any #f)))
(error? ; invalid hash-function return value
(let ([ht (make-hashtable (lambda (x) 1+2i) equal?)])
(hashtable-ref ht 'any #f)))
; hashtable-contains?
(error? ; invalid hash-function return value
(let ([ht (make-hashtable (lambda (x) "oops") equal?)])
(hashtable-contains? ht 'any)))
#;(error? ; invalid hash-function return value
(let ([ht (make-hashtable (lambda (x) -7) equal?)])
(hashtable-contains? ht 'any)))
(error? ; invalid hash-function return value
(let ([ht (make-hashtable (lambda (x) 3.5) equal?)])
(hashtable-contains? ht 'any)))
(error? ; invalid hash-function return value
(let ([ht (make-hashtable (lambda (x) 1+2i) equal?)])
(hashtable-contains? ht 'any)))
; hashtable-set!
(error? ; invalid hash-function return value
(let ([ht (make-hashtable (lambda (x) "oops") equal?)])
(hashtable-set! ht 'any 'spam)))
#;(error? ; invalid hash-function return value
(let ([ht (make-hashtable (lambda (x) -7) equal?)])
(hashtable-set! ht 'any 'spam)))
(error? ; invalid hash-function return value
(let ([ht (make-hashtable (lambda (x) 3.5) equal?)])
(hashtable-set! ht 'any 'spam)))
(error? ; invalid hash-function return value
(let ([ht (make-hashtable (lambda (x) 1+2i) equal?)])
(hashtable-set! ht 'any 'spam)))
; hashtable-update!
(error? ; invalid hash-function return value
(let ([ht (make-hashtable (lambda (x) "oops") equal?)])
(hashtable-update! ht 'any values 'spam)))
#;(error? ; invalid hash-function return value
(let ([ht (make-hashtable (lambda (x) -7) equal?)])
(hashtable-update! ht 'any values 'spam)))
(error? ; invalid hash-function return value
(let ([ht (make-hashtable (lambda (x) 3.5) equal?)])
(hashtable-update! ht 'any values 'spam)))
(error? ; invalid hash-function return value
(let ([ht (make-hashtable (lambda (x) 1+2i) equal?)])
(hashtable-update! ht 'any values 'spam)))
; hashtable-cell
(error? ; invalid hash-function return value
(let ([ht (make-hashtable (lambda (x) "oops") equal?)])
(hashtable-cell ht 'any 0)))
#;(error? ; invalid hash-function return value
(let ([ht (make-hashtable (lambda (x) -7) equal?)])
(hashtable-cell ht 'any 0)))
(error? ; invalid hash-function return value
(let ([ht (make-hashtable (lambda (x) 3.5) equal?)])
(hashtable-cell ht 'any 0)))
(error? ; invalid hash-function return value
(let ([ht (make-hashtable (lambda (x) 1+2i) equal?)])
(hashtable-cell ht 'any 0)))
; hashtable-delete!
(error? ; invalid hash-function return value
(let ([ht (make-hashtable (lambda (x) "oops") equal?)])
(hashtable-delete! ht 'any)))
#;(error? ; invalid hash-function return value
(let ([ht (make-hashtable (lambda (x) -7) equal?)])
(hashtable-delete! ht 'any)))
(error? ; invalid hash-function return value
(let ([ht (make-hashtable (lambda (x) 3.5) equal?)])
(hashtable-delete! ht 'any)))
(error? ; invalid hash-function return value
(let ([ht (make-hashtable (lambda (x) 1+2i) equal?)])
(hashtable-delete! ht 'any)))
)
(mat eq-hashtable-arguments
; make-weak-eq-hashtable
(error? ; wrong argument count
(make-weak-eq-hashtable 3 #t))
(error? ; invalid size
(make-weak-eq-hashtable -1))
(error? ; invalid size
(make-weak-eq-hashtable #t))
(error? ; invalid size
(make-weak-eq-hashtable #f))
(begin
(define $wht (make-weak-eq-hashtable 50))
(define $imht (hashtable-copy $wht))
(define $wht2 (make-weak-eq-hashtable))
(and (hashtable? $wht)
(eq-hashtable? $wht)
(hashtable-weak? $wht)
(eq-hashtable-weak? $wht)
(hashtable-mutable? $wht)
(hashtable? $imht)
(eq-hashtable? $imht)
(hashtable-weak? $imht)
(eq-hashtable-weak? $imht)
(not (hashtable-mutable? $imht))
(hashtable? $wht2)
(eq-hashtable? $wht2)
(hashtable-weak? $wht2)
(eq-hashtable-weak? $wht2)
(hashtable-mutable? $wht2)))
; eq-hashtable-ref
(error? ; wrong argument count
(eq-hashtable-ref))
(error? ; wrong argument count
(eq-hashtable-ref $wht))
(error? ; wrong argument count
(eq-hashtable-ref $wht 'a))
(error? ; wrong argument count
(eq-hashtable-ref $wht 'a 'b 'c))
(error? ; not a hashtable
(eq-hashtable-ref '(hash . table) 'a 'b))
; eq-hashtable-contains?
(error? ; wrong argument count
(eq-hashtable-contains?))
(error? ; wrong argument count
(eq-hashtable-contains? $wht))
(error? ; wrong argument count
(eq-hashtable-contains? $wht 'a 'b))
(error? ; not a hashtable
(eq-hashtable-contains? '(hash . table) 'a))
; eq-hashtable-set!
(error? ; wrong argument count
(eq-hashtable-set!))
(error? ; wrong argument count
(eq-hashtable-set! $wht))
(error? ; wrong argument count
(eq-hashtable-set! $wht 'a))
(error? ; wrong argument count
(eq-hashtable-set! $wht 'a 'b 'c))
(error? ; not a hashtable
(eq-hashtable-set! '(hash . table) 'a 'b))
(error? ; hashtable not mutable
(eq-hashtable-set! $imht 'a 'b))
; eq-hashtable-update!
(error? ; wrong argument count
(eq-hashtable-update!))
(error? ; wrong argument count
(eq-hashtable-update! $wht))
(error? ; wrong argument count
(eq-hashtable-update! $wht 'a values))
(error? ; wrong argument count
(eq-hashtable-update! $wht 'a values 'c 'd))
(error? ; not a hashtable
(eq-hashtable-update! '(hash . table) 'a values 'b))
(error? ; hashtable not mutable
(eq-hashtable-update! $imht 'a values 'b))
(error? ; not a procedure
(eq-hashtable-update! $wht 'a "not a procedure" 'b))
; eq-hashtable-delete!
(error? ; wrong argument count
(eq-hashtable-delete!))
(error? ; wrong argument count
(eq-hashtable-delete! $wht))
(error? ; wrong argument count
(eq-hashtable-delete! $wht 'a 'b))
(error? ; not a hashtable
(eq-hashtable-delete! '(hash . table) 'a))
(error? ; hashtable not mutable
(eq-hashtable-delete! $imht 'a))
; eq-hashtable-cell
(error? ; wrong argument count
(eq-hashtable-cell))
(error? ; wrong argument count
(eq-hashtable-cell $wht))
(error? ; wrong argument count
(eq-hashtable-cell $wht 'a))
(error? ; wrong argument count
(eq-hashtable-cell $wht 'a 'b 'c))
(error? ; not a hashtable
(eq-hashtable-cell '(hash . table) 'a 'b))
; eq-hashtable-weak?
(error? ; wrong argument count
(eq-hashtable-weak?))
(error? ; wrong argument count
(eq-hashtable-weak? $ht 3))
(error? ; not a hashtable
(eq-hashtable-weak? '(hash . table)))
)
(mat symbol-hashtable-arguments
(begin
(define $symht (make-hashtable symbol-hash eq? 50))
(define $imsymht (hashtable-copy $symht))
#t)
; symbol-hashtable-ref
(error? ; wrong argument count
(symbol-hashtable-ref))
(error? ; wrong argument count
(symbol-hashtable-ref $symht))
(error? ; wrong argument count
(symbol-hashtable-ref $symht 'a))
(error? ; wrong argument count
(symbol-hashtable-ref $symht 'a 'b 'c))
(error? ; not a hashtable
(symbol-hashtable-ref '(hash . table) 'a 'b))
(error? ; not a symbol hashtable
(symbol-hashtable-ref $ht 'a 'b))
(error? ; not a symbol
(symbol-hashtable-ref $symht '(a) 'b))
(error? ; not a symbol
(hashtable-ref $symht '(a) 'b))
; symbol-hashtable-contains?
(error? ; wrong argument count
(symbol-hashtable-contains?))
(error? ; wrong argument count
(symbol-hashtable-contains? $symht))
(error? ; wrong argument count
(symbol-hashtable-contains? $symht 'a 'b))
(error? ; not a hashtable
(symbol-hashtable-contains? '(hash . table) 'a))
(error? ; not a symbol hashtable
(symbol-hashtable-contains? $ht 'a))
(error? ; not a symbol
(symbol-hashtable-contains? $symht '(a)))
(error? ; not a symbol
(hashtable-contains? $symht '(a)))
; symbol-hashtable-set!
(error? ; wrong argument count
(symbol-hashtable-set!))
(error? ; wrong argument count
(symbol-hashtable-set! $symht))
(error? ; wrong argument count
(symbol-hashtable-set! $symht 'a))
(error? ; wrong argument count
(symbol-hashtable-set! $symht 'a 'b 'c))
(error? ; not a hashtable
(symbol-hashtable-set! '(hash . table) 'a 'b))
(error? ; not a symbol hashtable
(symbol-hashtable-set! $ht 'a 'b))
(error? ; not a symbol
(symbol-hashtable-set! $symht '(a) 'b))
(error? ; not a symbol
(hashtable-set! $symht '(a) 'b))
(error? ; hashtable not mutable
(symbol-hashtable-set! $imsymht 'a 'b))
; symbol-hashtable-update!
(error? ; wrong argument count
(symbol-hashtable-update!))
(error? ; wrong argument count
(symbol-hashtable-update! $symht))
(error? ; wrong argument count
(symbol-hashtable-update! $symht 'a values))
(error? ; wrong argument count
(symbol-hashtable-update! $symht 'a values 'c 'd))
(error? ; not a hashtable
(symbol-hashtable-update! '(hash . table) 'a values 'b))
(error? ; not a symbol hashtable
(symbol-hashtable-update! $ht 'a values 'b))
(error? ; not a symbol
(symbol-hashtable-update! $symht '(a) values 'b))
(error? ; not a symbol
(hashtable-update! $symht '(a) values 'b))
(error? ; hashtable not mutable
(symbol-hashtable-update! $imsymht 'a values 'b))
(error? ; not a procedure
(symbol-hashtable-update! $symht 'a "not a procedure" 'b))
; symbol-hashtable-delete!
(error? ; wrong argument count
(symbol-hashtable-delete!))
(error? ; wrong argument count
(symbol-hashtable-delete! $symht))
(error? ; wrong argument count
(symbol-hashtable-delete! $symht 'a 'b))
(error? ; not a hashtable
(symbol-hashtable-delete! '(hash . table) 'a))
(error? ; not a symbol hashtable
(symbol-hashtable-delete! $ht 'a))
(error? ; not a symbol
(symbol-hashtable-delete! $symht '(a)))
(error? ; not a symbol
(hashtable-delete! $symht '(a)))
(error? ; hashtable not mutable
(symbol-hashtable-delete! $imsymht 'a))
; symbol-hashtable-cell
(error? ; wrong argument count
(symbol-hashtable-cell))
(error? ; wrong argument count
(symbol-hashtable-cell $symht))
(error? ; wrong argument count
(symbol-hashtable-cell $symht 'a))
(error? ; wrong argument count
(symbol-hashtable-cell $symht 'a 'b 'c))
(error? ; not a hashtable
(symbol-hashtable-cell '(hash . table) 'a 'b))
(error? ; not a symbol hashtable
(symbol-hashtable-cell $ht 'a 'b))
(error? ; not a symbol
(symbol-hashtable-cell $symht '(a) 'b))
(error? ; not a symbol
(hashtable-cell $symht '(a) 'b))
)
(mat eqv-hashtable-arguments
; make-weak-eqv-hashtable
(error? ; wrong argument count
(make-weak-eqv-hashtable 3 #t))
(error? ; invalid size
(make-weak-eqv-hashtable -1))
(error? ; invalid size
(make-weak-eqv-hashtable #t))
(error? ; invalid size
(make-weak-eqv-hashtable #f))
)
(mat nonweak-eq-hashtable
(begin
(define h (make-eq-hashtable 32))
(and (hashtable? h)
(eq-hashtable? h)
(hashtable-mutable? h)
(not (eq-hashtable-weak? h))
(not (hashtable-weak? h))))
(eq? (hashtable-hash-function h) #f)
(eq? (hashtable-equivalence-function h) eq?)
(equal? (hashtable-size h) 0)
(same-elements? (hashtable-keys h) '#())
(same-elements? (hashtable-values h) '#())
(equal-entries? (hashtable-entries h) '#() '#())
(eqv? (hashtable-set! h 'a 'aval) (void))
(equal?
(list
(hashtable-contains? h 'a)
(hashtable-contains? h 'b)
(hashtable-contains? h 'c))
'(#t #f #f))
(eqv? (hashtable-set! h 'b 'bval) (void))
(equal?
(list
(hashtable-contains? h 'a)
(hashtable-contains? h 'b)
(hashtable-contains? h 'c))
'(#t #t #f))
(eqv? (hashtable-set! h 'c 'cval) (void))
(equal?
(list
(hashtable-contains? h 'a)
(hashtable-contains? h 'b)
(hashtable-contains? h 'c))
'(#t #t #t))
(equal? (hashtable-size h) 3)
(same-elements? (hashtable-keys h) '#(a b c))
(same-elements? (hashtable-values h) '#(bval cval aval))
(equal-entries? (hashtable-entries h) '#(b c a) '#(bval cval aval))
#;(same-elements? (list->vector (hashtable-map h cons)) '#((a . aval) (b . bval) (c . cval)))
#;(same-elements?
(let ([v (make-vector 3)] [i 0])
(hashtable-for-each h (lambda (key val) (vector-set! v i (cons key val)) (set! i (fx+ i 1))))
v)
'#((a . aval) (b . bval) (c . cval)))
#;(same-elements?
(let ([v (make-vector 3)] [i 0])
(hashtable-for-each-cell h (lambda (a) (vector-set! v i a) (set! i (fx+ i 1))))
v)
'#((a . aval) (b . bval) (c . cval)))
(equal? (hashtable-ref h 'a 1) 'aval)
(equal? (hashtable-ref h 'b #f) 'bval)
(equal? (hashtable-ref h 'c 'nope) 'cval)
(eqv? (hashtable-delete! h 'b) (void))
(equal? (hashtable-size h) 2)
(same-elements? (hashtable-keys h) '#(a c))
(same-elements? (hashtable-values h) '#(aval cval))
(equal-entries? (hashtable-entries h) '#(a c) '#(aval cval))
(begin
(define h2 (hashtable-copy h #t))
(and (hashtable? h2)
(eq-hashtable? h2)
(hashtable-mutable? h2)
(not (hashtable-weak? h2))
(not (eq-hashtable-weak? h2))))
(eq? (hashtable-hash-function h2) #f)
(eq? (hashtable-equivalence-function h2) eq?)
(equal? (hashtable-size h2) 2)
(same-elements? (hashtable-keys h2) '#(a c))
(same-elements? (hashtable-values h2) '#(aval cval))
(equal-entries? (hashtable-entries h2) '#(a c) '#(aval cval))
(eqv? (hashtable-clear! h 4) (void))
(equal?
(list
(hashtable-size h)
(hashtable-ref h 'a 1)
(hashtable-ref h 'b #f)
(hashtable-ref h 'c 'nope))
'(0 1 #f nope))
(same-elements? (hashtable-keys h) '#())
(same-elements? (hashtable-values h) '#())
(equal-entries? (hashtable-entries h) '#() '#())
(equal?
(list
(hashtable-size h2)
(hashtable-ref h2 'a 1)
(hashtable-ref h2 'b #f)
(hashtable-ref h2 'c 'nope))
'(2 aval #f cval))
(same-elements? (hashtable-keys h2) '#(a c))
(same-elements? (hashtable-values h2) '#(aval cval))
(equal-entries? (hashtable-entries h2) '#(a c) '#(aval cval))
(eqv?
(hashtable-update! h 'q
(lambda (x) (+ x 1))
17)
(void))
(equal? (hashtable-ref h 'q #f) 18)
(eqv?
(hashtable-update! h 'q
(lambda (x) (+ x 1))
17)
(void))
(equal? (hashtable-ref h 'q #f) 19)
(equal? (hashtable-size h) 1)
; test hashtable-copy when some keys may have moved
(let ([t (parameterize ([collect-request-handler void])
(let ([h4a (make-eq-hashtable 32)]
[k* (map list (make-list 100))])
(for-each (lambda (x) (hashtable-set! h4a x x)) k*)
(collect)
; create copy after collection but before otherwise touching h4a
(let ([h4b (hashtable-copy h4a #t)])
(andmap
(lambda (k) (eq? (hashtable-ref h4b k #f) k))
k*))))])
(collect)
t)
; test for proper shrinkage
(eqv?
(let ([ht (make-eq-hashtable 32)])
(for-each
(lambda (k*) (for-each (lambda (k) (hashtable-delete! ht k)) k*))
(let ([k** (map (lambda (x) (map list (make-list 1000)))
(make-list 100))])
(for-each
(lambda (k*) (map (lambda (k) (hashtable-set! ht k 75)) k*))
k**)
k**))
(#%$hashtable-veclen ht))
32)
)
(mat weak-eq-hashtable
(begin
(define ka (list 'a))
(define kb (list 'b))
(define kc (list 'c))
(define kq (list 'q))
(define ky (list 'y))
(define kz (list 'z))
#t)
(begin
(define h (make-weak-eq-hashtable 32))
(and (hashtable? h)
(eq-hashtable? h)
(hashtable-mutable? h)
(hashtable-weak? h)
(eq-hashtable-weak? h)))
(eq? (hashtable-hash-function h) #f)
(eq? (hashtable-equivalence-function h) eq?)
(equal? (hashtable-size h) 0)
(same-elements? (hashtable-keys h) '#())
(same-elements? (hashtable-values h) '#())
(equal-entries? (hashtable-entries h) '#() '#())
(eqv? (hashtable-set! h ka 'aval) (void))
(equal?
(list
(hashtable-contains? h ka)
(hashtable-contains? h kb)
(hashtable-contains? h kc))
'(#t #f #f))
(eqv? (hashtable-set! h kb 'bval) (void))
(equal?
(list
(hashtable-contains? h ka)
(hashtable-contains? h kb)
(hashtable-contains? h kc))
'(#t #t #f))
(eqv? (hashtable-set! h kc 'cval) (void))
(equal?
(list
(hashtable-contains? h ka)
(hashtable-contains? h kb)
(hashtable-contains? h kc))
'(#t #t #t))
(equal? (hashtable-size h) 3)
(same-elements? (hashtable-keys h) '#((a) (b) (c)))
(same-elements? (hashtable-values h) '#(bval cval aval))
(equal-entries? (hashtable-entries h) '#((a) (b) (c)) '#(aval bval cval))
#;(same-elements? (list->vector (hashtable-map h cons)) '#(((a) . aval) ((b) . bval) ((c) . cval)))
#;(same-elements?
(let ([v (make-vector 3)] [i 0])
(hashtable-for-each h (lambda (key val) (vector-set! v i (cons key val)) (set! i (fx+ i 1))))
v)
'#(((a) . aval) ((b) . bval) ((c) . cval)))
#;(same-elements?
(let ([v (make-vector 3)] [i 0])
(hashtable-for-each-cell h (lambda (a) (vector-set! v i a) (set! i (fx+ i 1))))
v)
'#(((a) . aval) ((b) . bval) ((c) . cval)))
(equal? (hashtable-ref h ka 1) 'aval)
(equal? (hashtable-ref h kb #f) 'bval)
(equal? (hashtable-ref h kc 'nope) 'cval)
(eqv? (hashtable-delete! h kb) (void))
(equal? (hashtable-size h) 2)
(same-elements? (hashtable-keys h) '#((a) (c)))
(same-elements? (hashtable-values h) '#(aval cval))
(equal-entries? (hashtable-entries h) '#((a) (c)) '#(aval cval))
(begin
(define h2 (hashtable-copy h #t))
(and (hashtable? h2)
(eq-hashtable? h2)
(hashtable-mutable? h2)
(eq-hashtable-weak? h2)
(hashtable-weak? h2)))
(eq? (hashtable-hash-function h2) #f)
(eq? (hashtable-equivalence-function h2) eq?)
(equal? (hashtable-size h2) 2)
(same-elements? (hashtable-keys h2) '#((a) (c)))
(same-elements? (hashtable-values h2) '#(aval cval))
(equal-entries? (hashtable-entries h2) '#((a) (c)) '#(aval cval))
(eqv? (hashtable-clear! h 4) (void))
(equal?
(list
(hashtable-size h)
(hashtable-ref h ka 1)
(hashtable-ref h kb #f)
(hashtable-ref h kc 'nope))
'(0 1 #f nope))
(same-elements? (hashtable-keys h) '#())
(same-elements? (hashtable-values h) '#())
(equal-entries? (hashtable-entries h) '#() '#())
(equal?
(list
(hashtable-size h2)
(hashtable-ref h2 ka 1)
(hashtable-ref h2 kb #f)
(hashtable-ref h2 kc 'nope))
'(2 aval #f cval))
(same-elements? (hashtable-keys h2) '#((a) (c)))
(same-elements? (hashtable-values h2) '#(aval cval))
(equal-entries? (hashtable-entries h2) '#((a) (c)) '#(aval cval))
(eqv?
(hashtable-update! h kq
(lambda (x) (+ x 1))
17)
(void))
(equal? (hashtable-ref h kq #f) 18)
(eqv?
(hashtable-update! h kq
(lambda (x) (+ x 1))
17)
(void))
(equal? (hashtable-ref h kq #f) 19)
(equal? (hashtable-size h) 1)
(same-elements? (hashtable-keys h) '#((q)))
(same-elements? (hashtable-values h) '#(19))
(eqv?
(begin
(set! kq (void))
(collect (collect-maximum-generation))
(hashtable-size h))
0)
(same-elements? (hashtable-keys h) '#())
(same-elements? (hashtable-values h) '#())
(equal-entries? (hashtable-entries h) '#() '#())
#;(eqv? (hashtable-map h (lambda args (error #f "oops"))) '())
#;(eqv? (hashtable-for-each h (lambda args (error #f "oops"))) (void))
#;(eqv? (hashtable-for-each-cell h (lambda args (error #f "oops"))) (void))
(equal? (hashtable-ref h ky #f) #f)
(eqv?
(hashtable-set! h ky 'toad)
(void))
(equal? (hashtable-ref h ky #f) 'toad)
(equal? (hashtable-ref h kz #f) #f)
(eqv?
(hashtable-update! h kz list 'frog)
(void))
(equal? (hashtable-ref h kz #f) '(frog))
(same-elements? (hashtable-keys h) (vector ky kz))
(same-elements? (hashtable-values h) (vector (hashtable-ref h kz #f) 'toad))
(equal-entries?
(hashtable-entries h)
(vector kz ky)
(vector (hashtable-ref h kz #f) 'toad))
(eqv? (hashtable-ref h '(zippo) 'nil) 'nil)
(begin
(define h3 (hashtable-copy h2 #f))
(and (hashtable? h3)
(eq-hashtable? h3)
(not (hashtable-mutable? h3))
(eq-hashtable-weak? h3)
(hashtable-weak? h3)))
(same-elements? (hashtable-keys h2) '#((a) (c)))
(same-elements? (hashtable-keys h3) '#((a) (c)))
(same-elements? (hashtable-values h2) '#(aval cval))
(same-elements? (hashtable-values h3) '#(aval cval))
(equal?
(begin
(set! ka (void))
(collect (collect-maximum-generation))
(list (hashtable-size h2) (hashtable-size h3)))
'(1 1))
(same-elements? (hashtable-keys h2) '#((c)))
(same-elements? (hashtable-keys h3) '#((c)))
(same-elements? (hashtable-values h2) '#(cval))
(same-elements? (hashtable-values h3) '#(cval))
(equal-entries? (hashtable-entries h2) '#((c)) '#(cval))
(equal-entries? (hashtable-entries h3) '#((c)) '#(cval))
(eqv?
(begin
(set! h3 (void))
(collect (collect-maximum-generation))
(hashtable-size h2))
1)
(same-elements? (hashtable-keys h2) '#((c)))
(same-elements? (hashtable-values h2) '#(cval))
(equal-entries? (hashtable-entries h2) '#((c)) '#(cval))
; test for proper shrinkage
(eqv?
(let ([ht (make-eq-hashtable 32)])
(for-each
(lambda (k*) (for-each (lambda (k) (hashtable-delete! ht k)) k*))
(let ([k** (map (lambda (x) (map list (make-list 1000)))
(make-list 100))])
(for-each
(lambda (k*) (map (lambda (k) (hashtable-set! ht k 75)) k*))
k**)
k**))
(#%$hashtable-veclen ht))
32)
; test for proper shrinkage as objects are bwp'd
; uses delete to trigger final shrinkage
(equal?
(let* ([ht (make-weak-eq-hashtable 32)]
[len (#%$hashtable-veclen ht)])
(hashtable-set! ht 'a 'b)
(for-each
(lambda (k*) (map (lambda (k) (hashtable-set! ht k 75)) k*))
(map (lambda (x) (map list (make-list 1000))) (make-list 100)))
(collect (collect-maximum-generation))
(hashtable-delete! ht 'a)
(list (hashtable-size ht) (= (#%$hashtable-veclen ht) len)))
'(0 #t))
; test that weak-hashtable values do not imply that values
; are reachable
(let ([wk1 (list 1)]
[wk2 (list 2)]
[wk3 (list 3)]
[wk4 (list 4)]
[ht (make-weak-eq-hashtable)])
(hashtable-set! ht wk1 wk1)
(hashtable-set! ht wk2 wk1)
(hashtable-set! ht wk3 wk3)
(hashtable-set! ht wk4 wk2)
(collect (collect-maximum-generation))
(and
(same-elements? (hashtable-keys ht) '#((1) (2) (3) (4)))
(equal? (hashtable-ref ht wk1 #f) wk1)
(equal? (hashtable-ref ht wk2 #f) wk1)
(equal? (hashtable-ref ht wk3 #f) wk3)
(equal? (hashtable-ref ht wk4 #f) wk2)
(begin
(set! wk1 #f)
(set! wk2 #f)
(set! wk3 #f)
(collect (collect-maximum-generation))
(and
(same-elements? (hashtable-keys ht) '#((1) (2) (4)))
(equal? (hashtable-ref ht wk4 #f) '(2))
(begin
(set! wk4 #f)
(collect (collect-maximum-generation))
(same-elements? (hashtable-keys ht) '#()))))))
)
(mat eq-hashtable-cell
(let ()
(define-record fribble (x))
(define random-object
(lambda (x)
(case (random 9)
[(0) (cons 'a 'b)]
[(1) (vector 'c)]
[(2) (string #\a #\b)]
[(3) (make-fribble 'q)]
[(4) (gensym)]
[(5) (open-output-string)]
[(6) (fxvector 15 55)]
[(7) (lambda () x)]
[else (box 'top)])))
(let ([ls1 (let f ([n 10000])
(if (fx= n 0)
'()
(cons
(cons (random-object 4) (random-object 7))
(f (fx- n 1)))))]
[ht (make-eq-hashtable)]
[wht (make-weak-eq-hashtable)])
(let ([ls2 (map (lambda (a1) (eq-hashtable-cell ht (car a1) (cdr a1))) ls1)]
[ls3 (map (lambda (a1) (hashtable-cell wht (car a1) (cdr a1))) ls1)])
(unless (andmap (lambda (a1 a2 a3)
(and (eq? (car a1) (car a2))
(eq? (car a2) (car a3))))
ls1 ls2 ls3)
(errorf #f "keys are not eq"))
(unless (andmap (lambda (a1 a2 a3)
(and (eq? (cdr a1) (cdr a2))
(eq? (cdr a2) (cdr a3))))
ls1 ls2 ls3)
(errorf #f "values are not eq"))
(for-each
(lambda (a1)
(when (fx< (random 10) 5)
(set-car! a1 #f)))
ls1)
(let loop ([i (min (expt (collect-generation-radix) (collect-maximum-generation)) 1000)])
(unless (fx= i 0)
(collect)
(unless (andmap (lambda (a2 a3) (eq? (car a2) (car a3))) ls2 ls3)
(errorf #f "a2/a3 keys not eq after collection"))
(unless (andmap (lambda (a3) (not (bwp-object? (car a3)))) ls3)
(errorf #f "keys have been bwp'd"))
(loop (fx- i 1))))
(for-each
(lambda (a2)
(hashtable-delete! ht (car a2))
(set-car! a2 #f))
ls2)
(unless (and (equal? (hashtable-keys ht) '#())
(equal? (hashtable-values ht) '#())
(zero? (hashtable-size ht)))
(errorf #f "wht has not been cleared out"))
(let loop ([i (min (expt (collect-generation-radix) (collect-maximum-generation)) 1000)])
(unless (fx= i 0)
(collect)
(unless (andmap (lambda (a1 a3)
(or (not (car a1)) (eq? (car a1) (car a3))))
ls1 ls3)
(errorf #f "a1/a3 keys not eq after collection"))
(loop (fx- i 1))))
(for-each
(lambda (a1 a3)
(unless (or (car a1) (bwp-object? (car a3)))
(errorf #f "~s has not been bwp'd I" (car a3))))
ls1 ls3)
(for-each (lambda (a1) (set-car! a1 #f)) ls1)
(collect (collect-maximum-generation))
(unless (andmap (lambda (a3) (bwp-object? (car a3))) ls3)
(errorf #f "keys have not been bwp'd II"))
(unless (and (equal? (hashtable-keys wht) '#())
(equal? (hashtable-values wht) '#())
(zero? (hashtable-size ht)))
(errorf #f "wht has not been cleared out"))))
#t)
)
(mat $nonweak-eq-hashtable
(begin
(define h (make-eq-hashtable 32))
(and (hashtable? h)
(eq-hashtable? h)
(hashtable-mutable? h)
(not (eq-hashtable-weak? h))
(not (hashtable-weak? h))))
(eq? (hashtable-hash-function h) #f)
(eq? (hashtable-equivalence-function h) eq?)
(equal? (hashtable-size h) 0)
(same-elements? (hashtable-keys h) '#())
(same-elements? (hashtable-values h) '#())
(equal-entries? (hashtable-entries h) '#() '#())
(eqv? (eq-hashtable-set! h 'a 'aval) (void))
(equal?
(list
(eq-hashtable-contains? h 'a)
(eq-hashtable-contains? h 'b)
(eq-hashtable-contains? h 'c))
'(#t #f #f))
(eqv? (eq-hashtable-set! h 'b 'bval) (void))
(equal?
(list
(eq-hashtable-contains? h 'a)
(eq-hashtable-contains? h 'b)
(eq-hashtable-contains? h 'c))
'(#t #t #f))
(eqv? (eq-hashtable-set! h 'c 'cval) (void))
(equal?
(list
(eq-hashtable-contains? h 'a)
(eq-hashtable-contains? h 'b)
(eq-hashtable-contains? h 'c))
'(#t #t #t))
(equal? (hashtable-size h) 3)
(same-elements? (hashtable-keys h) '#(a b c))
(same-elements? (hashtable-values h) '#(bval cval aval))
(equal-entries? (hashtable-entries h) '#(b c a) '#(bval cval aval))
(equal? (eq-hashtable-ref h 'a 1) 'aval)
(equal? (eq-hashtable-ref h 'b #f) 'bval)
(equal? (eq-hashtable-ref h 'c 'nope) 'cval)
(eqv? (eq-hashtable-delete! h 'b) (void))
(equal? (hashtable-size h) 2)
(same-elements? (hashtable-keys h) '#(a c))
(same-elements? (hashtable-values h) '#(aval cval))
(equal-entries? (hashtable-entries h) '#(a c) '#(aval cval))
(begin
(define h2 (hashtable-copy h #t))
(and (hashtable? h2)
(eq-hashtable? h2)
(hashtable-mutable? h2)
(not (eq-hashtable-weak? h2))
(not (hashtable-weak? h2))))
(equal? (hashtable-size h2) 2)
(same-elements? (hashtable-keys h2) '#(a c))
(same-elements? (hashtable-values h2) '#(aval cval))
(equal-entries? (hashtable-entries h2) '#(a c) '#(aval cval))
(eqv? (hashtable-clear! h 4) (void))
(equal?
(list
(hashtable-size h)
(eq-hashtable-ref h 'a 1)
(eq-hashtable-ref h 'b #f)
(eq-hashtable-ref h 'c 'nope))
'(0 1 #f nope))
(same-elements? (hashtable-keys h) '#())
(same-elements? (hashtable-values h) '#())
(equal-entries? (hashtable-entries h) '#() '#())
(equal?
(list
(hashtable-size h2)
(eq-hashtable-ref h2 'a 1)
(eq-hashtable-ref h2 'b #f)
(eq-hashtable-ref h2 'c 'nope))
'(2 aval #f cval))
(same-elements? (hashtable-keys h2) '#(a c))
(same-elements? (hashtable-values h2) '#(aval cval))
(equal-entries? (hashtable-entries h2) '#(a c) '#(aval cval))
(eqv?
(eq-hashtable-update! h 'q
(lambda (x) (+ x 1))
17)
(void))
(equal? (eq-hashtable-ref h 'q #f) 18)
(eqv?
(eq-hashtable-update! h 'q
(lambda (x) (+ x 1))
17)
(void))
(equal? (eq-hashtable-ref h 'q #f) 19)
(equal? (hashtable-size h) 1)
; test hashtable-copy when some keys may have moved
(let ([t (parameterize ([collect-request-handler void])
(let ([h4a (make-eq-hashtable 32)]
[k* (map list (make-list 100))])
(for-each (lambda (x) (eq-hashtable-set! h4a x x)) k*)
(collect)
; create copy after collection but before otherwise touching h4a
(let ([h4b (hashtable-copy h4a #t)])
(andmap
(lambda (k) (eq? (eq-hashtable-ref h4b k #f) k))
k*))))])
(collect)
t)
; test for proper shrinkage, etc.
(equal?
(let* ([ht (make-eq-hashtable)] [minlen (#%$hashtable-veclen ht)])
(define power-of-two? (lambda (n) (fx= (fxbit-count n) 1)))
(let f ([i 0])
(unless (fx= i (expt 2 17))
(let ([k (fx* i 2)])
(eq-hashtable-set! ht k i)
(f (fx+ i 1))
(assert (eq-hashtable-contains? ht k))
(assert (power-of-two? (#%$hashtable-veclen ht)))
(eq-hashtable-delete! ht k))))
(list (hashtable-size ht) (fx= (#%$hashtable-veclen ht) minlen)))
'(0 #t))
(equal?
(let ([ht (make-eq-hashtable 32)])
(define power-of-two? (lambda (n) (fx= (fxbit-count n) 1)))
(let f ([i 0])
(unless (fx= i (expt 2 17))
(let ([k (fx* i 2)])
(eq-hashtable-set! ht k i)
(f (fx+ i 1))
(assert (eq-hashtable-contains? ht k))
(assert (power-of-two? (#%$hashtable-veclen ht)))
(eq-hashtable-delete! ht k))))
(list (hashtable-size ht) (#%$hashtable-veclen ht)))
'(0 32))
)
(mat $weak-eq-hashtable
(begin
(define ka (list 'a))
(define kb (list 'b))
(define kc (list 'c))
(define kq (list 'q))
(define ky (list 'y))
(define kz (list 'z))
#t)
(begin
(define h (make-weak-eq-hashtable 32))
(and (hashtable? h)
(eq-hashtable? h)
(hashtable-mutable? h)
(eq-hashtable-weak? h)
(hashtable-weak? h)))
(eq? (hashtable-hash-function h) #f)
(eq? (hashtable-equivalence-function h) eq?)
(equal? (hashtable-size h) 0)
(same-elements? (hashtable-keys h) '#())
(same-elements? (hashtable-values h) '#())
(equal-entries? (hashtable-entries h) '#() '#())
(eqv? (eq-hashtable-set! h ka 'aval) (void))
(equal?
(list
(eq-hashtable-contains? h ka)
(eq-hashtable-contains? h kb)
(eq-hashtable-contains? h kc))
'(#t #f #f))
(eqv? (eq-hashtable-set! h kb 'bval) (void))
(equal?
(list
(eq-hashtable-contains? h ka)
(eq-hashtable-contains? h kb)
(eq-hashtable-contains? h kc))
'(#t #t #f))
(eqv? (eq-hashtable-set! h kc 'cval) (void))
(equal?
(list
(eq-hashtable-contains? h ka)
(eq-hashtable-contains? h kb)
(eq-hashtable-contains? h kc))
'(#t #t #t))
(equal? (hashtable-size h) 3)
(same-elements? (hashtable-keys h) '#((a) (b) (c)))
(same-elements? (hashtable-values h) '#(aval bval cval))
(equal-entries? (hashtable-entries h) '#((a) (b) (c)) '#(aval bval cval))
(equal? (eq-hashtable-ref h ka 1) 'aval)
(equal? (eq-hashtable-ref h kb #f) 'bval)
(equal? (eq-hashtable-ref h kc 'nope) 'cval)
(eqv? (eq-hashtable-delete! h kb) (void))
(equal? (hashtable-size h) 2)
(same-elements? (hashtable-keys h) '#((a) (c)))
(same-elements? (hashtable-values h) '#(aval cval))
(equal-entries? (hashtable-entries h) '#((a) (c)) '#(aval cval))
(begin
(define h2 (hashtable-copy h #t))
(and (hashtable? h2)
(eq-hashtable? h2)
(hashtable-mutable? h2)
(hashtable-weak? h2)
(eq-hashtable-weak? h2)))
(equal? (hashtable-size h2) 2)
(same-elements? (hashtable-keys h2) '#((a) (c)))
(same-elements? (hashtable-values h2) '#(aval cval))
(equal-entries? (hashtable-entries h2) '#((a) (c)) '#(aval cval))
(eqv? (hashtable-clear! h 4) (void))
(equal?
(list
(hashtable-size h)
(eq-hashtable-ref h ka 1)
(eq-hashtable-ref h kb #f)
(eq-hashtable-ref h kc 'nope))
'(0 1 #f nope))
(same-elements? (hashtable-keys h) '#())
(same-elements? (hashtable-values h) '#())
(equal-entries? (hashtable-entries h) '#() '#())
(equal?
(list
(hashtable-size h2)
(eq-hashtable-ref h2 ka 1)
(eq-hashtable-ref h2 kb #f)
(eq-hashtable-ref h2 kc 'nope))
'(2 aval #f cval))
(same-elements? (hashtable-keys h2) '#((a) (c)))
(same-elements? (hashtable-values h2) '#(aval cval))
(equal-entries? (hashtable-entries h2) '#((a) (c)) '#(aval cval))
(eqv?
(eq-hashtable-update! h kq
(lambda (x) (+ x 1))
17)
(void))
(equal? (eq-hashtable-ref h kq #f) 18)
(eqv?
(eq-hashtable-update! h kq
(lambda (x) (+ x 1))
17)
(void))
(equal? (eq-hashtable-ref h kq #f) 19)
(equal? (hashtable-size h) 1)
(same-elements? (hashtable-keys h) '#((q)))
(same-elements? (hashtable-values h) '#(19))
(eqv?
(begin
(set! kq (void))
(collect (collect-maximum-generation))
(hashtable-size h))
0)
(same-elements? (hashtable-keys h) '#())
(same-elements? (hashtable-values h) '#())
(equal-entries? (hashtable-entries h) '#() '#())
(equal? (eq-hashtable-ref h ky #f) #f)
(eqv?
(eq-hashtable-set! h ky 'toad)
(void))
(equal? (eq-hashtable-ref h ky #f) 'toad)
(equal? (eq-hashtable-ref h kz #f) #f)
(eqv?
(eq-hashtable-update! h kz list 'frog)
(void))
(equal? (eq-hashtable-ref h kz #f) '(frog))
(same-elements? (hashtable-keys h) (vector ky kz))
(same-elements? (hashtable-values h) (vector (eq-hashtable-ref h kz #f) 'toad))
(equal-entries?
(hashtable-entries h)
(vector kz ky)
(vector (eq-hashtable-ref h kz #f) 'toad))
(eqv? (eq-hashtable-ref h '(zippo) 'nil) 'nil)
(begin
(define h3 (hashtable-copy h2 #f))
(and (hashtable? h3)
(eq-hashtable? h3)
(not (hashtable-mutable? h3))
(eq-hashtable-weak? h3)
(hashtable-weak? h3)))
(same-elements? (hashtable-keys h2) '#((a) (c)))
(same-elements? (hashtable-keys h3) '#((a) (c)))
(same-elements? (hashtable-values h2) '#(aval cval))
(same-elements? (hashtable-values h3) '#(aval cval))
(equal?
(begin
(set! ka (void))
(collect (collect-maximum-generation))
(list (hashtable-size h2) (hashtable-size h3)))
'(1 1))
(same-elements? (hashtable-keys h2) '#((c)))
(same-elements? (hashtable-keys h3) '#((c)))
(same-elements? (hashtable-values h2) '#(cval))
(same-elements? (hashtable-values h3) '#(cval))
(equal-entries? (hashtable-entries h2) '#((c)) '#(cval))
(equal-entries? (hashtable-entries h3) '#((c)) '#(cval))
(eqv?
(begin
(set! h3 (void))
(collect (collect-maximum-generation))
(hashtable-size h2))
1)
(same-elements? (hashtable-keys h2) '#((c)))
(same-elements? (hashtable-values h2) '#(cval))
(equal-entries? (hashtable-entries h2) '#((c)) '#(cval))
; test for proper shrinkage
(eqv?
(let ([ht (make-eq-hashtable 32)])
(for-each
(lambda (k*) (for-each (lambda (k) (eq-hashtable-delete! ht k)) k*))
(let ([k** (map (lambda (x) (map list (make-list 1000)))
(make-list 100))])
(for-each
(lambda (k*) (map (lambda (k) (eq-hashtable-set! ht k 75)) k*))
k**)
k**))
(#%$hashtable-veclen ht))
32)
; test for proper shrinkage as objects are bwp'd
; uses delete to trigger final shrinkage
(equal?
(let* ([ht (make-weak-eq-hashtable 32)]
[len (#%$hashtable-veclen ht)])
(eq-hashtable-set! ht 'a 'b)
(for-each
(lambda (k*) (map (lambda (k) (eq-hashtable-set! ht k 75)) k*))
(map (lambda (x) (map list (make-list 1000))) (make-list 100)))
(collect (collect-maximum-generation))
(eq-hashtable-delete! ht 'a)
(list (hashtable-size ht) (= (#%$hashtable-veclen ht) len)))
'(0 #t))
)
(mat eq-strange
(begin
(define $ht (make-eq-hashtable))
(define $wht (make-weak-eq-hashtable))
(and (hashtable? $ht)
(eq-hashtable? $ht)
(hashtable? $wht)
(eq-hashtable? $wht)))
(eqv? (hashtable-set! $ht #f 75) (void))
(eqv? (hashtable-ref $ht #f 80) 75)
(eqv? (hashtable-set! $wht #f 75) (void))
(eqv? (hashtable-ref $wht #f 80) 75)
(eqv? (hashtable-set! $ht #!bwp "hello") (void))
(equal? (hashtable-ref $ht #!bwp "goodbye") "hello")
(eqv? (hashtable-set! $wht #!bwp "hello") (void))
(and (member (hashtable-ref $wht #!bwp "goodbye") '("hello" "goodbye")) #t)
; make sure that association isn't added before procedure is called
(equal?
(begin
(hashtable-update! $ht 'cupie
(lambda (x) (hashtable-ref $ht 'cupie (cons 'barbie x)))
'doll)
(hashtable-ref $ht 'cupie 'oops))
'(barbie . doll))
(equal?
(begin
(hashtable-update! $wht 'cupie
(lambda (x) (hashtable-ref $wht 'cupie (cons 'barbie x)))
'doll)
(hashtable-ref $wht 'cupie 'oops))
'(barbie . doll))
)
(mat eq-hashtable-stress
; stress tests
(let () ; nonweak
(define pick
(lambda (ls)
(list-ref ls (random (length ls)))))
(define ht (make-eq-hashtable 4))
(let ([ls (remq '|| (oblist))] [n 50000])
(let f ([i 0] [keep '()] [drop '()])
(if (= i n)
(and (= (hashtable-size ht) (- n (length drop)))
(andmap (lambda (k)
(string=?
(symbol->string (hashtable-ref ht k #f))
(cond
[(string? k) k]
[(pair? k) (car k)]
[(vector? k) (vector-ref k 0)])))
keep)
(andmap (lambda (k) (eq? (hashtable-ref ht k 'no) 'no))
drop))
(let* ([x (pick ls)] [s (string-copy (symbol->string x))])
(let ([k (case (pick '(string pair vector))
[(string) s]
[(pair) (list s)]
[(vector) (vector s)])])
(hashtable-set! ht k x)
(let ([keep (if (= (modulo i 5) 0) (cons k keep) keep)])
(if (= (modulo i 17) 5)
(let ([k (pick keep)])
(hashtable-delete! ht k)
(let ([drop (cons k drop)])
(when (= (random 5) 3)
(hashtable-delete! ht (pick drop)))
(f (+ i 1) (remq k keep) drop)))
(f (+ i 1) keep drop)))))))))
(let () ; weak
(define pick
(lambda (ls)
(list-ref ls (random (length ls)))))
(define ht (make-weak-eq-hashtable 4))
(let ([ls (remq '|| (oblist))] [n 50000])
(let f ([i 0] [keep '()] [drop '()])
(if (= i n)
(and (<= (hashtable-size ht) (- n (length drop)))
(begin
(collect (collect-maximum-generation))
(= (hashtable-size ht) (length keep)))
(andmap (lambda (k)
(string=?
(symbol->string (hashtable-ref ht k #f))
(cond
[(string? k) k]
[(pair? k) (car k)]
[(vector? k) (vector-ref k 0)])))
keep)
(andmap (lambda (k) (eq? (hashtable-ref ht k 'no) 'no))
drop))
(let* ([x (pick ls)] [s (string-copy (symbol->string x))])
(let ([k (case (pick '(string pair vector))
[(string) s]
[(pair) (list s)]
[(vector) (vector s)])])
(hashtable-set! ht k x)
(let ([keep (if (= (modulo i 5) 0) (cons k keep) keep)])
(if (= (modulo i 17) 5)
(let ([k (pick keep)])
(hashtable-delete! ht k)
(let ([drop (cons k drop)])
(when (= (random 5) 3)
(hashtable-delete! ht (pick drop)))
(f (+ i 1) (remq k keep) drop)))
(f (+ i 1) keep drop)))))))))
)
(mat nonweak-eqv-hashtable
(begin
(define h (make-eqv-hashtable 32))
(and (hashtable? h)
(not (eq-hashtable? h))
(hashtable-mutable? h)
(not (hashtable-weak? h))))
(eq? (hashtable-hash-function h) #f)
(eq? (hashtable-equivalence-function h) eqv?)
(equal? (hashtable-size h) 0)
(same-elements? (hashtable-keys h) '#())
(same-elements? (hashtable-values h) '#())
(equal-entries? (hashtable-entries h) '#() '#())
(eqv? (hashtable-set! h 'a 'aval) (void))
(equal?
(list
(hashtable-contains? h 'a)
(hashtable-contains? h 3.4)
(hashtable-contains? h 'c))
'(#t #f #f))
(eqv? (hashtable-set! h 3.4 'bval) (void))
(equal?
(list
(hashtable-contains? h 'a)
(hashtable-contains? h 3.4)
(hashtable-contains? h 'c))
'(#t #t #f))
(eqv? (hashtable-set! h 'c 'cval) (void))
(equal?
(list
(hashtable-contains? h 'a)
(hashtable-contains? h 3.4)
(hashtable-contains? h 'c))
'(#t #t #t))
(equal? (hashtable-size h) 3)
(same-elements? (hashtable-keys h) '#(a 3.4 c))
(same-elements? (hashtable-values h) '#(bval cval aval))
(equal-entries? (hashtable-entries h) '#(3.4 c a) '#(bval cval aval))
#;(same-elements? (list->vector (hashtable-map h cons)) '#((a . aval) (3.4 . bval) (c . cval)))
#;(same-elements?
(let ([v (make-vector 3)] [i 0])
(hashtable-for-each h (lambda (key val) (vector-set! v i (cons key val)) (set! i (fx+ i 1))))
v)
'#((a . aval) (3.4 . bval) (c . cval)))
#;(same-elements?
(let ([v (make-vector 3)] [i 0])
(hashtable-for-each-cell h (lambda (a) (vector-set! v i a) (set! i (fx+ i 1))))
v)
'#((a . aval) (3.4 . bval) (c . cval)))
(equal? (hashtable-ref h 'a 1) 'aval)
(equal? (hashtable-ref h 3.4 #f) 'bval)
(equal? (hashtable-ref h 'c 'nope) 'cval)
(eqv? (hashtable-delete! h 3.4) (void))
(equal? (hashtable-size h) 2)
(same-elements? (hashtable-keys h) '#(a c))
(same-elements? (hashtable-values h) '#(aval cval))
(equal-entries? (hashtable-entries h) '#(a c) '#(aval cval))
(begin
(define h2 (hashtable-copy h #t))
(and (hashtable? h2)
(hashtable-mutable? h2)
(not (hashtable-weak? h2))))
(eq? (hashtable-hash-function h2) #f)
(eq? (hashtable-equivalence-function h2) eqv?)
(equal? (hashtable-size h2) 2)
(same-elements? (hashtable-keys h2) '#(a c))
(same-elements? (hashtable-values h2) '#(aval cval))
(equal-entries? (hashtable-entries h2) '#(a c) '#(aval cval))
(eqv? (hashtable-clear! h 4) (void))
(equal?
(list
(hashtable-size h)
(hashtable-ref h 'a 1)
(hashtable-ref h 3.4 #f)
(hashtable-ref h 'c 'nope))
'(0 1 #f nope))
(same-elements? (hashtable-keys h) '#())
(same-elements? (hashtable-values h) '#())
(equal-entries? (hashtable-entries h) '#() '#())
(equal?
(list
(hashtable-size h2)
(hashtable-ref h2 'a 1)
(hashtable-ref h2 3.4 #f)
(hashtable-ref h2 'c 'nope))
'(2 aval #f cval))
(same-elements? (hashtable-keys h2) '#(a c))
(same-elements? (hashtable-values h2) '#(aval cval))
(equal-entries? (hashtable-entries h2) '#(a c) '#(aval cval))
(eqv?
(hashtable-update! h 'q
(lambda (x) (+ x 1))
17)
(void))
(equal? (hashtable-ref h 'q #f) 18)
(eqv?
(hashtable-update! h 'q
(lambda (x) (+ x 1))
17)
(void))
(equal? (hashtable-ref h 'q #f) 19)
(equal? (hashtable-size h) 1)
; test hashtable-copy when some keys may have moved
(let ([t (parameterize ([collect-request-handler void])
(let ([h4a (make-eqv-hashtable 32)]
[k* (map list (make-list 100))])
(for-each (lambda (x) (hashtable-set! h4a x x)) k*)
(collect)
; create copy after collection but before otherwise touching h4a
(let ([h4b (hashtable-copy h4a #t)])
(andmap
(lambda (k) (eqv? (hashtable-ref h4b k #f) k))
k*))))])
(collect)
t)
; test for proper shrinkage
(equal?
(let ([ht (make-eqv-hashtable 32)])
(for-each
(lambda (k*) (for-each (lambda (k) (hashtable-delete! ht k)) k*))
(let ([k** (map (lambda (x) (map list (make-list 1000)))
(make-list 100))])
(for-each
(lambda (k*) (map (lambda (k) (hashtable-set! ht k 75)) k*))
k**)
k**))
(call-with-values (lambda () (#%$hashtable-veclen ht)) cons))
'(32 . 32))
)
(mat weak-eqv-hashtable
(begin
(define ka (list 'a))
(define kb (list 'b))
(define kc (list 'c))
(define kq (list 'q))
(define ky (list 'y))
(define kz (list 'z))
(define km -5.75)
(define kn 17)
(define ko (+ (most-positive-fixnum) 5))
#t)
(begin
(define h (make-weak-eqv-hashtable 32))
(and (hashtable? h)
(not (eq-hashtable? h))
(hashtable-mutable? h)
(hashtable-weak? h)))
(eq? (hashtable-hash-function h) #f)
(eq? (hashtable-equivalence-function h) eqv?)
(equal? (hashtable-size h) 0)
(same-elements? (hashtable-keys h) '#())
(same-elements? (hashtable-values h) '#())
(equal-entries? (hashtable-entries h) '#() '#())
(eqv? (hashtable-set! h ka 'aval) (void))
(equal?
(list
(hashtable-contains? h ka)
(hashtable-contains? h kb)
(hashtable-contains? h kc)
(hashtable-contains? h km)
(hashtable-contains? h kn)
(hashtable-contains? h ko))
'(#t #f #f #f #f #f))
(eqv? (hashtable-set! h kb 'bval) (void))
(equal?
(list
(hashtable-contains? h ka)
(hashtable-contains? h kb)
(hashtable-contains? h kc)
(hashtable-contains? h km)
(hashtable-contains? h kn)
(hashtable-contains? h ko))
'(#t #t #f #f #f #f))
(eqv? (hashtable-set! h kc 'cval) (void))
(equal?
(list
(hashtable-contains? h ka)
(hashtable-contains? h kb)
(hashtable-contains? h kc)
(hashtable-contains? h km)
(hashtable-contains? h kn)
(hashtable-contains? h ko))
'(#t #t #t #f #f #f))
(eqv? (hashtable-set! h km 'mval) (void))
(equal?
(list
(hashtable-contains? h ka)
(hashtable-contains? h kb)
(hashtable-contains? h kc)
(hashtable-contains? h km)
(hashtable-contains? h kn)
(hashtable-contains? h ko))
'(#t #t #t #t #f #f))
(eqv? (hashtable-set! h kn 'nval) (void))
(equal?
(list
(hashtable-contains? h ka)
(hashtable-contains? h kb)
(hashtable-contains? h kc)
(hashtable-contains? h km)
(hashtable-contains? h kn)
(hashtable-contains? h ko))
'(#t #t #t #t #t #f))
(eqv? (hashtable-set! h ko 'oval) (void))
(equal?
(list
(hashtable-contains? h ka)
(hashtable-contains? h kb)
(hashtable-contains? h kc)
(hashtable-contains? h km)
(hashtable-contains? h kn)
(hashtable-contains? h ko))
'(#t #t #t #t #t #t))
(equal? (hashtable-size h) 6)
(same-elements? (hashtable-keys h) `#((a) (b) (c) -5.75 17 ,ko))
(same-elements? (hashtable-values h) '#(aval bval cval mval nval oval))
(equal-entries? (hashtable-entries h) `#((a) (b) (c) -5.75 17 ,ko) '#(aval bval cval mval nval oval))
#;(same-elements?
(list->vector (hashtable-map h cons))
`#(((a) . aval) ((b) . bval) ((c) . cval) (-5.75 . mval) (17 . nval) (,ko . oval)))
#;(same-elements?
(let ([v (make-vector 6)] [i 0])
(hashtable-for-each h (lambda (key val) (vector-set! v i (cons key val)) (set! i (fx+ i 1))))
v)
`#(((a) . aval) ((b) . bval) ((c) . cval) (-5.75 . mval) (17 . nval) (,ko . oval)))
#;(same-elements?
(let ([v (make-vector 6)] [i 0])
(hashtable-for-each-cell h (lambda (a) (vector-set! v i a) (set! i (fx+ i 1))))
v)
`#(((a) . aval) ((b) . bval) ((c) . cval) (-5.75 . mval) (17 . nval) (,ko . oval)))
(eq? (hashtable-ref h ka 1) 'aval)
(eq? (hashtable-ref h kb #f) 'bval)
(eq? (hashtable-ref h kc 'nope) 'cval)
(eq? (hashtable-ref h (+ 2 -7.75) 'ugh) 'mval)
(eq? (hashtable-ref h (/ 34 2) 'ugh) 'nval)
(eq? (hashtable-ref h (+ (most-positive-fixnum) 7 -2) 'ugh) 'oval)
(eqv? (hashtable-delete! h kb) (void))
(equal? (hashtable-size h) 5)
(same-elements? (hashtable-keys h) `#((a) (c) -5.75 17 ,ko))
(same-elements? (hashtable-values h) '#(aval cval mval nval oval))
(equal-entries? (hashtable-entries h) `#((a) (c) -5.75 17 ,ko) '#(aval cval mval nval oval))
(begin
(define h2 (hashtable-copy h #t))
(and (hashtable? h2)
(hashtable-mutable? h2)
(hashtable-weak? h2)))
(eq? (hashtable-hash-function h2) #f)
(eq? (hashtable-equivalence-function h2) eqv?)
(equal? (hashtable-size h2) 5)
(same-elements? (hashtable-keys h) `#((a) (c) -5.75 17 ,ko))
(same-elements? (hashtable-values h) '#(aval cval mval nval oval))
(equal-entries? (hashtable-entries h) `#((a) (c) -5.75 17 ,ko) '#(aval cval mval nval oval))
(eqv? (hashtable-clear! h 4) (void))
(equal?
(list
(hashtable-size h)
(hashtable-ref h ka 1)
(hashtable-ref h kb #f)
(hashtable-ref h kc 'nope)
(hashtable-ref h km 'nope)
(hashtable-ref h kn 'nope)
(hashtable-ref h ko 'nope))
'(0 1 #f nope nope nope nope))
(same-elements? (hashtable-keys h) '#())
(same-elements? (hashtable-values h) '#())
(equal-entries? (hashtable-entries h) '#() '#())
(equal?
(list
(hashtable-size h2)
(hashtable-ref h2 ka 1)
(hashtable-ref h2 kb #f)
(hashtable-ref h2 kc 'nope)
(hashtable-ref h2 (- (+ km 1) 1) 'nope)
(hashtable-ref h2 (- (+ kn 1) 1) 'nope)
(hashtable-ref h2 (- (+ ko 1) 1) 'nope))
'(5 aval #f cval mval nval oval))
(same-elements? (hashtable-keys h2) `#((a) (c) -5.75 17 ,ko))
(same-elements? (hashtable-values h2) '#(aval cval mval nval oval))
(equal-entries? (hashtable-entries h2) `#((a) (c) -5.75 17 ,ko) '#(aval cval mval nval oval))
(eqv?
(hashtable-update! h kq
(lambda (x) (+ x 1))
17)
(void))
(equal? (hashtable-ref h kq #f) 18)
(eqv?
(hashtable-update! h kq
(lambda (x) (+ x 1))
17)
(void))
(equal? (hashtable-ref h kq #f) 19)
(equal? (hashtable-size h) 1)
(same-elements? (hashtable-keys h) '#((q)))
(eqv?
(begin
(set! kq (void))
(collect (collect-maximum-generation))
(hashtable-size h))
0)
(same-elements? (hashtable-keys h) '#())
(same-elements? (hashtable-values h) '#())
(equal-entries? (hashtable-entries h) '#() '#())
(equal? (hashtable-ref h ky #f) #f)
(eqv?
(hashtable-set! h ky 'toad)
(void))
(equal? (hashtable-ref h ky #f) 'toad)
(equal? (hashtable-ref h kz #f) #f)
(eqv?
(hashtable-update! h kz list 'frog)
(void))
(equal? (hashtable-ref h kz #f) '(frog))
(same-elements? (hashtable-keys h) (vector ky kz))
(same-elements? (hashtable-values h) (vector (hashtable-ref h kz #f) 'toad))
(equal-entries?
(hashtable-entries h)
(vector kz ky)
(vector (hashtable-ref h kz #f) 'toad))
(eqv? (hashtable-ref h '(zippo) 'nil) 'nil)
(begin
(define h3 (hashtable-copy h2 #f))
(and (hashtable? h3)
(not (hashtable-mutable? h3))
(hashtable-weak? h3)))
(same-elements? (hashtable-keys h2) `#((a) (c) -5.75 17 ,ko))
(same-elements? (hashtable-keys h3) `#((a) (c) -5.75 17 ,ko))
(equal?
(begin
(set! ka (void))
(set! km (void))
(set! kn (void))
(set! ko (void))
(collect (collect-maximum-generation))
(list (hashtable-size h2) (hashtable-size h3)))
'(4 4))
(same-elements? (hashtable-keys h2) `#((c) -5.75 17 ,(+ (most-positive-fixnum) 5)))
(same-elements? (hashtable-keys h3) `#((c) -5.75 17 ,(+ (most-positive-fixnum) 5)))
(same-elements? (hashtable-values h2) '#(cval mval nval oval))
(same-elements? (hashtable-values h3) '#(cval mval nval oval))
(equal-entries? (hashtable-entries h2) `#((c) -5.75 17 ,(+ (most-positive-fixnum) 5)) '#(cval mval nval oval))
(equal-entries? (hashtable-entries h3) `#((c) -5.75 17 ,(+ (most-positive-fixnum) 5)) '#(cval mval nval oval))
(eqv?
(begin
(set! h3 (void))
(collect (collect-maximum-generation))
(hashtable-size h2))
4)
(same-elements? (hashtable-keys h2) `#((c) -5.75 17 ,(+ (most-positive-fixnum) 5)))
(same-elements? (hashtable-values h2) '#(cval mval nval oval))
(equal-entries? (hashtable-entries h2) `#((c) -5.75 17 ,(+ (most-positive-fixnum) 5)) '#(cval mval nval oval))
; test for proper shrinkage
(equal?
(let ([ht (make-eqv-hashtable 32)])
(for-each
(lambda (k*) (for-each (lambda (k) (hashtable-delete! ht k)) k*))
(let ([k** (map (lambda (x) (map list (make-list 1000)))
(make-list 100))])
(for-each
(lambda (k*) (map (lambda (k) (hashtable-set! ht k 75)) k*))
k**)
k**))
(call-with-values (lambda () (#%$hashtable-veclen ht)) cons))
'(32 . 32))
; test for proper shrinkage as objects are bwp'd
; uses delete to trigger final shrinkage
(equal?
(let ([ht (make-weak-eqv-hashtable 32)])
(hashtable-set! ht 'a 'b)
(for-each
(lambda (k*) (map (lambda (k) (hashtable-set! ht k 75)) k*))
(map (lambda (x) (map list (make-list 1000))) (make-list 100)))
(collect (collect-maximum-generation))
(hashtable-delete! ht 'a)
(list (hashtable-size ht)
(let-values ([(n1 n2) (#%$hashtable-veclen ht)])
(= n1 n2 32))))
'(0 #t))
)
(mat eqv-hashtable-cell
(let ()
(define-record fribble (x))
(define random-object
(lambda (x)
(case (random 9)
[(0) (cons 'a 3.4)]
[(1) (vector 'c)]
[(2) (string #\a #\b)]
[(3) (make-fribble 'q)]
[(4) (gensym)]
[(5) (open-output-string)]
[(6) (fxvector 15 55)]
[(7) (lambda () x)]
[else (box 'top)])))
(let ([ls1 (let f ([n 10000])
(if (fx= n 0)
'()
(cons
(cons (random-object 4) (random-object 7))
(f (fx- n 1)))))]
[ht (make-eqv-hashtable)]
[wht (make-weak-eqv-hashtable)])
(let ([ls2 (map (lambda (a1) (hashtable-cell ht (car a1) (cdr a1))) ls1)]
[ls3 (map (lambda (a1) (hashtable-cell wht (car a1) (cdr a1))) ls1)])
(unless (andmap (lambda (a1 a2 a3)
(and (eqv? (car a1) (car a2))
(eqv? (car a2) (car a3))))
ls1 ls2 ls3)
(errorf #f "keys are not eqv"))
(unless (andmap (lambda (a1 a2 a3)
(and (eqv? (cdr a1) (cdr a2))
(eqv? (cdr a2) (cdr a3))))
ls1 ls2 ls3)
(errorf #f "values are not eqv"))
(for-each
(lambda (a1)
(when (fx< (random 10) 5)
(set-car! a1 #f)))
ls1)
(let loop ([i (min (expt (collect-generation-radix) (collect-maximum-generation)) 1000)])
(unless (fx= i 0)
(collect)
(unless (andmap (lambda (a2 a3) (eqv? (car a2) (car a3))) ls2 ls3)
(errorf #f "a2/a3 keys not eqv after collection"))
(unless (andmap (lambda (a3) (not (bwp-object? (car a3)))) ls3)
(errorf #f "keys have been bwp'd"))
(loop (fx- i 1))))
(for-each
(lambda (a2)
(hashtable-delete! ht (car a2))
(set-car! a2 #f))
ls2)
(unless (and (equal? (hashtable-keys ht) '#())
(equal? (hashtable-values ht) '#())
(zero? (hashtable-size ht)))
(errorf #f "wht has not been cleared out"))
(let loop ([i (min (expt (collect-generation-radix) (collect-maximum-generation)) 1000)])
(unless (fx= i 0)
(collect)
(unless (andmap (lambda (a1 a3)
(or (not (car a1)) (eqv? (car a1) (car a3))))
ls1 ls3)
(errorf #f "a1/a3 keys not eqv after collection"))
(loop (fx- i 1))))
(for-each
(lambda (a1 a3)
(unless (or (car a1) (bwp-object? (car a3)))
(errorf #f "~s has not been bwp'd I" (car a3))))
ls1 ls3)
(for-each (lambda (a1) (set-car! a1 #f)) ls1)
(collect (collect-maximum-generation))
(unless (andmap (lambda (a3) (bwp-object? (car a3))) ls3)
(errorf #f "keys have not been bwp'd II"))
(unless (and (equal? (hashtable-keys wht) '#())
(equal? (hashtable-values ht) '#())
(zero? (hashtable-size ht)))
(errorf #f "wht has not been cleared out"))))
#t)
)
(mat eqv-strange
(begin
(define $ht (make-eqv-hashtable))
(define $wht (make-weak-eqv-hashtable))
(and (hashtable? $ht)
(hashtable? $wht)))
(eqv? (hashtable-set! $ht #f 75) (void))
(eqv? (hashtable-ref $ht #f 80) 75)
(eqv? (hashtable-set! $wht #f 75) (void))
(eqv? (hashtable-ref $wht #f 80) 75)
(eqv? (hashtable-set! $ht #!bwp "hello") (void))
(equal? (hashtable-ref $ht #!bwp "goodbye") "hello")
(eqv? (hashtable-set! $wht #!bwp "hello") (void))
(and (member (hashtable-ref $wht #!bwp "goodbye") '("hello" "goodbye")) #t)
; make sure that association isn't added before procedure is called
(equal?
(begin
(hashtable-update! $ht 'cupie
(lambda (x) (hashtable-ref $ht 'cupie (cons 'barbie x)))
'doll)
(hashtable-ref $ht 'cupie 'oops))
'(barbie . doll))
(equal?
(begin
(hashtable-update! $wht 'cupie
(lambda (x) (hashtable-ref $wht 'cupie (cons 'barbie x)))
'doll)
(hashtable-ref $wht 'cupie 'oops))
'(barbie . doll))
)
(mat eqv-hashtable-stress
; stress tests
(let () ; nonweak
(define pick
(lambda (ls)
(list-ref ls (random (length ls)))))
(define ht (make-eqv-hashtable 4))
(let ([ls (remq '|| (oblist))] [n 50000])
(let f ([i 0] [keep '()] [drop '()])
(if (= i n)
(and (= (hashtable-size ht) (- n (length drop)))
(andmap (lambda (k)
(string=?
(symbol->string (hashtable-ref ht k #f))
(cond
[(string? k) k]
[(pair? k) (car k)]
[(vector? k) (vector-ref k 0)])))
keep)
(andmap (lambda (k) (eqv? (hashtable-ref ht k 'no) 'no))
drop))
(let* ([x (pick ls)] [s (string-copy (symbol->string x))])
(let ([k (case (pick '(string pair vector))
[(string) s]
[(pair) (list s)]
[(vector) (vector s)])])
(hashtable-set! ht k x)
(let ([keep (if (= (modulo i 5) 0) (cons k keep) keep)])
(if (= (modulo i 17) 5)
(let ([k (pick keep)])
(hashtable-delete! ht k)
(let ([drop (cons k drop)])
(when (= (random 5) 3)
(hashtable-delete! ht (pick drop)))
(f (+ i 1) (remq k keep) drop)))
(f (+ i 1) keep drop)))))))))
(let () ; weak
(define pick
(lambda (ls)
(list-ref ls (random (length ls)))))
(define ht (make-weak-eqv-hashtable 4))
(let ([ls (remq '|| (oblist))] [n 50000])
(let f ([i 0] [keep '()] [drop '()])
(if (= i n)
(and (<= (hashtable-size ht) (- n (length drop)))
(begin
(collect (collect-maximum-generation))
(= (hashtable-size ht) (length keep)))
(andmap (lambda (k)
(string=?
(symbol->string (hashtable-ref ht k #f))
(cond
[(string? k) k]
[(pair? k) (car k)]
[(vector? k) (vector-ref k 0)])))
keep)
(andmap (lambda (k) (eqv? (hashtable-ref ht k 'no) 'no))
drop))
(let* ([x (pick ls)] [s (string-copy (symbol->string x))])
(let ([k (case (pick '(string pair vector))
[(string) s]
[(pair) (list s)]
[(vector) (vector s)])])
(hashtable-set! ht k x)
(let ([keep (if (= (modulo i 5) 0) (cons k keep) keep)])
(if (= (modulo i 17) 5)
(let ([k (pick keep)])
(hashtable-delete! ht k)
(let ([drop (cons k drop)])
(when (= (random 5) 3)
(hashtable-delete! ht (pick drop)))
(f (+ i 1) (remq k keep) drop)))
(f (+ i 1) keep drop)))))))))
)
(mat symbol-hashtable
(let ([ht (make-hashtable symbol-hash eq?)])
(and (symbol-hashtable? ht) (eq? (hashtable-equivalence-function ht) eq?)))
(let ([ht (make-hashtable symbol-hash eqv?)])
(and (symbol-hashtable? ht) (eq? (hashtable-equivalence-function ht) eqv?)))
(let ([ht (make-hashtable symbol-hash equal?)])
(and (symbol-hashtable? ht) (eq? (hashtable-equivalence-function ht) equal?)))
(let ([ht (make-hashtable symbol-hash symbol=?)])
(and (symbol-hashtable? ht) (eq? (hashtable-equivalence-function ht) symbol=?)))
(let ([ht (make-hashtable symbol-hash eq? 17)])
(and (symbol-hashtable? ht) (eq? (hashtable-equivalence-function ht) eq?)))
(let ([ht (make-hashtable symbol-hash eqv? 17)])
(and (symbol-hashtable? ht) (eq? (hashtable-equivalence-function ht) eqv?)))
(let ([ht (make-hashtable symbol-hash equal? 17)])
(and (symbol-hashtable? ht) (eq? (hashtable-equivalence-function ht) equal?)))
(let ([ht (make-hashtable symbol-hash symbol=? 17)])
(and (symbol-hashtable? ht) (eq? (hashtable-equivalence-function ht) symbol=?)))
(begin
(define h (make-hashtable symbol-hash eq? 32))
(and (hashtable? h)
(symbol-hashtable? h)
(hashtable-mutable? h)
(not (eq-hashtable? h))
(not (hashtable-weak? h))))
(eq? (hashtable-hash-function h) symbol-hash)
(eq? (hashtable-equivalence-function h) eq?)
(equal? (hashtable-size h) 0)
(same-elements? (hashtable-keys h) '#())
(same-elements? (hashtable-values h) '#())
(equal-entries? (hashtable-entries h) '#() '#())
(eqv? (hashtable-set! h 'a 'aval) (void))
(equal?
(list
(hashtable-contains? h 'a)
(hashtable-contains? h 'b)
(hashtable-contains? h 'c))
'(#t #f #f))
(eqv? (hashtable-set! h 'b 'bval) (void))
(equal?
(list
(hashtable-contains? h 'a)
(hashtable-contains? h 'b)
(hashtable-contains? h 'c))
'(#t #t #f))
(eqv? (hashtable-set! h 'c 'cval) (void))
(equal?
(list
(hashtable-contains? h 'a)
(hashtable-contains? h 'b)
(hashtable-contains? h 'c))
'(#t #t #t))
(equal? (hashtable-size h) 3)
(same-elements? (hashtable-keys h) '#(a b c))
(same-elements? (hashtable-values h) '#(bval cval aval))
(equal-entries? (hashtable-entries h) '#(b c a) '#(bval cval aval))
#;(same-elements? (list->vector (hashtable-map h cons)) '#((a . aval) (b . bval) (c . cval)))
#;(same-elements?
(let ([v (make-vector 3)] [i 0])
(hashtable-for-each h (lambda (key val) (vector-set! v i (cons key val)) (set! i (fx+ i 1))))
v)
'#((a . aval) (b . bval) (c . cval)))
#;(same-elements?
(let ([v (make-vector 3)] [i 0])
(hashtable-for-each-cell h (lambda (a) (vector-set! v i a) (set! i (fx+ i 1))))
v)
'#((a . aval) (b . bval) (c . cval)))
(equal? (hashtable-ref h 'a 1) 'aval)
(equal? (hashtable-ref h 'b #f) 'bval)
(equal? (hashtable-ref h 'c 'nope) 'cval)
(eqv? (hashtable-delete! h 'b) (void))
(equal? (hashtable-size h) 2)
(same-elements? (hashtable-keys h) '#(a c))
(same-elements? (hashtable-values h) '#(aval cval))
(equal-entries? (hashtable-entries h) '#(a c) '#(aval cval))
(begin
(define h2 (hashtable-copy h #t))
(and (hashtable? h2)
(symbol-hashtable? h2)
(hashtable-mutable? h2)
(not (hashtable-weak? h2))
(not (eq-hashtable? h2))))
(eq? (hashtable-hash-function h2) symbol-hash)
(eq? (hashtable-equivalence-function h2) eq?)
(equal? (hashtable-size h2) 2)
(same-elements? (hashtable-keys h2) '#(a c))
(same-elements? (hashtable-values h2) '#(aval cval))
(equal-entries? (hashtable-entries h2) '#(a c) '#(aval cval))
(eqv? (hashtable-clear! h 4) (void))
(equal?
(list
(hashtable-size h)
(hashtable-ref h 'a 1)
(hashtable-ref h 'b #f)
(hashtable-ref h 'c 'nope))
'(0 1 #f nope))
(same-elements? (hashtable-keys h) '#())
(same-elements? (hashtable-values h) '#())
(equal-entries? (hashtable-entries h) '#() '#())
(equal?
(list
(hashtable-size h2)
(hashtable-ref h2 'a 1)
(hashtable-ref h2 'b #f)
(hashtable-ref h2 'c 'nope))
'(2 aval #f cval))
(same-elements? (hashtable-keys h2) '#(a c))
(same-elements? (hashtable-values h2) '#(aval cval))
(equal-entries? (hashtable-entries h2) '#(a c) '#(aval cval))
(eqv?
(hashtable-update! h 'q
(lambda (x) (+ x 1))
17)
(void))
(equal? (hashtable-ref h 'q #f) 18)
(eqv?
(hashtable-update! h 'q
(lambda (x) (+ x 1))
17)
(void))
(equal? (hashtable-ref h 'q #f) 19)
(equal? (hashtable-size h) 1)
; test hashtable-copy when some keys may have moved
; symbol hashes don't change, but keeping test adapted from eq-hashtable mats anyway
(let ([t (parameterize ([collect-request-handler void])
(let ([h4a (make-hashtable symbol-hash eqv? 32)]
[k* (list-head (oblist) 100)])
(for-each (lambda (x) (hashtable-set! h4a x x)) k*)
(collect)
; create copy after collection but before otherwise touching h4a
(let ([h4b (hashtable-copy h4a #t)])
(andmap
(lambda (k) (eq? (hashtable-ref h4b k #f) k))
k*))))])
(collect)
t)
; test for proper shrinkage
(eqv?
(let ([ht (make-hashtable symbol-hash equal? 32)])
(for-each
(lambda (k*) (for-each (lambda (k) (hashtable-delete! ht k)) k*))
(let ([k** (map (lambda (x) (list-head (oblist) 1000)) (make-list 100))])
(for-each
(lambda (k*) (map (lambda (k) (hashtable-set! ht k 75)) k*))
k**)
k**))
(#%$hashtable-veclen ht))
32)
)
(mat $symbol-hashtable
(begin
(define h (make-hashtable symbol-hash eq? 32))
(and (hashtable? h)
(symbol-hashtable? h)
(hashtable-mutable? h)
(not (eq-hashtable? h))
(not (hashtable-weak? h))))
(eq? (hashtable-hash-function h) symbol-hash)
(eq? (hashtable-equivalence-function h) eq?)
(equal? (hashtable-size h) 0)
(same-elements? (hashtable-keys h) '#())
(same-elements? (hashtable-values h) '#())
(equal-entries? (hashtable-entries h) '#() '#())
(eqv? (symbol-hashtable-set! h 'a 'aval) (void))
(equal?
(list
(symbol-hashtable-contains? h 'a)
(symbol-hashtable-contains? h 'b)
(symbol-hashtable-contains? h 'c))
'(#t #f #f))
(eqv? (symbol-hashtable-set! h 'b 'bval) (void))
(equal?
(list
(symbol-hashtable-contains? h 'a)
(symbol-hashtable-contains? h 'b)
(symbol-hashtable-contains? h 'c))
'(#t #t #f))
(eqv? (symbol-hashtable-set! h 'c 'cval) (void))
(equal?
(list
(symbol-hashtable-contains? h 'a)
(symbol-hashtable-contains? h 'b)
(symbol-hashtable-contains? h 'c))
'(#t #t #t))
(equal? (hashtable-size h) 3)
(same-elements? (hashtable-keys h) '#(a b c))
(same-elements? (hashtable-values h) '#(bval cval aval))
(equal-entries? (hashtable-entries h) '#(b c a) '#(bval cval aval))
#;(same-elements? (list->vector (hashtable-map h cons)) '#((a . aval) (b . bval) (c . cval)))
#;(same-elements?
(let ([v (make-vector 3)] [i 0])
(hashtable-for-each h (lambda (key val) (vector-set! v i (cons key val)) (set! i (fx+ i 1))))
v)
'#((a . aval) (b . bval) (c . cval)))
#;(same-elements?
(let ([v (make-vector 3)] [i 0])
(hashtable-for-each-cell h (lambda (a) (vector-set! v i a) (set! i (fx+ i 1))))
v)
'#((a . aval) (b . bval) (c . cval)))
(equal? (symbol-hashtable-ref h 'a 1) 'aval)
(equal? (symbol-hashtable-ref h 'b #f) 'bval)
(equal? (symbol-hashtable-ref h 'c 'nope) 'cval)
(eqv? (symbol-hashtable-delete! h 'b) (void))
(equal? (hashtable-size h) 2)
(same-elements? (hashtable-keys h) '#(a c))
(same-elements? (hashtable-values h) '#(aval cval))
(equal-entries? (hashtable-entries h) '#(a c) '#(aval cval))
(begin
(define h2 (hashtable-copy h #t))
(and (hashtable? h2)
(symbol-hashtable? h2)
(hashtable-mutable? h2)
(not (hashtable-weak? h2))
(not (eq-hashtable? h2))))
(eq? (hashtable-hash-function h2) symbol-hash)
(eq? (hashtable-equivalence-function h2) eq?)
(equal? (hashtable-size h2) 2)
(same-elements? (hashtable-keys h2) '#(a c))
(same-elements? (hashtable-values h2) '#(aval cval))
(equal-entries? (hashtable-entries h2) '#(a c) '#(aval cval))
(eqv? (hashtable-clear! h 4) (void))
(equal?
(list
(hashtable-size h)
(symbol-hashtable-ref h 'a 1)
(symbol-hashtable-ref h 'b #f)
(symbol-hashtable-ref h 'c 'nope))
'(0 1 #f nope))
(same-elements? (hashtable-keys h) '#())
(same-elements? (hashtable-values h) '#())
(equal-entries? (hashtable-entries h) '#() '#())
(equal?
(list
(hashtable-size h2)
(symbol-hashtable-ref h2 'a 1)
(symbol-hashtable-ref h2 'b #f)
(symbol-hashtable-ref h2 'c 'nope))
'(2 aval #f cval))
(same-elements? (hashtable-keys h2) '#(a c))
(same-elements? (hashtable-values h2) '#(aval cval))
(equal-entries? (hashtable-entries h2) '#(a c) '#(aval cval))
(eqv?
(symbol-hashtable-update! h 'q
(lambda (x) (+ x 1))
17)
(void))
(equal? (symbol-hashtable-ref h 'q #f) 18)
(eqv?
(symbol-hashtable-update! h 'q
(lambda (x) (+ x 1))
17)
(void))
(equal? (symbol-hashtable-ref h 'q #f) 19)
(equal? (hashtable-size h) 1)
(let ([g (gensym)] [s "feisty"])
(let ([a (symbol-hashtable-cell h g s)])
(and (pair? a)
(eq? (car a) g)
(eq? (cdr a) s)
(begin
(hashtable-set! h g 'feisty)
(eq? (cdr a) 'feisty))
(begin
(set-cdr! a (list "feisty"))
(equal? (hashtable-ref h g #f) '("feisty"))))))
; test hashtable-copy when some keys may have moved
; symbol hashes don't change, but keeping test adapted from eq-hashtable mats anyway
(let ([t (parameterize ([collect-request-handler void])
(let ([h4a (make-hashtable symbol-hash eqv? 32)]
[k* (list-head (oblist) 100)])
(for-each (lambda (x) (symbol-hashtable-set! h4a x x)) k*)
(collect)
; create copy after collection but before otherwise touching h4a
(let ([h4b (hashtable-copy h4a #t)])
(andmap
(lambda (k) (eq? (symbol-hashtable-ref h4b k #f) k))
k*))))])
(collect)
t)
; test for proper shrinkage
(eqv?
(let ([ht (make-hashtable symbol-hash equal? 32)])
(for-each
(lambda (k*) (for-each (lambda (k) (symbol-hashtable-delete! ht k)) k*))
(let ([k** (map (lambda (x) (list-head (oblist) 1000)) (make-list 100))])
(for-each
(lambda (k*) (map (lambda (k) (symbol-hashtable-set! ht k 75)) k*))
k**)
k**))
(#%$hashtable-veclen ht))
32)
)
(mat symbol-hashtable-stress
; stress tests
(let () ; nonweak
(define pick
(lambda (ls)
(list-ref ls (random (length ls)))))
(define ht (make-hashtable symbol-hash eq? 4))
(let ([ls (remq '|| (oblist))] [n 50000])
(let f ([i 0] [keep '()] [drop '()])
(if (= i n)
(and (= (hashtable-size ht) (- n (length drop)))
(andmap (lambda (k)
(string=?
(symbol->string (hashtable-ref ht k #f))
(symbol->string k)))
keep)
(andmap (lambda (k) (eq? (hashtable-ref ht k 'no) 'no))
drop))
(let* ([x (pick ls)] [s (string-copy (symbol->string x))])
(let ([k (gensym s)])
(hashtable-set! ht k x)
(let ([keep (if (= (modulo i 5) 0) (cons k keep) keep)])
(if (= (modulo i 17) 5)
(let ([k (pick keep)])
(hashtable-delete! ht k)
(let ([drop (cons k drop)])
(when (= (random 5) 3)
(hashtable-delete! ht (pick drop)))
(f (+ i 1) (remq k keep) drop)))
(f (+ i 1) keep drop)))))))))
)
(mat generic-hashtable
(begin
(define $ght-keys1 '#(a b c d e f g))
(define $ght-vals1 '#(1 3 5 7 9 11 13))
(define $ght (make-hashtable equal-hash equal? 8))
(vector-for-each
(lambda (x i) (hashtable-set! $ght x i))
$ght-keys1
$ght-vals1)
(hashtable? $ght))
(not (eq-hashtable? $ght))
(eq? (hashtable-hash-function $ght) equal-hash)
(eq? (hashtable-equivalence-function $ght) equal?)
(eq? (hashtable-mutable? $ght) #t)
(not (hashtable-weak? $ght))
(eqv? (hashtable-size $ght) (vector-length $ght-keys1))
(eqv? (#%$hashtable-veclen $ght) 8)
(same-elements? (hashtable-keys $ght) $ght-keys1)
(same-elements? (hashtable-values $ght) $ght-vals1)
(equal-entries? (hashtable-entries $ght) $ght-keys1 $ght-vals1)
(begin
(define $ght-keys2 '#((a . b) (1 . 2) 3/4 3.4 3.5 1e23 #e1e50 1+1i 3+3.2i -15 #e1e-50 #1=(a . #1#) (#2=(#2# b c))))
(define $ght-vals2 '#(a b c d e f g h i j k l m))
(vector-for-each
(lambda (x i) (hashtable-set! $ght x i))
$ght-keys2
$ght-vals2)
(eq? (hashtable-size $ght) (+ (vector-length $ght-keys1) (vector-length $ght-keys2))))
(> (#%$hashtable-veclen $ght) 8)
(same-elements? (hashtable-keys $ght) ($vector-append $ght-keys1 $ght-keys2))
(same-elements? (hashtable-values $ght) ($vector-append $ght-vals1 $ght-vals2))
(equal-entries? (hashtable-entries $ght) ($vector-append $ght-keys1 $ght-keys2) ($vector-append $ght-vals1 $ght-vals2))
#;(same-elements?
(list->vector (hashtable-map $ght cons))
(vector-map cons ($vector-append $ght-keys1 $ght-keys2) ($vector-append $ght-vals1 $ght-vals2)))
#;(same-elements?
(let ([v (make-vector (+ (vector-length $ght-keys1) (vector-length $ght-keys2)))] [i 0])
(hashtable-for-each $ght (lambda (key val) (vector-set! v i (cons key val)) (set! i (fx+ i 1))))
v)
(vector-map cons ($vector-append $ght-keys1 $ght-keys2) ($vector-append $ght-vals1 $ght-vals2)))
#;(same-elements?
(let ([v (make-vector (+ (vector-length $ght-keys1) (vector-length $ght-keys2)))] [i 0])
(hashtable-for-each-cell $ght (lambda (a) (vector-set! v i a) (set! i (fx+ i 1))))
v)
(vector-map cons ($vector-append $ght-keys1 $ght-keys2) ($vector-append $ght-vals1 $ght-vals2)))
($vector-andmap
(lambda (k v) (equal? (hashtable-ref $ght k #f) v))
$ght-keys1
$ght-vals1)
($vector-andmap
(lambda (k v) (equal? (hashtable-ref $ght k #f) v))
$ght-keys2
$ght-vals2)
($vector-andmap
(lambda (k v) (equal? (hashtable-ref $ght k #f) v))
'#((a . b) (1 . 2) 3/4 3.4 3.5 1e23 #e1e50 1+1i 3+3.2i -15 #e1e-50 #3=(a . #3#) (#4=(#4# b c)))
$ght-vals2)
($vector-andmap
(lambda (k) (hashtable-contains? $ght k))
$ght-keys1)
($vector-andmap
(lambda (k) (hashtable-contains? $ght k))
$ght-keys2)
(not (hashtable-contains? $ght '(not a key)))
(eq? (hashtable-ref $ght '(not a key) 'not-a-key) 'not-a-key)
(begin
(define $ght2 (hashtable-copy $ght))
(and (hashtable? $ght2)
(not (hashtable-mutable? $ght2))
(not (hashtable-weak? $ght2))))
(eq? (hashtable-hash-function $ght) equal-hash)
(eq? (hashtable-equivalence-function $ght) equal?)
(begin
(define $ght3 (hashtable-copy $ght #t))
(and (hashtable? $ght3)
(hashtable-mutable? $ght3)
(not (hashtable-weak? $ght3))))
(eq? (hashtable-hash-function $ght) equal-hash)
(eq? (hashtable-equivalence-function $ght) equal?)
(begin
(vector-for-each
(lambda (k) (hashtable-delete! $ght k))
$ght-keys1)
#t)
(same-elements? (hashtable-keys $ght) $ght-keys2)
(same-elements? (hashtable-values $ght) $ght-vals2)
(equal-entries? (hashtable-entries $ght) $ght-keys2 $ght-vals2)
(eqv? (hashtable-size $ght) (vector-length $ght-keys2))
(begin
(vector-for-each
(lambda (k) (hashtable-delete! $ght k))
$ght-keys2)
#t)
(same-elements? (hashtable-keys $ght) '#())
(same-elements? (hashtable-values $ght) '#())
(equal-entries? (hashtable-entries $ght) '#() '#())
(eqv? (hashtable-size $ght) 0)
(eqv? (#%$hashtable-veclen $ght) 8)
; make sure copies are unaffected by deletions
(eq? (hashtable-size $ght2) (+ (vector-length $ght-keys1) (vector-length $ght-keys2)))
(same-elements? (hashtable-keys $ght2) ($vector-append $ght-keys1 $ght-keys2))
(same-elements? (hashtable-values $ght2) ($vector-append $ght-vals1 $ght-vals2))
(equal-entries? (hashtable-entries $ght2) ($vector-append $ght-keys1 $ght-keys2) ($vector-append $ght-vals1 $ght-vals2))
(eq? (hashtable-size $ght3) (+ (vector-length $ght-keys1) (vector-length $ght-keys2)))
(same-elements? (hashtable-keys $ght3) ($vector-append $ght-keys1 $ght-keys2))
(same-elements? (hashtable-values $ght3) ($vector-append $ght-vals1 $ght-vals2))
(equal-entries? (hashtable-entries $ght3) ($vector-append $ght-keys1 $ght-keys2) ($vector-append $ght-vals1 $ght-vals2))
(begin
(hashtable-clear! $ght3)
(and
(eqv? (hashtable-size $ght3) 0)
(eqv? (hashtable-size $ght2) (+ (vector-length $ght-keys1) (vector-length $ght-keys2)))))
(error? ; not mutable
(hashtable-clear! $ght2))
(error? ; not mutable
(hashtable-delete! $ght2 (vector-ref $ght-keys2 0)))
(error? ; not mutable
(hashtable-update! $ght2 (vector-ref $ght-keys2 0)
(lambda (x) (cons x x))
'oops))
(error? ; not mutable
(hashtable-update! $ght2 '(not a key)
(lambda (x) (cons x x))
'oops))
(eqv?
(hashtable-update! $ght3 '(a . b)
(lambda (x) (+ x 15))
17)
(void))
(eqv?
(hashtable-update! $ght3 '(a . b)
(lambda (x) (+ x 29))
17)
(void))
(eqv?
(hashtable-update! $ght3 1e23
(lambda (x) (- x 5))
19)
(void))
(equal?
(let ([a (hashtable-cell $ght3 '(a . b) 17)])
(set-cdr! a (+ (cdr a) 100))
a)
'((a . b) . 161))
(equal?
(let ([a (hashtable-cell $ght3 #vu8(1 2 3) 'bv)])
(set-cdr! a (cons (cdr a) 'vb))
a)
'(#vu8(1 2 3) . (bv . vb)))
(same-elements? (hashtable-keys $ght3) '#((a . b) 1e23 #vu8(1 2 3)))
(same-elements? (hashtable-values $ght3) '#(161 14 (bv . vb)))
(equal-entries? (hashtable-entries $ght3) '#((a . b) 1e23 #vu8(1 2 3)) '#(161 14 (bv . vb)))
(let () ; carl's test program, with a few additions
(define cov:prof-hash
(lambda (V)
(* (vector-ref V 0) (vector-ref V 1) (vector-ref V 2))))
(define cov:prof-equal?
(lambda (V W)
(let ((rv (and (= (vector-ref V 0) (vector-ref W 0))
(= (vector-ref V 1) (vector-ref W 1))
(= (vector-ref V 2) (vector-ref W 2)))))
rv)))
(define make-random-vector-key
(lambda ()
(vector (random 20000) (random 100) (random 1000))))
(define test-hash
(lambda (n)
(let ([ht (make-hashtable cov:prof-hash cov:prof-equal?)])
(let loop ([i 0])
(let ([str (make-random-vector-key)])
(hashtable-set! ht str i)
(hashtable-update! ht str (lambda (x) (* x 2)) -1)
(let ([a (hashtable-cell ht str 'a)]) (set-cdr! a (- (cdr a))))
(cond
[(= i n) (= (hashtable-size ht) 1000)]
[(and (hashtable-contains? ht str)
(= (hashtable-ref ht str #f) (* i -2)))
(when (= (hashtable-size ht) 1000)
(hashtable-delete! ht str))
(loop (+ i 1))]
[else (errorf 'test-hash "hashtable failure for key ~s" str)]))))))
(test-hash 100000))
)
(mat hash-functions
; equal-hash
(error? ; wrong argument count
(equal-hash))
(error? ; wrong argument count
(equal-hash 0 0))
; symbol-hash
(error? ; wrong argument count
(symbol-hash))
(error? ; wrong argument count
(symbol-hash 'a 'a))
(error? ; not a symbol
(symbol-hash "hello"))
; string-hash
(error? ; wrong argument count
(string-hash))
(error? ; wrong argument count
(string-hash 'a 'a))
(error? ; not a string
(string-hash 'hello))
; string-ci-hash
(error? ; wrong argument count
(string-ci-hash))
(error? ; wrong argument count
(string-ci-hash 'a 'a))
(error? ; not a string
(string-ci-hash 'hello))
(let ([hc (equal-hash '(a b c))])
(and (integer? hc)
(exact? hc)
(>= hc 0)
(= (equal-hash '(a b c)) hc)))
(let ([hc (string-hash "hello")])
(and (integer? hc)
(exact? hc)
(>= hc 0)
(= (string-hash "hello") hc)))
(let ([hc (string-ci-hash "hello")])
(and (integer? hc)
(exact? hc)
(>= hc 0)
(= (string-ci-hash "HelLo") hc)))
(let f ([ls (oblist)])
(define okay?
(lambda (x)
(let ([hc (symbol-hash x)])
(and (integer? hc)
(exact? hc)
(>= hc 0)
(= (symbol-hash x) hc)))))
(and (okay? (car ls))
(let g ([ls ls] [n 10])
(or (null? ls)
(if (= n 0)
(f ls)
(g (cdr ls) (- n 1)))))))
; adapted from Flatt's r6rs tests for string-ci=?
(eqv? (string-ci-hash "z") (string-ci-hash "Z"))
(not (eqv? (string-ci-hash "z") (string-ci-hash "a")))
(eqv? (string-ci-hash "Stra\xDF;e") (string-ci-hash "Strasse"))
(eqv? (string-ci-hash "Stra\xDF;e") (string-ci-hash "STRASSE"))
(eqv? (string-ci-hash "\x39E;\x391;\x39F;\x3A3;") (string-ci-hash "\x3BE;\x3B1;\x3BF;\x3C2;"))
(eqv? (string-ci-hash "\x39E;\x391;\x39F;\x3A3;") (string-ci-hash "\x3BE;\x3B1;\x3BF;\x3C3;"))
)
(mat fasl-eq-hashtable
; fasling out eq hash tables
(equal?
(let ([x (cons 'y '!)])
(define ht (make-eq-hashtable))
(eq-hashtable-set! ht x 'because)
(eq-hashtable-set! ht 'foo "foo")
(let ([p (open-file-output-port "testfile.ss" (file-options replace))])
(fasl-write (list x ht) p)
(close-port p))
(let-values ([(x2 ht2)
(apply values
(call-with-port
(open-file-input-port "testfile.ss")
fasl-read))])
(list
(eq-hashtable-ref ht2 x2 #f)
(eq-hashtable-ref ht2 'foo #f))))
'(because "foo"))
; fasling out weak eq hash table
(equal?
(with-interrupts-disabled
(let ([x (cons 'y '!)])
(define ht (make-weak-eq-hashtable))
(eq-hashtable-set! ht x 'because)
(eq-hashtable-set! ht 'foo "foo")
(let ([p (open-file-output-port "testfile.ss" (file-options replace))])
(fasl-write (list x ht) p)
(close-port p))
(let-values ([(x2 ht2)
(apply values
(call-with-port
(open-file-input-port "testfile.ss")
fasl-read))])
(list
(eq-hashtable-ref ht2 x2 #f)
(eq-hashtable-ref ht2 'foo #f)))))
'(because "foo"))
(equal?
(let ([ht2 (cadr (call-with-port
(open-file-input-port "testfile.ss")
fasl-read))])
(collect (collect-maximum-generation))
(list
(hashtable-keys ht2)
(eq-hashtable-ref ht2 'foo #f)))
'(#(foo) "foo"))
; fasling eq hash tables via compile-file
(begin
(with-output-to-file "testfile.ss"
(lambda ()
(pretty-print
'(begin
(define-syntax $feh-ls
(let ([ls '(1 2 3)])
(lambda (x)
#`(quote #,(datum->syntax #'* ls)))))
(define $feh-ht
(let ()
(define-syntax a
(let ([ht (make-eq-hashtable)])
(eq-hashtable-set! ht 'q 'p)
(eq-hashtable-set! ht $feh-ls (cdr $feh-ls))
(eq-hashtable-set! ht (cdr $feh-ls) (cddr $feh-ls))
(eq-hashtable-set! ht (cddr $feh-ls) $feh-ls)
(lambda (x) #`(quote #,(datum->syntax #'* ht)))))
a)))))
'replace)
(compile-file "testfile")
(load "testfile.so")
#t)
(eq? (eq-hashtable-ref $feh-ht 'q #f) 'p)
(eq? (eq-hashtable-ref $feh-ht $feh-ls #f) (cdr $feh-ls))
(eq? (eq-hashtable-ref $feh-ht (cdr $feh-ls) #f) (cddr $feh-ls))
(eq? (eq-hashtable-ref $feh-ht (cddr $feh-ls) #f) $feh-ls)
(begin
(eq-hashtable-set! $feh-ht 'p 'r)
#t)
(eq? (eq-hashtable-ref $feh-ht 'p #f) 'r)
(begin
(eq-hashtable-set! $feh-ht 'q 'not-p)
#t)
(eq? (eq-hashtable-ref $feh-ht 'q #f) 'not-p)
)
(mat fasl-symbol-hashtable
; fasling out symbol hash tables
(equal?
(let ()
(define ht (make-hashtable symbol-hash eq?))
(symbol-hashtable-set! ht 'why? 'because)
(symbol-hashtable-set! ht 'foo "foo")
(let ([p (open-file-output-port "testfile.ss" (file-options replace))])
(fasl-write ht p)
(close-port p))
(let ([ht2 (call-with-port (open-file-input-port "testfile.ss") fasl-read)])
(list
(symbol-hashtable-ref ht2 'why? #f)
(symbol-hashtable-ref ht2 'foo #f))))
'(because "foo"))
(#%$fasl-file-equal? "testfile.ss" "testfile.ss")
(eqv? (strip-fasl-file "testfile.ss" "testfile1.ss" (fasl-strip-options)) (void))
(#%$fasl-file-equal? "testfile.ss" "testfile1.ss")
(equal?
(let ([ht2 (call-with-port (open-file-input-port "testfile1.ss" (file-options compressed)) fasl-read)])
(list
(symbol-hashtable-ref ht2 'why? #f)
(symbol-hashtable-ref ht2 'foo #f)))
'(because "foo"))
(begin
(call-with-port (open-file-output-port "testfile1.ss" (file-options replace))
(lambda (p)
(fasl-write (call-with-port (open-file-input-port "testfile.ss") fasl-read) p)))
#t)
(#%$fasl-file-equal? "testfile.ss" "testfile1.ss")
(#%$fasl-file-equal? "testfile1.ss" "testfile.ss")
(begin
(call-with-port (open-file-output-port "testfile1.ss" (file-options replace))
(lambda (p)
(let ([ht (call-with-port (open-file-input-port "testfile.ss") fasl-read)])
(symbol-hashtable-set! ht 'why? 'why-not?)
(fasl-write ht p))))
#t)
(not (#%$fasl-file-equal? "testfile.ss" "testfile1.ss"))
(not (#%$fasl-file-equal? "testfile1.ss" "testfile.ss"))
(begin
(call-with-port (open-file-output-port "testfile1.ss" (file-options replace))
(lambda (p)
(let ([ht (call-with-port (open-file-input-port "testfile.ss") fasl-read)])
(symbol-hashtable-set! ht (gensym) 'foiled)
(fasl-write ht p))))
#t)
(not (#%$fasl-file-equal? "testfile.ss" "testfile1.ss"))
(not (#%$fasl-file-equal? "testfile1.ss" "testfile.ss"))
; fasling symbol hash tables via compile-file
(begin
(with-output-to-file "testfile.ss"
(lambda ()
(pretty-print
'(define $fsh-ht
(let ()
(define-syntax a
(let ([ht (make-hashtable symbol-hash symbol=?)])
(symbol-hashtable-set! ht 'q 'p)
(symbol-hashtable-set! ht 'p 's)
(let ([g (gensym "hello")])
(symbol-hashtable-set! ht g g)
(symbol-hashtable-set! ht 'g g))
(lambda (x) #`(quote #,(datum->syntax #'* ht)))))
a))))
'replace)
(compile-file "testfile")
(load "testfile.so")
#t)
(eq? (symbol-hashtable-ref $fsh-ht 'q #f) 'p)
(eq? (symbol-hashtable-ref $fsh-ht 'p #f) 's)
(let ([g (symbol-hashtable-ref $fsh-ht 'g #f)])
(eq? (symbol-hashtable-ref $fsh-ht g #f) g))
(eq? (symbol-hashtable-ref $fsh-ht 'spam #f) #f)
(begin
(symbol-hashtable-set! $fsh-ht 'p 'r)
#t)
(eq? (symbol-hashtable-ref $fsh-ht 'p #f) 'r)
(begin
(symbol-hashtable-set! $fsh-ht 'q 'not-p)
#t)
(eq? (symbol-hashtable-ref $fsh-ht 'q #f) 'not-p)
)
(mat fasl-other-hashtable
; can't fasl out other kinds of hashtables
(error?
(let ([x (cons 'y '!)])
(define ht (make-eqv-hashtable))
(hashtable-set! ht x 'because)
(hashtable-set! ht 'foo "foo")
(hashtable-set! ht 3.1415 "pi")
(let ([p (open-file-output-port "testfile.ss" (file-options replace))])
(with-exception-handler
(lambda (c) (close-port p) (raise-continuable c))
(lambda () (fasl-write (list x ht) p))))))
(error?
(let ([x (cons 'y '!)])
(define ht (make-hashtable string-hash string=?))
(hashtable-set! ht "hello" 'goodbye)
(let ([p (open-file-output-port "testfile.ss" (file-options replace))])
(with-exception-handler
(lambda (c) (close-port p) (raise-continuable c))
(lambda () (fasl-write (list x ht) p))))))
)
(mat ht
(begin
(display-string (separate-eval '(parameterize ([source-directories '("." "../s" "../../s")]) (load "ht.ss"))))
#t)
)