mirror of https://github.com/gofiber/fiber.git
♻️ Refactor: Add findNextNonEscapedCharPosition for single-byte charset cases (#3378)
♻️ 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/3382/merge
parent
d19e993597
commit
c2e39b7570
46
path.go
46
path.go
|
@ -123,16 +123,6 @@ var (
|
||||||
parameterDelimiterChars = append([]byte{paramStarterChar, escapeChar}, routeDelimiter...)
|
parameterDelimiterChars = append([]byte{paramStarterChar, escapeChar}, routeDelimiter...)
|
||||||
// list of chars to find the end of a parameter
|
// list of chars to find the end of a parameter
|
||||||
parameterEndChars = append([]byte{optionalParam}, parameterDelimiterChars...)
|
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.
|
// 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
|
// find constraint part if exists in the parameter part and remove it
|
||||||
if parameterEndPosition > 0 {
|
if parameterEndPosition > 0 {
|
||||||
parameterConstraintStart = findNextNonEscapedCharsetPosition(pattern[0:parameterEndPosition], parameterConstraintStartChars)
|
parameterConstraintStart = findNextNonEscapedCharPosition(pattern[:parameterEndPosition], paramConstraintStart)
|
||||||
parameterConstraintEnd = strings.LastIndexByte(pattern[0:parameterEndPosition+1], paramConstraintEnd)
|
parameterConstraintEnd = strings.LastIndexByte(pattern[:parameterEndPosition+1], paramConstraintEnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// cut params part
|
// cut params part
|
||||||
|
@ -351,11 +341,11 @@ func (parser *routeParser) analyseParameterPart(pattern string, customConstraint
|
||||||
|
|
||||||
if hasConstraint := parameterConstraintStart != -1 && parameterConstraintEnd != -1; hasConstraint {
|
if hasConstraint := parameterConstraintStart != -1 && parameterConstraintEnd != -1; hasConstraint {
|
||||||
constraintString := pattern[parameterConstraintStart+1 : parameterConstraintEnd]
|
constraintString := pattern[parameterConstraintStart+1 : parameterConstraintEnd]
|
||||||
userConstraints := splitNonEscaped(constraintString, string(parameterConstraintSeparatorChars))
|
userConstraints := splitNonEscaped(constraintString, paramConstraintSeparator)
|
||||||
constraints = make([]*Constraint, 0, len(userConstraints))
|
constraints = make([]*Constraint, 0, len(userConstraints))
|
||||||
|
|
||||||
for _, c := range userConstraints {
|
for _, c := range userConstraints {
|
||||||
start := findNextNonEscapedCharsetPosition(c, parameterConstraintDataStartChars)
|
start := findNextNonEscapedCharPosition(c, paramConstraintDataStart)
|
||||||
end := strings.LastIndexByte(c, paramConstraintDataEnd)
|
end := strings.LastIndexByte(c, paramConstraintDataEnd)
|
||||||
|
|
||||||
// Assign constraint
|
// Assign constraint
|
||||||
|
@ -368,7 +358,7 @@ func (parser *routeParser) analyseParameterPart(pattern string, customConstraint
|
||||||
|
|
||||||
// remove escapes from data
|
// remove escapes from data
|
||||||
if constraint.ID != regexConstraint {
|
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 {
|
if len(constraint.Data) == 1 {
|
||||||
constraint.Data[0] = RemoveEscapeChar(constraint.Data[0])
|
constraint.Data[0] = RemoveEscapeChar(constraint.Data[0])
|
||||||
} else if len(constraint.Data) == 2 { // This is fine, we simply expect two parts
|
} 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
|
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
|
// unlike findNextCharsetPosition, it takes care of constraint start-end chars to parse route pattern
|
||||||
func findNextCharsetPositionConstraint(search string, charset []byte) int {
|
func findNextCharsetPositionConstraint(search string, charset []byte) int {
|
||||||
constraintStart := findNextNonEscapedCharsetPosition(search, parameterConstraintStartChars)
|
constraintStart := findNextNonEscapedCharPosition(search, paramConstraintStart)
|
||||||
constraintEnd := findNextNonEscapedCharsetPosition(search, parameterConstraintEndChars)
|
constraintEnd := findNextNonEscapedCharPosition(search, paramConstraintEnd)
|
||||||
nextPosition := -1
|
nextPosition := -1
|
||||||
|
|
||||||
for _, char := range charset {
|
for _, char := range charset {
|
||||||
|
@ -452,7 +442,7 @@ func findNextCharsetPositionConstraint(search string, charset []byte) int {
|
||||||
return nextPosition
|
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 {
|
func findNextNonEscapedCharsetPosition(search string, charset []byte) int {
|
||||||
pos := findNextCharsetPosition(search, charset)
|
pos := findNextCharsetPosition(search, charset)
|
||||||
for pos > 0 && search[pos-1] == escapeChar {
|
for pos > 0 && search[pos-1] == escapeChar {
|
||||||
|
@ -471,16 +461,26 @@ func findNextNonEscapedCharsetPosition(search string, charset []byte) int {
|
||||||
return pos
|
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
|
// 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.
|
// 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
|
var result []string
|
||||||
i := findNextNonEscapedCharsetPosition(s, []byte(sep))
|
i := findNextNonEscapedCharPosition(s, sep)
|
||||||
|
|
||||||
for i > -1 {
|
for i > -1 {
|
||||||
result = append(result, s[:i])
|
result = append(result, s[:i])
|
||||||
s = s[i+len(sep):]
|
s = s[i+1:]
|
||||||
i = findNextNonEscapedCharsetPosition(s, []byte(sep))
|
i = findNextNonEscapedCharPosition(s, sep)
|
||||||
}
|
}
|
||||||
|
|
||||||
return append(result, s)
|
return append(result, s)
|
||||||
|
|
Loading…
Reference in New Issue