Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package io.vertx.web.sync.impl;

import io.vertx.web.sync.WebHandler;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;

import java.util.ArrayList;
import java.util.List;

import static io.vertx.web.sync.impl.Node.Type.*;
import static io.vertx.web.sync.impl.CTrie.Type.*;

public class Node {
public class CTrie<T> {

enum Type {
STATIC,
Expand All @@ -33,22 +34,22 @@ private static int countParams(String path) {
private Type type;
private int maxParams;
private String indices;
private List<Node> children;
private WebHandler[] handle;
private List<CTrie<T>> children;
private LList<T> data;
private int priority;

Node() {
public CTrie() {
this("", false, STATIC, 0, "", new ArrayList<>(), null, 0);
}

private Node(String path, boolean wildChild, Type type, int maxParams, String indices, List<Node> children, WebHandler[] handle, int priority) {
private CTrie(String path, boolean wildChild, Type type, int maxParams, String indices, List<CTrie<T>> children, LList<T> data, int priority) {
this.path = path;
this.wildChild = wildChild;
this.type = type;
this.maxParams = maxParams;
this.indices = indices;
this.children = children;
this.handle = handle;
this.data = data;
this.priority = priority;
}

Expand All @@ -59,7 +60,7 @@ private int addPriority(int pos) {
// Adjust position (move to from)
int newPos = pos;
while (newPos > 0 && children.get(newPos - 1).priority < prio) {
final Node temp = children.get(newPos);
final CTrie<T> temp = children.get(newPos);
children.set(newPos, children.get(newPos - 1));
children.set(newPos - 1, temp);
newPos--;
Expand All @@ -77,8 +78,8 @@ private int addPriority(int pos) {
return newPos;
}

private void insertChild(int numParams, String path, String fullPath, WebHandler[] handle) {
Node n = this;
private void insertChild(int numParams, String path, String fullPath, T handle) {
CTrie<T> n = this;
int offset = 0; // Already handled chars of the path

// Find prefix until first wildcard
Expand Down Expand Up @@ -116,7 +117,7 @@ private void insertChild(int numParams, String path, String fullPath, WebHandler
offset = i;
}

final Node child = new Node("", false, PARAM, numParams, "", new ArrayList<>(), null, 0);
final CTrie<T> child = new CTrie<>("", false, PARAM, numParams, "", new ArrayList<>(), null, 0);
n.children = new ArrayList<>();
n.children.add(child);
n.wildChild = true;
Expand All @@ -127,7 +128,7 @@ private void insertChild(int numParams, String path, String fullPath, WebHandler
n.path = path.substring(offset, end);
offset = end;

final Node staticChild = new Node(
final CTrie<T> staticChild = new CTrie<>(
"",
false,
STATIC,
Expand Down Expand Up @@ -158,22 +159,22 @@ private void insertChild(int numParams, String path, String fullPath, WebHandler
n.path = path.substring(offset, i);

// first node: catchAll node with empty path
final Node catchAllChild = new Node("", true, CATCH_ALL, 1, "", new ArrayList<>(), null, 0);
final CTrie<T> catchAllChild = new CTrie<>("", true, CATCH_ALL, 1, "", new ArrayList<>(), null, 0);
n.children = new ArrayList<>();
n.children.add(catchAllChild);
n.indices = path.substring(i, i + 1);
n = catchAllChild;
n.priority++;

// second node: node holding the variable
final Node child = new Node(
final CTrie<T> child = new CTrie<>(
path.substring(i),
false,
CATCH_ALL,
1,
"",
new ArrayList<>(),
handle,
new LList<>(handle),
1
);
n.children = new ArrayList<>();
Expand All @@ -185,12 +186,12 @@ private void insertChild(int numParams, String path, String fullPath, WebHandler

// insert remaining path part and handle to the leaf
n.path = path.substring(offset);
n.handle = handle;
n.data = new LList<>(handle);
}


public void addRoute(String path, WebHandler... handle) {
Node n = this;
public void add(String path, T handle) {
CTrie<T> n = this;
String fullPath = path;

n.priority++;
Expand All @@ -216,14 +217,14 @@ public void addRoute(String path, WebHandler... handle) {

// Split edge
if (i < n.path.length()) {
final Node child = new Node(
final CTrie<T> child = new CTrie<>(
n.path.substring(i),
n.wildChild,
STATIC,
0,
n.indices,
n.children,
n.handle,
n.data,
n.priority - 1
);

Expand All @@ -238,7 +239,7 @@ public void addRoute(String path, WebHandler... handle) {
n.children.add(child);
n.indices = n.path.substring(i, i + 1);
n.path = path.substring(0, i);
n.handle = null;
n.data = null;
n.wildChild = false;
}

Expand Down Expand Up @@ -298,7 +299,7 @@ public void addRoute(String path, WebHandler... handle) {
// Otherwise insert it
if (c != ':' && c != '*') {
n.indices += c;
final Node child = new Node(
final CTrie<T> child = new CTrie<>(
"",
false,
STATIC,
Expand All @@ -316,10 +317,12 @@ public void addRoute(String path, WebHandler... handle) {
return;
} else if (i == path.length()) {
// Make node a (in-path leaf)
if (n.handle != null) {
throw new IllegalStateException("A handle is already registered for path '" + fullPath + "'");
if (n.data != null) {
System.out.println("A handle is already registered for path '" + fullPath + "'");
n.data.push(handle);
} else {
n.data = new LList<>(handle);
}
n.handle = handle;
}
return;
}
Expand All @@ -332,22 +335,22 @@ public void addRoute(String path, WebHandler... handle) {

}

public WebHandler[] search(final RoutingContextInternal ctx) {
public LList<T> search(final RoutingContextInternal ctx) {
return search(ctx, true);
}

public WebHandler[] lookup(final RoutingContextInternal ctx) {
public LList<T> lookup(final RoutingContextInternal ctx) {
return search(ctx, false);
}

private WebHandler[] search(final RoutingContextInternal ctx, boolean updateParams) {
private LList<T> search(final RoutingContextInternal ctx, boolean updateParams) {
String path = ctx.path();
Node n = this;
CTrie<T> n = this;

walk:
while (true) {
if (path.length() > n.path.length()) {
if (path.substring(0, n.path.length()).equals(n.path)) {
if (path.startsWith(n.path)) {
path = path.substring(n.path.length());
// If this node does not have a wildcard child,
// we can just look up the next child node and continue
Expand Down Expand Up @@ -392,35 +395,62 @@ private WebHandler[] search(final RoutingContextInternal ctx, boolean updatePara
return null;
}

return n.handle;
return n.data;

case CATCH_ALL:
if (updateParams) {
ctx.addParam(n.path.substring(2), path);
}
return n.handle;
return n.data;

default:
throw new RuntimeException("invalid node type");
}
}
} else if (path.equals(n.path)) {
return n.handle;
return n.data;
}

return null;
}
}

private void printTree(String prefix) {
System.out.println(" " + priority + ":" + maxParams + " " + prefix + path + "[" + children.size() + "] " + handle + " " + wildChild + " " + type);
public JsonObject toJson() {
final JsonObject json = new JsonObject()
.put("path", path)
.put("wildChild", wildChild)
.put("type", type)
.put("maxParams", maxParams)
.put("indices", indices);

if (children != null) {
JsonArray arr = new JsonArray();
json.put("children", arr);
for (CTrie<T> trie : children) {
arr.add(trie.toJson());
}
}

for (int l = path.length(); l > 0; l--) {
prefix += " ";
if (data != null) {
JsonArray arr = new JsonArray();
json.put("data", arr);
data.forEach(el -> {
arr.add(el.toString());
});
}

for (Node child : children) {
child.printTree(prefix);
json.put("priority", priority);

return json;
}

private void printTree(String prefix) {
System.out.println(" " + priority + ":" + maxParams + " " + prefix + path + "[" + children.size() + "] " + data + " " + wildChild + " " + type);

String indent = " ".repeat(path.length());

for (CTrie<T> child : children) {
child.printTree(indent);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package io.vertx.web.sync.impl;

import java.util.function.Consumer;
import java.util.function.Function;

public class LList<T> {

Node<T> head;
Node<T> tail;

// Linked list Node.
static class Node<T> {
T data;
Node<T> next;

// Constructor
Node(T d) {
data = d;
next = null;
}
}

LList(T data) {
push(data);
}

LList() {
}

public void push(T data) {
// Create a new node with given data
Node<T> new_node = new Node<>(data);

// If the Linked List is empty,
// then make the new node as head
if (head == null) {
head = new_node;
} else {
// Insert the new_node at tail node
tail.next = new_node;
}
// update tail
tail = new_node;
}

public void forEach(Function<T, Boolean> consumer) {
Node<T> currNode = head;
// Traverse
while (currNode != null) {
if (consumer.apply(currNode.data)) {
// Go to next node
currNode = currNode.next;
} else {
return;
}
}
}

public void forEach(Consumer<T> consumer) {
Node<T> currNode = head;
// Traverse
while (currNode != null) {
consumer.accept(currNode.data);
// Go to next node
currNode = currNode.next;
}
}
}
Loading