github.com/gramework/gramework@v1.8.1-0.20231027140105-82555c9057f5/fasthttprouter_tree.go (about) 1 // Copyright 2013 Julien Schmidt. All rights reserved. 2 // Copyright (c) 2015-2016, 招牌疯子 3 // Copyright (c) 2017, Kirill Danshin 4 // Use of this source code is governed by a BSD-style license that can be found 5 // in the 3rd-Party License/fasthttprouter file. 6 7 package gramework 8 9 import ( 10 "strings" 11 "unicode" 12 "unicode/utf8" 13 ) 14 15 type ( 16 nodeType uint8 17 18 node struct { 19 path string 20 wildChild bool 21 nType nodeType 22 maxParams uint8 23 indices string 24 children []*node 25 handle RequestHandler 26 priority uint32 27 hits uint32 28 router *router 29 prefixes []string 30 } 31 ) 32 33 // Slash constant used to minimize string allocations 34 const Slash = "/" 35 36 const ( 37 static nodeType = iota // default 38 root 39 param 40 catchAll 41 ) 42 43 func min(a, b int) int { 44 if a <= b { 45 return a 46 } 47 return b 48 } 49 50 func countParams(path string) uint8 { 51 var n uint 52 for i := zero; i < len(path); i++ { 53 if path[i] != ':' && path[i] != '*' { 54 continue 55 } 56 n++ 57 } 58 if n >= 255 { 59 return 255 60 } 61 return uint8(n) 62 } 63 64 // increments priority of the given child and reorders if necessary 65 func (n *node) incrementChildPrio(pos int) int { 66 n.children[pos].priority++ 67 prio := n.children[pos].priority 68 69 // adjust position (move to front) 70 newPos := pos 71 for newPos > zero && n.children[newPos-one].priority < prio { 72 // swap node positions 73 tmpN := n.children[newPos-one] 74 n.children[newPos-one] = n.children[newPos] 75 n.children[newPos] = tmpN 76 77 newPos-- 78 } 79 80 // build new index char string 81 if newPos != pos { 82 n.indices = n.indices[:newPos] + // unchanged prefix, might be empty 83 n.indices[pos:pos+one] + // the index char we move 84 n.indices[newPos:pos] + n.indices[pos+one:] // rest without char at 'pos' 85 } 86 87 return newPos 88 } 89 90 // addRoute adds a node with the given handle to the path. 91 // Not concurrency-safe! 92 func (n *node) addRoute(path string, handle RequestHandler, r *router, prefixes []string) { 93 fullPath := path 94 n.priority++ 95 numParams := countParams(path) 96 if n.router == nil { 97 n.router = r 98 } 99 100 // non-empty tree 101 if len(n.path) > zero || len(n.children) > zero { 102 walk: 103 for { 104 // Update maxParams of the current node 105 if numParams > n.maxParams { 106 n.maxParams = numParams 107 } 108 109 // Find the longest common prefix. 110 // This also implies that the common prefix contains no ':' or '*' 111 // since the existing key can't contain those chars. 112 i := zero 113 max := min(len(path), len(n.path)) 114 for i < max && path[i] == n.path[i] { 115 i++ 116 } 117 118 // Split edge 119 if i < len(n.path) { 120 child := node{ 121 path: n.path[i:], 122 wildChild: n.wildChild, 123 nType: static, 124 indices: n.indices, 125 children: n.children, 126 handle: n.handle, 127 priority: n.priority - one, 128 router: n.router, 129 prefixes: prefixes, 130 } 131 132 // Update maxParams (max of all children) 133 for i := range child.children { 134 if child.children[i].maxParams > child.maxParams { 135 child.maxParams = child.children[i].maxParams 136 } 137 } 138 139 n.children = []*node{&child} 140 // []byte for proper unicode char conversion, see #65 141 n.indices = string([]byte{n.path[i]}) 142 n.path = path[:i] 143 n.handle = nil 144 n.wildChild = false 145 } 146 147 // Make new node a child of this node 148 if i < len(path) { 149 path = path[i:] 150 151 if n.wildChild { 152 n = n.children[zero] 153 n.priority++ 154 155 // Update maxParams of the child node 156 if numParams > n.maxParams { 157 n.maxParams = numParams 158 } 159 numParams-- 160 161 // Check if the wildcard matches 162 if len(path) >= len(n.path) && n.path == path[:len(n.path)] && 163 // Check for longer wildcard, e.g. :name and :names 164 (len(n.path) >= len(path) || path[len(n.path)] == SlashByte) { 165 continue walk 166 } else { 167 // Wildcard conflict 168 pathSeg := strings.SplitN(path, PathSlash, 2)[zero] 169 prefix := fullPath[:strings.Index(fullPath, pathSeg)] + n.path 170 panic("'" + pathSeg + 171 "' in new path '" + fullPath + 172 "' conflicts with existing wildcard '" + n.path + 173 "' in existing prefix '" + prefix + 174 "'") 175 } 176 } 177 178 c := path[zero] 179 180 // slash after param 181 if n.nType == param && c == SlashByte && len(n.children) == one { 182 n = n.children[zero] 183 n.priority++ 184 continue walk 185 } 186 187 // Check if a child with the next path byte exists 188 for i := zero; i < len(n.indices); i++ { 189 if c == n.indices[i] { 190 i = n.incrementChildPrio(i) 191 n = n.children[i] 192 continue walk 193 } 194 } 195 196 // Otherwise insert it 197 if c != ':' && c != '*' { 198 // []byte for proper unicode char conversion, see #65 199 n.indices += string([]byte{c}) 200 child := &node{ 201 maxParams: numParams, 202 router: n.router, 203 } 204 n.children = append(n.children, child) 205 n.incrementChildPrio(len(n.indices) - one) 206 n = child 207 } 208 n.insertChild(numParams, path, fullPath, handle, prefixes) 209 return 210 211 } else if i == len(path) { // Make node a (in-path) leaf 212 if n.handle != nil { 213 panic("a handle is already registered for path '" + fullPath + "'") 214 } 215 n.handle = handle 216 } 217 return 218 } 219 } else { // Empty tree 220 n.insertChild(numParams, path, fullPath, handle, prefixes) 221 n.nType = root 222 } 223 } 224 225 func (n *node) insertChild(numParams uint8, path, fullPath string, handle RequestHandler, prefixes []string) { 226 var offset int // already handled bytes of the path 227 228 // find prefix until first wildcard (beginning with ':' or '*') 229 for i, max := zero, len(path); numParams > zero; i++ { 230 c := path[i] 231 if c != ':' && c != '*' { 232 continue 233 } 234 235 // find wildcard end (either '/' or path end) 236 end := i + one 237 for end < max && path[end] != SlashByte { 238 switch path[end] { 239 // the wildcard name must not contain ':' and '*' 240 case ':', '*': 241 panic("only one wildcard per path segment is allowed, has: '" + 242 path[i:] + "' in path '" + fullPath + "'") 243 default: 244 end++ 245 } 246 } 247 248 // check if this Node existing children which would be 249 // unreachable if we insert the wildcard here 250 if len(n.children) > zero { 251 panic("wildcard route '" + path[i:end] + 252 "' conflicts with existing children in path '" + fullPath + "'") 253 } 254 255 // check if the wildcard has a name 256 if end-i < 2 { 257 panic("wildcards must be named with a non-empty name in path '" + fullPath + "'") 258 } 259 260 if c == ':' { // param 261 // split path at the beginning of the wildcard 262 if i > zero { 263 n.path = path[offset:i] 264 offset = i 265 } 266 267 child := &node{ 268 nType: param, 269 maxParams: numParams, 270 router: n.router, 271 prefixes: prefixes, 272 } 273 n.children = []*node{child} 274 n.wildChild = true 275 n = child 276 n.priority++ 277 numParams-- 278 279 // if the path doesn't end with the wildcard, then there 280 // will be another non-wildcard subpath starting with '/' 281 if end < max { 282 n.path = path[offset:end] 283 offset = end 284 285 child := &node{ 286 maxParams: numParams, 287 priority: one, 288 router: n.router, 289 prefixes: prefixes, 290 } 291 n.children = []*node{child} 292 n = child 293 } 294 295 } else { // catchAll 296 if end != max || numParams > one { 297 panic("catch-all routes are only allowed at the end of the path in path '" + fullPath + "'") 298 } 299 300 if len(n.path) > zero && n.path[len(n.path)-one] == SlashByte { 301 panic("catch-all conflicts with existing handle for the path segment root in path '" + fullPath + "'") 302 } 303 304 // currently fixed width one for '/' 305 i-- 306 if path[i] != SlashByte { 307 panic("no / before catch-all in path '" + fullPath + "'") 308 } 309 310 n.path = path[offset:i] 311 312 // first node: catchAll node with empty path 313 child := &node{ 314 wildChild: true, 315 nType: catchAll, 316 maxParams: one, 317 router: n.router, 318 prefixes: prefixes, 319 } 320 n.children = []*node{child} 321 n.indices = string(path[i]) 322 n = child 323 n.priority++ 324 325 // second node: node holding the variable 326 child = &node{ 327 path: path[i:], 328 nType: catchAll, 329 maxParams: one, 330 handle: handle, 331 priority: one, 332 router: n.router, 333 prefixes: prefixes, 334 } 335 n.children = []*node{child} 336 337 return 338 } 339 } 340 341 // insert remaining path part and handle to the leaf 342 n.path = path[offset:] 343 n.handle = handle 344 } 345 346 // GetValue returns the handle registered with the given path (key). The values 347 // of wildcards are saved to a map. 348 // If no handle can be found, a TSR (trailing slash redirect) recommendation is 349 // made if a handle exists with an extra (without the) trailing slash for the 350 // given path. 351 func (n *node) GetValue(reqPath string, ctx *Context, method string) (handle RequestHandler, prefixes []string, tsr bool) { //nolint: golint 352 if n.router == nil { 353 panic("no router!") 354 } 355 if n.router.cache == nil { 356 panic("no cache!") 357 } 358 if ctx != nil { 359 if record, ok := n.router.cache.Get(reqPath, method); ok { 360 if record != nil { 361 for name, value := range record.values { 362 ctx.SetUserValue(name, value) 363 } 364 if ctx != nil { 365 ctx.subPrefixes = record.n.prefixes 366 } 367 return record.n.handle, record.n.prefixes, record.tsr 368 } 369 } 370 ctx.subPrefixes = n.prefixes 371 } 372 params := []struct{ k, v string }{} 373 path := reqPath 374 walk: // outer loop for walking the tree 375 for { 376 if len(path) > len(n.path) { 377 if path[:len(n.path)] == n.path { 378 path = path[len(n.path):] 379 // If this node does not have a wildcard (param or catchAll) 380 // child, we can just look up the next child node and continue 381 // to walk down the tree 382 if !n.wildChild { 383 c := path[zero] 384 for i := zero; i < len(n.indices); i++ { 385 if c == n.indices[i] { 386 n = n.children[i] 387 continue walk 388 } 389 } 390 391 // Nothing found. 392 // We can recommend to redirect to the same URL without a 393 // trailing slash if a leaf exists for that path. 394 tsr = (path == PathSlash && n.handle != nil) 395 return 396 } 397 398 // handle wildcard child 399 n = n.children[zero] 400 switch n.nType { 401 case param: 402 // find param end (either '/' or path end) 403 end := zero 404 for end < len(path) && path[end] != SlashByte { 405 end++ 406 } 407 408 // handle calls to Router.allowed method with nil context 409 if ctx != nil { 410 params = append(params, struct{ k, v string }{ 411 k: n.path[one:], 412 v: path[:end], 413 }) 414 ctx.SetUserValue(n.path[one:], path[:end]) 415 } 416 417 // we need to go deeper! 418 if end < len(path) { 419 if len(n.children) > zero { 420 path = path[end:] 421 n = n.children[zero] 422 continue walk 423 } 424 425 // ... but we can't 426 tsr = (len(path) == end+one) 427 return 428 } 429 430 if handle = n.handle; handle != nil { 431 n.hits++ 432 if n.hits > 32 { 433 n.router.cache.PutWild(reqPath, n, tsr, map[string]string{n.path[one:]: path[:end]}, method) 434 } 435 prefixes = n.prefixes 436 return 437 } else if len(n.children) == one { 438 // No handle found. Check if a handle for this path + a 439 // trailing slash exists for TSR recommendation 440 n = n.children[zero] 441 tsr = (n.path == PathSlash && n.handle != nil) 442 } 443 444 return 445 446 case catchAll: 447 if ctx != nil { 448 // save param value 449 params = append(params, struct{ k, v string }{ 450 k: n.path[2:], 451 v: path, 452 }) 453 ctx.SetUserValue(n.path[2:], path) 454 _ = params // solve false positive 455 } 456 handle = n.handle 457 n.hits++ 458 if n.hits > 32 { 459 n.router.cache.PutWild(reqPath, n, tsr, map[string]string{n.path[2:]: path}, method) 460 } 461 return 462 463 default: 464 panic("invalid node type") 465 } 466 } 467 } else if path == n.path { 468 // We should have reached the node containing the handle. 469 // Check if this node has a handle registered. 470 if handle = n.handle; handle != nil { 471 if len(params) != 0 { 472 values := map[string]string{} 473 for _, v := range params { 474 values[v.k] = v.v 475 } 476 n.router.cache.PutWild(reqPath, n, tsr, values, method) 477 return 478 } 479 n.router.cache.Put(reqPath, n, tsr, method) 480 tsr = (len(n.path) == one && n.handle != nil) || 481 (n.nType == catchAll && n.children[zero].handle != nil) || 482 (path == PathSlash && n.wildChild && n.nType != root) 483 return 484 } 485 486 if path == PathSlash && n.wildChild && n.nType != root { 487 tsr = true 488 return 489 } 490 491 // No handle found. Check if a handle for this path + a 492 // trailing slash exists for trailing slash recommendation 493 for i := zero; i < len(n.indices); i++ { 494 if n.indices[i] == SlashByte { 495 n = n.children[i] 496 tsr = (len(n.path) == one && n.handle != nil) || 497 (n.nType == catchAll && n.children[zero].handle != nil) 498 return 499 } 500 } 501 502 return 503 } 504 505 // Nothing found. We can recommend to redirect to the same URL with an 506 // extra trailing slash if a leaf exists for that path 507 tsr = (path == PathSlash) || 508 (len(n.path) == len(path)+one && n.path[len(path)] == SlashByte && 509 path == n.path[:len(n.path)-one] && n.handle != nil) 510 return 511 } 512 } 513 514 // FindCaseInsensitivePath makes a case-insensitive lookup of the given path 515 // and tries to find a handler. 516 // It can optionally also fix trailing slashes. 517 // It returns the case-corrected path and a bool indicating whether the lookup 518 // was successful. 519 func (n *node) FindCaseInsensitivePath(path string, fixTrailingSlash bool) ([]byte, bool) { 520 return n.findCaseInsensitivePathRec( 521 path, 522 toLower(path), 523 make([]byte, zero, len(path)+one), // preallocate enough memory for new path 524 [4]byte{}, // empty rune buffer 525 fixTrailingSlash, 526 ) 527 } 528 529 // shift bytes in array by n bytes left 530 func shiftNRuneBytes(rb [4]byte, n int) [4]byte { 531 switch n { 532 case zero: 533 return rb 534 case one: 535 return [4]byte{rb[one], rb[2], rb[3], zero} 536 case 2: 537 return [4]byte{rb[2], rb[3]} 538 case 3: 539 return [4]byte{rb[3]} 540 default: 541 return [4]byte{} 542 } 543 } 544 545 // recursive case-insensitive lookup function used by n.findCaseInsensitivePath 546 func (n *node) findCaseInsensitivePathRec(path, loPath string, ciPath []byte, rb [4]byte, fixTrailingSlash bool) ([]byte, bool) { 547 loNPath := toLower(n.path) 548 549 walk: // outer loop for walking the tree 550 for len(loPath) >= len(loNPath) && (len(loNPath) == zero || loPath[one:len(loNPath)] == loNPath[one:]) { 551 // add common path to result 552 ciPath = append(ciPath, n.path...) 553 554 if path = path[len(n.path):]; len(path) > zero { 555 loOld := loPath 556 loPath = loPath[len(loNPath):] 557 558 // If this node does not have a wildcard (param or catchAll) child, 559 // we can just look up the next child node and continue to walk down 560 // the tree 561 if !n.wildChild { 562 // skip rune bytes already processed 563 rb = shiftNRuneBytes(rb, len(loNPath)) 564 565 if rb[zero] != zero { 566 // old rune not finished 567 for i := zero; i < len(n.indices); i++ { 568 if n.indices[i] == rb[zero] { 569 // continue with child node 570 n = n.children[i] 571 loNPath = toLower(n.path) 572 continue walk 573 } 574 } 575 } else { 576 // process a new rune 577 var rv rune 578 579 // find rune start 580 // runes are up to 4 byte long, 581 // -4 would definitely be another rune 582 var off int 583 for max := min(len(loNPath), 3); off < max; off++ { 584 if i := len(loNPath) - off; utf8.RuneStart(loOld[i]) { 585 // read rune from cached lowercase path 586 rv, _ = utf8.DecodeRuneInString(loOld[i:]) 587 break 588 } 589 } 590 591 // calculate lowercase bytes of current rune 592 utf8.EncodeRune(rb[:], rv) 593 // skipp already processed bytes 594 rb = shiftNRuneBytes(rb, off) 595 596 for i := zero; i < len(n.indices); i++ { 597 // lowercase matches 598 if n.indices[i] == rb[zero] { 599 // must use a recursive approach since both the 600 // uppercase byte and the lowercase byte might exist 601 // as an index 602 if out, found := n.children[i].findCaseInsensitivePathRec( 603 path, loPath, ciPath, rb, fixTrailingSlash, 604 ); found { 605 return out, true 606 } 607 break 608 } 609 } 610 611 // same for uppercase rune, if it differs 612 if up := unicode.ToUpper(rv); up != rv { 613 utf8.EncodeRune(rb[:], up) 614 rb = shiftNRuneBytes(rb, off) 615 616 for i := zero; i < len(n.indices); i++ { 617 // uppercase matches 618 if n.indices[i] == rb[zero] { 619 // continue with child node 620 n = n.children[i] 621 loNPath = toLower(n.path) 622 continue walk 623 } 624 } 625 } 626 } 627 628 // Nothing found. We can recommend to redirect to the same URL 629 // without a trailing slash if a leaf exists for that path 630 return ciPath, (fixTrailingSlash && path == PathSlash && n.handle != nil) 631 } 632 633 n = n.children[zero] 634 switch n.nType { 635 case param: 636 // find param end (either '/' or path end) 637 k := zero 638 for k < len(path) && path[k] != SlashByte { 639 k++ 640 } 641 642 // add param value to case insensitive path 643 ciPath = append(ciPath, path[:k]...) 644 645 // we need to go deeper! 646 if k < len(path) { 647 if len(n.children) > zero { 648 // continue with child node 649 n = n.children[zero] 650 loNPath = toLower(n.path) 651 loPath = loPath[k:] 652 path = path[k:] 653 continue 654 } 655 656 // ... but we can't 657 if fixTrailingSlash && len(path) == k+one { 658 return ciPath, true 659 } 660 return ciPath, false 661 } 662 663 if n.handle != nil { 664 return ciPath, true 665 } else if fixTrailingSlash && len(n.children) == one { 666 // No handle found. Check if a handle for this path + a 667 // trailing slash exists 668 n = n.children[zero] 669 if n.path == PathSlash && n.handle != nil { 670 return append(ciPath, SlashByte), true 671 } 672 } 673 return ciPath, false 674 675 case catchAll: 676 return append(ciPath, path...), true 677 678 default: 679 panic("invalid node type") 680 } 681 } else { 682 // We should have reached the node containing the handle. 683 // Check if this node has a handle registered. 684 if n.handle != nil { 685 return ciPath, true 686 } 687 688 // No handle found. 689 // Try to fix the path by adding a trailing slash 690 if fixTrailingSlash { 691 for i := zero; i < len(n.indices); i++ { 692 if n.indices[i] == SlashByte { 693 n = n.children[i] 694 if (len(n.path) == one && n.handle != nil) || 695 (n.nType == catchAll && n.children[zero].handle != nil) { 696 return append(ciPath, SlashByte), true 697 } 698 return ciPath, false 699 } 700 } 701 } 702 return ciPath, false 703 } 704 } 705 706 // Nothing found. 707 // Try to fix the path by adding / removing a trailing slash 708 if fixTrailingSlash { 709 if path == PathSlash { 710 return ciPath, true 711 } 712 if len(loPath)+one == len(loNPath) && loNPath[len(loPath)] == SlashByte && 713 loPath[one:] == loNPath[one:len(loPath)] && n.handle != nil { 714 return append(ciPath, n.path...), true 715 } 716 } 717 return ciPath, false 718 }