mirror of
https://github.com/metosin/reitit.git
synced 2025-12-18 17:01:11 +00:00
commit
8f26ce8a32
6 changed files with 93 additions and 75 deletions
|
|
@ -1,15 +1,3 @@
|
||||||
; Copyright 2013 Relevance, Inc.
|
|
||||||
; Copyright 2014-2016 Cognitect, Inc.
|
|
||||||
|
|
||||||
; The use and distribution terms for this software are covered by the
|
|
||||||
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0)
|
|
||||||
; which can be found in the file epl-v10.html at the root of this distribution.
|
|
||||||
;
|
|
||||||
; By using this software in any fashion, you are agreeing to be bound by
|
|
||||||
; the terms of this license.
|
|
||||||
;
|
|
||||||
; You must not remove this notice, or any other, from this software.
|
|
||||||
|
|
||||||
(ns ^:no-doc reitit.impl
|
(ns ^:no-doc reitit.impl
|
||||||
#?(:cljs (:require-macros [reitit.impl]))
|
#?(:cljs (:require-macros [reitit.impl]))
|
||||||
(:require [clojure.string :as str]
|
(:require [clojure.string :as str]
|
||||||
|
|
@ -19,17 +7,22 @@
|
||||||
(java.util HashMap Map)
|
(java.util HashMap Map)
|
||||||
(java.net URLEncoder URLDecoder))))
|
(java.net URLEncoder URLDecoder))))
|
||||||
|
|
||||||
(defn map-kv
|
(defn maybe-map-values
|
||||||
"Applies a function to every value of a map.
|
"Applies a function to every value of a map, updates the value if not nil.
|
||||||
|
|
||||||
Also works on vectors. Maintains key for maps, order for vectors."
|
Also works on vectors. Maintains key for maps, order for vectors."
|
||||||
[f coll]
|
[f coll]
|
||||||
(reduce-kv
|
(reduce-kv
|
||||||
(fn [m k v]
|
(fn [coll k v]
|
||||||
(assoc m k (f v)))
|
(if-some [v' (f v)]
|
||||||
(empty coll)
|
(assoc coll k v')
|
||||||
|
coll))
|
||||||
|
coll
|
||||||
coll))
|
coll))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; https://github.com/pedestal/pedestal/blob/master/route/src/io/pedestal/http/route/prefix_tree.clj
|
||||||
|
;;
|
||||||
|
|
||||||
(defn wild? [s]
|
(defn wild? [s]
|
||||||
(contains? #{\: \*} (first (str s))))
|
(contains? #{\: \*} (first (str s))))
|
||||||
|
|
||||||
|
|
@ -191,17 +184,19 @@
|
||||||
#?(:clj (str/replace s #"[^A-Za-z0-9\!'\(\)\*_~.-]+" percent-encode)
|
#?(:clj (str/replace s #"[^A-Za-z0-9\!'\(\)\*_~.-]+" percent-encode)
|
||||||
:cljs (js/encodeURIComponent s))))
|
:cljs (js/encodeURIComponent s))))
|
||||||
|
|
||||||
(defn url-decode [s]
|
(defn maybe-url-decode [s]
|
||||||
(if s
|
(if s
|
||||||
#?(:clj (if (.contains ^String s "%")
|
#?(:clj (if (.contains ^String s "%")
|
||||||
(URLDecoder/decode
|
(URLDecoder/decode
|
||||||
(if (.contains ^String s "+")
|
(if (.contains ^String s "+")
|
||||||
(.replace ^String s "+" "%2B")
|
(.replace ^String s "+" "%2B")
|
||||||
s)
|
s)
|
||||||
"UTF-8")
|
"UTF-8"))
|
||||||
s)
|
|
||||||
:cljs (js/decodeURIComponent s))))
|
:cljs (js/decodeURIComponent s))))
|
||||||
|
|
||||||
|
(defn url-decode [s]
|
||||||
|
(or (maybe-url-decode s) s))
|
||||||
|
|
||||||
(defn form-encode [s]
|
(defn form-encode [s]
|
||||||
(if s
|
(if s
|
||||||
#?(:clj (URLEncoder/encode ^String s "UTF-8")
|
#?(:clj (URLEncoder/encode ^String s "UTF-8")
|
||||||
|
|
@ -217,7 +212,7 @@
|
||||||
(defn url-decode-coll
|
(defn url-decode-coll
|
||||||
"URL-decodes maps and vectors"
|
"URL-decodes maps and vectors"
|
||||||
[coll]
|
[coll]
|
||||||
(map-kv url-decode coll))
|
(maybe-map-values maybe-url-decode coll))
|
||||||
|
|
||||||
(defprotocol IntoString
|
(defprotocol IntoString
|
||||||
(into-string [_]))
|
(into-string [_]))
|
||||||
|
|
@ -251,7 +246,7 @@
|
||||||
(defn path-params
|
(defn path-params
|
||||||
"Convert parameters' values into URL-encoded strings, suitable for URL paths"
|
"Convert parameters' values into URL-encoded strings, suitable for URL paths"
|
||||||
[params]
|
[params]
|
||||||
(map-kv #(url-encode (into-string %)) params))
|
(maybe-map-values #(url-encode (into-string %)) params))
|
||||||
|
|
||||||
(defn query-string
|
(defn query-string
|
||||||
"shallow transform of query parameters into query string"
|
"shallow transform of query parameters into query string"
|
||||||
|
|
@ -270,7 +265,7 @@
|
||||||
(goog/inherits ~type ~base-type)
|
(goog/inherits ~type ~base-type)
|
||||||
|
|
||||||
~@(map
|
~@(map
|
||||||
(fn [method]
|
(fn [method]
|
||||||
`(set! (.. ~type -prototype ~(symbol (str "-" (first method))))
|
`(set! (.. ~type -prototype ~(symbol (str "-" (first method))))
|
||||||
(fn ~@(rest method))))
|
(fn ~@(rest method))))
|
||||||
methods)))
|
methods)))
|
||||||
|
|
|
||||||
|
|
@ -15,16 +15,16 @@
|
||||||
(-lookup [_ _ _]))
|
(-lookup [_ _ _]))
|
||||||
|
|
||||||
(defn- -catch-all [children catch-all path-params p ps]
|
(defn- -catch-all [children catch-all path-params p ps]
|
||||||
(if catch-all
|
(-lookup
|
||||||
(-lookup
|
(impl/fast-get children catch-all)
|
||||||
(impl/fast-get children catch-all)
|
nil
|
||||||
nil
|
(assoc path-params catch-all (str/join "/" (cons p ps)))))
|
||||||
(assoc path-params catch-all (str/join "/" (cons p ps))))))
|
|
||||||
|
|
||||||
(defn- segment
|
(defn- segment
|
||||||
([] (segment {} #{} nil nil))
|
([] (segment {} #{} nil nil))
|
||||||
([children wilds catch-all match]
|
([children wilds catch-all match]
|
||||||
(let [children' (impl/fast-map children)]
|
(let [children' (impl/fast-map children)
|
||||||
|
wilds? (seq wilds)]
|
||||||
^{:type ::segment}
|
^{:type ::segment}
|
||||||
(reify
|
(reify
|
||||||
Segment
|
Segment
|
||||||
|
|
@ -40,8 +40,8 @@
|
||||||
(if (nil? p)
|
(if (nil? p)
|
||||||
(when match (assoc match :path-params path-params))
|
(when match (assoc match :path-params path-params))
|
||||||
(or (-lookup (impl/fast-get children' p) ps path-params)
|
(or (-lookup (impl/fast-get children' p) ps path-params)
|
||||||
(some #(-lookup (impl/fast-get children' %) ps (assoc path-params % p)) wilds)
|
(if wilds? (some #(-lookup (impl/fast-get children' %) ps (assoc path-params % p)) wilds))
|
||||||
(-catch-all children' catch-all path-params p ps))))))))
|
(if catch-all (-catch-all children' catch-all path-params p ps)))))))))
|
||||||
|
|
||||||
(defn insert [root path data]
|
(defn insert [root path data]
|
||||||
(-insert (or root (segment)) (impl/segments path) (map->Match {:data data})))
|
(-insert (or root (segment)) (impl/segments path) (map->Match {:data data})))
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,7 @@
|
||||||
(:require [criterium.core :as cc]
|
(:require [criterium.core :as cc]
|
||||||
[reitit.perf-utils :refer :all]
|
[reitit.perf-utils :refer :all]
|
||||||
[reitit.ring :as ring]
|
[reitit.ring :as ring]
|
||||||
[clojure.string :as str]
|
[clojure.string :as str]))
|
||||||
[reitit.core :as r]))
|
|
||||||
|
|
||||||
;;
|
;;
|
||||||
;; start repl with `lein perf repl`
|
;; start repl with `lein perf repl`
|
||||||
|
|
@ -313,20 +312,23 @@
|
||||||
|
|
||||||
;; 40ns (httprouter)
|
;; 40ns (httprouter)
|
||||||
;; 140ns
|
;; 140ns
|
||||||
|
;; 120ns (faster decode params)
|
||||||
(let [req (map->Req {:request-method :get, :uri "/user/repos"})]
|
(let [req (map->Req {:request-method :get, :uri "/user/repos"})]
|
||||||
(title "static")
|
(title "static")
|
||||||
(assert (= {:status 200, :body "/user/repos"} (app req)))
|
(assert (= {:status 200, :body "/user/repos"} (app req)))
|
||||||
(cc/quick-bench (app req)))
|
(cc/quick-bench (app req)))
|
||||||
|
|
||||||
;; 160ns (httprouter)
|
;; 160ns (httprouter)
|
||||||
;; 990ns
|
;; 990ns
|
||||||
|
;; 830ns (faster decode params)
|
||||||
(let [req (map->Req {:request-method :get, :uri "/repos/julienschmidt/httprouter/stargazers"})]
|
(let [req (map->Req {:request-method :get, :uri "/repos/julienschmidt/httprouter/stargazers"})]
|
||||||
(title "param")
|
(title "param")
|
||||||
(assert (= {:status 200, :body "/repos/:owner/:repo/stargazers"} (app req)))
|
(assert (= {:status 200, :body "/repos/:owner/:repo/stargazers"} (app req)))
|
||||||
(cc/quick-bench (app req)))
|
(cc/quick-bench (app req)))
|
||||||
|
|
||||||
;; 30µs (httprouter)
|
;; 30µs (httprouter)
|
||||||
;; 190µs
|
;; 190µs
|
||||||
|
;; 160µs (faster decode params)
|
||||||
(let [requests (mapv route->req routes)]
|
(let [requests (mapv route->req routes)]
|
||||||
(title "all")
|
(title "all")
|
||||||
(cc/quick-bench
|
(cc/quick-bench
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@
|
||||||
(:require [criterium.core :as cc]
|
(:require [criterium.core :as cc]
|
||||||
[reitit.perf-utils :refer :all]
|
[reitit.perf-utils :refer :all]
|
||||||
[ring.util.codec]
|
[ring.util.codec]
|
||||||
[reitit.impl])
|
[reitit.impl]
|
||||||
|
[reitit.impl :as impl])
|
||||||
(:import (java.net URLDecoder URLEncoder)))
|
(:import (java.net URLDecoder URLEncoder)))
|
||||||
|
|
||||||
;;
|
;;
|
||||||
|
|
@ -163,8 +164,30 @@
|
||||||
"1"]]
|
"1"]]
|
||||||
(test! f s))))
|
(test! f s))))
|
||||||
|
|
||||||
|
(defn url-encode-coll! []
|
||||||
|
|
||||||
|
(suite "url-encode-coll")
|
||||||
|
|
||||||
|
;; 740ns
|
||||||
|
(test "something to decode")
|
||||||
|
(test! impl/url-decode-coll
|
||||||
|
{:a "aja%20hiljaa+sillalla"
|
||||||
|
:b "aja_hiljaa_sillalla"
|
||||||
|
:c "1+1"
|
||||||
|
:d "1"})
|
||||||
|
|
||||||
|
;; 124ns
|
||||||
|
;; 50ns (maybe-map-values)
|
||||||
|
(test "nothing to decode")
|
||||||
|
(test! impl/url-decode-coll
|
||||||
|
{:a "aja+20hiljaa+sillalla"
|
||||||
|
:b "aja_hiljaa_sillalla"
|
||||||
|
:c "1+1"
|
||||||
|
:d "1"}))
|
||||||
|
|
||||||
(comment
|
(comment
|
||||||
(url-decode!)
|
(url-decode!)
|
||||||
(url-encode!)
|
(url-encode!)
|
||||||
(form-decode!)
|
(form-decode!)
|
||||||
(form-encode!))
|
(form-encode!)
|
||||||
|
(url-encode-coll!))
|
||||||
|
|
|
||||||
|
|
@ -440,6 +440,7 @@
|
||||||
;; 723ns (segment-router)
|
;; 723ns (segment-router)
|
||||||
;; 702ns (before path-parameters)
|
;; 702ns (before path-parameters)
|
||||||
;; 806ns (decode path-parameters)
|
;; 806ns (decode path-parameters)
|
||||||
|
;; 735ns (maybe-map-values)
|
||||||
(b! "reitit-ring" reitit-ring-f)
|
(b! "reitit-ring" reitit-ring-f)
|
||||||
|
|
||||||
;; 2821ns
|
;; 2821ns
|
||||||
|
|
|
||||||
|
|
@ -80,39 +80,36 @@
|
||||||
|
|
||||||
(defn bench! []
|
(defn bench! []
|
||||||
|
|
||||||
;; 2.3ms
|
;; 2.3µs
|
||||||
(cc/quick-bench
|
|
||||||
(dotimes [_ 1000]
|
|
||||||
(p/lookup pedestal-tree "/v1/orgs/1/topics")))
|
|
||||||
|
|
||||||
;; 3.1ms
|
|
||||||
;; 2.5ms (string equals)
|
|
||||||
;; 2.5ms (protocol)
|
|
||||||
;; 2.3ms (nil childs)
|
|
||||||
;; 2.0ms (rando impros)
|
|
||||||
;; 1.9ms (wild & catch shortcuts)
|
|
||||||
;; 1.5ms (inline child fetching)
|
|
||||||
;; 1.5ms (WildNode also backtracks)
|
|
||||||
;; 1.4ms (precalculate segment-size)
|
|
||||||
;; 1.3ms (fast-map)
|
|
||||||
;; 1.3ms (dissoc wild & catch-all from children)
|
|
||||||
;; 1.3ms (reified protocols)
|
|
||||||
;; 0.8ms (flattened matching)
|
|
||||||
;; 0.8ms (return route-data)
|
|
||||||
;; 0.8ms (fix payloads)
|
|
||||||
#_(cc/quick-bench
|
#_(cc/quick-bench
|
||||||
(dotimes [_ 1000]
|
(p/lookup pedestal-tree "/v1/orgs/1/topics"))
|
||||||
(trie/lookup reitit-tree "/v1/orgs/1/topics" {})))
|
|
||||||
|
|
||||||
;; 0.9ms (initial)
|
;; 3.1µs
|
||||||
;; 0.5ms (protocols)
|
;; 2.5µs (string equals)
|
||||||
;; 1.0ms (with path params)
|
;; 2.5µs (protocol)
|
||||||
;; 1.0ms (Match records)
|
;; 2.3µs (nil childs)
|
||||||
;; 0.63ms (Single sweep path params)
|
;; 2.0µs (rando impros)
|
||||||
;; 0.51ms (Cleanup)
|
;; 1.9µs (wild & catch shortcuts)
|
||||||
|
;; 1.5µs (inline child fetching)
|
||||||
|
;; 1.5µs (WildNode also backtracks)
|
||||||
|
;; 1.4µs (precalculate segment-size)
|
||||||
|
;; 1.3µs (fast-map)
|
||||||
|
;; 1.3µs (dissoc wild & catch-all from children)
|
||||||
|
;; 1.3µs (reified protocols)
|
||||||
|
;; 0.8µs (flattened matching)
|
||||||
|
;; 0.8µs (return route-data)
|
||||||
|
;; 0.8µs (fix payloads)
|
||||||
|
#_(cc/quick-bench
|
||||||
|
(trie/lookup reitit-tree "/v1/orgs/1/topics" {}))
|
||||||
|
|
||||||
|
;; 0.9µs (initial)
|
||||||
|
;; 0.5µs (protocols)
|
||||||
|
;; 1.0µs (with path paraµs)
|
||||||
|
;; 1.0µs (Match records)
|
||||||
|
;; 0.63µs (Single sweep path paraµs)
|
||||||
|
;; 0.51µs (Cleanup)
|
||||||
(cc/quick-bench
|
(cc/quick-bench
|
||||||
(dotimes [_ 1000]
|
(segment/lookup reitit-segment "/v1/orgs/1/topics")))
|
||||||
(segment/lookup reitit-segment "/v1/orgs/1/topics"))))
|
|
||||||
|
|
||||||
(comment
|
(comment
|
||||||
(bench!))
|
(bench!))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue