mirror of
https://github.com/metosin/reitit.git
synced 2025-12-16 16:01:11 +00:00
linear-router is backed by a segment-router
This commit is contained in:
parent
4e7963be91
commit
95ebdfa6a4
6 changed files with 40 additions and 50 deletions
|
|
@ -4,7 +4,8 @@
|
|||
### `reitit-core`
|
||||
|
||||
* `reitit.core/Expand` can be extended, fixes [#201](https://github.com/metosin/reitit/issues/201).
|
||||
* new snappy Java-backed `SegmentTrie` routing algorithm and data structure, making wildcard routing ~2x faster on the JVM
|
||||
* new snappy Java-backed `SegmentTrie` routing algorithm and data structure (for `reitit.core/segment-router`, making wildcard routing ~2x faster on the JVM
|
||||
* `reitit.core/linear-router` uses the segment router behind the scenes, 2-4x faster on the JVM too.
|
||||
|
||||
### `reitit-ring`
|
||||
|
||||
|
|
|
|||
|
|
@ -297,6 +297,10 @@ public class SegmentTrie {
|
|||
}
|
||||
}
|
||||
|
||||
public static Matcher scanner(List<Matcher> matchers) {
|
||||
return new LinearMatcher(matchers);
|
||||
}
|
||||
|
||||
public static Match lookup(Matcher matcher, String path) {
|
||||
return matcher.match(0, split(path), new Match());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@
|
|||
(mapv (comp vec (partial take 2)) routes))
|
||||
|
||||
(defn route-info [route]
|
||||
(select-keys (impl/create route) [:path :path-parts :path-params :result :data]))
|
||||
(impl/create route))
|
||||
|
||||
(defprotocol Router
|
||||
(router-name [this])
|
||||
|
|
@ -177,11 +177,12 @@
|
|||
f #(if-let [path (impl/path-for route %)]
|
||||
(->Match p data result (impl/url-decode-coll %) path)
|
||||
(->PartialMatch p data result % path-params))]
|
||||
[(conj pl route)
|
||||
[(conj pl (-> (segment/insert nil p (->Match p data result nil nil)) (segment/compile)))
|
||||
(if name (assoc nl name f) nl)]))
|
||||
[[] {}]
|
||||
compiled-routes)
|
||||
lookup (impl/fast-map nl)
|
||||
scanner (segment/scanner pl)
|
||||
routes (uncompile-routes compiled-routes)]
|
||||
^{:type ::router}
|
||||
(reify
|
||||
|
|
@ -197,11 +198,10 @@
|
|||
(route-names [_]
|
||||
names)
|
||||
(match-by-path [_ path]
|
||||
(reduce
|
||||
(fn [_ ^Route route]
|
||||
(if-let [path-params ((:matcher route) path)]
|
||||
(reduced (->Match (:path route) (:data route) (:result route) (impl/url-decode-coll path-params) path))))
|
||||
nil pl))
|
||||
(if-let [match (segment/lookup scanner path)]
|
||||
(-> (:data match)
|
||||
(assoc :path-params (:path-params match))
|
||||
(assoc :path path))))
|
||||
(match-by-name [_ name]
|
||||
(if-let [match (impl/fast-get lookup name)]
|
||||
(match nil)))
|
||||
|
|
|
|||
|
|
@ -66,18 +66,16 @@
|
|||
(let [key (keyword token)]
|
||||
(-> out
|
||||
(update-in [:path-parts] conj key)
|
||||
(update-in [:path-params] conj key)
|
||||
(assoc-in [:path-constraints key] "([^/]+)"))))
|
||||
(update-in [:path-params] conj key))))
|
||||
#"^\*(.*)$" :>> (fn [[_ token]]
|
||||
(let [key (keyword token)]
|
||||
(-> out
|
||||
(update-in [:path-parts] conj key)
|
||||
(update-in [:path-params] conj key)
|
||||
(assoc-in [:path-constraints key] "(.*)"))))
|
||||
(update-in [:path-params] conj key))))
|
||||
(update-in out [:path-parts] conj string)))
|
||||
|
||||
(defn- parse-path
|
||||
([pattern] (parse-path {:path-parts [] :path-params [] :path-constraints {}} pattern))
|
||||
([pattern] (parse-path {:path-parts [] :path-params #{}} pattern))
|
||||
([accumulated-info pattern]
|
||||
(if-let [m (re-matches #"/(.*)" pattern)]
|
||||
(let [[_ path] m]
|
||||
|
|
@ -86,45 +84,21 @@
|
|||
(str/split path #"/")))
|
||||
(throw (ex-info "Routes must start from the root, so they must begin with a '/'" {:pattern pattern})))))
|
||||
|
||||
;; TODO: is this correct?
|
||||
(defn- re-quote [x]
|
||||
#?(:clj (Pattern/quote x)
|
||||
:cljs (str/replace x #"([.?*+^$[\\]\\\\(){}|-])" "\\$1")))
|
||||
|
||||
(defn- path-regex [{:keys [path-parts path-constraints] :as route}]
|
||||
(let [[pp & pps] path-parts
|
||||
path-parts (if (and (seq pps) (string? pp) (empty? pp)) pps path-parts)]
|
||||
(re-pattern
|
||||
(apply str
|
||||
(interleave (repeat "/")
|
||||
(map #(or (get path-constraints %) (re-quote %))
|
||||
path-parts))))))
|
||||
|
||||
(defn- path-matcher [route]
|
||||
(let [{:keys [path-re path-params]} route]
|
||||
(fn [path]
|
||||
(when-let [m (re-matches path-re path)]
|
||||
(zipmap path-params (rest m))))))
|
||||
|
||||
;;
|
||||
;; Routing (c) Metosin
|
||||
;;
|
||||
|
||||
(defrecord Route [path matcher path-parts path-params data result])
|
||||
(defrecord Route [path path-parts path-params data result])
|
||||
|
||||
(defn create [[path data result]]
|
||||
(let [path #?(:clj (.intern ^String path) :cljs path)]
|
||||
(as-> (parse-path path) $
|
||||
(assoc $ :path-re (path-regex $))
|
||||
(merge $ {:path path
|
||||
:matcher (if (contains-wilds? path)
|
||||
(path-matcher $)
|
||||
#(if (#?(:clj .equals, :cljs =) path %) {}))
|
||||
:result result
|
||||
:data data})
|
||||
(dissoc $ :path-re :path-constraints)
|
||||
(update $ :path-params set)
|
||||
(map->Route $))))
|
||||
(let [path #?(:clj (.intern ^String path) :cljs path)
|
||||
{:keys [path-parts path-params]} (parse-path path)]
|
||||
(map->Route
|
||||
{:path-params path-params
|
||||
:path-parts path-parts
|
||||
:path path
|
||||
:result result
|
||||
:data data})))
|
||||
|
||||
(defn wild-route? [[path]]
|
||||
(contains-wilds? path))
|
||||
|
|
|
|||
|
|
@ -59,6 +59,11 @@
|
|||
#?(:cljs trie
|
||||
:clj (.matcher ^SegmentTrie (or trie (SegmentTrie.)))))
|
||||
|
||||
(defn scanner [compiled-tries]
|
||||
"Returns a new compiled trie that does linear scan on the given compiled tries on [[lookup]]."
|
||||
#?(:cljs (fn [path] (some (fn [trie] (lookup trie path)) compiled-tries))
|
||||
:clj (SegmentTrie/scanner compiled-tries)))
|
||||
|
||||
(defn lookup [trie path]
|
||||
"Looks the path from a Segment Trie. Returns a [[Match]] or `nil`."
|
||||
#?(:cljs (if-let [match (-lookup trie (impl/segments path) {})]
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@
|
|||
|
||||
[io.pedestal.http.route.definition.table :as table]
|
||||
[io.pedestal.http.route.map-tree :as map-tree]
|
||||
[io.pedestal.http.route.router :as pedestal]))
|
||||
[io.pedestal.http.route.router :as pedestal]
|
||||
[reitit.core :as r]))
|
||||
|
||||
;;
|
||||
;; start repl with `lein perf repl`
|
||||
|
|
@ -551,6 +552,7 @@
|
|||
router (reitit/router routes)
|
||||
reitit-f #(reitit/match-by-path router (:uri %))
|
||||
reitit-ring-f (ring/ring-handler (ring/router opensensors-routes))
|
||||
reitit-ring-linear-f (ring/ring-handler (ring/router opensensors-routes {:router r/linear-router}))
|
||||
reitit-ring-fast-f (ring/ring-handler (ring/router opensensors-routes) nil {:inject-router? false, :inject-match? false})
|
||||
bidi-f #(bidi/match-route opensensors-bidi-routes (:uri %))
|
||||
calfpath-macros-f opensensors-calfpath-macro-handler
|
||||
|
|
@ -579,19 +581,23 @@
|
|||
(b! "reitit-ring" reitit-ring-f)
|
||||
|
||||
;; 385ns (java-segment-router, no injects)
|
||||
(b! "reitit-ring-fast" reitit-ring-fast-f)
|
||||
#_(b! "reitit-ring-fast" reitit-ring-fast-f)
|
||||
|
||||
;; 2553ns (linear-router)
|
||||
;; 630ns (segment-router-backed)
|
||||
#_(b! "reitit-ring-linear" reitit-ring-linear-f)
|
||||
|
||||
;; 2137ns
|
||||
(b! "calfpath-walker" calfpath-walker-f)
|
||||
|
||||
;; 4774ns
|
||||
(b! "calfpath-unroll" calfpath-unroll-f)
|
||||
#_(b! "calfpath-unroll" calfpath-unroll-f)
|
||||
|
||||
;; 2821ns
|
||||
(b! "pedestal" pedestal-f)
|
||||
|
||||
;; 4803ns
|
||||
(b! "calfpath-macros" calfpath-macros-f)
|
||||
#_(b! "calfpath-macros" calfpath-macros-f)
|
||||
|
||||
;; 11615ns
|
||||
(b! "compojure" compojure-f)
|
||||
|
|
|
|||
Loading…
Reference in a new issue