;;;; Dyn maps and keywords: the literal, the operations, the printing, and a ;;;; collection loop that outruns the GC floor. ;;;; ;;;; Every value here is dyn. {:a 1} is a heap map the collector traces; :a is ;;;; an interned keyword, so two spellings of one name are the same word and ;;;; equality never reads the bytes; [1 2] where a dyn is wanted is the ;;;; runtime's own vec. The printed forms are the dyn renderer's — a space ;;;; before every element, strings quoted when nested — pinned here across ;;;; both backends the way test/dyn_ops.c pins them from C. ;; A dyn global: rooted once at startup, so what main stores in it survives ;; every collection the churn loop below causes. (defvar config dyn) (defn main [] i32 ;; The literal, and what it prints as. (let [m {:a 1 :b "two" :xs [1 2 3] :inner {:c 2.5}}] (println m) (println (len m)) (println (get m :a)) (println (get m :b)) (println (get m :xs)) (println (get (get m :inner) :c)) ;; Absence is nil — an answer, not a trap — and has-key? is the question ;; that stays askable when nil might also be stored. (println (get m :missing)) (println (= (get m :missing) nil)) (println (has-key? m :a)) (println (has-key? m :missing)) (put m :flag nil) (println (get m :flag)) (println (has-key? m :flag)) ;; put replaces an equal key's value in place; the length holds still. (put m :a 99) (println (get m :a)) (println (len m)) ;; Keywords: identity equality, printing, and the runtime constructor — ;; (keyword "a") has to be the same word as the literal :a. (println (= :a :a)) (println (= :a :b)) (println (= :a "a")) (println (= (keyword "a") :a)) (println :standalone) ;; Keys are whole values compared structurally: a text and a keyword are ;; two keys, and a vec of numbers can key a map. (put m "a" "text key") (println (len m)) (println (get m "a")) (put m [1 2] "vec key") (println (get m [1 2])) ;; Maps compare structurally, by lookup and not by insertion order. (println (= {:x 1 :y 2} {:y 2 :x 1})) (println (= {:x 1} {:x 2})) (println (= {:x 1} {:x 1 :y 2}))) ;; The churn: enough map, vec and text allocation to pass the 1 MiB floor ;; many times over, against one live map held in a rooted global. A marker ;; that lost track of a map's keys or values frees something live, and the ;; sum at the end comes out wrong — or ASan speaks, in the sanitize sweep. (set config {:total 0}) (dotimes [i 200000] (let [row {:i 1 :s "forty-seven bytes of text to fatten each row" :v [1 2 3]}] (put config :total (+ (get config :total) (len row))))) (println (get config :total)) 0)