github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/buffer/view.go (about)

     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  	"github.com/nicocha30/gvisor-ligolo/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  }