71 lines
2.7 KiB
Plaintext
71 lines
2.7 KiB
Plaintext
;;;; 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.
|
|
(defonce 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 (length 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 (length 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 (length 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) (length row)))))
|
|
(println (get config :total))
|
|
0)
|