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

     1  package siafile
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"fmt"
     7  	"io"
     8  
     9  	"gitlab.com/NebulousLabs/errors"
    10  
    11  	"gitlab.com/SiaPrime/SiaPrime/modules"
    12  )
    13  
    14  // RSSubCode is a Reed-Solomon encoder/decoder. It implements the
    15  // modules.ErasureCoder interface in a way that every crypto.SegmentSize bytes
    16  // of encoded data can be recovered separately.
    17  type RSSubCode struct {
    18  	RSCode
    19  	staticSegmentSize uint64
    20  	staticType        modules.ErasureCoderType
    21  }
    22  
    23  // Encode splits data into equal-length pieces, some containing the original
    24  // data and some containing parity data.
    25  func (rs *RSSubCode) Encode(data []byte) ([][]byte, error) {
    26  	pieces, err := rs.enc.Split(data)
    27  	if err != nil {
    28  		return nil, err
    29  	}
    30  	return rs.EncodeShards(pieces[:rs.MinPieces()])
    31  }
    32  
    33  // EncodeShards encodes data in a way that every segmentSize bytes of the
    34  // encoded data can be decoded independently.
    35  func (rs *RSSubCode) EncodeShards(pieces [][]byte) ([][]byte, error) {
    36  	// Check that there are enough pieces.
    37  	if len(pieces) != rs.MinPieces() {
    38  		return nil, fmt.Errorf("not enough segments expected %v but was %v",
    39  			rs.MinPieces(), len(pieces))
    40  	}
    41  	// Since all the pieces should have the same length, get the pieceSize from
    42  	// the first one.
    43  	pieceSize := uint64(len(pieces[0]))
    44  	// pieceSize must be divisible by segmentSize
    45  	if pieceSize%rs.staticSegmentSize != 0 {
    46  		return nil, errors.New("pieceSize not divisible by segmentSize")
    47  	}
    48  	// Each piece should have pieceSize bytes.
    49  	for _, piece := range pieces {
    50  		if uint64(len(piece)) != pieceSize {
    51  			return nil, fmt.Errorf("pieces don't have right size expected %v but was %v",
    52  				pieceSize, len(piece))
    53  		}
    54  	}
    55  	// Flatten the pieces into a byte slice.
    56  	data := make([]byte, uint64(len(pieces))*pieceSize)
    57  	for i, piece := range pieces {
    58  		copy(data[uint64(i)*pieceSize:], piece)
    59  		pieces[i] = pieces[i][:0]
    60  	}
    61  	// Add parity shards to pieces.
    62  	parityShards := make([][]byte, rs.NumPieces()-len(pieces))
    63  	pieces = append(pieces, parityShards...)
    64  	// Encode the pieces.
    65  	segmentOffset := uint64(0)
    66  	for buf := bytes.NewBuffer(data); buf.Len() > 0; {
    67  		// Get the next segments to encode.
    68  		s := buf.Next(int(rs.staticSegmentSize) * rs.MinPieces())
    69  
    70  		// Create a copy of it.
    71  		segments := make([]byte, len(s))
    72  		copy(segments, s)
    73  
    74  		// Encode the segment
    75  		encodedSegments, err := rs.RSCode.Encode(segments)
    76  		if err != nil {
    77  			return nil, err
    78  		}
    79  
    80  		// Write the encoded segments back to pieces.
    81  		for i, segment := range encodedSegments {
    82  			pieces[i] = append(pieces[i], segment...)
    83  		}
    84  		segmentOffset += rs.staticSegmentSize
    85  	}
    86  	return pieces, nil
    87  }
    88  
    89  // Identifier returns an identifier for an erasure coder which can be used to
    90  // identify erasure coders of the same type, dataPieces and parityPieces.
    91  func (rs *RSSubCode) Identifier() modules.ErasureCoderIdentifier {
    92  	t := rs.Type()
    93  	dataPieces := rs.MinPieces()
    94  	parityPieces := rs.NumPieces() - dataPieces
    95  	id := fmt.Sprintf("%v+%v+%v", binary.BigEndian.Uint32(t[:]), dataPieces, parityPieces)
    96  	return modules.ErasureCoderIdentifier(id)
    97  }
    98  
    99  // Reconstruct recovers the full set of encoded shards from the provided
   100  // pieces, of which at least MinPieces must be non-nil.
   101  func (rs *RSSubCode) Reconstruct(pieces [][]byte) error {
   102  	// Check the length of pieces.
   103  	if len(pieces) != rs.NumPieces() {
   104  		return fmt.Errorf("expected pieces to have len %v but was %v",
   105  			rs.NumPieces(), len(pieces))
   106  	}
   107  
   108  	// Since all the pieces should have the same length, get the pieceSize from
   109  	// the first piece that was set.
   110  	var pieceSize uint64
   111  	for _, piece := range pieces {
   112  		if uint64(len(piece)) > pieceSize {
   113  			pieceSize = uint64(len(piece))
   114  			break
   115  		}
   116  	}
   117  
   118  	// pieceSize must be divisible by segmentSize
   119  	if pieceSize%rs.staticSegmentSize != 0 {
   120  		return errors.New("pieceSize not divisible by segmentSize")
   121  	}
   122  
   123  	isNil := make([]bool, len(pieces))
   124  	for i := range pieces {
   125  		isNil[i] = len(pieces[i]) == 0
   126  		pieces[i] = pieces[i][:0]
   127  	}
   128  
   129  	// Extract the segment from the pieces.
   130  	segment := make([][]byte, len(pieces))
   131  	for segmentIndex := 0; uint64(segmentIndex) < pieceSize/rs.staticSegmentSize; segmentIndex++ {
   132  		off := uint64(segmentIndex) * rs.staticSegmentSize
   133  		for i, piece := range pieces {
   134  			if isNil[i] {
   135  				segment[i] = piece[off:off]
   136  			} else {
   137  				segment[i] = piece[off:][:rs.staticSegmentSize]
   138  			}
   139  		}
   140  		// Reconstruct the segment.
   141  		if err := rs.RSCode.Reconstruct(segment); err != nil {
   142  			return err
   143  		}
   144  		for i := range pieces {
   145  			pieces[i] = append(pieces[i], segment[i]...)
   146  		}
   147  	}
   148  	return nil
   149  }
   150  
   151  // Recover accepts encoded pieces and decodes the segment at
   152  // segmentIndex. The size of the decoded data is segmentSize * dataPieces.
   153  func (rs *RSSubCode) Recover(pieces [][]byte, n uint64, w io.Writer) error {
   154  	// Check the length of pieces.
   155  	if len(pieces) != rs.NumPieces() {
   156  		return fmt.Errorf("expected pieces to have len %v but was %v",
   157  			rs.NumPieces(), len(pieces))
   158  	}
   159  	// Since all the pieces should have the same length, get the pieceSize from
   160  	// the first piece that was set.
   161  	var pieceSize uint64
   162  	for _, piece := range pieces {
   163  		if uint64(len(piece)) > pieceSize {
   164  			pieceSize = uint64(len(piece))
   165  			break
   166  		}
   167  	}
   168  
   169  	// pieceSize must be divisible by segmentSize
   170  	if pieceSize%rs.staticSegmentSize != 0 {
   171  		return errors.New("pieceSize not divisible by segmentSize")
   172  	}
   173  
   174  	// Extract the segment from the pieces.
   175  	decodedSegmentSize := rs.staticSegmentSize * uint64(rs.MinPieces())
   176  	segment := make([][]byte, len(pieces))
   177  	for i := range segment {
   178  		segment[i] = make([]byte, 0, rs.staticSegmentSize)
   179  	}
   180  	for segmentIndex := 0; uint64(segmentIndex) < pieceSize/rs.staticSegmentSize && n > 0; segmentIndex++ {
   181  		off := uint64(segmentIndex) * rs.staticSegmentSize
   182  		for i, piece := range pieces {
   183  			if uint64(len(piece)) >= off+rs.staticSegmentSize {
   184  				segment[i] = append(segment[i][:0], piece[off:off+rs.staticSegmentSize]...)
   185  			} else {
   186  				segment[i] = segment[i][:0]
   187  			}
   188  		}
   189  		// Reconstruct the segment.
   190  		if n < decodedSegmentSize {
   191  			decodedSegmentSize = n
   192  		}
   193  		if err := rs.RSCode.Recover(segment, decodedSegmentSize, w); err != nil {
   194  			return err
   195  		}
   196  		n -= decodedSegmentSize
   197  	}
   198  	return nil
   199  }
   200  
   201  // SupportsPartialEncoding returns true for the custom reed-solomon encoder.
   202  func (rs *RSSubCode) SupportsPartialEncoding() bool {
   203  	return true
   204  }
   205  
   206  // Type returns the erasure coders type identifier.
   207  func (rs *RSSubCode) Type() modules.ErasureCoderType {
   208  	return rs.staticType
   209  }
   210  
   211  // ExtractSegment is a convenience method that extracts the data of the segment
   212  // at segmentIndex from pieces.
   213  func ExtractSegment(pieces [][]byte, segmentIndex int, segmentSize uint64) [][]byte {
   214  	segment := make([][]byte, len(pieces))
   215  	off := uint64(segmentIndex) * segmentSize
   216  	for i, piece := range pieces {
   217  		if uint64(len(piece)) >= off+segmentSize {
   218  			segment[i] = piece[off : off+segmentSize]
   219  		} else {
   220  			segment[i] = nil
   221  		}
   222  	}
   223  	return segment
   224  }
   225  
   226  // NewRSSubCode creates a new Reed-Solomon encoder/decoder using the supplied
   227  // parameters.
   228  func NewRSSubCode(nData, nParity int, segmentSize uint64) (modules.ErasureCoder, error) {
   229  	rs, err := newRSCode(nData, nParity)
   230  	if err != nil {
   231  		return nil, err
   232  	}
   233  	// Get the correct type from the segmentSize.
   234  	var t modules.ErasureCoderType
   235  	switch segmentSize {
   236  	case 64:
   237  		t = ecReedSolomonSubShards64
   238  	default:
   239  		return nil, errors.New("unsupported segmentSize")
   240  	}
   241  	// Create the encoder.
   242  	return &RSSubCode{
   243  		*rs,
   244  		segmentSize,
   245  		t,
   246  	}, nil
   247  }