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 }