github.com/binkynet/BinkyNet@v1.12.1-0.20240421190447-da4e34c20be0/proto_vendor/golang.org/x/net/http2/databuffer.go (about)

     1  // Copyright 2014 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package http2
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"sync"
    11  )
    12  
    13  // Buffer chunks are allocated from a pool to reduce pressure on GC.
    14  // The maximum wasted space per dataBuffer is 2x the largest size class,
    15  // which happens when the dataBuffer has multiple chunks and there is
    16  // one unread byte in both the first and last chunks. We use a few size
    17  // classes to minimize overheads for servers that typically receive very
    18  // small request bodies.
    19  //
    20  // TODO: Benchmark to determine if the pools are necessary. The GC may have
    21  // improved enough that we can instead allocate chunks like this:
    22  // make([]byte, max(16<<10, expectedBytesRemaining))
    23  var (
    24  	dataChunkSizeClasses = []int{
    25  		1 << 10,
    26  		2 << 10,
    27  		4 << 10,
    28  		8 << 10,
    29  		16 << 10,
    30  	}
    31  	dataChunkPools = [...]sync.Pool{
    32  		{New: func() interface{} { return make([]byte, 1<<10) }},
    33  		{New: func() interface{} { return make([]byte, 2<<10) }},
    34  		{New: func() interface{} { return make([]byte, 4<<10) }},
    35  		{New: func() interface{} { return make([]byte, 8<<10) }},
    36  		{New: func() interface{} { return make([]byte, 16<<10) }},
    37  	}
    38  )
    39  
    40  func getDataBufferChunk(size int64) []byte {
    41  	i := 0
    42  	for ; i < len(dataChunkSizeClasses)-1; i++ {
    43  		if size <= int64(dataChunkSizeClasses[i]) {
    44  			break
    45  		}
    46  	}
    47  	return dataChunkPools[i].Get().([]byte)
    48  }
    49  
    50  func putDataBufferChunk(p []byte) {
    51  	for i, n := range dataChunkSizeClasses {
    52  		if len(p) == n {
    53  			dataChunkPools[i].Put(p)
    54  			return
    55  		}
    56  	}
    57  	panic(fmt.Sprintf("unexpected buffer len=%v", len(p)))
    58  }
    59  
    60  // dataBuffer is an io.ReadWriter backed by a list of data chunks.
    61  // Each dataBuffer is used to read DATA frames on a single stream.
    62  // The buffer is divided into chunks so the server can limit the
    63  // total memory used by a single connection without limiting the
    64  // request body size on any single stream.
    65  type dataBuffer struct {
    66  	chunks   [][]byte
    67  	r        int   // next byte to read is chunks[0][r]
    68  	w        int   // next byte to write is chunks[len(chunks)-1][w]
    69  	size     int   // total buffered bytes
    70  	expected int64 // we expect at least this many bytes in future Write calls (ignored if <= 0)
    71  }
    72  
    73  var errReadEmpty = errors.New("read from empty dataBuffer")
    74  
    75  // Read copies bytes from the buffer into p.
    76  // It is an error to read when no data is available.
    77  func (b *dataBuffer) Read(p []byte) (int, error) {
    78  	if b.size == 0 {
    79  		return 0, errReadEmpty
    80  	}
    81  	var ntotal int
    82  	for len(p) > 0 && b.size > 0 {
    83  		readFrom := b.bytesFromFirstChunk()
    84  		n := copy(p, readFrom)
    85  		p = p[n:]
    86  		ntotal += n
    87  		b.r += n
    88  		b.size -= n
    89  		// If the first chunk has been consumed, advance to the next chunk.
    90  		if b.r == len(b.chunks[0]) {
    91  			putDataBufferChunk(b.chunks[0])
    92  			end := len(b.chunks) - 1
    93  			copy(b.chunks[:end], b.chunks[1:])
    94  			b.chunks[end] = nil
    95  			b.chunks = b.chunks[:end]
    96  			b.r = 0
    97  		}
    98  	}
    99  	return ntotal, nil
   100  }
   101  
   102  func (b *dataBuffer) bytesFromFirstChunk() []byte {
   103  	if len(b.chunks) == 1 {
   104  		return b.chunks[0][b.r:b.w]
   105  	}
   106  	return b.chunks[0][b.r:]
   107  }
   108  
   109  // Len returns the number of bytes of the unread portion of the buffer.
   110  func (b *dataBuffer) Len() int {
   111  	return b.size
   112  }
   113  
   114  // Write appends p to the buffer.
   115  func (b *dataBuffer) Write(p []byte) (int, error) {
   116  	ntotal := len(p)
   117  	for len(p) > 0 {
   118  		// If the last chunk is empty, allocate a new chunk. Try to allocate
   119  		// enough to fully copy p plus any additional bytes we expect to
   120  		// receive. However, this may allocate less than len(p).
   121  		want := int64(len(p))
   122  		if b.expected > want {
   123  			want = b.expected
   124  		}
   125  		chunk := b.lastChunkOrAlloc(want)
   126  		n := copy(chunk[b.w:], p)
   127  		p = p[n:]
   128  		b.w += n
   129  		b.size += n
   130  		b.expected -= int64(n)
   131  	}
   132  	return ntotal, nil
   133  }
   134  
   135  func (b *dataBuffer) lastChunkOrAlloc(want int64) []byte {
   136  	if len(b.chunks) != 0 {
   137  		last := b.chunks[len(b.chunks)-1]
   138  		if b.w < len(last) {
   139  			return last
   140  		}
   141  	}
   142  	chunk := getDataBufferChunk(want)
   143  	b.chunks = append(b.chunks, chunk)
   144  	b.w = 0
   145  	return chunk
   146  }