github.com/Finschia/finschia-sdk@v0.48.1/snapshots/chunk.go (about)

     1  package snapshots
     2  
     3  import (
     4  	"io"
     5  
     6  	sdkerrors "github.com/Finschia/finschia-sdk/types/errors"
     7  )
     8  
     9  // ChunkWriter reads an input stream, splits it into fixed-size chunks, and writes them to a
    10  // sequence of io.ReadClosers via a channel.
    11  type ChunkWriter struct {
    12  	ch        chan<- io.ReadCloser
    13  	pipe      *io.PipeWriter
    14  	chunkSize uint64
    15  	written   uint64
    16  	closed    bool
    17  }
    18  
    19  // NewChunkWriter creates a new ChunkWriter. If chunkSize is 0, no chunking will be done.
    20  func NewChunkWriter(ch chan<- io.ReadCloser, chunkSize uint64) *ChunkWriter {
    21  	return &ChunkWriter{
    22  		ch:        ch,
    23  		chunkSize: chunkSize,
    24  	}
    25  }
    26  
    27  // chunk creates a new chunk.
    28  func (w *ChunkWriter) chunk() error {
    29  	if w.pipe != nil {
    30  		err := w.pipe.Close()
    31  		if err != nil {
    32  			return err
    33  		}
    34  	}
    35  	pr, pw := io.Pipe()
    36  	w.ch <- pr
    37  	w.pipe = pw
    38  	w.written = 0
    39  	return nil
    40  }
    41  
    42  // Close implements io.Closer.
    43  func (w *ChunkWriter) Close() error {
    44  	if !w.closed {
    45  		w.closed = true
    46  		close(w.ch)
    47  		var err error
    48  		if w.pipe != nil {
    49  			err = w.pipe.Close()
    50  		}
    51  		return err
    52  	}
    53  	return nil
    54  }
    55  
    56  // CloseWithError closes the writer and sends an error to the reader.
    57  func (w *ChunkWriter) CloseWithError(err error) {
    58  	if !w.closed {
    59  		w.closed = true
    60  		close(w.ch)
    61  		if w.pipe != nil {
    62  			w.pipe.CloseWithError(err)
    63  		}
    64  	}
    65  }
    66  
    67  // Write implements io.Writer.
    68  func (w *ChunkWriter) Write(data []byte) (int, error) {
    69  	if w.closed {
    70  		return 0, sdkerrors.Wrap(sdkerrors.ErrLogic, "cannot write to closed ChunkWriter")
    71  	}
    72  	nTotal := 0
    73  	for len(data) > 0 {
    74  		if w.pipe == nil || (w.written >= w.chunkSize && w.chunkSize > 0) {
    75  			err := w.chunk()
    76  			if err != nil {
    77  				return nTotal, err
    78  			}
    79  		}
    80  
    81  		var writeSize uint64
    82  		if w.chunkSize == 0 {
    83  			writeSize = uint64(len(data))
    84  		} else {
    85  			writeSize = w.chunkSize - w.written
    86  		}
    87  		if writeSize > uint64(len(data)) {
    88  			writeSize = uint64(len(data))
    89  		}
    90  
    91  		n, err := w.pipe.Write(data[:writeSize])
    92  		w.written += uint64(n)
    93  		nTotal += n
    94  		if err != nil {
    95  			return nTotal, err
    96  		}
    97  		data = data[writeSize:]
    98  	}
    99  	return nTotal, nil
   100  }
   101  
   102  // ChunkReader reads chunks from a channel of io.ReadClosers and outputs them as an io.Reader
   103  type ChunkReader struct {
   104  	ch     <-chan io.ReadCloser
   105  	reader io.ReadCloser
   106  }
   107  
   108  // NewChunkReader creates a new ChunkReader.
   109  func NewChunkReader(ch <-chan io.ReadCloser) *ChunkReader {
   110  	return &ChunkReader{ch: ch}
   111  }
   112  
   113  // next fetches the next chunk from the channel, or returns io.EOF if there are no more chunks.
   114  func (r *ChunkReader) next() error {
   115  	reader, ok := <-r.ch
   116  	if !ok {
   117  		return io.EOF
   118  	}
   119  	r.reader = reader
   120  	return nil
   121  }
   122  
   123  // Close implements io.ReadCloser.
   124  func (r *ChunkReader) Close() error {
   125  	var err error
   126  	if r.reader != nil {
   127  		err = r.reader.Close()
   128  		r.reader = nil
   129  	}
   130  	for reader := range r.ch {
   131  		if e := reader.Close(); e != nil && err == nil {
   132  			err = e
   133  		}
   134  	}
   135  	return err
   136  }
   137  
   138  // Read implements io.Reader.
   139  func (r *ChunkReader) Read(p []byte) (int, error) {
   140  	if r.reader == nil {
   141  		err := r.next()
   142  		if err != nil {
   143  			return 0, err
   144  		}
   145  	}
   146  	n, err := r.reader.Read(p)
   147  	if err == io.EOF {
   148  		err = r.reader.Close()
   149  		r.reader = nil
   150  		if err != nil {
   151  			return 0, err
   152  		}
   153  		return r.Read(p)
   154  	}
   155  	return n, err
   156  }
   157  
   158  // DrainChunks drains and closes all remaining chunks from a chunk channel.
   159  func DrainChunks(chunks <-chan io.ReadCloser) {
   160  	for chunk := range chunks {
   161  		_ = chunk.Close()
   162  	}
   163  }