cubefs

Форк
0
/x
/
databuffer.go 
146 строк · 3.9 Кб
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
}
147

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.