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 }