Skip to content

Commit

Permalink
types: improve the performance of sprintName by ~30% and halve allocs (
Browse files Browse the repository at this point in the history
…#1008)

```
benchmark                                          old ns/op     new ns/op     delta
BenchmarkSprintName-12                             174           117           -32.76%

benchmark                                          old allocs     new allocs     delta
BenchmarkSprintName-12                             2              1              -50.00%

benchmark                                          old bytes     new bytes     delta
BenchmarkSprintName-12                             48            32            -33.33%
```
  • Loading branch information
charlievieth authored and miekg committed Sep 23, 2019
1 parent 9578cae commit e393768
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 18 deletions.
55 changes: 37 additions & 18 deletions types.go
Expand Up @@ -438,25 +438,54 @@ func (rr *TXT) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) }

func sprintName(s string) string {
var dst strings.Builder
dst.Grow(len(s))

for i := 0; i < len(s); {
if i+1 < len(s) && s[i] == '\\' && s[i+1] == '.' {
dst.WriteString(s[i : i+2])
if dst.Len() != 0 {
dst.WriteString(s[i : i+2])
}
i += 2
continue
}

b, n := nextByte(s, i)
switch {
case n == 0:
i++ // dangling back slash
case b == '.':
dst.WriteByte('.')
if n == 0 {
i++
continue
}
if b == '.' {
if dst.Len() != 0 {
dst.WriteByte('.')
}
i += n
continue
}
switch b {
case ' ', '\'', '@', ';', '(', ')', '"', '\\': // additional chars to escape
if dst.Len() == 0 {
dst.Grow(len(s) * 2)
dst.WriteString(s[:i])
}
dst.WriteByte('\\')
dst.WriteByte(b)
default:
writeDomainNameByte(&dst, b)
if ' ' <= b && b <= '~' {
if dst.Len() != 0 {
dst.WriteByte(b)
}
} else {
if dst.Len() == 0 {
dst.Grow(len(s) * 2)
dst.WriteString(s[:i])
}
dst.WriteString(escapeByte(b))
}
}
i += n
}
if dst.Len() == 0 {
return s
}
return dst.String()
}

Expand Down Expand Up @@ -510,16 +539,6 @@ func sprintTxt(txt []string) string {
return out.String()
}

func writeDomainNameByte(s *strings.Builder, b byte) {
switch b {
case '.', ' ', '\'', '@', ';', '(', ')': // additional chars to escape
s.WriteByte('\\')
s.WriteByte(b)
default:
writeTXTStringByte(s, b)
}
}

func writeTXTStringByte(s *strings.Builder, b byte) {
switch {
case b == '"' || b == '\\':
Expand Down
11 changes: 11 additions & 0 deletions types_test.go
Expand Up @@ -133,6 +133,16 @@ func BenchmarkSprintName(b *testing.B) {
}
}

func BenchmarkSprintName_NoEscape(b *testing.B) {
for n := 0; n < b.N; n++ {
got := sprintName("large.example.com")

if want := "large.example.com"; got != want {
b.Fatalf("expected %q, got %q", got, want)
}
}
}

func BenchmarkSprintTxtOctet(b *testing.B) {
for n := 0; n < b.N; n++ {
got := sprintTxtOctet("abc\\.def\007\"\127@\255\x05\xef\\")
Expand All @@ -149,6 +159,7 @@ func BenchmarkSprintTxt(b *testing.B) {
"example.com",
}

b.ResetTimer()
for n := 0; n < b.N; n++ {
got := sprintTxt(txt)

Expand Down

0 comments on commit e393768

Please sign in to comment.