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  }