mirror of
https://github.com/golang/go
synced 2024-11-26 02:57:57 -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) {
|
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 {
|
for {
|
||||||
kv, err := r.readContinuedLineSlice()
|
kv, err := r.readContinuedLineSlice()
|
||||||
if len(kv) == 0 {
|
if len(kv) == 0 {
|
||||||
@ -483,7 +492,18 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) {
|
|||||||
}
|
}
|
||||||
value := string(kv[i:])
|
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 {
|
if err != nil {
|
||||||
return m, err
|
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
|
// CanonicalMIMEHeaderKey returns the canonical format of the
|
||||||
// MIME header key s. The canonicalization converts the first
|
// MIME header key s. The canonicalization converts the first
|
||||||
// letter and any letter following a hyphen to upper case;
|
// letter and any letter following a hyphen to upper case;
|
||||||
|
Loading…
Reference in New Issue
Block a user