mirror of
https://github.com/golang/go
synced 2024-11-22 14:54:46 -07:00
net/textproto: reduce allocations in ReadMIMEHeader
ReadMIMEHeader is used by net/http, net/mail, and mime/multipart. Don't do so many small allocations. Calculate up front how much we'll probably need. benchmark old ns/op new ns/op delta BenchmarkReadMIMEHeader 8433 7467 -11.45% benchmark old allocs new allocs delta BenchmarkReadMIMEHeader 23 14 -39.13% benchmark old bytes new bytes delta BenchmarkReadMIMEHeader 1705 1343 -21.23% R=golang-dev, r, iant, adg CC=golang-dev https://golang.org/cl/8179043
This commit is contained in:
parent
b4d4ff9381
commit
16c3f82ed4
@ -456,7 +456,16 @@ func (r *Reader) ReadDotLines() ([]string, error) {
|
||||
// }
|
||||
//
|
||||
func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) {
|
||||
m := make(MIMEHeader, 4)
|
||||
// Avoid lots of small slice allocations later by allocating one
|
||||
// large one ahead of time which we'll cut up into smaller
|
||||
// slices. If this isn't big enough later, we allocate small ones.
|
||||
var strs []string
|
||||
hint := r.upcomingHeaderNewlines()
|
||||
if hint > 0 {
|
||||
strs = make([]string, hint)
|
||||
}
|
||||
|
||||
m := make(MIMEHeader, hint)
|
||||
for {
|
||||
kv, err := r.readContinuedLineSlice()
|
||||
if len(kv) == 0 {
|
||||
@ -483,7 +492,18 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) {
|
||||
}
|
||||
value := string(kv[i:])
|
||||
|
||||
m[key] = append(m[key], value)
|
||||
vv := m[key]
|
||||
if vv == nil && len(strs) > 0 {
|
||||
// More than likely this will be a single-element key.
|
||||
// Most headers aren't multi-valued.
|
||||
// Set the capacity on strs[0] to 1, so any future append
|
||||
// won't extend the slice into the other strings.
|
||||
vv, strs = strs[:1:1], strs[1:]
|
||||
vv[0] = value
|
||||
m[key] = vv
|
||||
} else {
|
||||
m[key] = append(vv, value)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return m, err
|
||||
@ -491,6 +511,29 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// upcomingHeaderNewlines returns an approximation of the number of newlines
|
||||
// that will be in this header. If it gets confused, it returns 0.
|
||||
func (r *Reader) upcomingHeaderNewlines() (n int) {
|
||||
// Try to determine the 'hint' size.
|
||||
r.R.Peek(1) // force a buffer load if empty
|
||||
s := r.R.Buffered()
|
||||
if s == 0 {
|
||||
return
|
||||
}
|
||||
peek, _ := r.R.Peek(s)
|
||||
for len(peek) > 0 {
|
||||
i := bytes.IndexByte(peek, '\n')
|
||||
if i < 3 {
|
||||
// Not present (-1) or found within the next few bytes,
|
||||
// implying we're at the end ("\r\n\r\n" or "\n\n")
|
||||
return
|
||||
}
|
||||
n++
|
||||
peek = peek[i+1:]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CanonicalMIMEHeaderKey returns the canonical format of the
|
||||
// MIME header key s. The canonicalization converts the first
|
||||
// letter and any letter following a hyphen to upper case;
|
||||
|
Loading…
Reference in New Issue
Block a user