From 0de76a5d885cbb64df97958a5839bcc7caeb0f55 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Mon, 6 Jan 2020 22:23:58 +0800 Subject: [PATCH] path: use stack buffer in CleanPath to avoid allocs in common case Sync from https://github.com/julienschmidt/httprouter/commit/8222db13dbb3b3ab1eb84edb61a7030708b93bfa --- path.go | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/path.go b/path.go index d1f59622..a6bac7bd 100644 --- a/path.go +++ b/path.go @@ -5,6 +5,8 @@ package gin +const stackBufSize = 128 + // cleanPath is the URL version of path.Clean, it returns a canonical URL path // for p, eliminating . and .. elements. // @@ -24,8 +26,11 @@ func cleanPath(p string) string { 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) - var buf []byte // Invariants: // reading from path; r is index of next byte to process. @@ -37,7 +42,12 @@ func cleanPath(p string) string { if p[0] != '/' { r = 0 - buf = make([]byte, n+1) + + if n+1 > stackBufSize { + buf = make([]byte, n+1) + } else { + buf = buf[:n+1] + } buf[0] = '/' } @@ -69,7 +79,7 @@ func cleanPath(p string) string { // can backtrack w-- - if buf == nil { + if len(buf) == 0 { for w > 1 && p[w] != '/' { w-- } @@ -103,7 +113,7 @@ func cleanPath(p string) string { w++ } - if buf == nil { + if len(buf) == 0 { return p[:w] } return string(buf[:w]) @@ -111,13 +121,20 @@ func cleanPath(p string) string { // internal helper to lazily create a buffer if necessary. func bufApp(buf *[]byte, s string, w int, c byte) { - if *buf == nil { + b := *buf + if len(b) == 0 { if s[w] == c { return } - *buf = make([]byte, len(s)) - copy(*buf, s[:w]) + if l := len(s); l > cap(b) { + *buf = make([]byte, len(s)) + } else { + *buf = (*buf)[:l] + } + b = *buf + + copy(b, s[:w]) } - (*buf)[w] = c + b[w] = c }