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
|
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
|
// EscapeStringField returns a copy of in with any double quotes or
|
||||||
// backslashes with escaped values
|
// backslashes with escaped values
|
||||||
func EscapeStringField(in string) string {
|
func EscapeStringField(in string) string {
|
||||||
var out []byte
|
return escapeStringFieldReplacer.Replace(in)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// unescapeStringField returns a copy of in with any escaped double-quotes
|
// unescapeStringField returns a copy of in with any escaped double-quotes
|
||||||
|
|
|
@ -26,6 +26,8 @@ var (
|
||||||
}
|
}
|
||||||
maxFloat64 = strconv.FormatFloat(math.MaxFloat64, 'f', 1, 64)
|
maxFloat64 = strconv.FormatFloat(math.MaxFloat64, 'f', 1, 64)
|
||||||
minFloat64 = strconv.FormatFloat(-math.MaxFloat64, 'f', 1, 64)
|
minFloat64 = strconv.FormatFloat(-math.MaxFloat64, 'f', 1, 64)
|
||||||
|
|
||||||
|
sink interface{}
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMarshal(t *testing.T) {
|
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)
|
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