;;;; (map-remove m k) — the operation a Map has been missing. ;;;; ;;;; Removal is the one map operation that can break the *other* ones: a ;;;; lookup stops at the first group of slots holding an empty one, so a slot ;;;; emptied in the middle of a probe sequence hides every entry placed past ;;;; it, and the entries it hides are found by no test that only removes and ;;;; asks about what it removed. So the rows below are mostly about the ;;;; survivors. ;;;; ;;;; A removed slot is marked deleted, and emptied outright only where no probe ;;;; can have passed through it. A version that emptied every removed slot ;;;; passes row 1 and fails row 3; a version whose deleted slots were never ;;;; swept grows without end under row 7. (defstruct Cell [x i32 y i32]) (defn main [] i32 ;; (1) The value comes back, the length drops, and the key is gone. An ;; absent key is None and changes nothing — the same answer get gives, since ;; a key that was not there is an answer and not a failure. (let [m (map-new i32 i64)] (put m 1 100) (put m 2 200) (match (map-remove m 1) (Some v) (do (print v) (println "")) ; 100 None (println "missing")) (print (length m)) (println "") ; 1 (print (has-key? m 1)) (println "") ; false (match (map-remove m 1) (Some v) (do (print v) (println "")) None (println "gone")) (println (length m)) ; gone, then 1 (free m)) ;; (2) Removing every entry empties the map, and it is usable afterwards: ;; the block is still there and the slots are empty rather than poisoned. (let [m (map-new i32 i32)] (dotimes [i 64] (put m i i)) (dotimes [i 64] (map-remove m i)) (print (length m)) (println "") ; 0 (put m 7 77) (match (get m 7) (Some v) (do (print v) (println "")) None (println "?")) ; 77 (print (length m)) (println "") ; 1 (free m)) ;; (3) The survivors, which is the row that matters. 2000 entries is eight ;; grows, so the runs are long and interleaved; removing the even keys and ;; then asking after every odd one is asking whether any probe sequence was ;; cut. (let [m (map-new i32 i64)] (dotimes [i 2000] (put m i (* (i64 i) 3))) (let [taken 0] (dotimes [i 2000] (if (= 0 (% i 2)) (match (map-remove m i) (Some v) (if (= v (* (i64 i) 3)) (set taken (+ taken 1))) None (set taken taken)))) (print taken) (println "")) ; 1000 (print (length m)) (println "") ; 1000 (let [lost 0 ghosts 0] (dotimes [i 2000] (match (get m i) (Some v) (if (or (= 0 (% i 2)) (not (= v (* (i64 i) 3)))) (set ghosts (+ ghosts 1))) None (if (not (= 0 (% i 2))) (set lost (+ lost 1))))) (print lost) (println "") ; 0 (print ghosts) (println "")) ; 0 ;; Re-inserting what was taken out puts the length back, through no grow: ;; the block still has the room the removals freed up. (dotimes [i 2000] (if (= 0 (% i 2)) (put m i (* (i64 i) 3)))) (print (length m)) (println "") ; 2000 (free m)) ;; (4) A struct key and a string key, so the emitted hash/equality pair is ;; on the removal path too and not only on get's. (let [g (map-new Cell i32)] (dotimes [i 20] (dotimes [j 20] (put g (Cell {.x i .y j}) (+ (* i 100) j)))) (match (map-remove g (Cell {.x 7 .y 9})) (Some v) (do (print v) (println "")) ; 709 None (println "?")) (print (has-key? g (Cell {.x 7 .y 9}))) (println "") ; false (print (has-key? g (Cell {.x 7 .y 10}))) (println "") ; true (print (length g)) (println "") ; 399 (free g)) (let [s (map-new string i32)] (put s "alpha" 1) (put s "beta" 2) (match (map-remove s "alpha") (Some v) (do (print v) (println "")) ; 1 None (println "?")) (print (has-key? s "beta")) (println "") ; true (print (length s)) (println "") ; 1 (free s)) ;; (5) Iteration after removals answers exactly the survivors. (let [m (map-new i32 i32)] (dotimes [i 100] (put m i i)) (dotimes [i 100] (if (= 0 (% i 3)) (map-remove m i))) (let [cur (i64 0) k 0 v 0 seen 0 sum 0] (while (map-next m (addr cur) (addr k) (addr v)) (set seen (+ seen 1)) (if (not (= k v)) (set sum (+ sum 1)))) (print seen) (println "") ; 66 (print sum) (println "")) ; 0 (print (length m)) (println "") ; 66 (free m)) ;; (6) An arena, which refuses can-free. Removal asks the allocator for ;; nothing and hands it back nothing — a key and a value live inside the one ;; block the map allocated — so it means here exactly what it means on the ;; heap, and the region is released whole as always. (let [ar (arena-new 1048576)] (with-allocator ar (let [t (map-new i32 i32)] (dotimes [i 300] (put t i (* i 2))) (dotimes [i 300] (if (= 0 (% i 2)) (map-remove t i))) (print (length t)) (println "") ; 150 (match (get t 299) (Some v) (do (print v) (println "")) None (println "?")) ; 598 (print (has-key? t 298)) (println ""))) ; false (free-all ar)) ;; (7) Churn at a steady size, checked against a plain array. Keys are drawn ;; from 512 and the map hovers around half of them, so removals leave deleted ;; slots that inserts must reuse and rebuilds must sweep. Every get is checked ;; against the array, and at the end so is the length and a full walk. (let [m (map-new i32 i64) ref (vec-new i64) x (u64 12345) bad 0 live 0] (dotimes [i 512] (push ref (i64 -1))) (dotimes [step 200000] (set x (+ (* x (u64 6364136223846793005)) (u64 1442695040888963407))) (let [k (i32 (% (>> x 33) (u64 512))) op (i32 (% (>> x 20) (u64 4)))] (if (< op 2) (do (if (< (at ref k) 0) (set live (+ live 1))) (put m k (i64 step)) (set (at ref k) (i64 step))) (if (= op 2) (match (map-remove m k) (Some v) (do (if (not (= v (at ref k))) (set bad (+ bad 1))) (set (at ref k) (i64 -1)) (set live (- live 1))) None (if (>= (at ref k) 0) (set bad (+ bad 1)))) (match (get m k) (Some v) (if (not (= v (at ref k))) (set bad (+ bad 1))) None (if (>= (at ref k) 0) (set bad (+ bad 1)))))))) (print bad) (println "") ; 0 (print (= (length m) (i64 live))) (println "") ; true (let [cur (i64 0) k 0 v (i64 0) seen 0] (while (map-next m (addr cur) (addr k) (addr v)) (set seen (+ seen 1)) (if (not (= v (at ref k))) (set bad (+ bad 1)))) (print (= seen live)) (println "") ; true (print bad) (println "")) ; 0 ;; Removing while walking visits every survivor once: nothing moves. (let [cur (i64 0) k 0 v (i64 0) seen 0] (while (map-next m (addr cur) (addr k) (addr v)) (set seen (+ seen 1)) (map-remove m k)) (print (= seen live)) (println "") ; true (print (length m)) (println "")) ; 0 (free ref) (free m)) 0)