@@ -328,52 +328,45 @@ func (n *node) checkMethodNotAllowed() HandlerFunc {
328
328
// - Reset it `Context#Reset()`
329
329
// - Return it `Echo#ReleaseContext()`.
330
330
func (r * Router ) Find (method , path string , c Context ) {
331
- const backTrackingDepth = 10
332
-
333
331
ctx := c .(* context )
334
332
ctx .path = path
335
333
cn := r .tree // Current node as root
336
334
337
335
var (
338
- search = path
339
- n int // Param counter
340
- pvalues = ctx .pvalues // Use the internal slice so the interface can keep the illusion of a dynamic slice
341
-
342
- // Backtracking Information
343
- state [backTrackingDepth ]struct {
344
- nk kind
345
- nn * node
346
- ns string
347
- np int
348
- }
349
- stateIndex int = - 1
336
+ search = path
337
+ searchIndex = 0
338
+ n int // Param counter
339
+ pvalues = ctx .pvalues // Use the internal slice so the interface can keep the illusion of a dynamic slice
350
340
)
351
341
352
- pushNext := func (nodeKind kind ) {
353
- stateIndex ++
354
- if stateIndex >= backTrackingDepth {
355
- panic ("Max backtracking depth reached. TODO: this must be detected during registering the paths" )
356
- }
342
+ // backtracking happens when we reach dead end when matching nodes in the router tree. To backtrack we set
343
+ // current node to parent to current node (this was previous node we checked) and choose next node kind to check.
344
+ // Next node kind relies on routing priority (static->param->any). So for example if there is no static node match we
345
+ // should check parent next sibling by kind (param). Backtracking itself does not check if there is next sibling this
346
+ // is left up to matching logic
347
+ backtrackToNextNodeKind := func (fromKind kind ) (nextNodeKind kind , valid bool ) {
348
+ previous := cn
349
+ cn = previous .parent
350
+ valid = cn != nil
357
351
358
- state [stateIndex ].nk = nodeKind
359
- state [stateIndex ].nn = cn
360
- state [stateIndex ].ns = search
361
- state [stateIndex ].np = n
362
- }
352
+ // next node type by priority
353
+ // NOTE: by current implementation we never backtrack from any route so `previous.kind` value is here always static or any
354
+ // if this requirement is to change then for any route next kind would be static kind and this statement should be changed
355
+ nextNodeKind = previous .kind + 1
363
356
364
- popNext := func () ( nodeKind kind , valid bool ) {
365
- if stateIndex < 0 {
357
+ if fromKind == skind {
358
+ // when backtracking is done from static kind block we did not change search so nothing to restore
366
359
return
367
360
}
368
361
369
- last := state [ stateIndex ]
370
- stateIndex --
371
-
372
- nodeKind = last . nk
373
- cn = last . nn
374
- search = last . ns
375
- n = last . np
376
- valid = cn != nil
362
+ if previous . kind == skind {
363
+ searchIndex -= len ( previous . prefix )
364
+ search = path [ searchIndex :]
365
+ } else {
366
+ n --
367
+ searchIndex -= len ( pvalues [ n ])
368
+ search = path [ searchIndex :]
369
+ }
377
370
return
378
371
}
379
372
@@ -397,21 +390,23 @@ func (r *Router) Find(method, path string, c Context) {
397
390
398
391
if l != pl {
399
392
// No matching prefix, let's backtrack to the first possible alternative node of the decision path
400
- nk , ok := popNext ( )
393
+ nk , ok := backtrackToNextNodeKind ( skind )
401
394
if ! ok {
402
395
return // No other possibilities on the decision path
403
396
} else if nk == pkind {
404
397
goto Param
405
- } else if nk == akind {
406
- goto Any
398
+ // NOTE: this case (backtracking from static node to previous any node) can not happen by current any matching logic. Any node is end of search currently
399
+ //} else if nk == akind {
400
+ // goto Any
407
401
} else {
408
- // Not found
402
+ // Not found (this should never be possible for static node we are looking currently)
409
403
return
410
404
}
411
405
}
412
406
413
407
// The full prefix has matched, remove the prefix from the remaining search
414
408
search = search [l :]
409
+ searchIndex = searchIndex + l
415
410
416
411
// Finish routing if no remaining search and we are on an leaf node
417
412
if search == "" && cn .ppath != "" {
@@ -421,12 +416,6 @@ func (r *Router) Find(method, path string, c Context) {
421
416
// Static node
422
417
if search != "" {
423
418
if child := cn .findStaticChild (search [0 ]); child != nil {
424
- if cn .paramChildren != nil || cn .anyChildren != nil {
425
- // Push a new entry into the decision path, if we don't find anything downtree
426
- // try the current node again searching for a param or any node
427
- // Optimization: The node is only pushed for backtracking if there's an praramChildren or an anyChildren
428
- pushNext (pkind )
429
- }
430
419
cn = child
431
420
continue
432
421
}
@@ -435,20 +424,14 @@ func (r *Router) Find(method, path string, c Context) {
435
424
Param:
436
425
// Param node
437
426
if child := cn .paramChildren ; search != "" && child != nil {
438
- if cn .anyChildren != nil {
439
- // Push a new entry into the decision path, if we have nothing found downtree try the current node again
440
- // searching for an any node.
441
- // Optimization: The node is only pushed for backtracking if there's an anyChildren
442
- pushNext (akind )
443
- }
444
-
445
427
cn = child
446
428
i , l := 0 , len (search )
447
429
for ; i < l && search [i ] != '/' ; i ++ {
448
430
}
449
431
pvalues [n ] = search [:i ]
450
432
n ++
451
433
search = search [i :]
434
+ searchIndex = searchIndex + i
452
435
continue
453
436
}
454
437
@@ -462,7 +445,7 @@ func (r *Router) Find(method, path string, c Context) {
462
445
}
463
446
464
447
// Let's backtrack to the first possible alternative node of the decision path
465
- nk , ok := popNext ( )
448
+ nk , ok := backtrackToNextNodeKind ( akind )
466
449
if ! ok {
467
450
return // No other possibilities on the decision path
468
451
} else if nk == pkind {
0 commit comments