github.com/damirazo/docker@v1.9.0/pkg/ioutils/bytespipe.go (about)

     1  package ioutils
     2  
     3  const maxCap = 1e6
     4  
     5  // BytesPipe is io.ReadWriter which works similarly to pipe(queue).
     6  // All written data could be read only once. Also BytesPipe is allocating
     7  // and releasing new byte slices to adjust to current needs, so there won't be
     8  // overgrown buffer after high load peak.
     9  // BytesPipe isn't goroutine-safe, caller must synchronize it if needed.
    10  type BytesPipe struct {
    11  	buf      [][]byte // slice of byte-slices of buffered data
    12  	lastRead int      // index in the first slice to a read point
    13  	bufLen   int      // length of data buffered over the slices
    14  }
    15  
    16  // NewBytesPipe creates new BytesPipe, initialized by specified slice.
    17  // If buf is nil, then it will be initialized with slice which cap is 64.
    18  // buf will be adjusted in a way that len(buf) == 0, cap(buf) == cap(buf).
    19  func NewBytesPipe(buf []byte) *BytesPipe {
    20  	if cap(buf) == 0 {
    21  		buf = make([]byte, 0, 64)
    22  	}
    23  	return &BytesPipe{
    24  		buf: [][]byte{buf[:0]},
    25  	}
    26  }
    27  
    28  // Write writes p to BytesPipe.
    29  // It can allocate new []byte slices in a process of writing.
    30  func (bp *BytesPipe) Write(p []byte) (n int, err error) {
    31  	for {
    32  		// write data to the last buffer
    33  		b := bp.buf[len(bp.buf)-1]
    34  		// copy data to the current empty allocated area
    35  		n := copy(b[len(b):cap(b)], p)
    36  		// increment buffered data length
    37  		bp.bufLen += n
    38  		// include written data in last buffer
    39  		bp.buf[len(bp.buf)-1] = b[:len(b)+n]
    40  
    41  		// if there was enough room to write all then break
    42  		if len(p) == n {
    43  			break
    44  		}
    45  
    46  		// more data: write to the next slice
    47  		p = p[n:]
    48  		// allocate slice that has twice the size of the last unless maximum reached
    49  		nextCap := 2 * cap(bp.buf[len(bp.buf)-1])
    50  		if maxCap < nextCap {
    51  			nextCap = maxCap
    52  		}
    53  		// add new byte slice to the buffers slice and continue writing
    54  		bp.buf = append(bp.buf, make([]byte, 0, nextCap))
    55  	}
    56  	return
    57  }
    58  
    59  func (bp *BytesPipe) len() int {
    60  	return bp.bufLen - bp.lastRead
    61  }
    62  
    63  // Read reads bytes from BytesPipe.
    64  // Data could be read only once.
    65  func (bp *BytesPipe) Read(p []byte) (n int, err error) {
    66  	for {
    67  		read := copy(p, bp.buf[0][bp.lastRead:])
    68  		n += read
    69  		bp.lastRead += read
    70  		if bp.len() == 0 {
    71  			// we have read everything. reset to the beginning.
    72  			bp.lastRead = 0
    73  			bp.bufLen -= len(bp.buf[0])
    74  			bp.buf[0] = bp.buf[0][:0]
    75  			break
    76  		}
    77  		// break if everything was read
    78  		if len(p) == read {
    79  			break
    80  		}
    81  		// more buffered data and more asked. read from next slice.
    82  		p = p[read:]
    83  		bp.lastRead = 0
    84  		bp.bufLen -= len(bp.buf[0])
    85  		bp.buf[0] = nil     // throw away old slice
    86  		bp.buf = bp.buf[1:] // switch to next
    87  	}
    88  	return
    89  }