diff --git a/internal/sanitize/benchmmark.sh b/internal/sanitize/benchmmark.sh index 06842c0aa..ec0f7b03a 100644 --- a/internal/sanitize/benchmmark.sh +++ b/internal/sanitize/benchmmark.sh @@ -43,7 +43,7 @@ for i in "${!commits[@]}"; do } # Sanitized commmit message - commit_message=$(git log -1 --pretty=format:"%s" | tr ' ' '_') + commit_message=$(git log -1 --pretty=format:"%s" | tr -c '[:alnum:]-_' '_') # Benchmark data will go there bench_file="${benchmarks_dir}/${i}_${commit_message}.bench" @@ -56,4 +56,5 @@ for i in "${!commits[@]}"; do bench_files+=("$bench_file") done +# go install golang.org/x/perf/cmd/benchstat[@latest] benchstat "${bench_files[@]}" diff --git a/internal/sanitize/sanitize.go b/internal/sanitize/sanitize.go index e0ae9bedb..b516817cb 100644 --- a/internal/sanitize/sanitize.go +++ b/internal/sanitize/sanitize.go @@ -145,41 +145,59 @@ func (q *Query) init(sql string) { } func QuoteString(dst []byte, str string) []byte { - const quote = "'" + const quote = '\'' - n := strings.Count(str, quote) + // Preallocate space for the worst case scenario + dst = slices.Grow(dst, len(str)*2+2) - dst = append(dst, quote...) + // Add opening quote + dst = append(dst, quote) - p := slices.Grow(dst[len(dst):], 2*len(quote)+len(str)+2*n) - - for len(str) > 0 { - i := strings.Index(str, quote) - if i < 0 { - p = append(p, str...) - break + // Iterate through the string without allocating + for i := 0; i < len(str); i++ { + if str[i] == quote { + dst = append(dst, quote, quote) + } else { + dst = append(dst, str[i]) } - p = append(p, str[:i]...) - p = append(p, "''"...) - str = str[i+1:] } - dst = append(dst, p...) - - dst = append(dst, quote...) + // Add closing quote + dst = append(dst, quote) return dst } func QuoteBytes(dst, buf []byte) []byte { - dst = append(dst, `'\x`...) + if len(buf) == 0 { + return append(dst, `'\x'`...) + } + + // Calculate required length + requiredLen := 3 + hex.EncodedLen(len(buf)) + 1 + + // Ensure dst has enough capacity + if cap(dst)-len(dst) < requiredLen { + newDst := make([]byte, len(dst), len(dst)+requiredLen) + copy(newDst, dst) + dst = newDst + } + + // Record original length and extend slice + origLen := len(dst) + dst = dst[:origLen+requiredLen] + + // Add prefix + dst[origLen] = '\'' + dst[origLen+1] = '\\' + dst[origLen+2] = 'x' + + // Encode bytes directly into dst + hex.Encode(dst[origLen+3:len(dst)-1], buf) - n := hex.EncodedLen(len(buf)) - p := slices.Grow(dst[len(dst):], n)[:n] - hex.Encode(p, buf) - dst = append(dst, p...) + // Add suffix + dst[len(dst)-1] = '\'' - dst = append(dst, `'`...) return dst }