gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/renter/siafile/rscode.go (about)

     1  package siafile
     2  
     3  import (
     4  	"encoding/binary"
     5  	"fmt"
     6  	"io"
     7  
     8  	"github.com/klauspost/reedsolomon"
     9  
    10  	"gitlab.com/SiaPrime/SiaPrime/modules"
    11  )
    12  
    13  // RSCode is a Reed-Solomon encoder/decoder. It implements the
    14  // modules.ErasureCoder interface.
    15  type RSCode struct {
    16  	enc reedsolomon.Encoder
    17  
    18  	numPieces  int
    19  	dataPieces int
    20  }
    21  
    22  // NumPieces returns the number of pieces returned by Encode.
    23  func (rs *RSCode) NumPieces() int { return rs.numPieces }
    24  
    25  // MinPieces return the minimum number of pieces that must be present to
    26  // recover the original data.
    27  func (rs *RSCode) MinPieces() int { return rs.dataPieces }
    28  
    29  // Encode splits data into equal-length pieces, some containing the original
    30  // data and some containing parity data.
    31  func (rs *RSCode) Encode(data []byte) ([][]byte, error) {
    32  	pieces, err := rs.enc.Split(data)
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  	return rs.EncodeShards(pieces)
    37  }
    38  
    39  // EncodeShards creates the parity shards for an already sharded input.
    40  func (rs *RSCode) EncodeShards(pieces [][]byte) ([][]byte, error) {
    41  	// Check that the caller provided the minimum amount of pieces.
    42  	if len(pieces) < rs.MinPieces() {
    43  		return nil, fmt.Errorf("invalid number of pieces given %v < %v", len(pieces), rs.MinPieces())
    44  	}
    45  	// Since all the pieces should have the same length, get the pieceSize from
    46  	// the first one.
    47  	pieceSize := len(pieces[0])
    48  	// Add the parity shards to pieces.
    49  	for len(pieces) < rs.NumPieces() {
    50  		pieces = append(pieces, make([]byte, pieceSize))
    51  	}
    52  	err := rs.enc.Encode(pieces)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  	return pieces, nil
    57  }
    58  
    59  // Identifier returns an identifier for an erasure coder which can be used to
    60  // identify erasure coders of the same type, dataPieces and parityPieces.
    61  func (rs *RSCode) Identifier() modules.ErasureCoderIdentifier {
    62  	t := rs.Type()
    63  	dataPieces := rs.MinPieces()
    64  	parityPieces := rs.NumPieces() - dataPieces
    65  	id := fmt.Sprintf("%v+%v+%v", binary.BigEndian.Uint32(t[:]), dataPieces, parityPieces)
    66  	return modules.ErasureCoderIdentifier(id)
    67  }
    68  
    69  // Reconstruct recovers the full set of encoded shards from the provided pieces,
    70  // of which at least MinPieces must be non-nil.
    71  func (rs *RSCode) Reconstruct(pieces [][]byte) error {
    72  	return rs.enc.Reconstruct(pieces)
    73  }
    74  
    75  // Recover recovers the original data from pieces and writes it to w.
    76  // pieces should be identical to the slice returned by Encode (length and
    77  // order must be preserved), but with missing elements set to nil.
    78  func (rs *RSCode) Recover(pieces [][]byte, n uint64, w io.Writer) error {
    79  	err := rs.enc.ReconstructData(pieces)
    80  	if err != nil {
    81  		return err
    82  	}
    83  	return rs.enc.Join(w, pieces, int(n))
    84  }
    85  
    86  // SupportsPartialEncoding returns false for the basic reed-solomon encoder.
    87  func (rs *RSCode) SupportsPartialEncoding() bool {
    88  	return false
    89  }
    90  
    91  // Type returns the erasure coders type identifier.
    92  func (rs *RSCode) Type() modules.ErasureCoderType {
    93  	return ecReedSolomon
    94  }
    95  
    96  // NewRSCode creates a new Reed-Solomon encoder/decoder using the supplied
    97  // parameters.
    98  func NewRSCode(nData, nParity int) (modules.ErasureCoder, error) {
    99  	return newRSCode(nData, nParity)
   100  }
   101  
   102  // newRSCode creates a new Reed-Solomon encoder/decoder using the supplied
   103  // parameters.
   104  func newRSCode(nData, nParity int) (*RSCode, error) {
   105  	enc, err := reedsolomon.New(nData, nParity)
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  	return &RSCode{
   110  		enc:        enc,
   111  		numPieces:  nData + nParity,
   112  		dataPieces: nData,
   113  	}, nil
   114  }