Skip to content

Commit

Permalink
feat: add Len API to track the number of route in the router
Browse files Browse the repository at this point in the history
  • Loading branch information
tigerwill90 committed Nov 30, 2024
1 parent 9b79004 commit e88a4b3
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 0 deletions.
6 changes: 6 additions & 0 deletions fox.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,12 @@ func (fox *Router) Lookup(w ResponseWriter, r *http.Request) (route *Route, cc C
return nil, nil, tsr
}

// Len returns the number of registered route.
func (fox *Router) Len() int {
tree := fox.getRoot()
return tree.size
}

// Iter returns a collection of range iterators for traversing registered methods and routes. It creates a
// point-in-time snapshot of the routing tree. Therefore, all iterators returned by Iter will not observe subsequent
// write on the router. This function is safe for concurrent use by multiple goroutine and while mutation on
Expand Down
8 changes: 8 additions & 0 deletions fox_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,8 @@ func TestStaticRoute(t *testing.T) {
require.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, route.path, w.Body.String())
}

assert.Equal(t, iterutil.Len2(f.Iter().All()), f.Len())
}

func TestStaticRouteTxn(t *testing.T) {
Expand All @@ -664,6 +666,8 @@ func TestStaticRouteTxn(t *testing.T) {
require.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, route.path, w.Body.String())
}

assert.Equal(t, iterutil.Len2(f.Iter().All()), f.Len())
}

func TestStaticRouteWithStaticDomain(t *testing.T) {
Expand All @@ -681,6 +685,8 @@ func TestStaticRouteWithStaticDomain(t *testing.T) {
require.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, route.path, w.Body.String())
}

assert.Equal(t, iterutil.Len2(f.Iter().All()), f.Len())
}

func TestStaticRouteWithStaticDomainTxn(t *testing.T) {
Expand All @@ -703,6 +709,8 @@ func TestStaticRouteWithStaticDomainTxn(t *testing.T) {
require.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, route.path, w.Body.String())
}

assert.Equal(t, iterutil.Len2(f.Iter().All()), f.Len())
}

func TestStaticRouteMalloc(t *testing.T) {
Expand Down
10 changes: 10 additions & 0 deletions tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type iTree struct {
ctx sync.Pool
fox *Router
root roots
size int
maxParams uint32
depth uint32
}
Expand All @@ -27,6 +28,7 @@ func (t *iTree) txn(cache bool) *tXn {
return &tXn{
tree: t,
root: t.root,
size: t.size,
maxParams: t.maxParams,
depth: t.depth,
cache: cache,
Expand All @@ -46,6 +48,7 @@ type tXn struct {
tree *iTree
writable *simplelru.LRU[*node, any]
root roots
size int
maxParams uint32
depth uint32
cache bool
Expand All @@ -55,6 +58,7 @@ func (t *tXn) commit() *iTree {
nt := &iTree{
root: t.root,
fox: t.tree.fox,
size: t.size,
maxParams: t.maxParams,
depth: t.depth,
}
Expand All @@ -76,6 +80,7 @@ func (t *tXn) clone() *tXn {
tx := &tXn{
tree: t.tree,
root: t.root,
size: t.size,
maxParams: t.maxParams,
depth: t.depth,
}
Expand Down Expand Up @@ -204,6 +209,7 @@ func (t *tXn) insert(method string, route *Route, paramsN uint32) error {
result.matched.wildcardChildIndex,
)

t.size++
t.updateMaxParams(paramsN)
result.p.updateEdge(n)
case keyEndMidEdge:
Expand Down Expand Up @@ -244,6 +250,7 @@ func (t *tXn) insert(method string, route *Route, paramsN uint32) error {
[]*node{child},
)

t.size++
t.updateMaxParams(paramsN)
t.updateMaxDepth(result.depth + 1)
result.p.updateEdge(parent)
Expand Down Expand Up @@ -287,6 +294,7 @@ func (t *tXn) insert(method string, route *Route, paramsN uint32) error {
edges,
)

t.size++
t.updateMaxDepth(result.depth + addDepth)
t.updateMaxParams(paramsN)

Expand Down Expand Up @@ -375,6 +383,7 @@ func (t *tXn) insert(method string, route *Route, paramsN uint32) error {
// n3 children never start with a param
n3 := newNode(cPrefix, nil, []*node{n1, n2}) // intermediary node

t.size++
t.updateMaxDepth(result.depth + addDepth)
t.updateMaxParams(paramsN)
result.p.updateEdge(n3)
Expand Down Expand Up @@ -427,6 +436,7 @@ func (t *tXn) remove(method, path string) bool {
return false
}

t.size--
if len(result.matched.children) > 1 {
n := newNodeFromRef(
result.matched.key,
Expand Down
5 changes: 5 additions & 0 deletions txn.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,11 @@ func (txn *Txn) Iter() Iter {
}
}

// Len returns the number of registered route.
func (txn *Txn) Len() int {
return txn.rootTxn.size
}

// Commit finalize the transaction. This is a noop for read transactions, already aborted or
// committed transactions. This function is NOT thread-safe and should be run serially,
// along with all other [Txn] APIs.
Expand Down
21 changes: 21 additions & 0 deletions txn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,27 @@ func TestTxn_Isolation(t *testing.T) {
assert.Equal(t, err, want)
assert.Equal(t, len(staticRoutes), iterutil.Len2(f.Iter().All()))
})

t.Run("track registered route", func(t *testing.T) {
f := New()
require.NoError(t, f.Updates(func(txn *Txn) error {
for _, rte := range staticRoutes {
if _, err := txn.Handle(rte.method, "example.com"+rte.path, emptyHandler); err != nil {
return err
}
}
assert.Equal(t, len(staticRoutes), txn.Len())

for _, rte := range staticRoutes {
if err := txn.Delete(rte.method, "example.com"+rte.path); err != nil {
return err
}
}
assert.Zero(t, txn.Len())

return nil
}))
})
}

func TestTxn_WriteOnReadTransaction(t *testing.T) {
Expand Down

0 comments on commit e88a4b3

Please sign in to comment.