Merge pull request #114 from jolt-lang/refactor-phase3c-dedups

Refactor phase 3c: dedup collection readers + phm bucket scans (jolt-26dm)
This commit is contained in:
Dmitri Sotnikov 2026-06-15 07:15:44 +00:00 committed by GitHub
commit 7632be811b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 38 additions and 58 deletions

View file

@ -39,25 +39,26 @@
(defn- hash-idx [m k] (defn- hash-idx [m k]
(if (nil? k) 0 (mod (hash (ck k)) (length (m :buckets))))) (if (nil? k) 0 (mod (hash (ck k)) (length (m :buckets)))))
(defn- phm-bucket-find [bucket k] # Index of key k in a bucket (entries are stored stride-2: k v k v ...), or nil.
(var i 0) (var n (length bucket)) (var found nil) # The single scan all the bucket ops share — keeping it one place stops the
# stride logic drifting between them.
(defn- bucket-index-of [bucket k]
(var i 0) (def n (length bucket)) (var found nil)
(while (< i n) (while (< i n)
(if (key= k (in bucket i)) (do (set found (in bucket (+ i 1))) (break))) (if (key= k (in bucket i)) (do (set found i) (break)))
(+= i 2)) (+= i 2))
found) found)
(defn- phm-bucket-find [bucket k]
(let [i (bucket-index-of bucket k)]
(if i (in bucket (+ i 1)) nil)))
(defn phm-bucket-contains? [bucket k] (defn phm-bucket-contains? [bucket k]
(var i 0) (var n (length bucket)) (var found false) (not (nil? (bucket-index-of bucket k))))
(while (< i n)
(if (key= k (in bucket i)) (do (set found true) (break)))
(+= i 2))
found)
(defn- phm-bucket-assoc [bucket k v] (defn- phm-bucket-assoc [bucket k v]
(var i 0) (var n (length bucket)) (var found-i nil) (def n (length bucket))
(while (< i n) (def found-i (bucket-index-of bucket k))
(if (key= k (in bucket i)) (do (set found-i i) (break)))
(+= i 2))
(if (not (nil? found-i)) (if (not (nil? found-i))
(let [nb @[]] (var j 0) (let [nb @[]] (var j 0)
(while (< j n) (array/push nb (if (= j (+ found-i 1)) v (in bucket j))) (++ j)) nb) (while (< j n) (array/push nb (if (= j (+ found-i 1)) v (in bucket j))) (++ j)) nb)
@ -66,10 +67,8 @@
(array/push nb k) (array/push nb v) nb))) (array/push nb k) (array/push nb v) nb)))
(defn- phm-bucket-dissoc [bucket k] (defn- phm-bucket-dissoc [bucket k]
(var i 0) (var n (length bucket)) (var found-i nil) (def n (length bucket))
(while (< i n) (def found-i (bucket-index-of bucket k))
(if (key= k (in bucket i)) (do (set found-i i) (break)))
(+= i 2))
(if (nil? found-i) bucket (if (nil? found-i) bucket
(if (= n 2) nil (if (= n 2) nil
(let [nb @[]] (var j 0) (let [nb @[]] (var j 0)
@ -82,12 +81,8 @@
# Single pass with a presence flag (not nil-of-value): a key mapped to nil # Single pass with a presence flag (not nil-of-value): a key mapped to nil
# is still present, so return nil (not the default) when it exists. # is still present, so return nil (not the default) when it exists.
(if bucket (if bucket
(do (let [i (bucket-index-of bucket k)]
(var i 0) (var n (length bucket)) (var result default) (if i (in bucket (+ i 1)) default))
(while (< i n)
(if (key= k (in bucket i)) (do (set result (in bucket (+ i 1))) (break)))
(+= i 2))
result)
default))) default)))
# Rehash every entry of `buckets` into a fresh array of `nb` buckets. # Rehash every entry of `buckets` into a fresh array of `nb` buckets.

View file

@ -298,39 +298,35 @@
(+ exp-end 1) exp-end)] (+ exp-end 1) exp-end)]
[(if neg (- val) val) fin])))))) [(if neg (- val) val) fin]))))))
(defn read-list [s pos] # Read forms until `close` (a byte), returning [items end-pos]. Shared by the
# pos is at opening paren # list/vector/set readers: skip #_ discards and splice #?@ items uniformly, so the
(defn read-list-items [s pos items] # skip/splice handling can't drift between them (it did once). The map reader
# keeps its own loop — its key/value pairing needs a different value-slot scan.
(defn read-delimited [s start-pos close err]
(defn read-items [s pos items]
(let [pos (skip-whitespace s pos)] (let [pos (skip-whitespace s pos)]
(if (>= pos (length s)) (if (>= pos (length s))
(reader-error "Unterminated list" pos)) (reader-error err pos))
(if (= (s pos) 41) # ) (if (= (s pos) close)
[items (+ pos 1)] [items (+ pos 1)]
(let [[form new-pos] (read-form s pos)] (let [[form new-pos] (read-form s pos)]
# skip #_ discarded forms # skip #_ discarded forms
(if (and (struct? form) (= :jolt/skip (form :jolt/type))) (if (and (struct? form) (= :jolt/skip (form :jolt/type)))
(read-list-items s new-pos items) (read-items s new-pos items)
# splice #?@ items into the list # splice #?@ items into the collection
(if (and (struct? form) (= :jolt/splice (form :jolt/type))) (if (and (struct? form) (= :jolt/splice (form :jolt/type)))
(read-list-items s new-pos (array/concat items (form :items))) (read-items s new-pos (array/concat items (form :items)))
(read-list-items s new-pos (array/push items form)))))))) (read-items s new-pos (array/push items form))))))))
(read-list-items s (+ pos 1) @[])) (read-items s start-pos @[]))
(defn read-list [s pos]
# pos is at opening paren
(read-delimited s (+ pos 1) 41 "Unterminated list")) # )
(defn read-vector [s pos] (defn read-vector [s pos]
# pos is at opening bracket # pos is at opening bracket
(defn read-vec-items [s pos items] (let [[items end] (read-delimited s (+ pos 1) 93 "Unterminated vector")] # ]
(let [pos (skip-whitespace s pos)] [(tuple/slice (tuple ;items)) end]))
(if (>= pos (length s))
(reader-error "Unterminated vector" pos))
(if (= (s pos) 93) # ]
[(tuple/slice (tuple ;items)) (+ pos 1)]
(let [[form new-pos] (read-form s pos)]
(if (and (struct? form) (= :jolt/skip (form :jolt/type)))
(read-vec-items s new-pos items)
(if (and (struct? form) (= :jolt/splice (form :jolt/type)))
(read-vec-items s new-pos (array/concat items (form :items)))
(read-vec-items s new-pos (array/push items form))))))))
(read-vec-items s (+ pos 1) @[]))
# A map-literal form. Janet structs drop nil keys/values, so when a key or value # A map-literal form. Janet structs drop nil keys/values, so when a key or value
# is nil (e.g. {:a nil}) build a phm — it preserves nil, matching Clojure. The # is nil (e.g. {:a nil}) build a phm — it preserves nil, matching Clojure. The
@ -400,19 +396,8 @@
(defn read-set [s pos] (defn read-set [s pos]
# pos is at #, next char is { # pos is at #, next char is {
(defn read-set-items [s pos items] (let [[items end] (read-delimited s (+ pos 2) 125 "Unterminated set")] # }
(let [pos (skip-whitespace s pos)] [{:jolt/type :jolt/set :value (tuple/slice (tuple ;items))} end]))
(if (>= pos (length s))
(reader-error "Unterminated set" pos))
(if (= (s pos) 125) # }
[{:jolt/type :jolt/set :value (tuple/slice (tuple ;items))} (+ pos 1)]
(let [[form new-pos] (read-form s pos)]
(if (and (struct? form) (= :jolt/skip (form :jolt/type)))
(read-set-items s new-pos items)
(if (and (struct? form) (= :jolt/splice (form :jolt/type)))
(read-set-items s new-pos (array/concat items (form :items)))
(read-set-items s new-pos (array/push items form))))))))
(read-set-items s (+ pos 2) @[]))
(defn read-char-name-end [s pos] (defn read-char-name-end [s pos]
(if (and (< pos (length s)) (symbol-char? (s pos))) (if (and (< pos (length s)) (symbol-char? (s pos)))