Use strings.Replacer to escape string field
benchmark old ns/op new ns/op delta BenchmarkEscapeStringField_Plain-4 167 65.3 -60.90% BenchmarkEscapeString_Quotes-4 167 165 -1.20% BenchmarkEscapeString_Backslashes-4 211 184 -12.80% BenchmarkEscapeString_QuotesAndBackslashes-4 413 397 -3.87% BenchmarkExportTSMStrings_100s_250vps-4 33833611 27381442 -19.07% BenchmarkExportWALStrings_100s_250vps-4 34977761 29222717 -16.45% benchmark old allocs new allocs delta BenchmarkEscapeStringField_Plain-4 4 1 -75.00% BenchmarkEscapeString_Quotes-4 4 3 -25.00% BenchmarkEscapeString_Backslashes-4 5 3 -40.00% BenchmarkEscapeString_QuotesAndBackslashes-4 9 5 -44.44% BenchmarkExportTSMStrings_100s_250vps-4 201605 76938 -61.84% BenchmarkExportWALStrings_100s_250vps-4 225371 100728 -55.31% benchmark old bytes new bytes delta BenchmarkEscapeStringField_Plain-4 56 16 -71.43% BenchmarkEscapeString_Quotes-4 56 48 -14.29% BenchmarkEscapeString_Backslashes-4 104 80 -23.08% BenchmarkEscapeString_QuotesAndBackslashes-4 208 160 -23.08% BenchmarkExportTSMStrings_100s_250vps-4 10872629 6062048 -44.24% BenchmarkExportWALStrings_100s_250vps-4 10094933 5269980 -47.80%pull/7741/head
parent
9effe6f364
commit
15ba7958f8
|
@ -1095,34 +1095,17 @@ func unescapeTag(in []byte) []byte {
|
|||
return in
|
||||
}
|
||||
|
||||
// escapeStringFieldReplacer replaces double quotes and backslashes
|
||||
// with the same character preceded by a backslash.
|
||||
// As of Go 1.7 this benchmarked better in allocations and CPU time
|
||||
// compared to iterating through a string byte-by-byte and appending to a new byte slice,
|
||||
// calling strings.Replace twice, and better than (*Regex).ReplaceAllString.
|
||||
var escapeStringFieldReplacer = strings.NewReplacer(`"`, `\"`, `\`, `\\`)
|
||||
|
||||
// EscapeStringField returns a copy of in with any double quotes or
|
||||
// backslashes with escaped values
|
||||
func EscapeStringField(in string) string {
|
||||
var out []byte
|
||||
i := 0
|
||||
for {
|
||||
if i >= len(in) {
|
||||
break
|
||||
}
|
||||
// escape double-quotes
|
||||
if in[i] == '\\' {
|
||||
out = append(out, '\\')
|
||||
out = append(out, '\\')
|
||||
i++
|
||||
continue
|
||||
}
|
||||
// escape double-quotes
|
||||
if in[i] == '"' {
|
||||
out = append(out, '\\')
|
||||
out = append(out, '"')
|
||||
i++
|
||||
continue
|
||||
}
|
||||
out = append(out, in[i])
|
||||
i++
|
||||
|
||||
}
|
||||
return string(out)
|
||||
return escapeStringFieldReplacer.Replace(in)
|
||||
}
|
||||
|
||||
// unescapeStringField returns a copy of in with any escaped double-quotes
|
||||
|
|
|
@ -26,6 +26,8 @@ var (
|
|||
}
|
||||
maxFloat64 = strconv.FormatFloat(math.MaxFloat64, 'f', 1, 64)
|
||||
minFloat64 = strconv.FormatFloat(-math.MaxFloat64, 'f', 1, 64)
|
||||
|
||||
sink interface{}
|
||||
)
|
||||
|
||||
func TestMarshal(t *testing.T) {
|
||||
|
@ -2168,3 +2170,61 @@ func TestPoint_FieldIterator_Delete_Twice(t *testing.T) {
|
|||
t.Fatalf("Delete failed, got %#v, exp %#v", got, exp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEscapeStringField(t *testing.T) {
|
||||
cases := []struct {
|
||||
in string
|
||||
expOut string
|
||||
}{
|
||||
{in: "abcdefg", expOut: "abcdefg"},
|
||||
{in: `one double quote " .`, expOut: `one double quote \" .`},
|
||||
{in: `quote " then backslash \ .`, expOut: `quote \" then backslash \\ .`},
|
||||
{in: `backslash \ then quote " .`, expOut: `backslash \\ then quote \" .`},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
// Unescapes as expected.
|
||||
got := models.EscapeStringField(c.in)
|
||||
if got != c.expOut {
|
||||
t.Errorf("unexpected result from EscapeStringField(%s)\ngot [%s]\nexp [%s]\n", c.in, got, c.expOut)
|
||||
continue
|
||||
}
|
||||
|
||||
pointLine := fmt.Sprintf(`t s="%s"`, got)
|
||||
test(t, pointLine, NewTestPoint(
|
||||
"t",
|
||||
models.NewTags(nil),
|
||||
models.Fields{"s": c.in},
|
||||
time.Unix(0, 0),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEscapeStringField_Plain(b *testing.B) {
|
||||
s := "nothing special"
|
||||
for i := 0; i < b.N; i++ {
|
||||
sink = models.EscapeStringField(s)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEscapeString_Quotes(b *testing.B) {
|
||||
s := `Hello, "world"`
|
||||
for i := 0; i < b.N; i++ {
|
||||
sink = models.EscapeStringField(s)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEscapeString_Backslashes(b *testing.B) {
|
||||
s := `C:\windows\system32`
|
||||
for i := 0; i < b.N; i++ {
|
||||
sink = models.EscapeStringField(s)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEscapeString_QuotesAndBackslashes(b *testing.B) {
|
||||
s1 := `a quote " then backslash \ .`
|
||||
s2 := `a backslash \ then quote " .`
|
||||
for i := 0; i < b.N; i++ {
|
||||
sink = [...]string{models.EscapeStringField(s1), models.EscapeStringField(s2)}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue