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 }