;; Walking a map, which is what flan_map_next exists for. Every other map ;; operation addresses one entry by hashing it; this is the only thing that ;; reads the block in order. ;; ;; Block order is the hash's order and not the insertion's, so nothing here ;; may depend on which entry comes first: the checks are a sum, a count and a ;; membership test, all of them order-free. That is not a weakness of the test, ;; it is the contract — a caller that wants an order sorts what it collected. (defn sum-and-count [] () (let [m (map-new i32 i32)] (put m 1 10) (put m 2 20) (put m 3 30) (put m 4 40) (let [cur (i64 0) k 0 v 0 keys 0 vals 0 n 0] (while (map-next m (addr cur) (addr k) (addr v)) (set keys (+ keys k)) (set vals (+ vals v)) (set n (+ n 1))) (print n) (print " ") (print keys) (print " ") (print vals) (println "")) (free m))) ;; A map that never allocated has no block at all, and one that allocated and ;; holds nothing has a block of nothing but zeroed hashes. Both walk zero ;; times, and they are different code paths to get there. (defn the-empty-cases [] () (let [m (map-new i32 i32) cur (i64 0) k 0 v 0 n 0] (while (map-next m (addr cur) (addr k) (addr v)) (set n (+ n 1))) (print "never allocated: ") (print n) (println "") (reserve m 64) (set cur (i64 0)) (while (map-next m (addr cur) (addr k) (addr v)) (set n (+ n 1))) (print "allocated and empty: ") (print n) (println "") (free m))) ;; A cursor left past the end keeps answering false rather than wrapping, so a ;; second loop over a spent cursor is empty and not a repeat. (defn a-spent-cursor [] () (let [m (map-new i32 i32)] (put m 5 50) (put m 6 60) (let [cur (i64 0) k 0 v 0 n 0] (while (map-next m (addr cur) (addr k) (addr v)) (set n (+ n 1))) (while (map-next m (addr cur) (addr k) (addr v)) (set n (+ n 1))) (print "spent: ") (print n) (println "")) (free m))) ;; A string key and a struct value: the key run and the value run have ;; different element sizes and different cell packing, so this is the case that ;; would catch the two runs being indexed with one geometry. (defstruct Point [x i32 y i32]) (defn wider-entries [] () (let [m (map-new string Point)] (put m "a" (Point {.x 1 .y 2})) (put m "bb" (Point {.x 3 .y 4})) (put m "ccc" (Point {.x 5 .y 6})) (let [cur (i64 0) k "" v (Point {}) chars 0 xs 0 ys 0] (while (map-next m (addr cur) (addr k) (addr v)) (set chars (+ chars (len k))) (set xs (+ xs (.x v))) (set ys (+ ys (.y v)))) (print chars) (print " ") (print xs) (print " ") (print ys) (println "")) (free m))) ;; Growth past the 75% threshold rehashes into a new block, so this walks a map ;; whose layout is nothing like its insertion order and at a capacity several ;; doublings past the minimum. (defn after-growth [] () (let [m (map-new i64 i64)] (dotimes [i 500] (put m (i64 i) (* (i64 i) 2))) (let [cur (i64 0) k (i64 0) v (i64 0) n 0 doubled 0] (while (map-next m (addr cur) (addr k) (addr v)) (set n (+ n 1)) (when (= v (* k 2)) (set doubled (+ doubled 1)))) (print n) (print " ") (print doubled) (print " ") (print (len m)) (println "")) (free m))) (defn main [] i32 (sum-and-count) (the-empty-cases) (a-spent-cursor) (wider-entries) (after-growth) 0)