github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/module/blobs/blob_io.go (about) 1 package blobs 2 3 import ( 4 "bytes" 5 "errors" 6 "io" 7 8 "github.com/ipfs/go-cid" 9 ) 10 11 var ErrBlobChannelWriterClosed = errors.New("blob channel writer is already closed") 12 13 // BlobChannelWriter is a writer which splits the data written to it into blobs and sends them to a blob channel. 14 type BlobChannelWriter struct { 15 maxBlobSize int 16 blobs chan<- Blob 17 buf *bytes.Buffer 18 cids []cid.Cid 19 closed bool 20 bytesSent uint64 21 } 22 23 var _ io.WriteCloser = (*BlobChannelWriter)(nil) 24 var _ io.ByteWriter = (*BlobChannelWriter)(nil) 25 26 func (bw *BlobChannelWriter) BytesSent() uint64 { 27 return bw.bytesSent 28 } 29 30 func (bw *BlobChannelWriter) CidsSent() []cid.Cid { 31 return bw.cids 32 } 33 34 // Write writes len(data) bytes from data to the underlying blob channel. It returns the number of bytes written 35 // from data (0 <= n <= len(data)) or ErrBlobChannelWriterClosed if the Blob Channel Writer was already previously 36 // closed via a call to Close. It will always return a non-nil error if it returns n < len(data). 37 func (bw *BlobChannelWriter) Write(data []byte) (int, error) { 38 if bw.closed { 39 return 0, ErrBlobChannelWriterClosed 40 } 41 42 var n int 43 for n < len(data) { 44 size := bw.maxBlobSize - bw.buf.Len() 45 if n+size > len(data) { 46 size = len(data) - n 47 } 48 49 m, err := bw.buf.Write(data[n : n+size]) 50 n += m 51 if err != nil { 52 return n, err 53 } 54 55 // if we have a full blob, send it to the blob channel 56 if bw.buf.Len() >= bw.maxBlobSize { 57 bw.sendNewBlob() 58 } 59 } 60 61 return n, nil 62 } 63 64 // sendNewBlob sends the currently buffered data to the blob channel and resets the buffer. 65 func (bw *BlobChannelWriter) sendNewBlob() { 66 blob := NewBlob(bw.buf.Bytes()) 67 if bw.blobs != nil { 68 bw.blobs <- blob 69 } 70 bw.cids = append(bw.cids, blob.Cid()) 71 bw.bytesSent += uint64(bw.buf.Len()) 72 73 // reset the buffer 74 bw.buf = &bytes.Buffer{} 75 } 76 77 // WriteByte writes a single byte to the underlying blob channel. It returns an error if the byte could not be written. 78 // Returns ErrBlobChannelWriterClosed if the Blob Channel Writer was already previously closed via a call to Close 79 func (bw *BlobChannelWriter) WriteByte(c byte) error { 80 if bw.closed { 81 return ErrBlobChannelWriterClosed 82 } 83 84 if err := bw.buf.WriteByte(c); err != nil { 85 return err 86 } 87 88 if bw.buf.Len() >= bw.maxBlobSize { 89 bw.sendNewBlob() 90 } 91 92 return nil 93 } 94 95 // Flush flushes any buffered data to the underlying blob channel as a new blob. It returns an error if the flush failed. 96 // Returns ErrBlobChannelWriterClosed if the Blob Channel Writer was already previously closed via a call to Close 97 func (bw *BlobChannelWriter) Flush() error { 98 if bw.closed { 99 return ErrBlobChannelWriterClosed 100 } 101 102 if bw.buf.Len() > 0 { 103 bw.sendNewBlob() 104 } 105 106 return nil 107 } 108 109 // Close flushes any buffered data to the underlying blob channel and closes the blob channel. 110 func (bw *BlobChannelWriter) Close() error { 111 if err := bw.Flush(); err != nil { 112 return err 113 } 114 115 bw.closed = true 116 return nil 117 } 118 119 func NewBlobChannelWriter(blobChan chan<- Blob, maxBlobSize int) *BlobChannelWriter { 120 return &BlobChannelWriter{ 121 maxBlobSize: maxBlobSize, 122 blobs: blobChan, 123 buf: &bytes.Buffer{}, 124 } 125 } 126 127 // BlobChannelReader is a reader which reads data from a blob channel. 128 type BlobChannelReader struct { 129 blobs <-chan Blob 130 buf *bytes.Buffer 131 cids []cid.Cid 132 finished bool 133 bytesReceived uint64 134 } 135 136 var _ io.Reader = (*BlobChannelReader)(nil) 137 var _ io.ByteReader = (*BlobChannelReader)(nil) 138 139 func (br *BlobChannelReader) BytesReceived() uint64 { 140 return br.bytesReceived 141 } 142 143 func (br *BlobChannelReader) CidsReceived() []cid.Cid { 144 return br.cids 145 } 146 147 // Read reads up to len(data) bytes from the underlying blob channel into data. It returns the number of bytes read 148 // (0 <= n <= len(data)) and any error encountered. 149 // Returns io.EOF if the incoming blob channel was closed and all available data has been read. 150 func (br *BlobChannelReader) Read(data []byte) (int, error) { 151 if br.finished { 152 return 0, io.EOF 153 } 154 155 if len(data) == 0 { 156 return 0, nil 157 } 158 159 // if all the data from the current buffer has been read, receive the next blob from the channel 160 for br.buf.Len() == 0 { 161 if !br.receiveNewBlob() { 162 br.finished = true 163 return 0, io.EOF 164 } 165 } 166 167 return br.buf.Read(data) 168 } 169 170 // retrieveNewBlob retrieves a new blob from the blob channel and sets the buffer to the blob's data. 171 func (br *BlobChannelReader) receiveNewBlob() bool { 172 blob, ok := <-br.blobs 173 if !ok { 174 return false 175 } 176 177 br.buf = bytes.NewBuffer(blob.RawData()) 178 br.cids = append(br.cids, blob.Cid()) 179 br.bytesReceived += uint64(len(blob.RawData())) 180 181 return true 182 } 183 184 // ReadByte reads a single byte from the underlying blob channel. It returns an error if the byte could not be read. 185 // Returns io.EOF if the incoming blob channel was closed and all available data has been read. 186 func (br *BlobChannelReader) ReadByte() (byte, error) { 187 if br.finished { 188 return 0, io.EOF 189 } 190 191 // use a for loop here to guard against empty blobs 192 for br.buf.Len() == 0 { 193 if !br.receiveNewBlob() { 194 br.finished = true 195 return 0, io.EOF 196 } 197 } 198 199 return br.buf.ReadByte() 200 } 201 202 func NewBlobChannelReader(blobChan <-chan Blob) *BlobChannelReader { 203 return &BlobChannelReader{blobs: blobChan, buf: new(bytes.Buffer)} 204 }