golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/quic/pipe.go (about)

     1  // Copyright 2023 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  //go:build go1.21
     6  
     7  package quic
     8  
     9  import (
    10  	"sync"
    11  )
    12  
    13  // A pipe is a byte buffer used in implementing streams.
    14  //
    15  // A pipe contains a window of stream data.
    16  // Random access reads and writes are supported within the window.
    17  // Writing past the end of the window extends it.
    18  // Data may be discarded from the start of the pipe, advancing the window.
    19  type pipe struct {
    20  	start int64    // stream position of first stored byte
    21  	end   int64    // stream position just past the last stored byte
    22  	head  *pipebuf // if non-nil, then head.off + len(head.b) > start
    23  	tail  *pipebuf // if non-nil, then tail.off + len(tail.b) == end
    24  }
    25  
    26  type pipebuf struct {
    27  	off  int64 // stream position of b[0]
    28  	b    []byte
    29  	next *pipebuf
    30  }
    31  
    32  func (pb *pipebuf) end() int64 {
    33  	return pb.off + int64(len(pb.b))
    34  }
    35  
    36  var pipebufPool = sync.Pool{
    37  	New: func() any {
    38  		return &pipebuf{
    39  			b: make([]byte, 4096),
    40  		}
    41  	},
    42  }
    43  
    44  func newPipebuf() *pipebuf {
    45  	return pipebufPool.Get().(*pipebuf)
    46  }
    47  
    48  func (b *pipebuf) recycle() {
    49  	b.off = 0
    50  	b.next = nil
    51  	pipebufPool.Put(b)
    52  }
    53  
    54  // writeAt writes len(b) bytes to the pipe at offset off.
    55  //
    56  // Writes to offsets before p.start are discarded.
    57  // Writes to offsets after p.end extend the pipe window.
    58  func (p *pipe) writeAt(b []byte, off int64) {
    59  	end := off + int64(len(b))
    60  	if end > p.end {
    61  		p.end = end
    62  	} else if end <= p.start {
    63  		return
    64  	}
    65  
    66  	if off < p.start {
    67  		// Discard the portion of b which falls before p.start.
    68  		trim := p.start - off
    69  		b = b[trim:]
    70  		off = p.start
    71  	}
    72  
    73  	if p.head == nil {
    74  		p.head = newPipebuf()
    75  		p.head.off = p.start
    76  		p.tail = p.head
    77  	}
    78  	pb := p.head
    79  	if off >= p.tail.off {
    80  		// Common case: Writing past the end of the pipe.
    81  		pb = p.tail
    82  	}
    83  	for {
    84  		pboff := off - pb.off
    85  		if pboff < int64(len(pb.b)) {
    86  			n := copy(pb.b[pboff:], b)
    87  			if n == len(b) {
    88  				return
    89  			}
    90  			off += int64(n)
    91  			b = b[n:]
    92  		}
    93  		if pb.next == nil {
    94  			pb.next = newPipebuf()
    95  			pb.next.off = pb.off + int64(len(pb.b))
    96  			p.tail = pb.next
    97  		}
    98  		pb = pb.next
    99  	}
   100  }
   101  
   102  // copy copies len(b) bytes into b starting from off.
   103  // The pipe must contain [off, off+len(b)).
   104  func (p *pipe) copy(off int64, b []byte) {
   105  	dst := b[:0]
   106  	p.read(off, len(b), func(c []byte) error {
   107  		dst = append(dst, c...)
   108  		return nil
   109  	})
   110  }
   111  
   112  // read calls f with the data in [off, off+n)
   113  // The data may be provided sequentially across multiple calls to f.
   114  // Note that read (unlike an io.Reader) does not consume the read data.
   115  func (p *pipe) read(off int64, n int, f func([]byte) error) error {
   116  	if off < p.start {
   117  		panic("invalid read range")
   118  	}
   119  	for pb := p.head; pb != nil && n > 0; pb = pb.next {
   120  		if off >= pb.end() {
   121  			continue
   122  		}
   123  		b := pb.b[off-pb.off:]
   124  		if len(b) > n {
   125  			b = b[:n]
   126  		}
   127  		off += int64(len(b))
   128  		n -= len(b)
   129  		if err := f(b); err != nil {
   130  			return err
   131  		}
   132  	}
   133  	if n > 0 {
   134  		panic("invalid read range")
   135  	}
   136  	return nil
   137  }
   138  
   139  // peek returns a reference to up to n bytes of internal data buffer, starting at p.start.
   140  // The returned slice is valid until the next call to discardBefore.
   141  // The length of the returned slice will be in the range [0,n].
   142  func (p *pipe) peek(n int64) []byte {
   143  	pb := p.head
   144  	if pb == nil {
   145  		return nil
   146  	}
   147  	b := pb.b[p.start-pb.off:]
   148  	return b[:min(int64(len(b)), n)]
   149  }
   150  
   151  // availableBuffer returns the available contiguous, allocated buffer space
   152  // following the pipe window.
   153  //
   154  // This is used by the stream write fast path, which makes multiple writes into the pipe buffer
   155  // without a lock, and then adjusts p.end at a later time with a lock held.
   156  func (p *pipe) availableBuffer() []byte {
   157  	if p.tail == nil {
   158  		return nil
   159  	}
   160  	return p.tail.b[p.end-p.tail.off:]
   161  }
   162  
   163  // discardBefore discards all data prior to off.
   164  func (p *pipe) discardBefore(off int64) {
   165  	for p.head != nil && p.head.end() < off {
   166  		head := p.head
   167  		p.head = p.head.next
   168  		head.recycle()
   169  	}
   170  	if p.head == nil {
   171  		p.tail = nil
   172  	}
   173  	p.start = off
   174  	p.end = max(p.end, off)
   175  }