@@ -7,7 +7,6 @@ package fiber
7
7
import (
8
8
"fmt"
9
9
"html"
10
- "sort"
11
10
"strconv"
12
11
"strings"
13
12
"sync/atomic"
@@ -47,9 +46,8 @@ type Router interface {
47
46
48
47
// Route is a struct that holds all metadata for each registered handler.
49
48
type Route struct {
50
- // ### important: always keep in sync with the copy method "app.copyRoute" ###
49
+ // ### important: always keep in sync with the copy method "app.copyRoute" and all creations of Route struct ###
51
50
// Data for routing
52
- pos uint32 // Position in stack -> important for the sort of the matched routes
53
51
use bool // USE matches path prefixes
54
52
mount bool // Indicated a mounted app on a specific route
55
53
star bool // Path equals '*'
@@ -215,9 +213,6 @@ func (*App) copyRoute(route *Route) *Route {
215
213
path : route .path ,
216
214
routeParser : route .routeParser ,
217
215
218
- // misc
219
- pos : route .pos ,
220
-
221
216
// Public data
222
217
Path : route .Path ,
223
218
Params : route .Params ,
@@ -298,11 +293,11 @@ func (app *App) register(method, pathRaw string, group *Group, handlers ...Handl
298
293
for _ , m := range app .config .RequestMethods {
299
294
// Create a route copy to avoid duplicates during compression
300
295
r := route
301
- app .addRoute (m , & r , isMount )
296
+ app .addRoute (m , & r )
302
297
}
303
298
} else {
304
299
// Add route to stack
305
- app .addRoute (method , & route , isMount )
300
+ app .addRoute (method , & route )
306
301
}
307
302
}
308
303
@@ -428,12 +423,20 @@ func (app *App) registerStatic(prefix, root string, config ...Static) {
428
423
// Create route metadata without pointer
429
424
route := Route {
430
425
// Router booleans
431
- use : true ,
432
- root : isRoot ,
426
+ use : true ,
427
+ mount : false ,
428
+ star : isStar ,
429
+ root : isRoot ,
430
+
431
+ // Path data
433
432
path : prefix ,
433
+
434
+ // Group data
435
+ group : nil ,
436
+
434
437
// Public data
435
- Method : MethodGet ,
436
438
Path : prefix ,
439
+ Method : MethodGet ,
437
440
Handlers : []Handler {handler },
438
441
}
439
442
// Increment global handler count
@@ -444,13 +447,7 @@ func (app *App) registerStatic(prefix, root string, config ...Static) {
444
447
app .addRoute (MethodHead , & route )
445
448
}
446
449
447
- func (app * App ) addRoute (method string , route * Route , isMounted ... bool ) {
448
- // Check mounted routes
449
- var mounted bool
450
- if len (isMounted ) > 0 {
451
- mounted = isMounted [0 ]
452
- }
453
-
450
+ func (app * App ) addRoute (method string , route * Route ) {
454
451
// Get unique HTTP method identifier
455
452
m := app .methodInt (method )
456
453
@@ -460,16 +457,14 @@ func (app *App) addRoute(method string, route *Route, isMounted ...bool) {
460
457
preRoute := app .stack [m ][l - 1 ]
461
458
preRoute .Handlers = append (preRoute .Handlers , route .Handlers ... )
462
459
} else {
463
- // Increment global route position
464
- route .pos = atomic .AddUint32 (& app .routesCount , 1 )
465
460
route .Method = method
466
461
// Add route to the stack
467
462
app .stack [m ] = append (app .stack [m ], route )
468
463
app .routesRefreshed = true
469
464
}
470
465
471
466
// Execute onRoute hooks & change latestRoute if not adding mounted route
472
- if ! mounted {
467
+ if ! route . mount {
473
468
app .mutex .Lock ()
474
469
app .latestRoute = route
475
470
if err := app .hooks .executeOnRouteHooks (* route ); err != nil {
@@ -481,38 +476,59 @@ func (app *App) addRoute(method string, route *Route, isMounted ...bool) {
481
476
482
477
// buildTree build the prefix tree from the previously registered routes
483
478
func (app * App ) buildTree () * App {
479
+ // If routes haven't been refreshed, nothing to do
484
480
if ! app .routesRefreshed {
485
481
return app
486
482
}
487
483
488
- // loop all the methods and stacks and create the prefix tree
489
- for m := range app .config .RequestMethods {
490
- tsMap := make (map [string ][]* Route )
491
- for _ , route := range app .stack [m ] {
492
- treePath := ""
484
+ // 1) First loop: determine all possible 3-char prefixes ("treePaths") for each method
485
+ for method := range app .config .RequestMethods {
486
+ prefixSet := map [string ]struct {}{
487
+ "" : {},
488
+ }
489
+ for _ , route := range app .stack [method ] {
493
490
if len (route .routeParser .segs ) > 0 && len (route .routeParser .segs [0 ].Const ) >= 3 {
494
- treePath = route .routeParser .segs [0 ].Const [:3 ]
491
+ prefix := route .routeParser .segs [0 ].Const [:3 ]
492
+ prefixSet [prefix ] = struct {}{}
495
493
}
496
- // create tree stack
497
- tsMap [treePath ] = append (tsMap [treePath ], route )
498
494
}
499
- app .treeStack [m ] = tsMap
500
- }
495
+ tsMap := make (map [string ][]* Route , len (prefixSet ))
496
+ for prefix := range prefixSet {
497
+ tsMap [prefix ] = nil
498
+ }
499
+ app .treeStack [method ] = tsMap
500
+ }
501
+
502
+ // 2) Second loop: for each method and each discovered treePath, assign matching routes
503
+ for method := range app .config .RequestMethods {
504
+ // get the map of buckets for this method
505
+ tsMap := app .treeStack [method ]
506
+
507
+ // for every treePath key (including the empty one)
508
+ for treePath := range tsMap {
509
+ // iterate all routes of this method
510
+ for _ , route := range app .stack [method ] {
511
+ // compute this route's own prefix ("" or first 3 chars)
512
+ routePath := ""
513
+ if len (route .routeParser .segs ) > 0 && len (route .routeParser .segs [0 ].Const ) >= 3 {
514
+ routePath = route .routeParser .segs [0 ].Const [:3 ]
515
+ }
501
516
502
- // loop the methods and tree stacks and add global stack and sort everything
503
- for m := range app . config . RequestMethods {
504
- tsMap := app . treeStack [ m ]
505
- for treePart := range tsMap {
506
- if treePart != "" {
507
- // merge global tree routes in current tree stack
508
- tsMap [ treePart ] = uniqueRouteStack ( append ( tsMap [ treePart ], tsMap [ "" ] ... ))
517
+ // if it's a global route, assign to every bucket
518
+ if routePath == "" {
519
+ tsMap [ treePath ] = append ( tsMap [ treePath ], route )
520
+ // otherwise only assign if this route's prefix matches the current bucket's key
521
+ } else if routePath == treePath {
522
+ tsMap [ treePath ] = append ( tsMap [ treePath ], route )
523
+ }
509
524
}
510
- // sort tree slices with the positions
511
- slc := tsMap [ treePart ]
512
- sort . Slice ( slc , func ( i , j int ) bool { return slc [ i ]. pos < slc [ j ]. pos } )
525
+
526
+ // after collecting, dedupe the bucket if it's not the global one
527
+ tsMap [ treePath ] = uniqueRouteStack ( tsMap [ treePath ] )
513
528
}
514
529
}
515
- app .routesRefreshed = false
516
530
531
+ // reset the flag and return
532
+ app .routesRefreshed = false
517
533
return app
518
534
}
0 commit comments