Skip to content

Commit

Permalink
Optimize some string handling (#170)
Browse files Browse the repository at this point in the history
* Don't use fmt.Sprintf on simple strings
* Use bytes.Buffer in encodeTomlString

name                old time/op    new time/op    delta
Lexer-8                162µs ± 0%     161µs ± 0%   -0.12%
TreeToTomlString-8    19.7µs ± 0%     7.5µs ± 0%  -61.80%

name                old alloc/op   new alloc/op   delta
TreeToTomlString-8    9.75kB ± 0%    4.96kB ± 0%  -49.12%

name                old allocs/op  new allocs/op  delta
TreeToTomlString-8       485 ± 0%        78 ± 0%  -83.92%
  • Loading branch information
moorereason authored and pelletier committed Jun 2, 2017
1 parent 5ccdfb1 commit a60c713
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 14 deletions.
29 changes: 15 additions & 14 deletions tomltree_write.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,33 +13,34 @@ import (

// encodes a string to a TOML-compliant string value
func encodeTomlString(value string) string {
result := ""
var b bytes.Buffer

for _, rr := range value {
switch rr {
case '\b':
result += "\\b"
b.WriteString(`\b`)
case '\t':
result += "\\t"
b.WriteString(`\t`)
case '\n':
result += "\\n"
b.WriteString(`\n`)
case '\f':
result += "\\f"
b.WriteString(`\f`)
case '\r':
result += "\\r"
b.WriteString(`\r`)
case '"':
result += "\\\""
b.WriteString(`\"`)
case '\\':
result += "\\\\"
b.WriteString(`\\`)
default:
intRr := uint16(rr)
if intRr < 0x001F {
result += fmt.Sprintf("\\u%0.4X", intRr)
b.WriteString(fmt.Sprintf("\\u%0.4X", intRr))
} else {
result += string(rr)
b.WriteRune(rr)
}
}
}
return result
return b.String()
}

func tomlValueStringRepresentation(v interface{}) (string, error) {
Expand Down Expand Up @@ -111,7 +112,7 @@ func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) (
return bytesCount, err
}

kvRepr := fmt.Sprintf("%s%s = %s\n", indent, k, repr)
kvRepr := indent + k + " = " + repr + "\n"
writtenBytesCount, err := w.Write([]byte(kvRepr))
bytesCount += int64(writtenBytesCount)
if err != nil {
Expand All @@ -130,7 +131,7 @@ func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) (
switch node := v.(type) {
// node has to be of those two types given how keys are sorted above
case *Tree:
tableName := fmt.Sprintf("\n%s[%s]\n", indent, combinedKey)
tableName := "\n" + indent + "[" + combinedKey + "]\n"
writtenBytesCount, err := w.Write([]byte(tableName))
bytesCount += int64(writtenBytesCount)
if err != nil {
Expand All @@ -142,7 +143,7 @@ func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) (
}
case []*Tree:
for _, subTree := range node {
tableArrayName := fmt.Sprintf("\n%s[[%s]]\n", indent, combinedKey)
tableArrayName := "\n" + indent + "[[" + combinedKey + "]]\n"
writtenBytesCount, err := w.Write([]byte(tableArrayName))
bytesCount += int64(writtenBytesCount)
if err != nil {
Expand Down
48 changes: 48 additions & 0 deletions tomltree_write_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,3 +293,51 @@ func TestTreeWriteToMapWithArrayOfInlineTables(t *testing.T) {
treeMap := tree.ToMap()
testMaps(t, treeMap, expected)
}

func BenchmarkTreeToTomlString(b *testing.B) {
toml, err := Load(sampleHard)
if err != nil {
b.Fatal("Unexpected error:", err)
}

for i := 0; i < b.N; i++ {
_, err := toml.ToTomlString()
if err != nil {
b.Fatal(err)
}
}
}

var sampleHard = `# Test file for TOML
# Only this one tries to emulate a TOML file written by a user of the kind of parser writers probably hate
# This part you'll really hate
[the]
test_string = "You'll hate me after this - #" # " Annoying, isn't it?
[the.hard]
test_array = [ "] ", " # "] # ] There you go, parse this!
test_array2 = [ "Test #11 ]proved that", "Experiment #9 was a success" ]
# You didn't think it'd as easy as chucking out the last #, did you?
another_test_string = " Same thing, but with a string #"
harder_test_string = " And when \"'s are in the string, along with # \"" # "and comments are there too"
# Things will get harder
[the.hard."bit#"]
"what?" = "You don't think some user won't do that?"
multi_line_array = [
"]",
# ] Oh yes I did
]
# Each of the following keygroups/key value pairs should produce an error. Uncomment to them to test
#[error] if you didn't catch this, your parser is broken
#string = "Anything other than tabs, spaces and newline after a keygroup or key value pair has ended should produce an error unless it is a comment" like this
#array = [
# "This might most likely happen in multiline arrays",
# Like here,
# "or here,
# and here"
# ] End of array comment, forgot the #
#number = 3.14 pi <--again forgot the # `

0 comments on commit a60c713

Please sign in to comment.