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 }