Skip to content

Commit

Permalink
code style: test all green
Browse files Browse the repository at this point in the history
  • Loading branch information
vm-001 committed Dec 19, 2023
1 parent 4dc78d8 commit ddfc4de
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 135 deletions.
4 changes: 2 additions & 2 deletions benchmark/match-prefix.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ local match_times = 1000 * 1000

local routes = {}
for i = 1, route_count do
routes[i] = {paths = {"/" .. ngx.md5(i) .. "/*"}, metadata = i}
routes[i] = {paths = {"/" .. (i) .. "/*"}, metadata = i}
end

local rx = radix.new(routes)
Expand All @@ -13,7 +13,7 @@ ngx.update_time()
local start_time = ngx.now()

local res
local path = "/" .. ngx.md5(500) .. "/a"
local path = "/" .. 500 .. "/a"
for _ = 1, match_times do
res = rx:match(path)
end
Expand Down
2 changes: 1 addition & 1 deletion spec/router_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ describe("Router", function()
handler = "2",
},
})
assert.equal("1", router:match("/aa"))
assert.equal("1", router:match("/a"))
end)

describe("parameter matching", function()
Expand Down
2 changes: 2 additions & 0 deletions src/resty/router.lua
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ local function find_route(routes, ctx)
if route:is_match(ctx) then
return route
end
return nil
end

for _, route in ipairs(routes) do
if route:is_match(ctx) then
return route
Expand Down
210 changes: 78 additions & 132 deletions src/resty/trie.lua
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ function TrieNode.new(o)
local self = {
path = o.path or "",
children = o.children,
children_n = o.children_n or 0,
type = o.type,
value = o.value, -- 有没有可能这个 Trie 不做 value 的插入,只是返回最后的节点,但是 node.value 的设置交给外部,这样就可以根据 priority 做 value 优先级循序
value = o.value,
indexs = o.indexs or {},
full_path = o.full_path, -- 如果叶子节点可以有多个 values,那么这里的 full_path 不能代表完成的路径
}
Expand Down Expand Up @@ -83,6 +84,7 @@ local function insert_child(node, path, full_path, value, fn)
if token then
local child = TrieNode.new()
node.children = { child }
node.children_n = node.children_n + 1
local c = str_sub(token, 1, 1)
--if c ~= ":" and c ~= "*" then
node.indexs[c] = 1
Expand All @@ -100,6 +102,7 @@ local function split(node, path, prefix_n)
path = str_sub(node.path, prefix_n + 1),
full_path= node.full_path,
children = node.children,
children_n = node.children_n,
type = TYPES.STATIC,
value = node.value,
indexs = node.indexs,
Expand All @@ -110,6 +113,7 @@ local function split(node, path, prefix_n)
node.full_path = nil
node.path_n = #node.path
node.children = { child }
node.children_n = 1
node.type = TYPES.STATIC
node.value = nil
node.indexs = { [str_sub(child.path, 1, 1)] = 1 }
Expand All @@ -127,84 +131,54 @@ function TrieNode:add(path, value, conflict_cb)
local node = self

while true do
-- 换成这样的逻辑吧?
-- while true
-- if condition
-- break;

::continue::

local prefix_n = lcp(path, self.path)
local common_prefix_n = lcp(node.path, path)

if prefix_n < self.path_n then
split(self, path, prefix_n)
if common_prefix_n < node.path_n then
split(node, path, common_prefix_n)
end

if prefix_n < #path then


if self.type == TYPES.PARAM then
-- 当前的节点是 param 类型,所以应该是匹配的
path = str_sub(path, prefix_n + 1)
self = self.children[1]
goto continue
elseif self.type == TYPES.CATCH_ALL then
-- 当前的节点是catch-all类型,而且剩余的 path 是 *xxx的
-- 所以我们应该移除掉 *xxx 然后继续
local first_char = str_sub(path, 1, 1)
if first_char == "*" then
local parser = Parser.new(path, "default")
parser:next()
path = str_sub(path, parser.pos)
if path == "" then
break
end
if common_prefix_n < #path then
if node.type == TYPES.PARAM then
local idx = find(path, "/", nil, true)
if idx then
path = str_sub(path, idx)
else
break
end
elseif node.type == TYPES.CATCH_ALL then
break
else
path = str_sub(path, prefix_n + 1)
path = str_sub(path, common_prefix_n + 1)
end


local first_char = str_sub(path, 1, 1)
local index = self.indexs[first_char]
if index then
self = self.children[index]
if first_char == ":" then
local idx_slash = find(path, "/", nil, true)
if idx_slash then
path = ":" .. str_sub(path, idx_slash)
else
path = ""
break
end
end
if node.indexs[first_char] then
node = node.children[node.indexs[first_char]]
goto continue
end

-- 如果已经没有共同节点,
-- 最后插入到当前节点里?

-- 创建一个新的节点
local child = TrieNode.new()
self.children = self.children or {}
table.insert(self.children, child)
node.children = node.children or {}
table.insert(node.children, child)
node.children_n = node.children_n + 1
insert_child(child, path, full_path, value, conflict_cb)
self.indexs[first_char] = #self.children
node.indexs[first_char] = #node.children -- use self.children_n?
return
end

self:set(value, conflict_cb)
return
break
end

self:set(value, conflict_cb)
node:set(value, conflict_cb)
end

local matched_values = {} -- 叫做 visted_leaf 呢?表示所有访问过的叶子结点?

function TrieNode:traverse(path, ctx)
local binding_params = ctx.matched ~= nil
local param_i = 1
local param_n = 0

local matched_n = 0
clear_table(matched_values)
Expand All @@ -213,40 +187,26 @@ function TrieNode:traverse(path, ctx)
local prefix
local prefix_n
local path_n
local idx

while true do
::continue::

prefix = node.path
prefix_n = node.path_n
path_n = #path
path_n = #path -- path_n 有没有可能可以通过游标去减,而不是每次都 #path?

if path_n > prefix_n then
-- path: /a/b/c
-- prefix: /*
if starts_with(path, prefix, path_n, prefix_n) then
path = str_sub(path, prefix_n + 1)

-- 如果当前的子节点有后缀匹配
-- 要记录下来
--local catch_all_i = 0
--if catch_all_i then
-- local value = self.children[catch_all_i].value
-- if value then
-- table.insert(values, value)
-- end
--end
-- TODO 应该有索引
-- 如果两个路劲冲突了呢?"/aa/*cat", "/aa/*doge"
-- /aa/ children
-- 1:*cat
-- 2:*doge
--
local idx = node.indexs["*"]
idx = node.indexs["*"]
if idx then
local n = node.children[idx]
-- 如果子节点中有 catchall 类型,说明 child 节点是能匹配的
matched_n = matched_n + 1
matched_values[matched_n] = n.value
matched_values[matched_n] = node.children[idx].value -- todo 既然这里已经选中了,为什么不做参数绑定呢?
end

local first_char = str_sub(path, 1, 1)
Expand All @@ -256,38 +216,48 @@ function TrieNode:traverse(path, ctx)
goto continue
end

-- 走到这里后说明当前node的indexs不包含剩下 path 的内容
if not node.children or #node.children == 0 then
return nil, 0
-- 到这里说明有 * 或者 :
-- 如果当前节点既有 * 和 : 子节点呢?
-- /a/*
-- /a/:name/doge
-- /a/:name/dog*
-- match("/a/john/doge")
-- ctx.matched 就会变成 { "john/doge", "john" }
-- 如果外面参数绑定的时候能先去掉 john,再去掉 "/john/doge" 倒叙,那应该是没问题的?

-- 作用是啥?
idx = node.indexs["*"]
if idx then
matched_n = matched_n + 1
matched_values[matched_n] = node.children[idx].value
if binding_params then
param_n = param_n + 1
ctx.matched[param_n] = path
ctx.matched["_path"] = node.children[idx].full_path
end
end


-- 通配符

node = node.children[1] -- 为什么要选 [1] ?
if node.type == TYPES.PARAM then
-- 找到第一个 /
local idx = find(path, "/", 1, true)

if ctx and ctx.matched then
-- parameter binding
--local param_name = sub(self.path, 2) -- :name
local param_value = str_sub(path, 1, idx and idx - 1)
--print("param: " .. param)
ctx.matched[param_i] = param_value
param_i = param_i + 1
--ctx.matched[param_name] = param_value
idx = node.indexs[":"]
if idx then
node = node.children[idx]
local i = find(path, "/", 1, true) or #path + 1 -- todo 写一个 while 来实现
i = i - 1
if binding_params then
local param_value = str_sub(path, 1, i)
param_n = param_n + 1
ctx.matched[param_n] = param_value
end

if idx and idx < #path then
if #node.children > 0 then
path = str_sub(path, idx)
--print(path)
node = node.children[1]
goto continue
if i < #path then -- 还没到终点
if node.children_n > 0 then
path = str_sub(path, i + 1)
first_char = str_sub(path, 1, 1)
idx = node.indexs[first_char]
if idx then
node = node.children[idx]
goto continue
end
end
end

if node.value then
if binding_params then
ctx.matched["_path"] = node.full_path
Expand All @@ -296,25 +266,6 @@ function TrieNode:traverse(path, ctx)
matched_values[matched_n] = node.value
break
end

elseif node.type == TYPES.CATCH_ALL then
-- 前缀匹配
if node.value then
if binding_params then
--local param_name = sub(self.path, 2)
--if #param_name == 0 then
-- param_name = ":ext"
--end
--ctx.matched[param_name] = path
ctx.matched[param_i] = path
param_i = param_i + 1
ctx.matched["_path"] = node.full_path
end
-- 前面的 mached_valued 已经记录过了
break
end
else
--error("???")
end
end
elseif path == prefix then
Expand All @@ -327,20 +278,15 @@ function TrieNode:traverse(path, ctx)
break
end

for _, child in ipairs(node.children or EMPTY) do
if child.type == TYPES.CATCH_ALL then
if binding_params then
--local param_name = sub(child.path, 2)
--if #param_name == 0 then
-- param_name = ":ext"
--end
--ctx.matched[param_name] = ""
ctx.matched[param_i] = ""
param_i = param_i + 1
ctx.matched["_path"] = child.full_path
end
matched_n = matched_n + 1
matched_values[matched_n] = child.value
idx = node.indexs["*"]
if idx then
node = node.children[idx]
matched_n = matched_n + 1
matched_values[matched_n] = node.value
if binding_params then
param_n = param_n + 1
ctx.matched[param_n] = ""
ctx.matched["_path"] = node.full_path
end
end
end
Expand Down

0 comments on commit ddfc4de

Please sign in to comment.