Reduce allocations in UnpackDomainName by better sizing slice (#844)

* Reduce allocations in UnpackDomainName by better sizing slice

The maximum size of a domain name in presentation format is bounded by
the maximum length of a name in wire octet form and the maximum length
of a label. As s doesn't escape from UnpackDomainName, we can safely
give it the maximum capacity and it will never need to grow.

* Benchmark UnpackDomainName with lonest names possible

* Rename BenchmarkUnpackDomainNameLongestEscaped to match

* Improve maxDomainNamePresentationLength comment

* Further improve maxDomainNamePresentationLength comment
This commit is contained in:
Tom Thorogood 2018-11-30 06:25:51 +10:30 committed by Miek Gieben
parent b1bf6f1b9b
commit c0747f060e
3 changed files with 46 additions and 1 deletions

View File

@ -128,6 +128,36 @@ func BenchmarkUnpackDomainNameUnprintable(b *testing.B) {
}
}
func BenchmarkUnpackDomainNameLongest(b *testing.B) {
buf := make([]byte, len(longestDomain)+1)
n, err := PackDomainName(longestDomain, buf, 0, nil, false)
if err != nil {
b.Fatal(err)
}
if n != maxDomainNameWireOctets {
b.Fatalf("name wrong size in wire format, expected %d, got %d", maxDomainNameWireOctets, n)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _, _ = UnpackDomainName(buf, 0)
}
}
func BenchmarkUnpackDomainNameLongestUnprintable(b *testing.B) {
buf := make([]byte, len(longestUnprintableDomain)+1)
n, err := PackDomainName(longestUnprintableDomain, buf, 0, nil, false)
if err != nil {
b.Fatal(err)
}
if n != maxDomainNameWireOctets {
b.Fatalf("name wrong size in wire format, expected %d, got %d", maxDomainNameWireOctets, n)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _, _ = UnpackDomainName(buf, 0)
}
}
func BenchmarkCopy(b *testing.B) {
b.ReportAllocs()
m := new(Msg)

12
msg.go
View File

@ -36,6 +36,16 @@ const (
// not something a well written implementation should ever do, so we leave them
// to trip the maximum compression pointer check.
maxCompressionPointers = (maxDomainNameWireOctets+1)/2 - 2
// This is the maximum length of a domain name in presentation format. The
// maximum wire length of a domain name is 255 octets (see above), with the
// maximum label length being 63. The wire format requires one extra byte over
// the presentation format, reducing the number of octets by 1. Each label in
// the name will be separated by a single period, with each octet in the label
// expanding to at most 4 bytes (\DDD). If all other labels are of the maximum
// length, then the final label can only be 61 octets long to not exceed the
// maximum allowed wire length.
maxDomainNamePresentationLength = 61*4 + 1 + 63*4 + 1 + 63*4 + 1 + 63*4 + 1
)
// Errors defined in this package.
@ -372,7 +382,7 @@ func isRootLabel(s string, bs []byte, off, end int) bool {
// When an error is encountered, the unpacked name will be discarded
// and len(msg) will be returned as the offset.
func UnpackDomainName(msg []byte, off int) (string, int, error) {
s := make([]byte, 0, 64)
s := make([]byte, 0, maxDomainNamePresentationLength)
off1 := 0
lenmsg := len(msg)
budget := maxDomainNameWireOctets

View File

@ -13,6 +13,7 @@ const maxPrintableLabel = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0
var (
longDomain = maxPrintableLabel[:53] + strings.TrimSuffix(
strings.Join([]string{".", ".", ".", ".", "."}, maxPrintableLabel[:49]), ".")
reChar = regexp.MustCompile(`.`)
i = -1
maxUnprintableLabel = reChar.ReplaceAllStringFunc(maxPrintableLabel, func(ch string) string {
@ -21,6 +22,10 @@ var (
}
return fmt.Sprintf("\\%03d", i)
})
// These are the longest possible domain names in presentation format.
longestDomain = maxPrintableLabel[:61] + strings.Join([]string{".", ".", ".", "."}, maxPrintableLabel)
longestUnprintableDomain = maxUnprintableLabel[:61*4] + strings.Join([]string{".", ".", ".", "."}, maxUnprintableLabel)
)
func TestPackNoSideEffect(t *testing.T) {