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