Skip to content

Commit 11a4e11

Browse files
committed
Corrected bugs and optimized (srfi :126 r6rs-hashtables)
- make-hashtable must accept #f as hash procedure when equiv is eq? or eq?; - make-hashtable must accept a pair of hash procedures or a single procedure; - weakness argument of make-hashtable was not tested in Chez Scheme. Instead, Chez Scheme now signals an error if weakness is neither weak-key nor ephemeral-key (when version >= 9.5.0); - avoids consing lists in hashtable-sum and hashtable-map->lset; - hashtable-intern! uses hashtable-cell when available (only Chez Scheme supports that for now).
1 parent 23c1e35 commit 11a4e11

File tree

5 files changed

+598
-97
lines changed

5 files changed

+598
-97
lines changed

%3a126/126.body.scm

+116-68
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,19 @@
77
(rnrs:make-eq-hashtable)))
88
((capacity weakness)
99
(if weakness
10-
(begin
11-
(unless (weak-eq-hashtables-supported)
12-
(error 'make-eq-hashtable "weak hashtables unsupported"))
13-
(if capacity
14-
(make-weak-eq-hashtable capacity)
15-
(make-weak-eq-hashtable)))
10+
(cond
11+
((memq weakness (weak-eq-hashtables-supported))
12+
(if capacity
13+
((make-weak-eq-hashtable-procedure weakness) capacity)
14+
((make-weak-eq-hashtable-procedure weakness))))
15+
((memq weakness (ephemeral-eq-hashtables-supported))
1616
(if capacity
17-
(rnrs:make-eq-hashtable capacity)
18-
(rnrs:make-eq-hashtable))))))
17+
((make-ephemeral-eq-hashtable-procedure weakness) capacity)
18+
((make-ephemeral-eq-hashtable-procedure weakness))))
19+
(else (error 'make-eq-hashtable "weakness not supported" weakness)))
20+
(if capacity
21+
(rnrs:make-eq-hashtable capacity)
22+
(rnrs:make-eq-hashtable))))))
1923

2024
(define make-eqv-hashtable
2125
(case-lambda
@@ -26,41 +30,70 @@
2630
(rnrs:make-eqv-hashtable)))
2731
((capacity weakness)
2832
(if weakness
29-
(begin
30-
(unless (weak-eqv-hashtables-supported)
31-
(error 'make-eqv-hashtable "weak hashtables unsupported"))
33+
(cond
34+
((memq weakness (weak-eqv-hashtables-supported))
35+
(if capacity
36+
((make-weak-eqv-hashtable-procedure weakness) capacity)
37+
((make-weak-eqv-hashtable-procedure weakness))))
38+
((memq weakness (ephemeral-eqv-hashtables-supported))
3239
(if capacity
33-
(make-weak-eqv-hashtable capacity)
34-
(make-weak-eqv-hashtable)))
40+
((make-ephemeral-eqv-hashtable-procedure weakness) capacity)
41+
((make-ephemeral-eqv-hashtable-procedure weakness))))
42+
(else (error 'make-eqv-hashtable "weakness not supported" weakness)))
3543
(if capacity
3644
(rnrs:make-eqv-hashtable capacity)
3745
(rnrs:make-eqv-hashtable))))))
3846

3947
(define make-hashtable
4048
(case-lambda
41-
((hash equiv) (rnrs:make-hashtable hash equiv))
49+
((hash equiv)
50+
(if hash
51+
(rnrs:make-hashtable (if (pair? hash) (car hash) hash) equiv)
52+
(cond
53+
((eq? equiv eq?) (make-eq-hashtable))
54+
((eq? equiv eqv?) (make-eqv-hashtable))
55+
(else (error 'make-hashtable
56+
"hash procedure cannot be #f except with eq? or eqv?"
57+
hash equiv)))))
4258
((hash equiv capacity)
43-
(if capacity
44-
(rnrs:make-hashtable hash equiv capacity)
45-
(rnrs:make-hashtable hash equiv)))
59+
(if hash
60+
(if capacity
61+
(rnrs:make-hashtable (if (pair? hash) (car hash) hash) equiv
62+
capacity)
63+
(rnrs:make-hashtable (if (pair? hash) (car hash) hash) equiv))
64+
(cond
65+
((eq? equiv eq?) (make-eq-hashtable capacity))
66+
((eq? equiv eqv?) (make-eqv-hashtable capacity))
67+
(else (error 'make-hashtable
68+
"hash procedure cannot be #f except with eq? or eqv?"
69+
hash equiv)))))
4670
((hash equiv capacity weakness)
47-
(cond
48-
((and (not hash) (eq? equiv eq?))
49-
(make-eq-hashtable capacity weakness))
50-
((and (not hash) (eq? equiv eqv?))
51-
(make-eqv-hashtable capacity weakness))
52-
(else
53-
(let ((hash (if (pair? hash) (car hash) hash))) ;; why?
54-
(if weakness
55-
(begin
56-
(unless (weak-hashtables-supported)
57-
(error 'make-hashtable "weak hashtables unsupported"))
71+
(if hash
72+
(let ((hash (if (pair? hash) (car hash) hash))) ;; why? - read spec
73+
(if weakness
74+
(cond
75+
((memq weakness (weak-hashtables-supported))
76+
(if capacity
77+
((make-weak-hashtable-procedure weakness) hash equiv
78+
capacity)
79+
((make-weak-hashtable-procedure weakness) hash equiv)))
80+
((memq weakness (ephemeral-hashtables-supported))
81+
(if capacity
82+
((make-ephemeral-hashtable-procedure weakness) hash equiv
83+
capacity)
84+
((make-ephemeral-hashtable-procedure weakness) hash equiv)))
85+
(else (error 'make-hashtable "weakness not supported" weakness)))
5886
(if capacity
59-
(make-weak-hashtable hash equiv capacity)
60-
(make-weak-hashtable hash equiv)))
61-
(if capacity
62-
(rnrs:make-hashtable hash equiv capacity)
63-
(rnrs:make-hashtable hash equiv)))))))))
87+
(rnrs:make-hashtable hash equiv capacity)
88+
(rnrs:make-hashtable hash equiv))))
89+
(cond ; hash function not provided
90+
((eq? equiv eq?)
91+
(make-eq-hashtable capacity weakness))
92+
((eq? equiv eqv?)
93+
(make-eqv-hashtable capacity weakness))
94+
(else (error 'make-hashtable
95+
"hash procedure cannot be #f except with eq? or eqv?"
96+
hash equiv)))))))
6497

6598
(define (alist->eq-hashtable . args)
6699
(apply alist->hashtable #f eq? args))
@@ -121,25 +154,31 @@
121154

122155
(define hashtable-update!
123156
(case-lambda
124-
((hashtable key proc) (hashtable-update! hashtable key proc nil))
157+
((hashtable key proc)
158+
(rnrs:hashtable-update! hashtable key
159+
(lambda (value)
160+
(if (nil? value)
161+
(error "No such key in hashtable."
162+
hashtable key)
163+
(proc value)))
164+
nil))
125165
((hashtable key proc default)
126-
(rnrs:hashtable-update!
127-
hashtable key
128-
(lambda (value)
129-
(if (nil? value)
130-
(error "No such key in hashtable." hashtable key)
131-
(proc value)))
132-
default))))
166+
(rnrs:hashtable-update! hashtable key proc default))))
133167

134-
;;; XXX This could be implemented at the platform level to eliminate the second
135-
;;; lookup for the key.
136168
(define (hashtable-intern! hashtable key default-proc)
137-
(let ((value (hashtable-ref hashtable key nil)))
138-
(if (nil? value)
139-
(let ((value (default-proc)))
140-
(hashtable-set! hashtable key value)
141-
value)
142-
value)))
169+
(if (hashtable-cell-support)
170+
(let ((cell (hashtable-cell hashtable key nil)))
171+
(if (nil? (hashtable-cell-value cell))
172+
(let ((value (default-proc)))
173+
(set-hashtable-cell-value! cell value)
174+
value)
175+
(hashtable-cell-value cell)))
176+
(let ((value (rnrs:hashtable-ref hashtable key nil)))
177+
(if (nil? value)
178+
(let ((value (default-proc)))
179+
(hashtable-set! hashtable key value)
180+
value)
181+
value))))
143182

144183
(define hashtable-copy
145184
(case-lambda
@@ -171,9 +210,11 @@
171210

172211
#;(define hashtable-keys rnrs:hashtable-keys)
173212

174-
(define (hashtable-values hashtable)
175-
(let-values (((keys values) (hashtable-entries hashtable)))
176-
values))
213+
;;; Defined in helpers.sls
214+
215+
;; (define (hashtable-values hashtable)
216+
;; (let-values (((keys values) (hashtable-entries hashtable)))
217+
;; values))
177218

178219
#;(define hashtable-entries rnrs:hashtable-entries)
179220

@@ -215,19 +256,30 @@
215256
keys values)))
216257

217258
(define (hashtable-merge! hashtable-dest hashtable-source)
218-
(hashtable-walk hashtable-source
219-
(lambda (key value)
220-
(hashtable-set! hashtable-dest key value)))
259+
(let-values (((keys values) (hashtable-entries hashtable-source)))
260+
(vector-for-each (lambda (key value)
261+
(hashtable-set! hashtable-dest key value))
262+
keys values))
221263
hashtable-dest)
222264

223265
(define (hashtable-sum hashtable init proc)
224-
(let-values (((keys vals) (hashtable-entry-lists hashtable)))
225-
(fold proc init keys vals)))
266+
(let-values (((keys vals) (hashtable-entries hashtable)))
267+
(let ((size (vector-length keys)))
268+
(let loop ((i 0) (result init))
269+
(if (fx>=? i size)
270+
result
271+
(loop (fx+ i 1) (proc (vector-ref keys i)
272+
(vector-ref vals i)
273+
result)))))))
226274

227275
(define (hashtable-map->lset hashtable proc)
228-
(hashtable-sum hashtable '()
229-
(lambda (key value accumulator)
230-
(cons (proc key value) accumulator))))
276+
(let-values (((keys vals) (hashtable-entries hashtable)))
277+
(let ((size (vector-length keys)))
278+
(let loop ((i 0) (accumulator '()))
279+
(if (fx>=? i size)
280+
accumulator
281+
(loop (fx+ i 1) (cons (proc (vector-ref keys i) (vector-ref vals i))
282+
accumulator)))))))
231283

232284
;;; XXX If available, let-escape-continuation might be more efficient than
233285
;;; call/cc here.
@@ -241,7 +293,7 @@
241293
(return #f #f #f))))
242294

243295
(define (hashtable-empty? hashtable)
244-
(zero? (hashtable-size hashtable)))
296+
(fxzero? (hashtable-size hashtable)))
245297

246298
;;; XXX A platform-level implementation could avoid allocating the constant true
247299
;;; function and the lookup for the key in the delete operation.
@@ -269,20 +321,16 @@
269321

270322
#;(define hashtable-hash-function rnrs-hashtable-hash-function)
271323

272-
(define (hashtable-weakness hashtable) #f)
324+
;;; Defined in helpers.sls
325+
#;(define (hashtable-weakness hashtable) #f)
273326

274327
#;(define hashtable-mutable? rnrs-hashtable-mutable?)
275328

276329
(define *hash-salt*
277330
(let ((seed (get-environment-variable "SRFI_126_HASH_SEED")))
278331
(if (or (not seed) (string=? seed ""))
279332
(random-integer (greatest-fixnum))
280-
(modulo
281-
(fold (lambda (char result)
282-
(+ (char->integer char) result))
283-
0
284-
(string->list seed))
285-
(greatest-fixnum)))))
333+
(mod (string-hash seed) (greatest-fixnum)))))
286334

287335
(define (hash-salt) *hash-salt*)
288336

%3a126/helpers/helpers.chezscheme.sls

+121-11
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,121 @@
1-
(library (srfi :126 r6rs-hashtables helpers)
2-
(export make-weak-eq-hashtable weak-eq-hashtables-supported
3-
make-weak-eqv-hashtable weak-eqv-hashtables-supported
4-
make-weak-hashtable weak-hashtables-supported)
5-
(import (rnrs)
6-
(only (chezscheme) make-weak-eq-hashtable make-weak-eqv-hashtable make-parameter))
7-
(define weak-eq-hashtables-supported (make-parameter #t))
8-
(define weak-eqv-hashtables-supported (make-parameter #t))
9-
(define (make-weak-hashtable . args)
10-
(error 'make-weak-eq-hashtable "weak hashtables not supported"))
11-
(define weak-hashtables-supported (make-parameter #f)))
1+
(library (srfi :126 helpers helpers)
2+
(export make-weak-eq-hashtable-procedure weak-eq-hashtables-supported
3+
make-weak-eqv-hashtable-procedure weak-eqv-hashtables-supported
4+
make-weak-hashtable-procedure weak-hashtables-supported
5+
make-ephemeral-eq-hashtable-procedure ephemeral-eq-hashtables-supported
6+
make-ephemeral-eqv-hashtable-procedure
7+
ephemeral-eqv-hashtables-supported
8+
make-ephemeral-hashtable-procedure ephemeral-hashtables-supported
9+
hashtable-values hashtable-weakness
10+
hashtable-cell-support hashtable-cell hashtable-cell-key
11+
hashtable-cell-value set-hashtable-cell-value!
12+
get-environment-variable random-integer)
13+
(import (rename (chezscheme)
14+
(getenv get-environment-variable)))
15+
16+
(define (make-weak-eq-hashtable-procedure weakness)
17+
(if (eq? weakness 'weak-key)
18+
make-weak-eq-hashtable
19+
(error 'make-weak-eq-hashtable "weakness not supported"
20+
weakness)))
21+
22+
(define-syntax weak-eq-hashtables-supported
23+
(syntax-rules ()
24+
((weak-eq-hashtables-supported) '(weak-key))))
25+
26+
(define (make-weak-eqv-hashtable-procedure weakness)
27+
(if (eq? weakness 'weak-key)
28+
make-weak-eqv-hashtable
29+
(error 'make-weak-eqv-hashtable "weakness not supported"
30+
weakness)))
31+
32+
(define-syntax weak-eqv-hashtables-supported
33+
(syntax-rules ()
34+
((weak-eqv-hashtables-supported) '(weak-key))))
35+
36+
(define (make-weak-hashtable-procedure weakness)
37+
(error 'make-weak-hashtable "weak hashtables not supported"))
38+
39+
(define-syntax weak-hashtables-supported
40+
(syntax-rules ()
41+
((weak-hashtables-supported) '())))
42+
43+
(meta-cond
44+
((let-values (((major minor sub-minor) (scheme-version-number)))
45+
(or (> major 9)
46+
(and (= major 9) (>= minor 5))))
47+
;; has ephemeral eq- and eqv-hashtables
48+
49+
(define (make-ephemeral-eq-hashtable-procedure weakness)
50+
(if (eq? weakness 'ephemeral-key)
51+
make-ephemeron-eq-hashtable
52+
(error 'make-ephemeral-eq-hashtable "weakness not supported"
53+
weakness)))
54+
55+
(define-syntax ephemeral-eq-hashtables-supported
56+
(syntax-rules ()
57+
((ephemeral-eq-hashtables-supported) '(ephemeral-key))))
58+
59+
(define (make-ephemeral-eqv-hashtable-procedure weakness)
60+
(if (eq? weakness 'ephemeral-key)
61+
make-ephemeron-eqv-hashtable
62+
(error 'make-ephemeral-eqv-hashtable "weakness not supported"
63+
weakness)))
64+
65+
(define-syntax ephemeral-eqv-hashtables-supported
66+
(syntax-rules ()
67+
((ephemeral-eqv-hashtables-supported) '(ephemeral-key))))
68+
69+
(define (make-ephemeral-hashtable-procedure weakness)
70+
(error 'make-ephemeral-hashtable "weakness not supported" weakness))
71+
72+
(define-syntax ephemeral-hashtables-supported
73+
(syntax-rules ()
74+
((ephemeral-hashtables-supported) '(ephemeral-key))))
75+
76+
(define (hashtable-weakness hashtable)
77+
(cond ((hashtable-weak? hashtable) 'weak-key)
78+
((hashtable-ephemeron? hashtable) 'ephemeral-key)
79+
(else #f))))
80+
81+
(else ; no ephemeral hashtables
82+
(define (make-ephemeral-eq-hashtable-procedure weakness)
83+
(error 'make-ephemeral-hashtable "ephemeral eq hashtables not supported"))
84+
85+
(define-syntax ephemeral-eq-hashtables-supported
86+
(syntax-rules ()
87+
((ephemeral-eq-hashtables-supported) '())))
88+
89+
(define (make-ephemeral-eqv-hashtable-procedure weakness)
90+
(error 'make-ephemeral-hashtable "ephemeral eqv hashtables not supported"))
91+
92+
(define-syntax ephemeral-eqv-hashtables-supported
93+
(syntax-rules ()
94+
((ephemeral-eqv-hashtables-supported) '())))
95+
96+
(define (make-ephemeral-hashtable-procedure weakness)
97+
(error 'make-ephemeral-hashtable "ephemeral hashtables not supported"))
98+
99+
(define-syntax ephemeral-hashtables-supported
100+
(syntax-rules ()
101+
((ephemeral-hashtables-supported) '())))
102+
103+
(define (hashtable-weakness hashtable)
104+
(cond ((hashtable-weak? hashtable) 'weak-key)
105+
(else #f)))))
106+
107+
;; Support for hashtable cells
108+
109+
(define-syntax hashtable-cell-support
110+
(syntax-rules ()
111+
((hashtable-cell-support) #t)))
112+
113+
(define hashtable-cell-key car)
114+
(define hashtable-cell-value cdr)
115+
(define set-hashtable-cell-value! set-cdr!)
116+
117+
(define (random-integer seed)
118+
(fxmod (fxxor (random seed)
119+
(fx* 3 (fxdiv (random (time-nanosecond (current-time)))
120+
4)))
121+
seed)))

0 commit comments

Comments
 (0)