Reduce allocations in Walkdir (#17036)
This commit is contained in:
@@ -209,6 +209,9 @@ func checkObjectNameForLengthAndSlash(bucket, object string) error {
|
||||
// SlashSeparator - slash separator.
|
||||
const SlashSeparator = "/"
|
||||
|
||||
// SlashSeparatorChar - slash separator.
|
||||
const SlashSeparatorChar = '/'
|
||||
|
||||
// retainSlash - retains slash from a path.
|
||||
func retainSlash(s string) string {
|
||||
if s == "" {
|
||||
@@ -231,13 +234,102 @@ func pathsJoinPrefix(prefix string, elem ...string) (paths []string) {
|
||||
func pathJoin(elem ...string) string {
|
||||
trailingSlash := ""
|
||||
if len(elem) > 0 {
|
||||
if HasSuffix(elem[len(elem)-1], SlashSeparator) {
|
||||
if hasSuffixByte(elem[len(elem)-1], SlashSeparatorChar) {
|
||||
trailingSlash = SlashSeparator
|
||||
}
|
||||
}
|
||||
return path.Join(elem...) + trailingSlash
|
||||
}
|
||||
|
||||
// pathJoinBuf - like path.Join() but retains trailing SlashSeparator of the last element.
|
||||
// Provide a string builder to reduce allocation.
|
||||
func pathJoinBuf(dst *bytes.Buffer, elem ...string) string {
|
||||
trailingSlash := len(elem) > 0 && hasSuffixByte(elem[len(elem)-1], SlashSeparatorChar)
|
||||
dst.Reset()
|
||||
added := 0
|
||||
for _, e := range elem {
|
||||
if added > 0 || e != "" {
|
||||
if added > 0 {
|
||||
dst.WriteRune(SlashSeparatorChar)
|
||||
}
|
||||
dst.WriteString(e)
|
||||
added += len(e)
|
||||
}
|
||||
}
|
||||
|
||||
if pathNeedsClean(dst.Bytes()) {
|
||||
s := path.Clean(dst.String())
|
||||
if trailingSlash {
|
||||
return s + SlashSeparator
|
||||
}
|
||||
return s
|
||||
}
|
||||
if trailingSlash {
|
||||
dst.WriteRune(SlashSeparatorChar)
|
||||
}
|
||||
return dst.String()
|
||||
}
|
||||
|
||||
// hasSuffixByte returns true if the last byte of s is 'suffix'
|
||||
func hasSuffixByte(s string, suffix byte) bool {
|
||||
return len(s) > 0 && s[len(s)-1] == suffix
|
||||
}
|
||||
|
||||
// pathNeedsClean returns whether path.Clean may change the path.
|
||||
// Will detect all cases that will be cleaned,
|
||||
// but may produce false positives on non-trivial paths.
|
||||
func pathNeedsClean(path []byte) bool {
|
||||
if len(path) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
rooted := path[0] == '/'
|
||||
n := len(path)
|
||||
|
||||
r, w := 0, 0
|
||||
if rooted {
|
||||
r, w = 1, 1
|
||||
}
|
||||
|
||||
for r < n {
|
||||
switch {
|
||||
case path[r] > 127:
|
||||
// Non ascii.
|
||||
return true
|
||||
case path[r] == '/':
|
||||
// multiple / elements
|
||||
return true
|
||||
case path[r] == '.' && (r+1 == n || path[r+1] == '/'):
|
||||
// . element - assume it has to be cleaned.
|
||||
return true
|
||||
case path[r] == '.' && path[r+1] == '.' && (r+2 == n || path[r+2] == '/'):
|
||||
// .. element: remove to last / - assume it has to be cleaned.
|
||||
return true
|
||||
default:
|
||||
// real path element.
|
||||
// add slash if needed
|
||||
if rooted && w != 1 || !rooted && w != 0 {
|
||||
w++
|
||||
}
|
||||
// copy element
|
||||
for ; r < n && path[r] != '/'; r++ {
|
||||
w++
|
||||
}
|
||||
// allow one slash, not at end
|
||||
if r < n-1 && path[r] == '/' {
|
||||
r++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Turn empty string into "."
|
||||
if w == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// mustGetUUID - get a random UUID.
|
||||
func mustGetUUID() string {
|
||||
u, err := uuid.NewRandom()
|
||||
|
||||
Reference in New Issue
Block a user