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