path: use stack buffer in CleanPath to avoid allocs in common case

Sync from 8222db13db
This commit is contained in:
Andy Pan 2020-01-06 22:23:58 +08:00
parent 168fa94516
commit 0de76a5d88

33
path.go
View File

@ -5,6 +5,8 @@
package gin package gin
const stackBufSize = 128
// cleanPath is the URL version of path.Clean, it returns a canonical URL path // cleanPath is the URL version of path.Clean, it returns a canonical URL path
// for p, eliminating . and .. elements. // for p, eliminating . and .. elements.
// //
@ -24,8 +26,11 @@ func cleanPath(p string) string {
return "/" return "/"
} }
// Reasonably sized buffer on stack to avoid allocations in the common case.
// If a larger buffer is required, it gets allocated dynamically.
buf := make([]byte, 0, stackBufSize)
n := len(p) n := len(p)
var buf []byte
// Invariants: // Invariants:
// reading from path; r is index of next byte to process. // reading from path; r is index of next byte to process.
@ -37,7 +42,12 @@ func cleanPath(p string) string {
if p[0] != '/' { if p[0] != '/' {
r = 0 r = 0
buf = make([]byte, n+1)
if n+1 > stackBufSize {
buf = make([]byte, n+1)
} else {
buf = buf[:n+1]
}
buf[0] = '/' buf[0] = '/'
} }
@ -69,7 +79,7 @@ func cleanPath(p string) string {
// can backtrack // can backtrack
w-- w--
if buf == nil { if len(buf) == 0 {
for w > 1 && p[w] != '/' { for w > 1 && p[w] != '/' {
w-- w--
} }
@ -103,7 +113,7 @@ func cleanPath(p string) string {
w++ w++
} }
if buf == nil { if len(buf) == 0 {
return p[:w] return p[:w]
} }
return string(buf[:w]) return string(buf[:w])
@ -111,13 +121,20 @@ func cleanPath(p string) string {
// internal helper to lazily create a buffer if necessary. // internal helper to lazily create a buffer if necessary.
func bufApp(buf *[]byte, s string, w int, c byte) { func bufApp(buf *[]byte, s string, w int, c byte) {
if *buf == nil { b := *buf
if len(b) == 0 {
if s[w] == c { if s[w] == c {
return return
} }
*buf = make([]byte, len(s)) if l := len(s); l > cap(b) {
copy(*buf, s[:w]) *buf = make([]byte, len(s))
} else {
*buf = (*buf)[:l]
}
b = *buf
copy(b, s[:w])
} }
(*buf)[w] = c b[w] = c
} }