view.go raw

   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