1 // Copyright 2022 The gVisor Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 15 package buffer
16 17 import (
18 "fmt"
19 "io"
20 21 "gvisor.dev/gvisor/pkg/sync"
22 )
23 24 // ReadSize is the default amount that a View's size is increased by when an
25 // io.Reader has more data than a View can hold during calls to ReadFrom.
26 const ReadSize = 512
27 28 var viewPool = sync.Pool{
29 New: func() any {
30 return &View{}
31 },
32 }
33 34 // View is a window into a shared chunk. Views are held by Buffers in
35 // viewLists to represent contiguous memory.
36 //
37 // A View must be created with NewView, NewViewWithData, or Clone. Owners are
38 // responsible for maintaining ownership over their views. When Views need to be
39 // shared or copied, the owner should create a new View with Clone. Clone must
40 // only ever be called on a owned View, not a borrowed one.
41 //
42 // Users are responsible for calling Release when finished with their View so
43 // that its resources can be returned to the pool.
44 //
45 // Users must not write directly to slices returned by AsSlice. Instead, they
46 // must use Write/WriteAt/CopyIn to modify the underlying View. This preserves
47 // the safety guarantees of copy-on-write.
48 //
49 // +stateify savable
50 type View struct {
51 ViewEntry `state:"nosave"`
52 read int
53 write int
54 chunk *chunk
55 }
56 57 // NewView creates a new view with capacity at least as big as cap. It is
58 // analogous to make([]byte, 0, cap).
59 func NewView(cap int) *View {
60 c := newChunk(cap)
61 v := viewPool.Get().(*View)
62 *v = View{chunk: c}
63 return v
64 }
65 66 // NewViewSize creates a new view with capacity at least as big as size and
67 // length that is exactly size. It is analogous to make([]byte, size).
68 func NewViewSize(size int) *View {
69 v := NewView(size)
70 v.Grow(size)
71 return v
72 }
73 74 // NewViewWithData creates a new view and initializes it with data. This
75 // function should be used with caution to avoid unnecessary []byte allocations.
76 // When in doubt use NewWithView to maximize chunk reuse in production
77 // environments.
78 func NewViewWithData(data []byte) *View {
79 c := newChunk(len(data))
80 v := viewPool.Get().(*View)
81 *v = View{chunk: c}
82 v.Write(data)
83 return v
84 }
85 86 // Clone creates a shallow clone of v where the underlying chunk is shared.
87 //
88 // The caller must own the View to call Clone. It is not safe to call Clone
89 // on a borrowed or shared View because it can race with other View methods.
90 func (v *View) Clone() *View {
91 if v == nil {
92 panic("cannot clone a nil view")
93 }
94 v.chunk.IncRef()
95 newV := viewPool.Get().(*View)
96 newV.chunk = v.chunk
97 newV.read = v.read
98 newV.write = v.write
99 return newV
100 }
101 102 // Release releases the chunk held by v and returns v to the pool.
103 func (v *View) Release() {
104 if v == nil {
105 panic("cannot release a nil view")
106 }
107 v.chunk.DecRef()
108 *v = View{}
109 viewPool.Put(v)
110 }
111 112 // Reset sets the view's read and write indices back to zero.
113 func (v *View) Reset() {
114 if v == nil {
115 panic("cannot reset a nil view")
116 }
117 v.read = 0
118 v.write = 0
119 }
120 121 func (v *View) sharesChunk() bool {
122 return v.chunk.refCount.Load() > 1
123 }
124 125 // Full indicates the chunk is full.
126 //
127 // This indicates there is no capacity left to write.
128 func (v *View) Full() bool {
129 return v == nil || v.write == len(v.chunk.data)
130 }
131 132 // Capacity returns the total size of this view's chunk.
133 func (v *View) Capacity() int {
134 if v == nil {
135 return 0
136 }
137 return len(v.chunk.data)
138 }
139 140 // Size returns the size of data written to the view.
141 func (v *View) Size() int {
142 if v == nil {
143 return 0
144 }
145 return v.write - v.read
146 }
147 148 // TrimFront advances the read index by the given amount.
149 func (v *View) TrimFront(n int) {
150 if v.read+n > v.write {
151 panic("cannot trim past the end of a view")
152 }
153 v.read += n
154 }
155 156 // AsSlice returns a slice of the data written to this view.
157 func (v *View) AsSlice() []byte {
158 if v.Size() == 0 {
159 return nil
160 }
161 return v.chunk.data[v.read:v.write]
162 }
163 164 // ToSlice returns an owned copy of the data in this view.
165 func (v *View) ToSlice() []byte {
166 if v.Size() == 0 {
167 return nil
168 }
169 s := make([]byte, v.Size())
170 copy(s, v.AsSlice())
171 return s
172 }
173 174 // AvailableSize returns the number of bytes available for writing.
175 func (v *View) AvailableSize() int {
176 if v == nil {
177 return 0
178 }
179 return len(v.chunk.data) - v.write
180 }
181 182 // Read reads v's data into p.
183 //
184 // Implements the io.Reader interface.
185 func (v *View) Read(p []byte) (int, error) {
186 if len(p) == 0 {
187 return 0, nil
188 }
189 if v.Size() == 0 {
190 return 0, io.EOF
191 }
192 n := copy(p, v.AsSlice())
193 v.TrimFront(n)
194 return n, nil
195 }
196 197 // ReadByte implements the io.ByteReader interface.
198 func (v *View) ReadByte() (byte, error) {
199 if v.Size() == 0 {
200 return 0, io.EOF
201 }
202 b := v.AsSlice()[0]
203 v.read++
204 return b, nil
205 }
206 207 // WriteTo writes data to w until the view is empty or an error occurs. The
208 // return value n is the number of bytes written.
209 //
210 // WriteTo implements the io.WriterTo interface.
211 func (v *View) WriteTo(w io.Writer) (n int64, err error) {
212 if v.Size() > 0 {
213 sz := v.Size()
214 m, e := w.Write(v.AsSlice())
215 v.TrimFront(m)
216 n = int64(m)
217 if e != nil {
218 return n, e
219 }
220 if m != sz {
221 return n, io.ErrShortWrite
222 }
223 }
224 return n, nil
225 }
226 227 // ReadAt reads data to the p starting at offset.
228 //
229 // Implements the io.ReaderAt interface.
230 func (v *View) ReadAt(p []byte, off int) (int, error) {
231 if off < 0 || off > v.Size() {
232 return 0, fmt.Errorf("ReadAt(): offset out of bounds: want 0 < off < %d, got off=%d", v.Size(), off)
233 }
234 n := copy(p, v.AsSlice()[off:])
235 return n, nil
236 }
237 238 // Write writes data to the view's chunk starting at the v.write index. If the
239 // view's chunk has a reference count greater than 1, the chunk is copied first
240 // and then written to.
241 //
242 // Implements the io.Writer interface.
243 func (v *View) Write(p []byte) (int, error) {
244 if v == nil {
245 panic("cannot write to a nil view")
246 }
247 if v.AvailableSize() < len(p) {
248 v.growCap(len(p) - v.AvailableSize())
249 } else if v.sharesChunk() {
250 defer v.chunk.DecRef()
251 v.chunk = v.chunk.Clone()
252 }
253 n := copy(v.chunk.data[v.write:], p)
254 v.write += n
255 if n < len(p) {
256 return n, io.ErrShortWrite
257 }
258 return n, nil
259 }
260 261 // ReadFrom reads data from r until EOF and appends it to the buffer, growing
262 // the buffer as needed. The return value n is the number of bytes read. Any
263 // error except io.EOF encountered during the read is also returned.
264 //
265 // ReadFrom implements the io.ReaderFrom interface.
266 func (v *View) ReadFrom(r io.Reader) (n int64, err error) {
267 if v == nil {
268 panic("cannot write to a nil view")
269 }
270 if v.sharesChunk() {
271 defer v.chunk.DecRef()
272 v.chunk = v.chunk.Clone()
273 }
274 for {
275 // Check for EOF to avoid an unnnecesary allocation.
276 if _, e := r.Read(nil); e == io.EOF {
277 return n, nil
278 }
279 if v.AvailableSize() == 0 {
280 v.growCap(ReadSize)
281 }
282 m, e := r.Read(v.availableSlice())
283 v.write += m
284 n += int64(m)
285 286 if e == io.EOF {
287 return n, nil
288 }
289 if e != nil {
290 return n, e
291 }
292 }
293 }
294 295 // WriteAt writes data to the views's chunk starting at start. If the
296 // view's chunk has a reference count greater than 1, the chunk is copied first
297 // and then written to.
298 //
299 // Implements the io.WriterAt interface.
300 func (v *View) WriteAt(p []byte, off int) (int, error) {
301 if v == nil {
302 panic("cannot write to a nil view")
303 }
304 if off < 0 || off > v.Size() {
305 return 0, fmt.Errorf("write offset out of bounds: want 0 < off < %d, got off=%d", v.Size(), off)
306 }
307 if v.sharesChunk() {
308 defer v.chunk.DecRef()
309 v.chunk = v.chunk.Clone()
310 }
311 n := copy(v.AsSlice()[off:], p)
312 if n < len(p) {
313 return n, io.ErrShortWrite
314 }
315 return n, nil
316 }
317 318 // Grow increases the size of the view. If the new size is greater than the
319 // view's current capacity, Grow will reallocate the view with an increased
320 // capacity.
321 func (v *View) Grow(n int) {
322 if v == nil {
323 panic("cannot grow a nil view")
324 }
325 if v.write+n > v.Capacity() {
326 v.growCap(n)
327 }
328 v.write += n
329 }
330 331 // growCap increases the capacity of the view by at least n.
332 func (v *View) growCap(n int) {
333 if v == nil {
334 panic("cannot grow a nil view")
335 }
336 defer v.chunk.DecRef()
337 old := v.AsSlice()
338 v.chunk = newChunk(v.Capacity() + n)
339 copy(v.chunk.data, old)
340 v.read = 0
341 v.write = len(old)
342 }
343 344 // CapLength caps the length of the view's read slice to n. If n > v.Size(),
345 // the function is a no-op.
346 func (v *View) CapLength(n int) {
347 if v == nil {
348 panic("cannot resize a nil view")
349 }
350 if n < 0 {
351 panic("n must be >= 0")
352 }
353 if n > v.Size() {
354 n = v.Size()
355 }
356 v.write = v.read + n
357 }
358 359 func (v *View) availableSlice() []byte {
360 if v.sharesChunk() {
361 defer v.chunk.DecRef()
362 c := v.chunk.Clone()
363 v.chunk = c
364 }
365 return v.chunk.data[v.write:]
366 }
367