github.com/uppal0016/docker_new@v0.0.0-20240123060250-1c98be13ac2c/pkg/ioutils/bytespipe.go (about)

     1  package ioutils
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  	"sync"
     7  )
     8  
     9  // maxCap is the highest capacity to use in byte slices that buffer data.
    10  const maxCap = 1e6
    11  
    12  // minCap is the lowest capacity to use in byte slices that buffer data
    13  const minCap = 64
    14  
    15  // blockThreshold is the minimum number of bytes in the buffer which will cause
    16  // a write to BytesPipe to block when allocating a new slice.
    17  const blockThreshold = 1e6
    18  
    19  var (
    20  	// ErrClosed is returned when Write is called on a closed BytesPipe.
    21  	ErrClosed = errors.New("write to closed BytesPipe")
    22  
    23  	bufPools = make(map[int]*sync.Pool)
    24  )
    25  
    26  // BytesPipe is io.ReadWriteCloser which works similarly to pipe(queue).
    27  // All written data may be read at most once. Also, BytesPipe allocates
    28  // and releases new byte slices to adjust to current needs, so the buffer
    29  // won't be overgrown after peak loads.
    30  type BytesPipe struct {
    31  	mu       sync.Mutex
    32  	wait     *sync.Cond
    33  	buf      []*fixedBuffer
    34  	bufLen   int
    35  	closeErr error // error to return from next Read. set to nil if not closed.
    36  }
    37  
    38  // NewBytesPipe creates new BytesPipe, initialized by specified slice.
    39  // If buf is nil, then it will be initialized with slice which cap is 64.
    40  // buf will be adjusted in a way that len(buf) == 0, cap(buf) == cap(buf).
    41  func NewBytesPipe() *BytesPipe {
    42  	bp := &BytesPipe{}
    43  	bp.buf = append(bp.buf, getBuffer(minCap))
    44  	bp.wait = sync.NewCond(&bp.mu)
    45  	return bp
    46  }
    47  
    48  // Write writes p to BytesPipe.
    49  // It can allocate new []byte slices in a process of writing.
    50  func (bp *BytesPipe) Write(p []byte) (int, error) {
    51  	bp.mu.Lock()
    52  
    53  	written := 0
    54  loop0:
    55  	for {
    56  		if bp.closeErr != nil {
    57  			bp.mu.Unlock()
    58  			return written, ErrClosed
    59  		}
    60  
    61  		if len(bp.buf) == 0 {
    62  			bp.buf = append(bp.buf, getBuffer(64))
    63  		}
    64  		// get the last buffer
    65  		b := bp.buf[len(bp.buf)-1]
    66  
    67  		n, err := b.Write(p)
    68  		written += n
    69  		bp.bufLen += n
    70  
    71  		// errBufferFull is an error we expect to get if the buffer is full
    72  		if err != nil && err != errBufferFull {
    73  			bp.wait.Broadcast()
    74  			bp.mu.Unlock()
    75  			return written, err
    76  		}
    77  
    78  		// if there was enough room to write all then break
    79  		if len(p) == n {
    80  			break
    81  		}
    82  
    83  		// more data: write to the next slice
    84  		p = p[n:]
    85  
    86  		// make sure the buffer doesn't grow too big from this write
    87  		for bp.bufLen >= blockThreshold {
    88  			bp.wait.Wait()
    89  			if bp.closeErr != nil {
    90  				continue loop0
    91  			}
    92  		}
    93  
    94  		// add new byte slice to the buffers slice and continue writing
    95  		nextCap := b.Cap() * 2
    96  		if nextCap > maxCap {
    97  			nextCap = maxCap
    98  		}
    99  		bp.buf = append(bp.buf, getBuffer(nextCap))
   100  	}
   101  	bp.wait.Broadcast()
   102  	bp.mu.Unlock()
   103  	return written, nil
   104  }
   105  
   106  // CloseWithError causes further reads from a BytesPipe to return immediately.
   107  func (bp *BytesPipe) CloseWithError(err error) error {
   108  	bp.mu.Lock()
   109  	if err != nil {
   110  		bp.closeErr = err
   111  	} else {
   112  		bp.closeErr = io.EOF
   113  	}
   114  	bp.wait.Broadcast()
   115  	bp.mu.Unlock()
   116  	return nil
   117  }
   118  
   119  // Close causes further reads from a BytesPipe to return immediately.
   120  func (bp *BytesPipe) Close() error {
   121  	return bp.CloseWithError(nil)
   122  }
   123  
   124  // Read reads bytes from BytesPipe.
   125  // Data could be read only once.
   126  func (bp *BytesPipe) Read(p []byte) (n int, err error) {
   127  	bp.mu.Lock()
   128  	if bp.bufLen == 0 {
   129  		if bp.closeErr != nil {
   130  			bp.mu.Unlock()
   131  			return 0, bp.closeErr
   132  		}
   133  		bp.wait.Wait()
   134  		if bp.bufLen == 0 && bp.closeErr != nil {
   135  			bp.mu.Unlock()
   136  			return 0, bp.closeErr
   137  		}
   138  	}
   139  
   140  	for bp.bufLen > 0 {
   141  		b := bp.buf[0]
   142  		read, _ := b.Read(p) // ignore error since fixedBuffer doesn't really return an error
   143  		n += read
   144  		bp.bufLen -= read
   145  
   146  		if b.Len() == 0 {
   147  			// it's empty so return it to the pool and move to the next one
   148  			returnBuffer(b)
   149  			bp.buf[0] = nil
   150  			bp.buf = bp.buf[1:]
   151  		}
   152  
   153  		if len(p) == read {
   154  			break
   155  		}
   156  
   157  		p = p[read:]
   158  	}
   159  
   160  	bp.wait.Broadcast()
   161  	bp.mu.Unlock()
   162  	return
   163  }
   164  
   165  func returnBuffer(b *fixedBuffer) {
   166  	b.Reset()
   167  	pool := bufPools[b.Cap()]
   168  	if pool != nil {
   169  		pool.Put(b)
   170  	}
   171  }
   172  
   173  func getBuffer(size int) *fixedBuffer {
   174  	pool, ok := bufPools[size]
   175  	if !ok {
   176  		pool = &sync.Pool{New: func() interface{} { return &fixedBuffer{buf: make([]byte, 0, size)} }}
   177  		bufPools[size] = pool
   178  	}
   179  	return pool.Get().(*fixedBuffer)
   180  }