]> Cypherpunks repositories - gostls13.git/commitdiff
net/textproto: do not buffer a line if we know the next line is empty
authorTravis Bischel <travis.bischel@gmail.com>
Sun, 9 Sep 2018 22:00:38 +0000 (15:00 -0700)
committerBrad Fitzpatrick <bradfitz@golang.org>
Thu, 1 Nov 2018 15:43:45 +0000 (15:43 +0000)
readContinuedLineSlice intends to buffer a continued line of text, where
a continued line can continue through newlines so long as the next line
begins with a space or tab.

The current optimization is to not try to buffer and build a line if we
immediately see that the next line begins with an ASCII character.

This adds avoiding copying the line if we see that the next line is \n
or \r\n as well.

Notably, headers always end in \r\n\r\n. In the general, well formatted
header case, we can now avoid ever allocating textproto.Reader's
internal reusable buf.

This can mildly be seen in net/http's BenchmarkClientServer:

name            old time/op    new time/op    delta
ClientServer-4    66.4µs ± 0%    66.2µs ± 0%  -0.35%  (p=0.004 n=10+10)

name            old alloc/op   new alloc/op   delta
ClientServer-4    4.87kB ± 0%    4.82kB ± 0%  -1.01%  (p=0.000 n=6+10)

name            old allocs/op  new allocs/op  delta
ClientServer-4      64.0 ± 0%      63.0 ± 0%  -1.56%  (p=0.000 n=10+10)

Change-Id: Id8c2ab69086ac481b90abda289396dcb7bfe8851
Reviewed-on: https://go-review.googlesource.com/c/134227
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/net/textproto/reader.go

index feb464b2f284ec1b8db79319084f9ff71b13deaa..2c4f25d5ae693103662f5a43c8c4d8ba679d08f5 100644 (file)
@@ -129,12 +129,13 @@ func (r *Reader) readContinuedLineSlice() ([]byte, error) {
        }
 
        // Optimistically assume that we have started to buffer the next line
-       // and it starts with an ASCII letter (the next header key), so we can
-       // avoid copying that buffered data around in memory and skipping over
-       // non-existent whitespace.
+       // and it starts with an ASCII letter (the next header key), or a blank
+       // line, so we can avoid copying that buffered data around in memory
+       // and skipping over non-existent whitespace.
        if r.R.Buffered() > 1 {
-               peek, err := r.R.Peek(1)
-               if err == nil && isASCIILetter(peek[0]) {
+               peek, _ := r.R.Peek(2)
+               if len(peek) > 0 && (isASCIILetter(peek[0]) || peek[0] == '\n') ||
+                       len(peek) == 2 && peek[0] == '\r' && peek[1] == '\n' {
                        return trim(line), nil
                }
        }