又一个 Golang 但零分配并且泛型的动态路由 Tree

2023 年 2 月 9 日
 ClarkAbe

首先上 Benchmark

[neno@ArchOxO tux]$ go test -benchmem -run=^$ -bench ^(BenchmarkRouteTree_Get|BenchmarkRouteTree_Set)$ github.com/ClarkQAQ/tux/tree

goos: linux
goarch: amd64
pkg: github.com/ClarkQAQ/tux/tree
cpu: AMD Ryzen 7 5800H with Radeon Graphics         
BenchmarkRouteTree_Get-16    	130589565	         9.593 ns/op	       0 B/op	       0 allocs/op
BenchmarkRouteTree_Set-16    	12670129	        93.20 ns/op	       1 B/op	       0 allocs/op
PASS
ok  	github.com/ClarkQAQ/tux/tree	4.477s

然后...你们可以喷我了

只是昨天看到 链接uRouter 才想起来我也还有一个 Web 框架...

核心路由树 (不到百行)

由于想法过于疯狂导致半年前我朋友骂我是不是可乐喝多了脑袋坏了, 但是事实证明他是工作的而且还很快


package tree

import "unsafe"

const (
	asterisk = '*' // 通配符
	colon    = ':' // 冒号 (路径参数)
	slash    = '/' // 斜杠
)

// 路由树
type Tree[T any] struct {
	children [255]*Tree[T] // 子路由节点
	vpath    []uint8       // 注册的路由路径
	value    T             // 路由方法 (methodTyp 类型)
}

// 设置路由
func (t *Tree[T]) Set(route string, value T) {
	a := toByte(route)

	for i := 0; i < len(a); i++ {
		if t.children[a[i]] == nil {
			t.children[a[i]] = &Tree[T]{}
		}

		t = t.children[a[i]]

		// 判断是否为路径参数 (例如: :name *name)
		if a[i] == colon {
			i = slashReader(a, i)
		} else if a[i] == asterisk {
			break
		}
	}

	t.value = value
	t.vpath = a
}

// 斜杠读取器
// 读取到下一个斜杠或者结束, 然后返回下标位置
func slashReader(a []uint8, i int) int {
	for ; i < len(a) && a[i] != slash; i++ {
	}

	return i
}

// 获取路由
func (t *Tree[T]) Get(route string) (T, []uint8) {
	a := toByte(route)

	for i := 0; i < len(a); i++ {
		if t.children[asterisk] != nil { // 通配路径参数 (例如: *name)
			t = t.children[asterisk]
			break
		} else if t.children[colon] != nil { // 路径参数 (例如: :name)
			t = t.children[colon]
			i = slashReader(a, i)
		} else {
			if t.children[a[i]] == nil { // 路由不存在
				var v T
				return v, nil
			}

			t = t.children[a[i]]
		}
	}

	return t.value, t.vpath
}

// 转换为字节切片
func toByte(s string) []uint8 {
	return *(*[]uint8)(unsafe.Pointer(&s))
}

// 以下为转换为字节切片的另一种方式, 但是效率较低, 使用他将会比前者慢 4 ns/op
// func toByte(s string) []uint8 {
// 	return []uint8(s)
// }

960 次点击
所在节点    程序员
0 条回复

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://study.congcong.us/t/914528

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX