mirror of
https://github.com/metosin/reitit.git
synced 2025-12-18 00:41:12 +00:00
Snappier Trie
This commit is contained in:
parent
3f86e24b89
commit
80dea6cfef
12 changed files with 323 additions and 117 deletions
|
|
@ -2,10 +2,10 @@ package reitit;
|
||||||
|
|
||||||
import clojure.lang.Keyword;
|
import clojure.lang.Keyword;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
|
||||||
|
|
||||||
public class Trie {
|
public class Trie {
|
||||||
|
|
||||||
public static ArrayList<String> split(final String path) {
|
public static ArrayList<String> split(final String path) {
|
||||||
|
|
@ -23,8 +23,22 @@ public class Trie {
|
||||||
return segments;
|
return segments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String encode(String s) {
|
||||||
|
try {
|
||||||
|
if (s.contains("%")) {
|
||||||
|
String _s = s;
|
||||||
|
if (s.contains("+")) {
|
||||||
|
_s = s.replace("+", "%2B");
|
||||||
|
}
|
||||||
|
return URLEncoder.encode(_s, "UTF-8");
|
||||||
|
}
|
||||||
|
} catch (UnsupportedEncodingException ignored) {
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
public static class Match {
|
public static class Match {
|
||||||
public Map<Keyword, String> params = new HashMap<>();
|
public final Map<Keyword, String> params = new HashMap<>();
|
||||||
public Object data;
|
public Object data;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -41,59 +55,6 @@ public class Trie {
|
||||||
private Map<Keyword, Trie> catchAll = new HashMap<>();
|
private Map<Keyword, Trie> catchAll = new HashMap<>();
|
||||||
private Object data;
|
private Object data;
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
Map<Object, Object> m = new HashMap<>();
|
|
||||||
m.put(Keyword.intern("childs"), childs);
|
|
||||||
m.put(Keyword.intern("wilds"), wilds);
|
|
||||||
m.put(Keyword.intern("catchAll"), catchAll);
|
|
||||||
m.put(Keyword.intern("data"), data);
|
|
||||||
return m.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Match lookup(Trie root, String path) {
|
|
||||||
return lookup(root, new Match(), split(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Match lookup(Trie root, Match match, List<String> parts) {
|
|
||||||
Trie childTrie = null;
|
|
||||||
if (parts.isEmpty()) {
|
|
||||||
return match;
|
|
||||||
} else {
|
|
||||||
Trie trie = root;
|
|
||||||
int i = 0;
|
|
||||||
for (final String part : parts) {
|
|
||||||
i++;
|
|
||||||
childTrie = trie.childs.get(part);
|
|
||||||
if (childTrie != null) {
|
|
||||||
trie = childTrie;
|
|
||||||
} else {
|
|
||||||
for (final Map.Entry<Keyword, Trie> e : trie.wilds.entrySet()) {
|
|
||||||
childTrie = e.getValue();
|
|
||||||
match.data = childTrie.data;
|
|
||||||
Match m = lookup(childTrie, match, parts.subList(i, parts.size()));
|
|
||||||
if (m != null) {
|
|
||||||
match.params.put(e.getKey(), part);
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (Map.Entry<Keyword, Trie> e : trie.catchAll.entrySet()) {
|
|
||||||
childTrie = e.getValue();
|
|
||||||
match.params.put(e.getKey(), String.join("/", parts.subList(i - 1, parts.size())));
|
|
||||||
match.data = childTrie.data;
|
|
||||||
return match;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (childTrie != null) {
|
|
||||||
match.data = childTrie.data;
|
|
||||||
return match;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Trie add(String path, Object data) {
|
public Trie add(String path, Object data) {
|
||||||
List<String> paths = split(path);
|
List<String> paths = split(path);
|
||||||
Trie pointer = this;
|
Trie pointer = this;
|
||||||
|
|
@ -127,33 +88,247 @@ public class Trie {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Matcher staticMatcher() {
|
||||||
|
if (childs.size() == 1) {
|
||||||
|
return new StaticMatcher(childs.keySet().iterator().next(), childs.values().iterator().next().matcher());
|
||||||
|
} else {
|
||||||
|
Map<String, Matcher> m = new HashMap<>();
|
||||||
|
for (Map.Entry<String, Trie> e : childs.entrySet()) {
|
||||||
|
m.put(e.getKey(), e.getValue().matcher());
|
||||||
|
}
|
||||||
|
return new StaticMapMatcher(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Matcher matcher() {
|
||||||
|
Matcher m;
|
||||||
|
if (!catchAll.isEmpty()) {
|
||||||
|
m = new CatchAllMatcher(catchAll.keySet().iterator().next(), catchAll.values().iterator().next().data);
|
||||||
|
} else if (!wilds.isEmpty()) {
|
||||||
|
List<Matcher> matchers = new ArrayList<>();
|
||||||
|
if (data != null) {
|
||||||
|
matchers.add(new DataMatcher(data));
|
||||||
|
}
|
||||||
|
if (!childs.isEmpty()) {
|
||||||
|
matchers.add(staticMatcher());
|
||||||
|
}
|
||||||
|
for (Map.Entry<Keyword, Trie> e : wilds.entrySet()) {
|
||||||
|
matchers.add(new WildMatcher(e.getKey(), e.getValue().matcher()));
|
||||||
|
}
|
||||||
|
m = new LinearMatcher(matchers);
|
||||||
|
} else if (!childs.isEmpty()) {
|
||||||
|
m = staticMatcher();
|
||||||
|
} else {
|
||||||
|
return new DataMatcher(data);
|
||||||
|
}
|
||||||
|
if (data != null) {
|
||||||
|
m = new LinearMatcher(Arrays.asList(new DataMatcher(data), m));
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Matcher {
|
||||||
|
Match match(int i, List<String> segments, Match match);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class StaticMatcher implements Matcher {
|
||||||
|
private final String segment;
|
||||||
|
private final Matcher child;
|
||||||
|
|
||||||
|
StaticMatcher(String segment, Matcher child) {
|
||||||
|
this.segment = segment;
|
||||||
|
this.child = child;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Match match(int i, List<String> segments, Match match) {
|
||||||
|
if (i < segments.size() && segment.equals(segments.get(i))) {
|
||||||
|
return child.match(i + 1, segments, match);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "[\"" + segment + "\" " + child + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class WildMatcher implements Matcher {
|
||||||
|
private final Keyword parameter;
|
||||||
|
private final Matcher child;
|
||||||
|
|
||||||
|
WildMatcher(Keyword parameter, Matcher child) {
|
||||||
|
this.parameter = parameter;
|
||||||
|
this.child = child;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Match match(int i, List<String> segments, Match match) {
|
||||||
|
final Match m = child.match(i + 1, segments, match);
|
||||||
|
if (m != null) {
|
||||||
|
m.params.put(parameter, encode(segments.get(i)));
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "[" + parameter + " " + child + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class CatchAllMatcher implements Matcher {
|
||||||
|
private final Keyword parameter;
|
||||||
|
private final Object data;
|
||||||
|
|
||||||
|
CatchAllMatcher(Keyword parameter, Object data) {
|
||||||
|
this.parameter = parameter;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Match match(int i, List<String> segments, Match match) {
|
||||||
|
match.params.put(parameter, encode(String.join("/", segments.subList(i, segments.size()))));
|
||||||
|
match.data = data;
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "[" + parameter + " " + new DataMatcher(data) + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class StaticMapMatcher implements Matcher {
|
||||||
|
private final Map<String, Matcher> map;
|
||||||
|
|
||||||
|
StaticMapMatcher(Map<String, Matcher> map) {
|
||||||
|
this.map = map;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Match match(int i, List<String> segments, Match match) {
|
||||||
|
final Matcher child = map.get(segments.get(i));
|
||||||
|
if (child != null) {
|
||||||
|
return child.match(i + 1, segments, match);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
b.append("{");
|
||||||
|
List<String> keys = new ArrayList<>(map.keySet());
|
||||||
|
for (int i = 0; i < keys.size(); i++) {
|
||||||
|
String path = keys.get(i);
|
||||||
|
Matcher value = map.get(path);
|
||||||
|
b.append("\"").append(path).append("\" ").append(value);
|
||||||
|
if (i < keys.size() - 1) {
|
||||||
|
b.append(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.append("}");
|
||||||
|
return b.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class LinearMatcher implements Matcher {
|
||||||
|
|
||||||
|
private final List<Matcher> childs;
|
||||||
|
|
||||||
|
LinearMatcher(List<Matcher> childs) {
|
||||||
|
this.childs = childs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Match match(int i, List<String> segments, Match match) {
|
||||||
|
for (Matcher child : childs) {
|
||||||
|
final Match m = child.match(i, segments, match);
|
||||||
|
if (m != null) {
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return childs.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class DataMatcher implements Matcher {
|
||||||
|
private final Object data;
|
||||||
|
|
||||||
|
DataMatcher(Object data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Match match(int i, List<String> segments, Match match) {
|
||||||
|
if (i == segments.size()) {
|
||||||
|
match.data = data;
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return (data != null ? data.toString() : "null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Match lookup(Matcher matcher, String path) {
|
||||||
|
return matcher.match(0, split(path), new Match());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Matcher sample() {
|
||||||
|
Map<String, Matcher> m1 = new HashMap<>();
|
||||||
|
m1.put("profile", new WildMatcher(Keyword.intern("type"), new DataMatcher(1)));
|
||||||
|
m1.put("permissions", new DataMatcher(2));
|
||||||
|
|
||||||
|
Map<String, Matcher> m2 = new HashMap<>();
|
||||||
|
m2.put("user", new WildMatcher(Keyword.intern("id"), new StaticMapMatcher(m1)));
|
||||||
|
m2.put("company", new WildMatcher(Keyword.intern("cid"), new StaticMatcher("dept", new WildMatcher(Keyword.intern("did"), new DataMatcher(3)))));
|
||||||
|
m2.put("public", new CatchAllMatcher(Keyword.intern("*"), 4));
|
||||||
|
m2.put("kikka", new LinearMatcher(Arrays.asList(new StaticMatcher("ping", new DataMatcher(5)), new WildMatcher(Keyword.intern("id"), new StaticMatcher("ping", new DataMatcher(6))))));
|
||||||
|
return new StaticMapMatcher(m2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
Trie trie =
|
|
||||||
new Trie()
|
|
||||||
.add("/kikka", 1)
|
|
||||||
.add("/kakka", 2)
|
|
||||||
.add("/api/ping", 3)
|
|
||||||
.add("/api/pong", 4)
|
|
||||||
.add("/api/ipa/ping", 5)
|
|
||||||
.add("/api/ipa/pong", 6)
|
|
||||||
.add("/users/:user-id", 7)
|
|
||||||
.add("/users/:user-id/orders", 8)
|
|
||||||
.add("/users/:user-id/price", 9)
|
|
||||||
.add("/orders/:id/price", 10)
|
|
||||||
.add("/orders/:super", 11)
|
|
||||||
.add("/orders/:super/hyper/:giga", 12);
|
|
||||||
|
|
||||||
//System.out.println(lookup(trie, split("/kikka")));
|
Trie trie = new Trie();
|
||||||
System.out.println(lookup(trie, "/orders/mies/hyper/peikko"));
|
//trie.add("/kikka/:id/permissions", 1);
|
||||||
|
trie.add("/kikka/:id", 2);
|
||||||
|
trie.add("/kakka/ping", 3);
|
||||||
|
Matcher m = trie.matcher();
|
||||||
|
System.err.println(m);
|
||||||
|
System.out.println(lookup(m, "/kikka/1/permissions"));
|
||||||
|
System.out.println(lookup(m, "/kikka/1"));
|
||||||
|
|
||||||
System.out.println(lookup(
|
/*
|
||||||
new Trie().add("/user/:id/profile/:type/", 1),
|
Trie trie = new Trie();
|
||||||
"/user/1/profile/compat"));
|
trie.add("/user/:id/profile/:type", 1);
|
||||||
|
trie.add("/user/:id/permissions", 2);
|
||||||
|
trie.add("/company/:cid/dept/:did", 3);
|
||||||
|
trie.add("/this/is/a/static/route", 4);
|
||||||
|
Matcher m = trie.matcher();
|
||||||
|
System.out.println(m);
|
||||||
|
|
||||||
System.out.println(lookup(
|
System.err.println(lookup(m, "/this/is/a/static/route"));
|
||||||
new Trie().add("/user/*path", 1),
|
System.err.println(lookup(m, "/user/1234/profile/compact"));
|
||||||
"/user/1/profile/compat"));
|
System.err.println(lookup(m, "/company/1234/dept/5678"));
|
||||||
|
System.err.println();
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
System.err.println(lookup(sample(), "/user/1234/profile/compact"));
|
||||||
|
System.err.println(lookup(sample(), "/public/images/logo.jpg"));
|
||||||
|
System.err.println(lookup(sample(), "/kikka/ping"));
|
||||||
|
System.err.println(lookup(sample(), "/kikka/kukka/ping"));
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,4 +8,5 @@
|
||||||
:plugins [[lein-parent "0.3.2"]]
|
:plugins [[lein-parent "0.3.2"]]
|
||||||
:parent-project {:path "../../project.clj"
|
:parent-project {:path "../../project.clj"
|
||||||
:inherit [:deploy-repositories :managed-dependencies]}
|
:inherit [:deploy-repositories :managed-dependencies]}
|
||||||
|
:java-source-paths ["java-src"]
|
||||||
:dependencies [[meta-merge]])
|
:dependencies [[meta-merge]])
|
||||||
|
|
|
||||||
|
|
@ -271,6 +271,7 @@
|
||||||
(if name (assoc nl name f) nl)]))
|
(if name (assoc nl name f) nl)]))
|
||||||
[nil {}]
|
[nil {}]
|
||||||
compiled-routes)
|
compiled-routes)
|
||||||
|
pl (segment/compile pl)
|
||||||
lookup (impl/fast-map nl)
|
lookup (impl/fast-map nl)
|
||||||
routes (uncompile-routes compiled-routes)]
|
routes (uncompile-routes compiled-routes)]
|
||||||
^{:type ::router}
|
^{:type ::router}
|
||||||
|
|
@ -288,9 +289,7 @@
|
||||||
names)
|
names)
|
||||||
(match-by-path [_ path]
|
(match-by-path [_ path]
|
||||||
(if-let [match (segment/lookup pl path)]
|
(if-let [match (segment/lookup pl path)]
|
||||||
(-> (:data match)
|
(assoc (:data match) :path path)))
|
||||||
(assoc :path-params (impl/url-decode-coll (:path-params match)))
|
|
||||||
(assoc :path path))))
|
|
||||||
(match-by-name [_ name]
|
(match-by-name [_ name]
|
||||||
(if-let [match (impl/fast-get lookup name)]
|
(if-let [match (impl/fast-get lookup name)]
|
||||||
(match nil)))
|
(match nil)))
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(:import (java.util.regex Pattern)
|
(:import (java.util.regex Pattern)
|
||||||
(java.util HashMap Map)
|
(java.util HashMap Map)
|
||||||
(java.net URLEncoder URLDecoder))))
|
(java.net URLEncoder URLDecoder)
|
||||||
|
(reitit Trie))))
|
||||||
|
|
||||||
(defn maybe-map-values
|
(defn maybe-map-values
|
||||||
"Applies a function to every value of a map, updates the value if not nil.
|
"Applies a function to every value of a map, updates the value if not nil.
|
||||||
|
|
@ -19,6 +20,10 @@
|
||||||
coll
|
coll
|
||||||
coll))
|
coll))
|
||||||
|
|
||||||
|
(defn segments [path]
|
||||||
|
#?(:clj (Trie/split ^String path)
|
||||||
|
:cljs (.split path #"/" 666)))
|
||||||
|
|
||||||
;;
|
;;
|
||||||
;; https://github.com/pedestal/pedestal/blob/master/route/src/io/pedestal/http/route/prefix_tree.clj
|
;; https://github.com/pedestal/pedestal/blob/master/route/src/io/pedestal/http/route/prefix_tree.clj
|
||||||
;;
|
;;
|
||||||
|
|
@ -42,10 +47,6 @@
|
||||||
(defn wild-or-catch-all-param? [x]
|
(defn wild-or-catch-all-param? [x]
|
||||||
(boolean (or (wild-param x) (catch-all-param x))))
|
(boolean (or (wild-param x) (catch-all-param x))))
|
||||||
|
|
||||||
(defn segments [path]
|
|
||||||
#?(:clj (.split ^String path "/" 666)
|
|
||||||
:cljs (.split path #"/" 666)))
|
|
||||||
|
|
||||||
(defn contains-wilds? [path]
|
(defn contains-wilds? [path]
|
||||||
(boolean (some wild-or-catch-all-param? (segments path))))
|
(boolean (some wild-or-catch-all-param? (segments path))))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
(ns reitit.segment
|
(ns reitit.segment
|
||||||
(:refer-clojure :exclude [-lookup])
|
(:refer-clojure :exclude [-lookup compile])
|
||||||
(:require [reitit.impl :as impl]
|
(:require [reitit.impl :as impl]
|
||||||
[clojure.string :as str])
|
[clojure.string :as str])
|
||||||
#?(:clj (:import (reitit Trie Trie$Match))))
|
#?(:clj (:import (reitit Trie Trie$Match))))
|
||||||
|
|
@ -44,18 +44,17 @@
|
||||||
(if (and wilds? (not (str/blank? p))) (some #(-lookup (impl/fast-get children' %) ps (assoc path-params % p)) wilds))
|
(if (and wilds? (not (str/blank? p))) (some #(-lookup (impl/fast-get children' %) ps (assoc path-params % p)) wilds))
|
||||||
(if catch-all (-catch-all children' catch-all path-params p ps)))))))))
|
(if catch-all (-catch-all children' catch-all path-params p ps)))))))))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; public api
|
||||||
|
;;
|
||||||
|
|
||||||
(defn insert [root path data]
|
(defn insert [root path data]
|
||||||
#?(:cljs (-insert (or root (segment)) (impl/segments path) (map->Match {:data data}))
|
#?(:cljs (-insert (or root (segment)) (impl/segments path) (map->Match {:data data}))
|
||||||
:clj (.add (or ^Trie root ^Trie (Trie.)) ^String path data)))
|
:clj (.add (or ^Trie root ^Trie (Trie.)) ^String path data)))
|
||||||
|
|
||||||
(defn create [paths]
|
(defn compile [segment]
|
||||||
(reduce
|
#?(:cljs segment
|
||||||
(fn [segment [p data]]
|
:clj (.matcher ^Trie segment)))
|
||||||
#?(:cljs (insert segment p data)
|
|
||||||
:clj (.add ^Trie segment ^String p data)))
|
|
||||||
#?(:cljs nil
|
|
||||||
:clj (Trie.))
|
|
||||||
paths))
|
|
||||||
|
|
||||||
(defn lookup [segment path]
|
(defn lookup [segment path]
|
||||||
#?(:cljs (-lookup segment (impl/segments path) {})
|
#?(:cljs (-lookup segment (impl/segments path) {})
|
||||||
|
|
|
||||||
|
|
@ -89,10 +89,11 @@
|
||||||
|
|
||||||
;; 1600 µs
|
;; 1600 µs
|
||||||
(title "bidi")
|
(title "bidi")
|
||||||
(assert (bidi/match-route bidi-routes "/auth/login"))
|
(let [request "/auth/login"]
|
||||||
(cc/quick-bench
|
(assert (bidi/match-route bidi-routes request))
|
||||||
(dotimes [_ 1000]
|
(cc/quick-bench
|
||||||
(bidi/match-route bidi-routes "/auth/login")))
|
(dotimes [_ 1000]
|
||||||
|
(bidi/match-route bidi-routes request))))
|
||||||
|
|
||||||
;; 1400 µs
|
;; 1400 µs
|
||||||
(title "ataraxy")
|
(title "ataraxy")
|
||||||
|
|
@ -105,10 +106,10 @@
|
||||||
;; 1000 µs
|
;; 1000 µs
|
||||||
(title "pedestal - map-tree => prefix-tree")
|
(title "pedestal - map-tree => prefix-tree")
|
||||||
(let [request {:path-info "/auth/login" :request-method :get}]
|
(let [request {:path-info "/auth/login" :request-method :get}]
|
||||||
(assert (pedestal/find-route pedestal-router {:path-info "/auth/login" :request-method :get}))
|
(assert (pedestal/find-route pedestal-router request))
|
||||||
(cc/quick-bench
|
(cc/quick-bench
|
||||||
(dotimes [_ 1000]
|
(dotimes [_ 1000]
|
||||||
(pedestal/find-route pedestal-router {:path-info "/auth/login" :request-method :get}))))
|
(pedestal/find-route pedestal-router request))))
|
||||||
|
|
||||||
;; 1400 µs
|
;; 1400 µs
|
||||||
(title "compojure")
|
(title "compojure")
|
||||||
|
|
@ -163,6 +164,7 @@
|
||||||
;; 710 µs (3-18x)
|
;; 710 µs (3-18x)
|
||||||
;; 530 µs (4-24x) -25% prefix-tree-router
|
;; 530 µs (4-24x) -25% prefix-tree-router
|
||||||
;; 710 µs (3-18x) segment-router
|
;; 710 µs (3-18x) segment-router
|
||||||
|
;; 320 µs (6-40x) java-segment-router
|
||||||
(title "reitit")
|
(title "reitit")
|
||||||
(assert (reitit/match-by-path reitit-routes "/workspace/1/1"))
|
(assert (reitit/match-by-path reitit-routes "/workspace/1/1"))
|
||||||
(cc/quick-bench
|
(cc/quick-bench
|
||||||
|
|
|
||||||
|
|
@ -296,7 +296,9 @@
|
||||||
(def app
|
(def app
|
||||||
(ring/ring-handler
|
(ring/ring-handler
|
||||||
(ring/router
|
(ring/router
|
||||||
(reduce (partial add h) [] routes))))
|
(reduce (partial add h) [] routes))
|
||||||
|
(ring/create-default-handler)
|
||||||
|
{:inject-match? false, :inject-router? false}))
|
||||||
|
|
||||||
(defrecord Req [uri request-method])
|
(defrecord Req [uri request-method])
|
||||||
|
|
||||||
|
|
@ -313,6 +315,8 @@
|
||||||
;; 40ns (httprouter)
|
;; 40ns (httprouter)
|
||||||
;; 140ns
|
;; 140ns
|
||||||
;; 120ns (faster decode params)
|
;; 120ns (faster decode params)
|
||||||
|
;; 140µs (java-segment-router)
|
||||||
|
;; 60ns (java-segment-router, no injects)
|
||||||
(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)))
|
||||||
|
|
@ -321,6 +325,8 @@
|
||||||
;; 160ns (httprouter)
|
;; 160ns (httprouter)
|
||||||
;; 990ns
|
;; 990ns
|
||||||
;; 830ns (faster decode params)
|
;; 830ns (faster decode params)
|
||||||
|
;; 560µs (java-segment-router)
|
||||||
|
;; 490ns (java-segment-router, no injects)
|
||||||
(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)))
|
||||||
|
|
@ -329,6 +335,8 @@
|
||||||
;; 30µs (httprouter)
|
;; 30µs (httprouter)
|
||||||
;; 190µs
|
;; 190µs
|
||||||
;; 160µs (faster decode params)
|
;; 160µs (faster decode params)
|
||||||
|
;; 120µs (java-segment-router)
|
||||||
|
;; 100µs (java-segment-router, no injects)
|
||||||
(let [requests (mapv route->req routes)]
|
(let [requests (mapv route->req routes)]
|
||||||
(title "all")
|
(title "all")
|
||||||
(cc/quick-bench
|
(cc/quick-bench
|
||||||
|
|
|
||||||
|
|
@ -185,9 +185,19 @@
|
||||||
:c "1+1"
|
:c "1+1"
|
||||||
:d "1"}))
|
:d "1"}))
|
||||||
|
|
||||||
|
(defn split! []
|
||||||
|
|
||||||
|
(suite "split")
|
||||||
|
|
||||||
|
;; 114ns (String/split)
|
||||||
|
;; 82ns (Trie/split)
|
||||||
|
(test "Splitting a String")
|
||||||
|
(test! impl/segments "/olipa/kerran/:avaruus"))
|
||||||
|
|
||||||
(comment
|
(comment
|
||||||
(url-decode!)
|
(url-decode!)
|
||||||
(url-encode!)
|
(url-encode!)
|
||||||
(form-decode!)
|
(form-decode!)
|
||||||
(form-encode!)
|
(form-encode!)
|
||||||
(url-encode-coll!))
|
(url-encode-coll!)
|
||||||
|
(split!))
|
||||||
|
|
|
||||||
|
|
@ -432,6 +432,7 @@
|
||||||
;; 2065ns
|
;; 2065ns
|
||||||
;; 662ns (prefix-tree-router)
|
;; 662ns (prefix-tree-router)
|
||||||
;; 567ns (segment-router)
|
;; 567ns (segment-router)
|
||||||
|
;; 334ns (java-segment-router)
|
||||||
(b! "reitit" reitit-f)
|
(b! "reitit" reitit-f)
|
||||||
|
|
||||||
;; 2845ns
|
;; 2845ns
|
||||||
|
|
@ -441,6 +442,7 @@
|
||||||
;; 702ns (before path-parameters)
|
;; 702ns (before path-parameters)
|
||||||
;; 806ns (decode path-parameters)
|
;; 806ns (decode path-parameters)
|
||||||
;; 735ns (maybe-map-values)
|
;; 735ns (maybe-map-values)
|
||||||
|
;; 487ns (java-segment-router)
|
||||||
(b! "reitit-ring" reitit-ring-f)
|
(b! "reitit-ring" reitit-ring-f)
|
||||||
|
|
||||||
;; 2821ns
|
;; 2821ns
|
||||||
|
|
|
||||||
|
|
@ -69,14 +69,11 @@
|
||||||
(p/insert acc p d))
|
(p/insert acc p d))
|
||||||
nil routes))
|
nil routes))
|
||||||
|
|
||||||
#_(def reitit-tree
|
|
||||||
(reduce
|
|
||||||
(fn [acc [p d]]
|
|
||||||
(trie/insert acc p d))
|
|
||||||
nil routes))
|
|
||||||
|
|
||||||
(def reitit-segment
|
(def reitit-segment
|
||||||
(segment/create routes))
|
(reduce
|
||||||
|
(fn [acc [p d]]
|
||||||
|
(segment/insert acc p d))
|
||||||
|
nil routes))
|
||||||
|
|
||||||
(defn bench! []
|
(defn bench! []
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,21 +18,31 @@
|
||||||
;; Memory: 16 GB
|
;; Memory: 16 GB
|
||||||
;;
|
;;
|
||||||
|
|
||||||
(def app
|
(defn create-app [options]
|
||||||
(ring/ring-handler
|
(ring/ring-handler
|
||||||
(ring/router
|
(ring/router
|
||||||
[["/auth/login" identity]
|
[["/auth/login" identity]
|
||||||
["/auth/recovery/token/:token" identity]
|
["/auth/recovery/token/:token" identity]
|
||||||
["/workspace/:project/:page" identity]])))
|
["/workspace/:project/:page" identity]])
|
||||||
|
(ring/create-default-handler)
|
||||||
|
options))
|
||||||
|
|
||||||
(comment
|
(defn bench-app []
|
||||||
(let [request {:request-method :post, :uri "/auth/login"}]
|
(let [request {:request-method :post, :uri "/auth/login"}
|
||||||
|
app1 (create-app nil)
|
||||||
|
app2 (create-app {:inject-match? false, :inject-router? false})]
|
||||||
;; 192ns (initial)
|
;; 192ns (initial)
|
||||||
;; 163ns (always assoc path params)
|
;; 163ns (always assoc path params)
|
||||||
;; 132ns (expand methods)
|
;; 132ns (expand methods)
|
||||||
|
;; 111ns (java-segment-router)
|
||||||
(cc/quick-bench
|
(cc/quick-bench
|
||||||
(app request))
|
(app1 request))
|
||||||
|
|
||||||
;; 113ns (don't inject router)
|
;; 113ns (don't inject router)
|
||||||
;; 89ns (don't inject router & match)
|
;; 89ns (don't inject router & match)
|
||||||
))
|
;; 77ns (java-segment-router)
|
||||||
|
(cc/quick-bench
|
||||||
|
(app2 request))))
|
||||||
|
|
||||||
|
(comment
|
||||||
|
(bench-app))
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,8 @@
|
||||||
"modules/reitit-sieppari/src"
|
"modules/reitit-sieppari/src"
|
||||||
"modules/reitit-pedestal/src"]
|
"modules/reitit-pedestal/src"]
|
||||||
|
|
||||||
|
:java-source-paths ["modules/reitit-core/java-src"]
|
||||||
|
|
||||||
:dependencies [[org.clojure/clojure "1.10.0"]
|
:dependencies [[org.clojure/clojure "1.10.0"]
|
||||||
[org.clojure/clojurescript "1.10.439"]
|
[org.clojure/clojurescript "1.10.439"]
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue