gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

databuffer.go (4023B)


      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 }