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  }