cljs trie

This commit is contained in:
Tommi Reiman 2019-02-08 19:17:22 +02:00
parent 5b9f90d283
commit 54d5550fae

View file

@ -1,8 +1,8 @@
(ns reitit.trie (ns reitit.trie
(:refer-clojure :exclude [compile]) (:refer-clojure :exclude [compile -assoc!])
(:require [clojure.string :as str]) (:require [clojure.string :as str])
(:import [reitit Trie Trie$Match Trie$Matcher] #?(:clj (:import [reitit Trie Trie$Match Trie$Matcher]
(java.net URLDecoder))) (java.net URLDecoder))))
(defrecord Wild [value]) (defrecord Wild [value])
(defrecord CatchAll [value]) (defrecord CatchAll [value])
@ -13,7 +13,7 @@
(defn catch-all? [x] (instance? CatchAll x)) (defn catch-all? [x] (instance? CatchAll x))
(defprotocol Matcher (defprotocol Matcher
(match [this i max ^chars path]) (match [this i max path])
(view [this]) (view [this])
(depth [this])) (depth [this]))
@ -116,27 +116,27 @@
(update :children dissoc "")) (update :children dissoc ""))
node'))) node')))
(set! *warn-on-reflection* true)
(defn decode! (defn decode!
([chars start end] ([path start end]
(let [s (String. ^chars chars ^int start ^int (- end start))] #?(:clj (let [s (subs path start end)]
(if (str/index-of s \%) (if (str/index-of s \%)
(URLDecoder/decode (URLDecoder/decode
(if (str/index-of s \+) (.replace ^String s "+" "%2B") s) (if (str/index-of s \+) (.replace ^String s "+" "%2B") s)
"UTF-8") "UTF-8")
s))) s))
([chars start end percent? plus?] :cljs (js/decodeURIComponent (subs path start end))))
(let [s (String. ^chars chars ^int start ^int (- end start))] ([path start end percent? plus?]
(if percent? #?(:clj (let [s (String. ^chars path ^int start ^int (- end start))]
(URLDecoder/decode (if percent?
(if plus? (.replace ^String s "+" "%2B") s) (URLDecoder/decode
"UTF-8") (if plus? (.replace ^String s "+" "%2B") s)
s)))) "UTF-8")
s))
:cljs (js/decodeURIComponent (subs path start end)))))
(defn data-matcher [data] (defn data-matcher [data]
#?(:cljx (Trie/dataMatcher data) #?(:clj (Trie/dataMatcher data)
:clj (let [match (->Match data nil)] :cljs (let [match (->Match data nil)]
(reify Matcher (reify Matcher
(match [_ i max _] (match [_ i max _]
(if (= i max) (if (= i max)
@ -144,31 +144,30 @@
(view [_] data) (view [_] data)
(depth [_] 1))))) (depth [_] 1)))))
(defn static-matcher [^String path matcher] (defn static-matcher [path matcher]
#?(:cljx (Trie/staticMatcher path matcher) #?(:clj (Trie/staticMatcher ^String path ^Trie$Matcher matcher)
:clj (let [^chars chars (.toCharArray path) :cljs (let [size (count path)]
size (alength chars)]
(reify Matcher (reify Matcher
(match [_ i max path] (match [_ i max p]
(if-not (< max (+ ^int i size)) (if-not (< max (+ i size))
(loop [j 0] (loop [j 0]
(if (= j size) (if (= j size)
(match matcher (+ ^int i size) max path) (match matcher (+ i size) max p)
(if (= ^char (aget ^chars path (+ ^int i j)) ^char (aget ^chars chars j)) (if (= (get p (+ i j)) (get path j))
(recur (inc j))))))) (recur (inc j)))))))
(view [_] [path (view matcher)]) (view [_] [path (view matcher)])
(depth [_] (inc (depth matcher))))))) (depth [_] (inc (depth matcher)))))))
(defn wild-matcher [key matcher] (defn wild-matcher [key matcher]
#?(:cljx (Trie/wildMatcher key matcher) #?(:clj (Trie/wildMatcher key matcher)
:clj (reify Matcher :cljs (reify Matcher
(match [_ i max path] (match [_ i max path]
(if (and (< ^int i ^int max) (not= ^char (aget ^chars path i) \/)) (if (and (< i max) (not= (get path i) \/))
(loop [percent? false, plus? false, ^int j ^int i] (loop [percent? false, plus? false, j i]
(if (= ^int max j) (if (= max j)
(if-let [match (match matcher max max path)] (if-let [match (match matcher max max path)]
(-assoc! match key (decode! path i max percent? plus?))) (-assoc! match key (decode! path i max percent? plus?)))
(let [c ^char (aget ^chars path j)] (let [c ^char (get path j)]
(case c (case c
\/ (if-let [match (match matcher j max path)] \/ (if-let [match (match matcher j max path)]
(-assoc! match key (decode! path i j percent? plus?))) (-assoc! match key (decode! path i j percent? plus?)))
@ -179,24 +178,24 @@
(depth [_] (inc (depth matcher)))))) (depth [_] (inc (depth matcher))))))
(defn catch-all-matcher [key data] (defn catch-all-matcher [key data]
#?(:cljx (Trie/catchAllMatcher key data) #?(:clj (Trie/catchAllMatcher key data)
:clj (let [match (->Match data nil)] :cljs (let [match (->Match data nil)]
(reify Matcher (reify Matcher
(match [_ i max path] (match [_ i max path]
(if (< ^int i max) (if (< i max)
(-assoc! match key (decode! path i max)))) (-assoc! match key (decode! path i max))))
(view [_] [key [data]]) (view [_] [key [data]])
(depth [_] 1))))) (depth [_] 1)))))
(defn linear-matcher [matchers] (defn linear-matcher [matchers]
#?(:cljx (Trie/linearMatcher matchers) #?(:clj (Trie/linearMatcher matchers)
:clj (let [matchers (.toArray ^java.util.List (reverse (sort-by depth matchers))) :cljs (let [matchers (vec (reverse (sort-by depth matchers)))
size (alength matchers)] size (count matchers)]
(reify Matcher (reify Matcher
(match [_ i max path] (match [_ i max path]
(loop [j 0] (loop [j 0]
(if (< j size) (if (< j size)
(or (match (aget matchers j) i max path) (or (match (get matchers j) i max path)
(recur (inc j)))))) (recur (inc j))))))
(view [_] (mapv view matchers)) (view [_] (mapv view matchers))
(depth [_] (apply max (map depth matchers))))))) (depth [_] (apply max (map depth matchers)))))))
@ -227,85 +226,88 @@
(first matchers)))) (first matchers))))
(defn pretty [matcher] (defn pretty [matcher]
#?(:cljx (-> matcher str read-string eval) #?(:clj (-> matcher str read-string eval)
:clj (view matcher))) :cljs (view matcher)))
(defn lookup [matcher ^String path] (defn lookup [matcher path]
#?(:cljx (if-let [match ^Trie$Match (Trie/lookup ^Trie$Matcher matcher ^String path)] #?(:clj (if-let [match ^Trie$Match (Trie/lookup ^Trie$Matcher matcher ^String path)]
(->Match (.data match) (.parameters match))) (->Match (.data match) (.parameters match)))
:clj (let [chars (.toCharArray path)] :cljs (if-let [match (match matcher 0 (count path) path)]
(if-let [match (match matcher 0 (alength chars) chars)] (let [params (if-let [path-params (:path-params match)]
(let [params (if-let [path-params (:path-params match)] (persistent! path-params)
(persistent! path-params) {})]
{})] (assoc match :path-params params)))))
(assoc match :path-params params))))))
;; ;;
;; spike ;; spike
;; ;;
(-> (comment
[["/v2/whoami" 1] (->
["/v2/users/:user-id/datasets" 2] [["/v2/whoami" 1]
["/v2/public/projects/:project-id/datasets" 3] ["/v2/users/:user-id/datasets" 2]
["/v1/public/topics/:topic" 4] ["/v2/public/projects/:project-id/datasets" 3]
["/v1/users/:user-id/orgs/:org-id" 5] ["/v1/public/topics/:topic" 4]
["/v1/search/topics/:term" 6] ["/v1/users/:user-id/orgs/:org-id" 5]
["/v1/users/:user-id/invitations" 7] ["/v1/search/topics/:term" 6]
["/v1/users/:user-id/topics" 9] ["/v1/users/:user-id/invitations" 7]
["/v1/users/:user-id/bookmarks/followers" 10] ["/v1/users/:user-id/topics" 9]
["/v2/datasets/:dataset-id" 11] ["/v1/users/:user-id/bookmarks/followers" 10]
["/v1/orgs/:org-id/usage-stats" 12] ["/v2/datasets/:dataset-id" 11]
["/v1/orgs/:org-id/devices/:client-id" 13] ["/v1/orgs/:org-id/usage-stats" 12]
["/v1/messages/user/:user-id" 14] ["/v1/orgs/:org-id/devices/:client-id" 13]
["/v1/users/:user-id/devices" 15] ["/v1/messages/user/:user-id" 14]
["/v1/public/users/:user-id" 16] ["/v1/users/:user-id/devices" 15]
["/v1/orgs/:org-id/errors" 17] ["/v1/public/users/:user-id" 16]
["/v1/public/orgs/:org-id" 18] ["/v1/orgs/:org-id/errors" 17]
["/v1/orgs/:org-id/invitations" 19] ["/v1/public/orgs/:org-id" 18]
["/v1/users/:user-id/device-errors" 22] ["/v1/orgs/:org-id/invitations" 19]
["/v2/login" 23] ["/v1/users/:user-id/device-errors" 22]
["/v1/users/:user-id/usage-stats" 24] ["/v2/login" 23]
["/v2/users/:user-id/devices" 25] ["/v1/users/:user-id/usage-stats" 24]
["/v1/users/:user-id/claim-device/:client-id" 26] ["/v2/users/:user-id/devices" 25]
["/v2/public/projects/:project-id" 27] ["/v1/users/:user-id/claim-device/:client-id" 26]
["/v2/public/datasets/:dataset-id" 28] ["/v2/public/projects/:project-id" 27]
["/v2/users/:user-id/topics/bulk" 29] ["/v2/public/datasets/:dataset-id" 28]
["/v1/messages/device/:client-id" 30] ["/v2/users/:user-id/topics/bulk" 29]
["/v1/users/:user-id/owned-orgs" 31] ["/v1/messages/device/:client-id" 30]
["/v1/topics/:topic" 32] ["/v1/users/:user-id/owned-orgs" 31]
["/v1/users/:user-id/bookmark/:topic" 33] ["/v1/topics/:topic" 32]
["/v1/orgs/:org-id/members/:user-id" 34] ["/v1/users/:user-id/bookmark/:topic" 33]
["/v1/users/:user-id/devices/:client-id" 35] ["/v1/orgs/:org-id/members/:user-id" 34]
["/v1/users/:user-id" 36] ["/v1/users/:user-id/devices/:client-id" 35]
["/v1/orgs/:org-id/devices" 37] ["/v1/users/:user-id" 36]
["/v1/orgs/:org-id/members" 38] ["/v1/orgs/:org-id/devices" 37]
["/v2/orgs/:org-id/topics" 40] ["/v1/orgs/:org-id/members" 38]
["/v1/whoami" 41] ["/v2/orgs/:org-id/topics" 40]
["/v1/orgs/:org-id" 42] ["/v1/whoami" 41]
["/v1/users/:user-id/api-key" 43] ["/v1/orgs/:org-id" 42]
["/v2/schemas" 44] ["/v1/users/:user-id/api-key" 43]
["/v2/users/:user-id/topics" 45] ["/v2/schemas" 44]
["/v1/orgs/:org-id/confirm-membership/:token" 46] ["/v2/users/:user-id/topics" 45]
["/v2/topics/:topic" 47] ["/v1/orgs/:org-id/confirm-membership/:token" 46]
["/v1/messages/topic/:topic" 48] ["/v2/topics/:topic" 47]
["/v1/users/:user-id/devices/:client-id/reset-password" 49] ["/v1/messages/topic/:topic" 48]
["/v2/topics" 50] ["/v1/users/:user-id/devices/:client-id/reset-password" 49]
["/v1/login" 51] ["/v2/topics" 50]
["/v1/users/:user-id/orgs" 52] ["/v1/login" 51]
["/v2/public/messages/dataset/:dataset-id" 53] ["/v1/users/:user-id/orgs" 52]
["/v1/topics" 54] ["/v2/public/messages/dataset/:dataset-id" 53]
["/v1/orgs" 55] ["/v1/topics" 54]
["/v1/users/:user-id/bookmarks" 56] ["/v1/orgs" 55]
["/v1/orgs/:org-id/topics" 57]] ["/v1/users/:user-id/bookmarks" 56]
(insert) ["/v1/orgs/:org-id/topics" 57]]
(compile)
(pretty))
(-> [["/kikka" 2]
["/kikka/kakka/kukka" 3]
["/kikka/:kakka/kurkku" 4]
["/kikka/kuri/{user/doc}/html" 5]]
(insert) (insert)
(compile) (compile)
(pretty)) (pretty))
(-> [["/kikka" 2]
["/kikka/kakka/kukka" 3]
["/kikka/:kakka/kurkku" 4]
["/kikka/kuri/{user/doc}/html" 5]]
(insert)
(compile)
(pretty))
(map str (.toCharArray "\u2215\u0048\u0065\u006C\u006C\u006F"))
(count ["" "H" "e" "l" "l" "o" " " "W" "o" "r" "l" "d"]))