forked from ebhomengo/niki
1
0
Fork 0
niki/vendor/golang.org/x/net/http2/databuffer.go

268 lines
4.3 KiB
Go
Raw Normal View History

2024-02-18 10:42:21 +00:00
// Copyright 2014 The Go Authors. All rights reserved.
2024-02-18 10:42:21 +00:00
// Use of this source code is governed by a BSD-style
2024-02-18 10:42:21 +00:00
// license that can be found in the LICENSE file.
package http2
import (
"errors"
"fmt"
"sync"
)
// Buffer chunks are allocated from a pool to reduce pressure on GC.
2024-02-18 10:42:21 +00:00
// The maximum wasted space per dataBuffer is 2x the largest size class,
2024-02-18 10:42:21 +00:00
// which happens when the dataBuffer has multiple chunks and there is
2024-02-18 10:42:21 +00:00
// one unread byte in both the first and last chunks. We use a few size
2024-02-18 10:42:21 +00:00
// classes to minimize overheads for servers that typically receive very
2024-02-18 10:42:21 +00:00
// small request bodies.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// TODO: Benchmark to determine if the pools are necessary. The GC may have
2024-02-18 10:42:21 +00:00
// improved enough that we can instead allocate chunks like this:
2024-02-18 10:42:21 +00:00
// make([]byte, max(16<<10, expectedBytesRemaining))
2024-02-18 10:42:21 +00:00
var dataChunkPools = [...]sync.Pool{
2024-02-18 10:42:21 +00:00
{New: func() interface{} { return new([1 << 10]byte) }},
2024-02-18 10:42:21 +00:00
{New: func() interface{} { return new([2 << 10]byte) }},
2024-02-18 10:42:21 +00:00
{New: func() interface{} { return new([4 << 10]byte) }},
2024-02-18 10:42:21 +00:00
{New: func() interface{} { return new([8 << 10]byte) }},
2024-02-18 10:42:21 +00:00
{New: func() interface{} { return new([16 << 10]byte) }},
}
func getDataBufferChunk(size int64) []byte {
2024-02-18 10:42:21 +00:00
switch {
2024-02-18 10:42:21 +00:00
case size <= 1<<10:
2024-02-18 10:42:21 +00:00
return dataChunkPools[0].Get().(*[1 << 10]byte)[:]
2024-02-18 10:42:21 +00:00
case size <= 2<<10:
2024-02-18 10:42:21 +00:00
return dataChunkPools[1].Get().(*[2 << 10]byte)[:]
2024-02-18 10:42:21 +00:00
case size <= 4<<10:
2024-02-18 10:42:21 +00:00
return dataChunkPools[2].Get().(*[4 << 10]byte)[:]
2024-02-18 10:42:21 +00:00
case size <= 8<<10:
2024-02-18 10:42:21 +00:00
return dataChunkPools[3].Get().(*[8 << 10]byte)[:]
2024-02-18 10:42:21 +00:00
default:
2024-02-18 10:42:21 +00:00
return dataChunkPools[4].Get().(*[16 << 10]byte)[:]
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
func putDataBufferChunk(p []byte) {
2024-02-18 10:42:21 +00:00
switch len(p) {
2024-02-18 10:42:21 +00:00
case 1 << 10:
2024-02-18 10:42:21 +00:00
dataChunkPools[0].Put((*[1 << 10]byte)(p))
2024-02-18 10:42:21 +00:00
case 2 << 10:
2024-02-18 10:42:21 +00:00
dataChunkPools[1].Put((*[2 << 10]byte)(p))
2024-02-18 10:42:21 +00:00
case 4 << 10:
2024-02-18 10:42:21 +00:00
dataChunkPools[2].Put((*[4 << 10]byte)(p))
2024-02-18 10:42:21 +00:00
case 8 << 10:
2024-02-18 10:42:21 +00:00
dataChunkPools[3].Put((*[8 << 10]byte)(p))
2024-02-18 10:42:21 +00:00
case 16 << 10:
2024-02-18 10:42:21 +00:00
dataChunkPools[4].Put((*[16 << 10]byte)(p))
2024-02-18 10:42:21 +00:00
default:
2024-02-18 10:42:21 +00:00
panic(fmt.Sprintf("unexpected buffer len=%v", len(p)))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// dataBuffer is an io.ReadWriter backed by a list of data chunks.
2024-02-18 10:42:21 +00:00
// Each dataBuffer is used to read DATA frames on a single stream.
2024-02-18 10:42:21 +00:00
// The buffer is divided into chunks so the server can limit the
2024-02-18 10:42:21 +00:00
// total memory used by a single connection without limiting the
2024-02-18 10:42:21 +00:00
// request body size on any single stream.
2024-02-18 10:42:21 +00:00
type dataBuffer struct {
chunks [][]byte
r int // next byte to read is chunks[0][r]
w int // next byte to write is chunks[len(chunks)-1][w]
size int // total buffered bytes
2024-02-18 10:42:21 +00:00
expected int64 // we expect at least this many bytes in future Write calls (ignored if <= 0)
2024-02-18 10:42:21 +00:00
}
var errReadEmpty = errors.New("read from empty dataBuffer")
// Read copies bytes from the buffer into p.
2024-02-18 10:42:21 +00:00
// It is an error to read when no data is available.
2024-02-18 10:42:21 +00:00
func (b *dataBuffer) Read(p []byte) (int, error) {
2024-02-18 10:42:21 +00:00
if b.size == 0 {
2024-02-18 10:42:21 +00:00
return 0, errReadEmpty
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
var ntotal int
2024-02-18 10:42:21 +00:00
for len(p) > 0 && b.size > 0 {
2024-02-18 10:42:21 +00:00
readFrom := b.bytesFromFirstChunk()
2024-02-18 10:42:21 +00:00
n := copy(p, readFrom)
2024-02-18 10:42:21 +00:00
p = p[n:]
2024-02-18 10:42:21 +00:00
ntotal += n
2024-02-18 10:42:21 +00:00
b.r += n
2024-02-18 10:42:21 +00:00
b.size -= n
2024-02-18 10:42:21 +00:00
// If the first chunk has been consumed, advance to the next chunk.
2024-02-18 10:42:21 +00:00
if b.r == len(b.chunks[0]) {
2024-02-18 10:42:21 +00:00
putDataBufferChunk(b.chunks[0])
2024-02-18 10:42:21 +00:00
end := len(b.chunks) - 1
2024-02-18 10:42:21 +00:00
copy(b.chunks[:end], b.chunks[1:])
2024-02-18 10:42:21 +00:00
b.chunks[end] = nil
2024-02-18 10:42:21 +00:00
b.chunks = b.chunks[:end]
2024-02-18 10:42:21 +00:00
b.r = 0
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return ntotal, nil
2024-02-18 10:42:21 +00:00
}
func (b *dataBuffer) bytesFromFirstChunk() []byte {
2024-02-18 10:42:21 +00:00
if len(b.chunks) == 1 {
2024-02-18 10:42:21 +00:00
return b.chunks[0][b.r:b.w]
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return b.chunks[0][b.r:]
2024-02-18 10:42:21 +00:00
}
// Len returns the number of bytes of the unread portion of the buffer.
2024-02-18 10:42:21 +00:00
func (b *dataBuffer) Len() int {
2024-02-18 10:42:21 +00:00
return b.size
2024-02-18 10:42:21 +00:00
}
// Write appends p to the buffer.
2024-02-18 10:42:21 +00:00
func (b *dataBuffer) Write(p []byte) (int, error) {
2024-02-18 10:42:21 +00:00
ntotal := len(p)
2024-02-18 10:42:21 +00:00
for len(p) > 0 {
2024-02-18 10:42:21 +00:00
// If the last chunk is empty, allocate a new chunk. Try to allocate
2024-02-18 10:42:21 +00:00
// enough to fully copy p plus any additional bytes we expect to
2024-02-18 10:42:21 +00:00
// receive. However, this may allocate less than len(p).
2024-02-18 10:42:21 +00:00
want := int64(len(p))
2024-02-18 10:42:21 +00:00
if b.expected > want {
2024-02-18 10:42:21 +00:00
want = b.expected
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
chunk := b.lastChunkOrAlloc(want)
2024-02-18 10:42:21 +00:00
n := copy(chunk[b.w:], p)
2024-02-18 10:42:21 +00:00
p = p[n:]
2024-02-18 10:42:21 +00:00
b.w += n
2024-02-18 10:42:21 +00:00
b.size += n
2024-02-18 10:42:21 +00:00
b.expected -= int64(n)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return ntotal, nil
2024-02-18 10:42:21 +00:00
}
func (b *dataBuffer) lastChunkOrAlloc(want int64) []byte {
2024-02-18 10:42:21 +00:00
if len(b.chunks) != 0 {
2024-02-18 10:42:21 +00:00
last := b.chunks[len(b.chunks)-1]
2024-02-18 10:42:21 +00:00
if b.w < len(last) {
2024-02-18 10:42:21 +00:00
return last
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
chunk := getDataBufferChunk(want)
2024-02-18 10:42:21 +00:00
b.chunks = append(b.chunks, chunk)
2024-02-18 10:42:21 +00:00
b.w = 0
2024-02-18 10:42:21 +00:00
return chunk
2024-02-18 10:42:21 +00:00
}