♻️ Refactor: add findNextNonEscapedCharsetPosition to process a single-byte parameter

```
goos: linux
goarch: amd64
pkg: github.com/gofiber/fiber/v3
cpu: AMD EPYC 9J14 96-Core Processor
                                                                               │   old.txt   │              new.txt               │
                                                                               │   sec/op    │   sec/op     vs base               │
_RoutePatternMatch//api/v1/const_|_match_|_/api/v1/const-16                      160.4n ± 1%   159.0n ± 0%  -0.84% (p=0.000 n=20)
_RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1-16                        151.6n ± 0%   150.8n ± 0%  -0.53% (p=0.005 n=20)
_RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1/-16                       151.7n ± 0%   150.6n ± 0%  -0.73% (p=0.000 n=20)
_RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1/something-16              162.3n ± 0%   160.8n ± 0%  -0.96% (p=0.000 n=20)
_RoutePatternMatch//api/:param/fixedEnd_|_match_|_/api/abc/fixedEnd-16           452.9n ± 1%   435.8n ± 0%  -3.79% (p=0.000 n=20)
_RoutePatternMatch//api/:param/fixedEnd_|_not_match_|_/api/abc/def/fixedEnd-16   455.6n ± 1%   435.7n ± 0%  -4.38% (p=0.000 n=20)
_RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity-16                  524.4n ± 1%   507.6n ± 1%  -3.19% (p=0.000 n=20)
_RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity/-16                 528.2n ± 0%   508.7n ± 0%  -3.69% (p=0.000 n=20)
_RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity/1-16                528.1n ± 0%   510.6n ± 0%  -3.31% (p=0.000 n=20)
_RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v-16                      500.3n ± 0%   489.0n ± 0%  -2.27% (p=0.000 n=20)
_RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v2-16                     502.1n ± 0%   489.9n ± 0%  -2.44% (p=0.000 n=20)
_RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v1/-16                    515.5n ± 0%   498.8n ± 0%  -3.24% (p=0.000 n=20)
geomean                                                                          339.4n        331.1n       -2.46%

                                                                               │  old.txt   │               new.txt               │
                                                                               │    B/op    │    B/op     vs base                 │
_RoutePatternMatch//api/v1/const_|_match_|_/api/v1/const-16                      144.0 ± 0%   144.0 ± 0%       ~ (p=1.000 n=20) ¹
_RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1-16                        136.0 ± 0%   136.0 ± 0%       ~ (p=1.000 n=20) ¹
_RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1/-16                       136.0 ± 0%   136.0 ± 0%       ~ (p=1.000 n=20) ¹
_RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1/something-16              152.0 ± 0%   152.0 ± 0%       ~ (p=1.000 n=20) ¹
_RoutePatternMatch//api/:param/fixedEnd_|_match_|_/api/abc/fixedEnd-16           368.0 ± 0%   368.0 ± 0%       ~ (p=1.000 n=20) ¹
_RoutePatternMatch//api/:param/fixedEnd_|_not_match_|_/api/abc/def/fixedEnd-16   368.0 ± 0%   368.0 ± 0%       ~ (p=1.000 n=20) ¹
_RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity-16                  432.0 ± 0%   432.0 ± 0%       ~ (p=1.000 n=20) ¹
_RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity/-16                 432.0 ± 0%   432.0 ± 0%       ~ (p=1.000 n=20) ¹
_RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity/1-16                432.0 ± 0%   432.0 ± 0%       ~ (p=1.000 n=20) ¹
_RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v-16                      424.0 ± 0%   424.0 ± 0%       ~ (p=1.000 n=20) ¹
_RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v2-16                     424.0 ± 0%   424.0 ± 0%       ~ (p=1.000 n=20) ¹
_RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v1/-16                    424.0 ± 0%   424.0 ± 0%       ~ (p=1.000 n=20) ¹
geomean                                                                          288.8        288.8       +0.00%
¹ all samples are equal

                                                                               │  old.txt   │               new.txt               │
                                                                               │ allocs/op  │ allocs/op   vs base                 │
_RoutePatternMatch//api/v1/const_|_match_|_/api/v1/const-16                      4.000 ± 0%   4.000 ± 0%       ~ (p=1.000 n=20) ¹
_RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1-16                        4.000 ± 0%   4.000 ± 0%       ~ (p=1.000 n=20) ¹
_RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1/-16                       4.000 ± 0%   4.000 ± 0%       ~ (p=1.000 n=20) ¹
_RoutePatternMatch//api/v1/const_|_not_match_|_/api/v1/something-16              4.000 ± 0%   4.000 ± 0%       ~ (p=1.000 n=20) ¹
_RoutePatternMatch//api/:param/fixedEnd_|_match_|_/api/abc/fixedEnd-16           9.000 ± 0%   9.000 ± 0%       ~ (p=1.000 n=20) ¹
_RoutePatternMatch//api/:param/fixedEnd_|_not_match_|_/api/abc/def/fixedEnd-16   9.000 ± 0%   9.000 ± 0%       ~ (p=1.000 n=20) ¹
_RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity-16                  9.000 ± 0%   9.000 ± 0%       ~ (p=1.000 n=20) ¹
_RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity/-16                 9.000 ± 0%   9.000 ± 0%       ~ (p=1.000 n=20) ¹
_RoutePatternMatch//api/v1/:param/*_|_match_|_/api/v1/entity/1-16                9.000 ± 0%   9.000 ± 0%       ~ (p=1.000 n=20) ¹
_RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v-16                      9.000 ± 0%   9.000 ± 0%       ~ (p=1.000 n=20) ¹
_RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v2-16                     9.000 ± 0%   9.000 ± 0%       ~ (p=1.000 n=20) ¹
_RoutePatternMatch//api/v1/:param/*_|_not_match_|_/api/v1/-16                    9.000 ± 0%   9.000 ± 0%       ~ (p=1.000 n=20) ¹
geomean                                                                          6.868        6.868       +0.00%
¹ all samples are equal
```
pull/3378/head
ksw2000 2025-03-29 13:26:38 +08:00
parent e90fe8afbc
commit 53f0e42116
1 changed files with 23 additions and 23 deletions

46
path.go
View File

@ -123,16 +123,6 @@ var (
parameterDelimiterChars = append([]byte{paramStarterChar, escapeChar}, routeDelimiter...)
// list of chars to find the end of a parameter
parameterEndChars = append([]byte{optionalParam}, parameterDelimiterChars...)
// list of parameter constraint start
parameterConstraintStartChars = []byte{paramConstraintStart}
// list of parameter constraint end
parameterConstraintEndChars = []byte{paramConstraintEnd}
// list of parameter separator
parameterConstraintSeparatorChars = []byte{paramConstraintSeparator}
// list of parameter constraint data start
parameterConstraintDataStartChars = []byte{paramConstraintDataStart}
// list of parameter constraint data separator
parameterConstraintDataSeparatorChars = []byte{paramConstraintDataSeparator}
)
// RoutePatternMatch checks if a given path matches a Fiber route pattern.
@ -337,8 +327,8 @@ func (parser *routeParser) analyseParameterPart(pattern string, customConstraint
// find constraint part if exists in the parameter part and remove it
if parameterEndPosition > 0 {
parameterConstraintStart = findNextNonEscapedCharsetPosition(pattern[0:parameterEndPosition], parameterConstraintStartChars)
parameterConstraintEnd = strings.LastIndexByte(pattern[0:parameterEndPosition+1], paramConstraintEnd)
parameterConstraintStart = findNextNonEscapedCharPosition(pattern[:parameterEndPosition], paramConstraintStart)
parameterConstraintEnd = strings.LastIndexByte(pattern[:parameterEndPosition+1], paramConstraintEnd)
}
// cut params part
@ -351,11 +341,11 @@ func (parser *routeParser) analyseParameterPart(pattern string, customConstraint
if hasConstraint := parameterConstraintStart != -1 && parameterConstraintEnd != -1; hasConstraint {
constraintString := pattern[parameterConstraintStart+1 : parameterConstraintEnd]
userConstraints := splitNonEscaped(constraintString, string(parameterConstraintSeparatorChars))
userConstraints := splitNonEscaped(constraintString, paramConstraintSeparator)
constraints = make([]*Constraint, 0, len(userConstraints))
for _, c := range userConstraints {
start := findNextNonEscapedCharsetPosition(c, parameterConstraintDataStartChars)
start := findNextNonEscapedCharPosition(c, paramConstraintDataStart)
end := strings.LastIndexByte(c, paramConstraintDataEnd)
// Assign constraint
@ -368,7 +358,7 @@ func (parser *routeParser) analyseParameterPart(pattern string, customConstraint
// remove escapes from data
if constraint.ID != regexConstraint {
constraint.Data = splitNonEscaped(c[start+1:end], string(parameterConstraintDataSeparatorChars))
constraint.Data = splitNonEscaped(c[start+1:end], paramConstraintDataSeparator)
if len(constraint.Data) == 1 {
constraint.Data[0] = RemoveEscapeChar(constraint.Data[0])
} else if len(constraint.Data) == 2 { // This is fine, we simply expect two parts
@ -432,11 +422,11 @@ func findNextCharsetPosition(search string, charset []byte) int {
return nextPosition
}
// findNextCharsetPositionConstraint search the next char position from the charset
// findNextCharsetPositionConstraint searches the next char position from the charset
// unlike findNextCharsetPosition, it takes care of constraint start-end chars to parse route pattern
func findNextCharsetPositionConstraint(search string, charset []byte) int {
constraintStart := findNextNonEscapedCharsetPosition(search, parameterConstraintStartChars)
constraintEnd := findNextNonEscapedCharsetPosition(search, parameterConstraintEndChars)
constraintStart := findNextNonEscapedCharPosition(search, paramConstraintStart)
constraintEnd := findNextNonEscapedCharPosition(search, paramConstraintEnd)
nextPosition := -1
for _, char := range charset {
@ -452,7 +442,7 @@ func findNextCharsetPositionConstraint(search string, charset []byte) int {
return nextPosition
}
// findNextNonEscapedCharsetPosition search the next char position from the charset and skip the escaped characters
// findNextNonEscapedCharsetPosition searches the next char position from the charset and skips the escaped characters
func findNextNonEscapedCharsetPosition(search string, charset []byte) int {
pos := findNextCharsetPosition(search, charset)
for pos > 0 && search[pos-1] == escapeChar {
@ -471,16 +461,26 @@ func findNextNonEscapedCharsetPosition(search string, charset []byte) int {
return pos
}
// findNextNonEscapedCharPosition searches the next char position and skips the escaped characters
func findNextNonEscapedCharPosition(search string, char byte) int {
for i := 0; i < len(search); i++ {
if search[i] == char && (i == 0 || search[i-1] != escapeChar) {
return i
}
}
return -1
}
// splitNonEscaped slices s into all substrings separated by sep and returns a slice of the substrings between those separators
// This function also takes a care of escape char when splitting.
func splitNonEscaped(s, sep string) []string {
func splitNonEscaped(s string, sep byte) []string {
var result []string
i := findNextNonEscapedCharsetPosition(s, []byte(sep))
i := findNextNonEscapedCharPosition(s, sep)
for i > -1 {
result = append(result, s[:i])
s = s[i+len(sep):]
i = findNextNonEscapedCharsetPosition(s, []byte(sep))
s = s[i+1:]
i = findNextNonEscapedCharPosition(s, sep)
}
return append(result, s)