From 47a65c0bf2249bf89731dc1a0b7d3a6d6b739921 Mon Sep 17 00:00:00 2001 From: Steven Hartland Date: Tue, 25 Jul 2017 10:30:06 +0100 Subject: [PATCH] Optimised stripExcessSpaces performance Optimised stripExcessSpaces by using a hand rolled trim space avoiding the overhead of strings.TrimSpace and ensuring we only do a single pass on the string for both search and copy when stripping multiple spaces. This improves the performance on the test machine from 2378 ns/op to 1528 ns/op while keeping allocs the same. Also: * Added a blank and only trialing spaces test cases. --- aws/signer/v4/v4.go | 58 +++++++++++++++++++--------------------- aws/signer/v4/v4_test.go | 4 +++ 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/aws/signer/v4/v4.go b/aws/signer/v4/v4.go index a2bfc47e7d2..d68905acbb1 100644 --- a/aws/signer/v4/v4.go +++ b/aws/signer/v4/v4.go @@ -55,7 +55,6 @@ package v4 import ( - "bytes" "crypto/hmac" "crypto/sha256" "encoding/hex" @@ -717,47 +716,46 @@ func makeSha256Reader(reader io.ReadSeeker) []byte { return hash.Sum(nil) } -const doubleSpaces = " " - -var doubleSpaceBytes = []byte(doubleSpaces) +const doubleSpace = " " // stripExcessSpaces will rewrite the passed in slice's string values to not // contain muliple side-by-side spaces. func stripExcessSpaces(vals []string) { + var j, k, l, m, spaces int for i, str := range vals { - // Trim leading and trailing spaces - trimmed := strings.TrimSpace(str) + // Trim trailing spaces + for j = len(str) - 1; j >= 0 && str[j] == ' '; j-- { + } - idx := strings.Index(trimmed, doubleSpaces) - if idx < 0 { - vals[i] = trimmed - continue + // Trim leading spaces + for k = 0; k < j && str[k] == ' '; k++ { } + str = str[k : j+1] - buf := []byte(trimmed) - for idx > -1 { - idx++ // Start on the second space + // Strip multiple spaces. + j = strings.Index(str, doubleSpace) + if j < 0 { + vals[i] = str + continue + } - stripped := false - for j := idx; j < len(buf); j++ { - if buf[j] != ' ' { - buf = append(buf[:idx], buf[j:]...) - stripped = true - break + buf := []byte(str) + for k, m, l = j, j, len(buf); k < l; k++ { + if buf[k] == ' ' { + if spaces == 0 { + // First space. + buf[m] = buf[k] + m++ } - } - if !stripped { - break - } - - // Find next double space - origIdx := idx - idx = bytes.Index(buf[idx:], doubleSpaceBytes) - if idx > 0 { - idx += origIdx + spaces++ + } else { + // End of multiple spaces. + spaces = 0 + buf[m] = buf[k] + m++ } } - vals[i] = string(buf) + vals[i] = string(buf[:m]) } } diff --git a/aws/signer/v4/v4_test.go b/aws/signer/v4/v4_test.go index 7cb7078f5ab..e94e6349108 100644 --- a/aws/signer/v4/v4_test.go +++ b/aws/signer/v4/v4_test.go @@ -20,8 +20,10 @@ import ( func TestStripExcessHeaders(t *testing.T) { vals := []string{ + "", "123", "1 2 3", + "1 2 3 ", " 1 2 3", "1 2 3", "1 23", @@ -35,10 +37,12 @@ func TestStripExcessHeaders(t *testing.T) { } expected := []string{ + "", "123", "1 2 3", "1 2 3", "1 2 3", + "1 2 3", "1 23", "1 2 3", "1 2",