Skip to content

Commit 1b0cc0a

Browse files
committed
Faster path conflict resolution, O(n2) -> O(n)
1 parent b128a0f commit 1b0cc0a

File tree

3 files changed

+22
-18
lines changed

3 files changed

+22
-18
lines changed

modules/reitit-core/src/reitit/impl.cljc

+9-8
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,15 @@
7474
coerce (into [] (keep #(coerce % opts)))))
7575

7676
(defn path-conflicting-routes [routes opts]
77-
(-> (into {}
78-
(comp (map-indexed (fn [index route]
79-
[route (into #{}
80-
(filter #(trie/conflicting-paths? (first route) (first %) opts))
81-
(subvec routes (inc index)))]))
82-
(filter (comp seq second)))
83-
routes)
84-
(not-empty)))
77+
(let [parts-and-routes (mapv (fn [[s :as r]] [(trie/split-path s opts) r]) routes)]
78+
(-> (into {} (comp (map-indexed (fn [index [p r]]
79+
[r (reduce
80+
(fn [acc [p' r']]
81+
(if (trie/conflicting-parts? p p')
82+
(conj acc r') acc))
83+
#{} (subvec parts-and-routes (inc index)))]))
84+
(filter (comp seq second))) parts-and-routes)
85+
(not-empty))))
8586

8687
(defn unresolved-conflicts [path-conflicting]
8788
(-> (into {}

modules/reitit-core/src/reitit/trie.cljc

+11-10
Original file line numberDiff line numberDiff line change
@@ -132,17 +132,18 @@
132132
(concat [(subs x i)] xs)
133133
xs)))
134134

135+
(defn conflicting-parts? [parts1 parts2]
136+
(let [[[s1 & ss1] [s2 & ss2]] (-slice-start parts1 parts2)]
137+
(cond
138+
(= s1 s2 nil) true
139+
(or (nil? s1) (nil? s2)) false
140+
(or (catch-all? s1) (catch-all? s2)) true
141+
(or (wild? s1) (wild? s2)) (recur (-slice-end s1 ss1) (-slice-end s2 ss2))
142+
(not= s1 s2) false
143+
:else (recur ss1 ss2))))
144+
135145
(defn conflicting-paths? [path1 path2 opts]
136-
(loop [parts1 (split-path path1 opts)
137-
parts2 (split-path path2 opts)]
138-
(let [[[s1 & ss1] [s2 & ss2]] (-slice-start parts1 parts2)]
139-
(cond
140-
(= s1 s2 nil) true
141-
(or (nil? s1) (nil? s2)) false
142-
(or (catch-all? s1) (catch-all? s2)) true
143-
(or (wild? s1) (wild? s2)) (recur (-slice-end s1 ss1) (-slice-end s2 ss2))
144-
(not= s1 s2) false
145-
:else (recur ss1 ss2)))))
146+
(conflicting-parts? (split-path path1 opts) (split-path path2 opts)))
146147

147148
;;
148149
;; Creating Tries

perf-test/clj/reitit/router_creation_perf_test.clj

+2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
(suite "non-conflicting")
4141

4242
;; 104ms
43+
;; 11ms (reuse parts in conflict resolution)
4344
(bench! "default" (r/router hundred-routes))
4445

4546
;; 7ms
@@ -50,6 +51,7 @@
5051

5152
;; 205ms
5253
;; 105ms (cache path-conflicts)
54+
;; 13ms (reuse parts in conflict resolution)
5355
(bench! "default" (r/router routes {:conflicts nil}))))
5456

5557
(comment

0 commit comments

Comments
 (0)