github.com/ethersphere/bee/v2@v2.2.0/pkg/file/io.go (about) 1 // Copyright 2020 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package file 6 7 import ( 8 "bytes" 9 "context" 10 "errors" 11 "fmt" 12 13 "io" 14 15 "github.com/ethersphere/bee/v2/pkg/swarm" 16 ) 17 18 // simpleReadCloser wraps a byte slice in a io.ReadCloser implementation. 19 type simpleReadCloser struct { 20 buffer io.Reader 21 closed bool 22 } 23 24 // NewSimpleReadCloser creates a new simpleReadCloser. 25 func NewSimpleReadCloser(buffer []byte) io.ReadCloser { 26 return &simpleReadCloser{ 27 buffer: bytes.NewBuffer(buffer), 28 } 29 } 30 31 // Read implements io.Reader. 32 func (s *simpleReadCloser) Read(b []byte) (int, error) { 33 if s.closed { 34 return 0, errors.New("read on closed reader") 35 } 36 return s.buffer.Read(b) 37 } 38 39 // Close implements io.Closer. 40 func (s *simpleReadCloser) Close() error { 41 if s.closed { 42 return errors.New("close on already closed reader") 43 } 44 s.closed = true 45 return nil 46 } 47 48 // JoinReadAll reads all output from the provided Joiner. 49 func JoinReadAll(ctx context.Context, j Joiner, outFile io.Writer) (int64, error) { 50 l := j.Size() 51 52 // join, rinse, repeat until done 53 data := make([]byte, swarm.ChunkSize) 54 var total int64 55 for i := int64(0); i < l; i += swarm.ChunkSize { 56 cr, err := j.Read(data) 57 if err != nil { 58 return total, err 59 } 60 total += int64(cr) 61 cw, err := outFile.Write(data[:cr]) 62 if err != nil { 63 return total, err 64 } 65 if cw != cr { 66 return total, fmt.Errorf("short wrote %d of %d for chunk %d", cw, cr, i) 67 } 68 } 69 if total != l { 70 return total, fmt.Errorf("received only %d of %d total bytes", total, l) 71 } 72 return total, nil 73 } 74 75 // SplitWriteAll writes all input from provided reader to the provided splitter 76 func SplitWriteAll(ctx context.Context, s Splitter, r io.Reader, l int64, toEncrypt bool) (swarm.Address, error) { 77 chunkPipe := NewChunkPipe() 78 errC := make(chan error) 79 go func() { 80 buf := make([]byte, swarm.ChunkSize) 81 c, err := io.CopyBuffer(chunkPipe, r, buf) 82 if err != nil { 83 errC <- err 84 } 85 if c != l { 86 errC <- errors.New("read count mismatch") 87 } 88 err = chunkPipe.Close() 89 if err != nil { 90 errC <- err 91 } 92 close(errC) 93 }() 94 95 addr, err := s.Split(ctx, chunkPipe, l, toEncrypt) 96 if err != nil { 97 return swarm.ZeroAddress, err 98 } 99 100 select { 101 case err := <-errC: 102 if err != nil { 103 return swarm.ZeroAddress, err 104 } 105 case <-ctx.Done(): 106 return swarm.ZeroAddress, ctx.Err() 107 } 108 return addr, nil 109 } 110 111 type Loader interface { 112 // Load a reference in byte slice representation and return all content associated with the reference. 113 Load(context.Context, []byte) ([]byte, error) 114 } 115 116 type Saver interface { 117 // Save an arbitrary byte slice and return the reference byte slice representation. 118 Save(context.Context, []byte) ([]byte, error) 119 } 120 121 type LoadSaver interface { 122 Loader 123 Saver 124 }