From 7cc10ab6b90a3e643f15eb96fbc6d4a4f42d86e2 Mon Sep 17 00:00:00 2001 From: Sky Johnson Date: Thu, 5 Sep 2024 14:09:09 -0500 Subject: [PATCH] Simplify by removing generics --- router.go | 34 +++++++++++++++++----------------- tests/benchmark_test.go | 4 ++-- tests/router_test.go | 2 +- tree.go | 18 +++++++++--------- treeNode.go | 38 +++++++++++++++++++------------------- 5 files changed, 48 insertions(+), 48 deletions(-) diff --git a/router.go b/router.go index 839fade..d9bfa7d 100644 --- a/router.go +++ b/router.go @@ -1,30 +1,30 @@ package router -type Router[T any] struct { - get Tree[T] - post Tree[T] - delete Tree[T] - put Tree[T] - patch Tree[T] - head Tree[T] - connect Tree[T] - trace Tree[T] - options Tree[T] +type Router struct { + get Tree + post Tree + delete Tree + put Tree + patch Tree + head Tree + connect Tree + trace Tree + options Tree } // Create a new Router containing trees for every HTTP method. -func New[T any]() *Router[T] { - return &Router[T]{} +func New() *Router { + return &Router{} } // Registers a new handler for the given method and path. -func (router *Router[T]) Add(method string, path string, handler T) { +func (router *Router) Add(method string, path string, handler string) { tree := router.selectTree(method) tree.Add(path, handler) } // Finds the handler and parameters for the given route. -func (router *Router[T]) Lookup(method string, path string) (T, []Parameter) { +func (router *Router) Lookup(method string, path string) (string, []Parameter) { if method[0] == 'G' { return router.get.Lookup(path) } @@ -34,7 +34,7 @@ func (router *Router[T]) Lookup(method string, path string) (T, []Parameter) { } // Finds the handler and parameters for the given route without using any memory allocations. -func (router *Router[T]) LookupNoAlloc(method string, path string, addParameter func(string, string)) T { +func (router *Router) LookupNoAlloc(method string, path string, addParameter func(string, string)) string { if method[0] == 'G' { return router.get.LookupNoAlloc(path, addParameter) } @@ -44,7 +44,7 @@ func (router *Router[T]) LookupNoAlloc(method string, path string, addParameter } // Traverses all trees and calls the given function on every node. -func (router *Router[T]) Map(transform func(T) T) { +func (router *Router) Map(transform func(string) string) { router.get.Map(transform) router.post.Map(transform) router.delete.Map(transform) @@ -57,7 +57,7 @@ func (router *Router[T]) Map(transform func(T) T) { } // Returns the tree of the given HTTP method. -func (router *Router[T]) selectTree(method string) *Tree[T] { +func (router *Router) selectTree(method string) *Tree { switch method { case "GET": return &router.get diff --git a/tests/benchmark_test.go b/tests/benchmark_test.go index fa22e7f..c551fb4 100644 --- a/tests/benchmark_test.go +++ b/tests/benchmark_test.go @@ -11,7 +11,7 @@ import ( func BenchmarkBlog(b *testing.B) { routes := routes("blog.txt") - r := router.New[string]() + r := router.New() for _, route := range routes { r.Add(route.Method, route.Path, "") @@ -44,7 +44,7 @@ func BenchmarkBlog(b *testing.B) { func BenchmarkGithub(b *testing.B) { routes := routes("github.txt") - r := router.New[string]() + r := router.New() for _, route := range routes { r.Add(route.Method, route.Path, "") diff --git a/tests/router_test.go b/tests/router_test.go index 8c5abdc..7b8ecb1 100644 --- a/tests/router_test.go +++ b/tests/router_test.go @@ -7,7 +7,7 @@ import ( ) func TestHello(t *testing.T) { - r := router.New[string]() + r := router.New() r.Add("GET", "/blog", "Blog") r.Add("GET", "/blog/post", "Blog post") diff --git a/tree.go b/tree.go index bd1d438..a8df8ff 100644 --- a/tree.go +++ b/tree.go @@ -1,12 +1,12 @@ package router // Super-fancy radix tree -type Tree[T any] struct { - root TreeNode[T] +type Tree struct { + root TreeNode } // Adds a new element to the tree -func (tree *Tree[T]) Add(path string, data T) { +func (tree *Tree) Add(path string, data string) { // Search tree for equal parts until we can no longer proceed i := 0 offset := 0 @@ -79,7 +79,7 @@ func (tree *Tree[T]) Add(path string, data T) { } // Lookup finds the data for the given path. -func (tree *Tree[T]) Lookup(path string) (T, []Parameter) { +func (tree *Tree) Lookup(path string) (string, []Parameter) { var params []Parameter data := tree.LookupNoAlloc(path, func(key string, value string) { @@ -90,11 +90,11 @@ func (tree *Tree[T]) Lookup(path string) (T, []Parameter) { } // Finds the data for the given path without using any memory allocations. -func (tree *Tree[T]) LookupNoAlloc(path string, addParameter func(key string, value string)) T { +func (tree *Tree) LookupNoAlloc(path string, addParameter func(key string, value string)) string { var ( i uint wildcardPath string - wildcard *TreeNode[T] + wildcard *TreeNode node = &tree.root ) @@ -183,13 +183,13 @@ notFound: return wildcard.data } - var empty T + var empty string return empty } // Binds all handlers to a new one provided by the callback. -func (tree *Tree[T]) Map(transform func(T) T) { - tree.root.each(func(node *TreeNode[T]) { +func (tree *Tree) Map(transform func(string) string) { + tree.root.each(func(node *TreeNode) { node.data = transform(node.data) }) } diff --git a/treeNode.go b/treeNode.go index 1eb269b..748be90 100644 --- a/treeNode.go +++ b/treeNode.go @@ -10,12 +10,12 @@ const ( ) // A node on our radix tree -type TreeNode[T any] struct { +type TreeNode struct { prefix string - data T - children []*TreeNode[T] - parameter *TreeNode[T] - wildcard *TreeNode[T] + data string + children []*TreeNode + parameter *TreeNode + wildcard *TreeNode indexes []uint8 start uint8 end uint8 @@ -26,7 +26,7 @@ type TreeNode[T any] struct { // node with the given path and data. If path is empty, it will // not create another child node and instead assign the data // directly to the node. -func (node *TreeNode[T]) split(index int, path string, data T) { +func (node *TreeNode) split(index int, path string, data string) { // Create split node with the remaining string splitNode := node.clone(node.prefix[index:]) @@ -48,8 +48,8 @@ func (node *TreeNode[T]) split(index int, path string, data T) { } // Clone the node with a new prefix -func (node *TreeNode[T]) clone(prefix string) *TreeNode[T] { - return &TreeNode[T]{ +func (node *TreeNode) clone(prefix string) *TreeNode { + return &TreeNode{ prefix: prefix, data: node.data, children: node.children, @@ -63,8 +63,8 @@ func (node *TreeNode[T]) clone(prefix string) *TreeNode[T] { } // Reset the node, set the prefix -func (node *TreeNode[T]) reset(prefix string) { - var empty T +func (node *TreeNode) reset(prefix string) { + var empty string node.prefix = prefix node.data = empty node.children = nil @@ -77,7 +77,7 @@ func (node *TreeNode[T]) reset(prefix string) { } // Append the given path to the tree -func (node *TreeNode[T]) append(path string, data T) { +func (node *TreeNode) append(path string, data string) { // At this point, all we know is that somewhere // in the remaining string we have parameters. // node: /user| @@ -106,7 +106,7 @@ func (node *TreeNode[T]) append(path string, data T) { return } - child := &TreeNode[T]{ + child := &TreeNode{ prefix: path, data: data, } @@ -125,7 +125,7 @@ func (node *TreeNode[T]) append(path string, data T) { paramEnd = len(path) } - child := &TreeNode[T]{ + child := &TreeNode{ prefix: path[1:paramEnd], kind: path[paramStart], } @@ -156,7 +156,7 @@ func (node *TreeNode[T]) append(path string, data T) { } // Add a normal node with the path before the parameter start. - child := &TreeNode[T]{ + child := &TreeNode{ prefix: path[:paramStart], } @@ -173,7 +173,7 @@ func (node *TreeNode[T]) append(path string, data T) { } // Add a child tree node -func (node *TreeNode[T]) addChild(child *TreeNode[T]) { +func (node *TreeNode) addChild(child *TreeNode) { if len(node.children) == 0 { node.children = append(node.children, nil) } @@ -213,19 +213,19 @@ func (node *TreeNode[T]) addChild(child *TreeNode[T]) { node.children[index] = child } -func (node *TreeNode[T]) addTrailingSlash(data T) { +func (node *TreeNode) addTrailingSlash(data string) { if strings.HasSuffix(node.prefix, "/") || node.kind == wildcard || (separator >= node.start && separator < node.end && node.indexes[separator-node.start] != 0) { return } - node.addChild(&TreeNode[T]{ + node.addChild(&TreeNode{ prefix: "/", data: data, }) } // Traverses the tree and calls the given function on every node. -func (node *TreeNode[T]) each(callback func(*TreeNode[T])) { +func (node *TreeNode) each(callback func(*TreeNode)) { callback(node) for _, child := range node.children { @@ -247,7 +247,7 @@ func (node *TreeNode[T]) each(callback func(*TreeNode[T])) { // Called when the node was fully parsed and needs to decide the next control flow. // finish is only called from `tree.Add`. -func (node *TreeNode[T]) finish(path string, data T, i int, offset int) (*TreeNode[T], int, Flow) { +func (node *TreeNode) finish(path string, data string, i int, offset int) (*TreeNode, int, Flow) { char := path[i] if char >= node.start && char < node.end {