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 }