cubefs
1684 строки · 47.9 Кб
1/**
2* Reed-Solomon Coding over 8-bit values.
3*
4* Copyright 2015, Klaus Post
5* Copyright 2015, Backblaze, Inc.
6*/
7
8// Package reedsolomon enables Erasure Coding in Go
9//
10// For usage and examples, see https://github.com/klauspost/reedsolomon
11package reedsolomon
12
13import (
14"bytes"
15"errors"
16"fmt"
17"io"
18"runtime"
19"sync"
20
21"github.com/klauspost/cpuid/v2"
22)
23
24// Encoder is an interface to encode Reed-Salomon parity sets for your data.
25type Encoder interface {
26// Encode parity for a set of data shards.
27// Input is 'shards' containing data shards followed by parity shards.
28// The number of shards must match the number given to New().
29// Each shard is a byte array, and they must all be the same size.
30// The parity shards will always be overwritten and the data shards
31// will remain the same, so it is safe for you to read from the
32// data shards while this is running.
33Encode(shards [][]byte) error
34
35// EncodeIdx will add parity for a single data shard.
36// Parity shards should start out as 0. The caller must zero them.
37// Data shards must be delivered exactly once. There is no check for this.
38// The parity shards will always be updated and the data shards will remain the same.
39EncodeIdx(dataShard []byte, idx int, parity [][]byte) error
40
41// Verify returns true if the parity shards contain correct data.
42// The data is the same format as Encode. No data is modified, so
43// you are allowed to read from data while this is running.
44Verify(shards [][]byte) (bool, error)
45
46// Reconstruct will recreate the missing shards if possible.
47//
48// Given a list of shards, some of which contain data, fills in the
49// ones that don't have data.
50//
51// The length of the array must be equal to the total number of shards.
52// You indicate that a shard is missing by setting it to nil or zero-length.
53// If a shard is zero-length but has sufficient capacity, that memory will
54// be used, otherwise a new []byte will be allocated.
55//
56// If there are too few shards to reconstruct the missing
57// ones, ErrTooFewShards will be returned.
58//
59// The reconstructed shard set is complete, but integrity is not verified.
60// Use the Verify function to check if data set is ok.
61Reconstruct(shards [][]byte) error
62
63// ReconstructData will recreate any missing data shards, if possible.
64//
65// Given a list of shards, some of which contain data, fills in the
66// data shards that don't have data.
67//
68// The length of the array must be equal to Shards.
69// You indicate that a shard is missing by setting it to nil or zero-length.
70// If a shard is zero-length but has sufficient capacity, that memory will
71// be used, otherwise a new []byte will be allocated.
72//
73// If there are too few shards to reconstruct the missing
74// ones, ErrTooFewShards will be returned.
75//
76// As the reconstructed shard set may contain missing parity shards,
77// calling the Verify function is likely to fail.
78ReconstructData(shards [][]byte) error
79
80// ReconstructSome will recreate only requested data shards, if possible.
81//
82// Given a list of shards, some of which contain data, fills in the
83// data shards indicated by true values in the "required" parameter.
84// The length of "required" array must be equal to DataShards.
85//
86// The length of "shards" array must be equal to Shards.
87// You indicate that a shard is missing by setting it to nil or zero-length.
88// If a shard is zero-length but has sufficient capacity, that memory will
89// be used, otherwise a new []byte will be allocated.
90//
91// If there are too few shards to reconstruct the missing
92// ones, ErrTooFewShards will be returned.
93//
94// As the reconstructed shard set may contain missing parity shards,
95// calling the Verify function is likely to fail.
96ReconstructSome(shards [][]byte, required []bool) error
97
98// Update parity is use for change a few data shards and update it's parity.
99// Input 'newDatashards' containing data shards changed.
100// Input 'shards' containing old data shards (if data shard not changed, it can be nil) and old parity shards.
101// new parity shards will in shards[DataShards:]
102// Update is very useful if DataShards much larger than ParityShards and changed data shards is few. It will
103// faster than Encode and not need read all data shards to encode.
104Update(shards [][]byte, newDatashards [][]byte) error
105
106// Split a data slice into the number of shards given to the encoder,
107// and create empty parity shards if necessary.
108//
109// The data will be split into equally sized shards.
110// If the data size isn't divisible by the number of shards,
111// the last shard will contain extra zeros.
112//
113// If there is extra capacity on the provided data slice
114// it will be used instead of allocating parity shards.
115// It will be zeroed out.
116//
117// There must be at least 1 byte otherwise ErrShortData will be
118// returned.
119//
120// The data will not be copied, except for the last shard, so you
121// should not modify the data of the input slice afterwards.
122Split(data []byte) ([][]byte, error)
123
124// Join the shards and write the data segment to dst.
125//
126// Only the data shards are considered.
127// You must supply the exact output size you want.
128// If there are to few shards given, ErrTooFewShards will be returned.
129// If the total data size is less than outSize, ErrShortData will be returned.
130Join(dst io.Writer, shards [][]byte, outSize int) error
131}
132
133// Extensions is an optional interface.
134// All returned instances will support this interface.
135type Extensions interface {
136// ShardSizeMultiple will return the size the shard sizes must be a multiple of.
137ShardSizeMultiple() int
138
139// DataShards will return the number of data shards.
140DataShards() int
141
142// ParityShards will return the number of parity shards.
143ParityShards() int
144
145// TotalShards will return the total number of shards.
146TotalShards() int
147
148// AllocAligned will allocate TotalShards number of slices,
149// aligned to reasonable memory sizes.
150// Provide the size of each shard.
151AllocAligned(each int) [][]byte
152}
153
154const (
155avx2CodeGenMinSize = 64
156avx2CodeGenMinShards = 3
157avx2CodeGenMaxGoroutines = 8
158gfniCodeGenMaxGoroutines = 4
159
160intSize = 32 << (^uint(0) >> 63) // 32 or 64
161maxInt = 1<<(intSize-1) - 1
162)
163
164// reedSolomon contains a matrix for a specific
165// distribution of datashards and parity shards.
166// Construct if using New()
167type reedSolomon struct {
168dataShards int // Number of data shards, should not be modified.
169parityShards int // Number of parity shards, should not be modified.
170totalShards int // Total number of shards. Calculated, and should not be modified.
171m matrix
172tree *inversionTree
173parity [][]byte
174o options
175mPoolSz int
176mPool sync.Pool // Pool for temp matrices, etc
177}
178
179var _ = Extensions(&reedSolomon{})
180
181func (r *reedSolomon) ShardSizeMultiple() int {
182return 1
183}
184
185func (r *reedSolomon) DataShards() int {
186return r.dataShards
187}
188
189func (r *reedSolomon) ParityShards() int {
190return r.parityShards
191}
192
193func (r *reedSolomon) TotalShards() int {
194return r.totalShards
195}
196
197func (r *reedSolomon) AllocAligned(each int) [][]byte {
198return AllocAligned(r.totalShards, each)
199}
200
201// ErrInvShardNum will be returned by New, if you attempt to create
202// an Encoder with less than one data shard or less than zero parity
203// shards.
204var ErrInvShardNum = errors.New("cannot create Encoder with less than one data shard or less than zero parity shards")
205
206// ErrMaxShardNum will be returned by New, if you attempt to create an
207// Encoder where data and parity shards are bigger than the order of
208// GF(2^8).
209var ErrMaxShardNum = errors.New("cannot create Encoder with more than 256 data+parity shards")
210
211// ErrNotSupported is returned when an operation is not supported.
212var ErrNotSupported = errors.New("operation not supported")
213
214// buildMatrix creates the matrix to use for encoding, given the
215// number of data shards and the number of total shards.
216//
217// The top square of the matrix is guaranteed to be an identity
218// matrix, which means that the data shards are unchanged after
219// encoding.
220func buildMatrix(dataShards, totalShards int) (matrix, error) {
221// Start with a Vandermonde matrix. This matrix would work,
222// in theory, but doesn't have the property that the data
223// shards are unchanged after encoding.
224vm, err := vandermonde(totalShards, dataShards)
225if err != nil {
226return nil, err
227}
228
229// Multiply by the inverse of the top square of the matrix.
230// This will make the top square be the identity matrix, but
231// preserve the property that any square subset of rows is
232// invertible.
233top, err := vm.SubMatrix(0, 0, dataShards, dataShards)
234if err != nil {
235return nil, err
236}
237
238topInv, err := top.Invert()
239if err != nil {
240return nil, err
241}
242
243return vm.Multiply(topInv)
244}
245
246// buildMatrixJerasure creates the same encoding matrix as Jerasure library
247//
248// The top square of the matrix is guaranteed to be an identity
249// matrix, which means that the data shards are unchanged after
250// encoding.
251func buildMatrixJerasure(dataShards, totalShards int) (matrix, error) {
252// Start with a Vandermonde matrix. This matrix would work,
253// in theory, but doesn't have the property that the data
254// shards are unchanged after encoding.
255vm, err := vandermonde(totalShards, dataShards)
256if err != nil {
257return nil, err
258}
259
260// Jerasure does this:
261// first row is always 100..00
262vm[0][0] = 1
263for i := 1; i < dataShards; i++ {
264vm[0][i] = 0
265}
266// last row is always 000..01
267for i := 0; i < dataShards-1; i++ {
268vm[totalShards-1][i] = 0
269}
270vm[totalShards-1][dataShards-1] = 1
271
272for i := 0; i < dataShards; i++ {
273// Find the row where i'th col is not 0
274r := i
275for ; r < totalShards && vm[r][i] == 0; r++ {
276}
277if r != i {
278// Swap it with i'th row if not already
279t := vm[r]
280vm[r] = vm[i]
281vm[i] = t
282}
283// Multiply by the inverted matrix (same as vm.Multiply(vm[0:dataShards].Invert()))
284if vm[i][i] != 1 {
285// Make vm[i][i] = 1 by dividing the column by vm[i][i]
286tmp := galDivide(1, vm[i][i])
287for j := 0; j < totalShards; j++ {
288vm[j][i] = galMultiply(vm[j][i], tmp)
289}
290}
291for j := 0; j < dataShards; j++ {
292// Make vm[i][j] = 0 where j != i by adding vm[i][j]*vm[.][i] to each column
293tmp := vm[i][j]
294if j != i && tmp != 0 {
295for r := 0; r < totalShards; r++ {
296vm[r][j] = galAdd(vm[r][j], galMultiply(tmp, vm[r][i]))
297}
298}
299}
300}
301
302// Make vm[dataShards] row all ones - divide each column j by vm[dataShards][j]
303for j := 0; j < dataShards; j++ {
304tmp := vm[dataShards][j]
305if tmp != 1 {
306tmp = galDivide(1, tmp)
307for i := dataShards; i < totalShards; i++ {
308vm[i][j] = galMultiply(vm[i][j], tmp)
309}
310}
311}
312
313// Make vm[dataShards...totalShards-1][0] column all ones - divide each row
314for i := dataShards + 1; i < totalShards; i++ {
315tmp := vm[i][0]
316if tmp != 1 {
317tmp = galDivide(1, tmp)
318for j := 0; j < dataShards; j++ {
319vm[i][j] = galMultiply(vm[i][j], tmp)
320}
321}
322}
323
324return vm, nil
325}
326
327// buildMatrixPAR1 creates the matrix to use for encoding according to
328// the PARv1 spec, given the number of data shards and the number of
329// total shards. Note that the method they use is buggy, and may lead
330// to cases where recovery is impossible, even if there are enough
331// parity shards.
332//
333// The top square of the matrix is guaranteed to be an identity
334// matrix, which means that the data shards are unchanged after
335// encoding.
336func buildMatrixPAR1(dataShards, totalShards int) (matrix, error) {
337result, err := newMatrix(totalShards, dataShards)
338if err != nil {
339return nil, err
340}
341
342for r, row := range result {
343// The top portion of the matrix is the identity
344// matrix, and the bottom is a transposed Vandermonde
345// matrix starting at 1 instead of 0.
346if r < dataShards {
347result[r][r] = 1
348} else {
349for c := range row {
350result[r][c] = galExp(byte(c+1), r-dataShards)
351}
352}
353}
354return result, nil
355}
356
357func buildMatrixCauchy(dataShards, totalShards int) (matrix, error) {
358result, err := newMatrix(totalShards, dataShards)
359if err != nil {
360return nil, err
361}
362
363for r, row := range result {
364// The top portion of the matrix is the identity
365// matrix, and the bottom is a transposed Cauchy matrix.
366if r < dataShards {
367result[r][r] = 1
368} else {
369for c := range row {
370result[r][c] = invTable[(byte(r ^ c))]
371}
372}
373}
374return result, nil
375}
376
377// buildXorMatrix can be used to build a matrix with pure XOR
378// operations if there is only one parity shard.
379func buildXorMatrix(dataShards, totalShards int) (matrix, error) {
380if dataShards+1 != totalShards {
381return nil, errors.New("internal error")
382}
383result, err := newMatrix(totalShards, dataShards)
384if err != nil {
385return nil, err
386}
387
388for r, row := range result {
389// The top portion of the matrix is the identity
390// matrix.
391if r < dataShards {
392result[r][r] = 1
393} else {
394// Set all values to 1 (XOR)
395for c := range row {
396result[r][c] = 1
397}
398}
399}
400return result, nil
401}
402
403// New creates a new encoder and initializes it to
404// the number of data shards and parity shards that
405// you want to use. You can reuse this encoder.
406// Note that the maximum number of total shards is 65536, with some
407// restrictions for a total larger than 256:
408//
409// - Shard sizes must be multiple of 64
410// - The methods Join/Split/Update/EncodeIdx are not supported
411//
412// If no options are supplied, default options are used.
413func New(dataShards, parityShards int, opts ...Option) (Encoder, error) {
414o := defaultOptions
415for _, opt := range opts {
416opt(&o)
417}
418
419totShards := dataShards + parityShards
420switch {
421case o.withLeopard == leopardGF16 && parityShards > 0 || totShards > 256:
422return newFF16(dataShards, parityShards, o)
423case o.withLeopard == leopardAlways && parityShards > 0:
424return newFF8(dataShards, parityShards, o)
425}
426if totShards > 256 {
427return nil, ErrMaxShardNum
428}
429
430r := reedSolomon{
431dataShards: dataShards,
432parityShards: parityShards,
433totalShards: dataShards + parityShards,
434o: o,
435}
436
437if dataShards <= 0 || parityShards < 0 {
438return nil, ErrInvShardNum
439}
440
441if parityShards == 0 {
442return &r, nil
443}
444
445var err error
446switch {
447case r.o.customMatrix != nil:
448if len(r.o.customMatrix) < parityShards {
449return nil, errors.New("coding matrix must contain at least parityShards rows")
450}
451r.m = make([][]byte, r.totalShards)
452for i := 0; i < dataShards; i++ {
453r.m[i] = make([]byte, dataShards)
454r.m[i][i] = 1
455}
456for k, row := range r.o.customMatrix {
457if len(row) < dataShards {
458return nil, errors.New("coding matrix must contain at least dataShards columns")
459}
460r.m[dataShards+k] = make([]byte, dataShards)
461copy(r.m[dataShards+k], row)
462}
463case r.o.fastOneParity && parityShards == 1:
464r.m, err = buildXorMatrix(dataShards, r.totalShards)
465case r.o.useCauchy:
466r.m, err = buildMatrixCauchy(dataShards, r.totalShards)
467case r.o.usePAR1Matrix:
468r.m, err = buildMatrixPAR1(dataShards, r.totalShards)
469case r.o.useJerasureMatrix:
470r.m, err = buildMatrixJerasure(dataShards, r.totalShards)
471default:
472r.m, err = buildMatrix(dataShards, r.totalShards)
473}
474if err != nil {
475return nil, err
476}
477
478// Calculate what we want per round
479r.o.perRound = cpuid.CPU.Cache.L2
480
481divide := parityShards + 1
482if avx2CodeGen && r.o.useAVX2 && (dataShards > maxAvx2Inputs || parityShards > maxAvx2Outputs) {
483// Base on L1 cache if we have many inputs.
484r.o.perRound = cpuid.CPU.Cache.L1D
485divide = 0
486if dataShards > maxAvx2Inputs {
487divide += maxAvx2Inputs
488} else {
489divide += dataShards
490}
491if parityShards > maxAvx2Inputs {
492divide += maxAvx2Outputs
493} else {
494divide += parityShards
495}
496}
497
498if r.o.perRound <= 0 {
499// Set to 128K if undetectable.
500r.o.perRound = 128 << 10
501}
502
503if cpuid.CPU.ThreadsPerCore > 1 && r.o.maxGoroutines > cpuid.CPU.PhysicalCores {
504// If multiple threads per core, make sure they don't contend for cache.
505r.o.perRound /= cpuid.CPU.ThreadsPerCore
506}
507
508// 1 input + parity must fit in cache, and we add one more to be safer.
509r.o.perRound = r.o.perRound / divide
510// Align to 64 bytes.
511r.o.perRound = ((r.o.perRound + 63) / 64) * 64
512
513if r.o.minSplitSize <= 0 {
514// Set minsplit as high as we can, but still have parity in L1.
515cacheSize := cpuid.CPU.Cache.L1D
516if cacheSize <= 0 {
517cacheSize = 32 << 10
518}
519
520r.o.minSplitSize = cacheSize / (parityShards + 1)
521// Min 1K
522if r.o.minSplitSize < 1024 {
523r.o.minSplitSize = 1024
524}
525}
526
527if r.o.shardSize > 0 {
528p := runtime.GOMAXPROCS(0)
529if p == 1 || r.o.shardSize <= r.o.minSplitSize*2 {
530// Not worth it.
531r.o.maxGoroutines = 1
532} else {
533g := r.o.shardSize / r.o.perRound
534
535// Overprovision by a factor of 2.
536if g < p*2 && r.o.perRound > r.o.minSplitSize*2 {
537g = p * 2
538r.o.perRound /= 2
539}
540
541// Have g be multiple of p
542g += p - 1
543g -= g % p
544
545r.o.maxGoroutines = g
546}
547}
548
549// Generated AVX2 does not need data to stay in L1 cache between runs.
550// We will be purely limited by RAM speed.
551if r.canAVX2C(avx2CodeGenMinSize, maxAvx2Inputs, maxAvx2Outputs) && r.o.maxGoroutines > avx2CodeGenMaxGoroutines {
552r.o.maxGoroutines = avx2CodeGenMaxGoroutines
553}
554
555if r.canGFNI(avx2CodeGenMinSize, maxAvx2Inputs, maxAvx2Outputs) && r.o.maxGoroutines > gfniCodeGenMaxGoroutines {
556r.o.maxGoroutines = gfniCodeGenMaxGoroutines
557}
558
559// Inverted matrices are cached in a tree keyed by the indices
560// of the invalid rows of the data to reconstruct.
561// The inversion root node will have the identity matrix as
562// its inversion matrix because it implies there are no errors
563// with the original data.
564if r.o.inversionCache {
565r.tree = newInversionTree(dataShards, parityShards)
566}
567
568r.parity = make([][]byte, parityShards)
569for i := range r.parity {
570r.parity[i] = r.m[dataShards+i]
571}
572
573if avx2CodeGen && r.o.useAVX2 {
574sz := r.dataShards * r.parityShards * 2 * 32
575r.mPool.New = func() interface{} {
576return AllocAligned(1, sz)[0]
577}
578r.mPoolSz = sz
579}
580return &r, err
581}
582
583func (r *reedSolomon) getTmpSlice() []byte {
584return r.mPool.Get().([]byte)
585}
586
587func (r *reedSolomon) putTmpSlice(b []byte) {
588if b != nil && cap(b) >= r.mPoolSz {
589r.mPool.Put(b[:r.mPoolSz])
590return
591}
592if false {
593// Sanity check
594panic(fmt.Sprintf("got short tmp returned, want %d, got %d", r.mPoolSz, cap(b)))
595}
596}
597
598// ErrTooFewShards is returned if too few shards where given to
599// Encode/Verify/Reconstruct/Update. It will also be returned from Reconstruct
600// if there were too few shards to reconstruct the missing data.
601var ErrTooFewShards = errors.New("too few shards given")
602
603// Encode parity for a set of data shards.
604// An array 'shards' containing data shards followed by parity shards.
605// The number of shards must match the number given to New.
606// Each shard is a byte array, and they must all be the same size.
607// The parity shards will always be overwritten and the data shards
608// will remain the same.
609func (r *reedSolomon) Encode(shards [][]byte) error {
610if len(shards) != r.totalShards {
611return ErrTooFewShards
612}
613
614err := checkShards(shards, false)
615if err != nil {
616return err
617}
618
619// Get the slice of output buffers.
620output := shards[r.dataShards:]
621
622// Do the coding.
623r.codeSomeShards(r.parity, shards[0:r.dataShards], output[:r.parityShards], len(shards[0]))
624return nil
625}
626
627// EncodeIdx will add parity for a single data shard.
628// Parity shards should start out zeroed. The caller must zero them before first call.
629// Data shards should only be delivered once. There is no check for this.
630// The parity shards will always be updated and the data shards will remain the unchanged.
631func (r *reedSolomon) EncodeIdx(dataShard []byte, idx int, parity [][]byte) error {
632if len(parity) != r.parityShards {
633return ErrTooFewShards
634}
635if len(parity) == 0 {
636return nil
637}
638if idx < 0 || idx >= r.dataShards {
639return ErrInvShardNum
640}
641err := checkShards(parity, false)
642if err != nil {
643return err
644}
645if len(parity[0]) != len(dataShard) {
646return ErrShardSize
647}
648
649// Process using no goroutines for now.
650start, end := 0, r.o.perRound
651if end > len(dataShard) {
652end = len(dataShard)
653}
654
655for start < len(dataShard) {
656in := dataShard[start:end]
657for iRow := 0; iRow < r.parityShards; iRow++ {
658galMulSliceXor(r.parity[iRow][idx], in, parity[iRow][start:end], &r.o)
659}
660start = end
661end += r.o.perRound
662if end > len(dataShard) {
663end = len(dataShard)
664}
665}
666return nil
667}
668
669// ErrInvalidInput is returned if invalid input parameter of Update.
670var ErrInvalidInput = errors.New("invalid input")
671
672func (r *reedSolomon) Update(shards [][]byte, newDatashards [][]byte) error {
673if len(shards) != r.totalShards {
674return ErrTooFewShards
675}
676
677if len(newDatashards) != r.dataShards {
678return ErrTooFewShards
679}
680
681err := checkShards(shards, true)
682if err != nil {
683return err
684}
685
686err = checkShards(newDatashards, true)
687if err != nil {
688return err
689}
690
691for i := range newDatashards {
692if newDatashards[i] != nil && shards[i] == nil {
693return ErrInvalidInput
694}
695}
696for _, p := range shards[r.dataShards:] {
697if p == nil {
698return ErrInvalidInput
699}
700}
701
702shardSize := shardSize(shards)
703
704// Get the slice of output buffers.
705output := shards[r.dataShards:]
706
707// Do the coding.
708r.updateParityShards(r.parity, shards[0:r.dataShards], newDatashards[0:r.dataShards], output, r.parityShards, shardSize)
709return nil
710}
711
712func (r *reedSolomon) updateParityShards(matrixRows, oldinputs, newinputs, outputs [][]byte, outputCount, byteCount int) {
713if len(outputs) == 0 {
714return
715}
716
717if r.o.maxGoroutines > 1 && byteCount > r.o.minSplitSize {
718r.updateParityShardsP(matrixRows, oldinputs, newinputs, outputs, outputCount, byteCount)
719return
720}
721
722for c := 0; c < r.dataShards; c++ {
723in := newinputs[c]
724if in == nil {
725continue
726}
727oldin := oldinputs[c]
728// oldinputs data will be changed
729sliceXor(in, oldin, &r.o)
730for iRow := 0; iRow < outputCount; iRow++ {
731galMulSliceXor(matrixRows[iRow][c], oldin, outputs[iRow], &r.o)
732}
733}
734}
735
736func (r *reedSolomon) updateParityShardsP(matrixRows, oldinputs, newinputs, outputs [][]byte, outputCount, byteCount int) {
737var wg sync.WaitGroup
738do := byteCount / r.o.maxGoroutines
739if do < r.o.minSplitSize {
740do = r.o.minSplitSize
741}
742start := 0
743for start < byteCount {
744if start+do > byteCount {
745do = byteCount - start
746}
747wg.Add(1)
748go func(start, stop int) {
749for c := 0; c < r.dataShards; c++ {
750in := newinputs[c]
751if in == nil {
752continue
753}
754oldin := oldinputs[c]
755// oldinputs data will be change
756sliceXor(in[start:stop], oldin[start:stop], &r.o)
757for iRow := 0; iRow < outputCount; iRow++ {
758galMulSliceXor(matrixRows[iRow][c], oldin[start:stop], outputs[iRow][start:stop], &r.o)
759}
760}
761wg.Done()
762}(start, start+do)
763start += do
764}
765wg.Wait()
766}
767
768// Verify returns true if the parity shards contain the right data.
769// The data is the same format as Encode. No data is modified.
770func (r *reedSolomon) Verify(shards [][]byte) (bool, error) {
771if len(shards) != r.totalShards {
772return false, ErrTooFewShards
773}
774err := checkShards(shards, false)
775if err != nil {
776return false, err
777}
778
779// Slice of buffers being checked.
780toCheck := shards[r.dataShards:]
781
782// Do the checking.
783return r.checkSomeShards(r.parity, shards[:r.dataShards], toCheck[:r.parityShards], len(shards[0])), nil
784}
785
786func (r *reedSolomon) canAVX2C(byteCount int, inputs, outputs int) bool {
787return avx2CodeGen && r.o.useAVX2 &&
788byteCount >= avx2CodeGenMinSize && inputs+outputs >= avx2CodeGenMinShards &&
789inputs <= maxAvx2Inputs && outputs <= maxAvx2Outputs
790}
791
792func (r *reedSolomon) canGFNI(byteCount int, inputs, outputs int) bool {
793return avx2CodeGen && r.o.useGFNI &&
794byteCount >= avx2CodeGenMinSize && inputs+outputs >= avx2CodeGenMinShards &&
795inputs <= maxAvx2Inputs && outputs <= maxAvx2Outputs
796}
797
798// Multiplies a subset of rows from a coding matrix by a full set of
799// input totalShards to produce some output totalShards.
800// 'matrixRows' is The rows from the matrix to use.
801// 'inputs' An array of byte arrays, each of which is one input shard.
802// The number of inputs used is determined by the length of each matrix row.
803// outputs Byte arrays where the computed totalShards are stored.
804// The number of outputs computed, and the
805// number of matrix rows used, is determined by
806// outputCount, which is the number of outputs to compute.
807func (r *reedSolomon) codeSomeShards(matrixRows, inputs, outputs [][]byte, byteCount int) {
808if len(outputs) == 0 {
809return
810}
811if byteCount > r.o.minSplitSize {
812r.codeSomeShardsP(matrixRows, inputs, outputs, byteCount)
813return
814}
815
816// Process using no goroutines
817start, end := 0, r.o.perRound
818if end > len(inputs[0]) {
819end = len(inputs[0])
820}
821if r.canGFNI(byteCount, len(inputs), len(outputs)) {
822var gfni [maxAvx2Inputs * maxAvx2Outputs]uint64
823m := genGFNIMatrix(matrixRows, len(inputs), 0, len(outputs), gfni[:])
824start += galMulSlicesGFNI(m, inputs, outputs, 0, byteCount)
825end = len(inputs[0])
826} else if r.canAVX2C(byteCount, len(inputs), len(outputs)) {
827m := genAvx2Matrix(matrixRows, len(inputs), 0, len(outputs), r.getTmpSlice())
828start += galMulSlicesAvx2(m, inputs, outputs, 0, byteCount)
829r.putTmpSlice(m)
830end = len(inputs[0])
831} else if len(inputs)+len(outputs) > avx2CodeGenMinShards && r.canAVX2C(byteCount, maxAvx2Inputs, maxAvx2Outputs) {
832var gfni [maxAvx2Inputs * maxAvx2Outputs]uint64
833end = len(inputs[0])
834inIdx := 0
835m := r.getTmpSlice()
836defer r.putTmpSlice(m)
837ins := inputs
838for len(ins) > 0 {
839inPer := ins
840if len(inPer) > maxAvx2Inputs {
841inPer = inPer[:maxAvx2Inputs]
842}
843outs := outputs
844outIdx := 0
845for len(outs) > 0 {
846outPer := outs
847if len(outPer) > maxAvx2Outputs {
848outPer = outPer[:maxAvx2Outputs]
849}
850if r.o.useGFNI {
851m := genGFNIMatrix(matrixRows[outIdx:], len(inPer), inIdx, len(outPer), gfni[:])
852if inIdx == 0 {
853galMulSlicesGFNI(m, inPer, outPer, 0, byteCount)
854} else {
855galMulSlicesGFNIXor(m, inPer, outPer, 0, byteCount)
856}
857} else {
858m = genAvx2Matrix(matrixRows[outIdx:], len(inPer), inIdx, len(outPer), m)
859if inIdx == 0 {
860galMulSlicesAvx2(m, inPer, outPer, 0, byteCount)
861} else {
862galMulSlicesAvx2Xor(m, inPer, outPer, 0, byteCount)
863}
864}
865start = byteCount & avxSizeMask
866outIdx += len(outPer)
867outs = outs[len(outPer):]
868}
869inIdx += len(inPer)
870ins = ins[len(inPer):]
871}
872if start >= end {
873return
874}
875}
876for start < len(inputs[0]) {
877for c := 0; c < len(inputs); c++ {
878in := inputs[c][start:end]
879for iRow := 0; iRow < len(outputs); iRow++ {
880if c == 0 {
881galMulSlice(matrixRows[iRow][c], in, outputs[iRow][start:end], &r.o)
882} else {
883galMulSliceXor(matrixRows[iRow][c], in, outputs[iRow][start:end], &r.o)
884}
885}
886}
887start = end
888end += r.o.perRound
889if end > len(inputs[0]) {
890end = len(inputs[0])
891}
892}
893}
894
895// Perform the same as codeSomeShards, but split the workload into
896// several goroutines.
897func (r *reedSolomon) codeSomeShardsP(matrixRows, inputs, outputs [][]byte, byteCount int) {
898var wg sync.WaitGroup
899gor := r.o.maxGoroutines
900
901var avx2Matrix []byte
902var gfniMatrix []uint64
903useAvx2 := r.canAVX2C(byteCount, len(inputs), len(outputs))
904useGFNI := r.canGFNI(byteCount, len(inputs), len(outputs))
905if useGFNI {
906var tmp [maxAvx2Inputs * maxAvx2Outputs]uint64
907gfniMatrix = genGFNIMatrix(matrixRows, len(inputs), 0, len(outputs), tmp[:])
908} else if useAvx2 {
909avx2Matrix = genAvx2Matrix(matrixRows, len(inputs), 0, len(outputs), r.getTmpSlice())
910defer r.putTmpSlice(avx2Matrix)
911} else if r.o.useGFNI && byteCount < 10<<20 && len(inputs)+len(outputs) > avx2CodeGenMinShards &&
912r.canAVX2C(byteCount/4, maxAvx2Inputs, maxAvx2Outputs) {
913// It appears there is a switchover point at around 10MB where
914// Regular processing is faster...
915r.codeSomeShardsAVXP(matrixRows, inputs, outputs, byteCount)
916return
917} else if r.o.useAVX2 && byteCount < 10<<20 && len(inputs)+len(outputs) > avx2CodeGenMinShards &&
918r.canAVX2C(byteCount/4, maxAvx2Inputs, maxAvx2Outputs) {
919// It appears there is a switchover point at around 10MB where
920// Regular processing is faster...
921r.codeSomeShardsAVXP(matrixRows, inputs, outputs, byteCount)
922return
923}
924
925do := byteCount / gor
926if do < r.o.minSplitSize {
927do = r.o.minSplitSize
928}
929
930exec := func(start, stop int) {
931if stop-start >= 64 {
932if useGFNI {
933start += galMulSlicesGFNI(gfniMatrix, inputs, outputs, start, stop)
934} else if useAvx2 {
935start += galMulSlicesAvx2(avx2Matrix, inputs, outputs, start, stop)
936}
937}
938
939lstart, lstop := start, start+r.o.perRound
940if lstop > stop {
941lstop = stop
942}
943for lstart < stop {
944for c := 0; c < len(inputs); c++ {
945in := inputs[c][lstart:lstop]
946for iRow := 0; iRow < len(outputs); iRow++ {
947if c == 0 {
948galMulSlice(matrixRows[iRow][c], in, outputs[iRow][lstart:lstop], &r.o)
949} else {
950galMulSliceXor(matrixRows[iRow][c], in, outputs[iRow][lstart:lstop], &r.o)
951}
952}
953}
954lstart = lstop
955lstop += r.o.perRound
956if lstop > stop {
957lstop = stop
958}
959}
960wg.Done()
961}
962if gor <= 1 {
963wg.Add(1)
964exec(0, byteCount)
965return
966}
967
968// Make sizes divisible by 64
969do = (do + 63) & (^63)
970start := 0
971for start < byteCount {
972if start+do > byteCount {
973do = byteCount - start
974}
975
976wg.Add(1)
977go exec(start, start+do)
978start += do
979}
980wg.Wait()
981}
982
983// Perform the same as codeSomeShards, but split the workload into
984// several goroutines.
985func (r *reedSolomon) codeSomeShardsAVXP(matrixRows, inputs, outputs [][]byte, byteCount int) {
986var wg sync.WaitGroup
987gor := r.o.maxGoroutines
988
989type state struct {
990input [][]byte
991output [][]byte
992m []byte
993first bool
994}
995// Make a plan...
996plan := make([]state, 0, ((len(inputs)+maxAvx2Inputs-1)/maxAvx2Inputs)*((len(outputs)+maxAvx2Outputs-1)/maxAvx2Outputs))
997
998tmp := r.getTmpSlice()
999defer r.putTmpSlice(tmp)
1000
1001// Flips between input first to output first.
1002// We put the smallest data load in the inner loop.
1003if len(inputs) > len(outputs) {
1004inIdx := 0
1005ins := inputs
1006for len(ins) > 0 {
1007inPer := ins
1008if len(inPer) > maxAvx2Inputs {
1009inPer = inPer[:maxAvx2Inputs]
1010}
1011outs := outputs
1012outIdx := 0
1013for len(outs) > 0 {
1014outPer := outs
1015if len(outPer) > maxAvx2Outputs {
1016outPer = outPer[:maxAvx2Outputs]
1017}
1018// Generate local matrix
1019m := genAvx2Matrix(matrixRows[outIdx:], len(inPer), inIdx, len(outPer), tmp)
1020tmp = tmp[len(m):]
1021plan = append(plan, state{
1022input: inPer,
1023output: outPer,
1024m: m,
1025first: inIdx == 0,
1026})
1027outIdx += len(outPer)
1028outs = outs[len(outPer):]
1029}
1030inIdx += len(inPer)
1031ins = ins[len(inPer):]
1032}
1033} else {
1034outs := outputs
1035outIdx := 0
1036for len(outs) > 0 {
1037outPer := outs
1038if len(outPer) > maxAvx2Outputs {
1039outPer = outPer[:maxAvx2Outputs]
1040}
1041
1042inIdx := 0
1043ins := inputs
1044for len(ins) > 0 {
1045inPer := ins
1046if len(inPer) > maxAvx2Inputs {
1047inPer = inPer[:maxAvx2Inputs]
1048}
1049// Generate local matrix
1050m := genAvx2Matrix(matrixRows[outIdx:], len(inPer), inIdx, len(outPer), tmp)
1051tmp = tmp[len(m):]
1052//fmt.Println("bytes:", len(inPer)*r.o.perRound, "out:", len(outPer)*r.o.perRound)
1053plan = append(plan, state{
1054input: inPer,
1055output: outPer,
1056m: m,
1057first: inIdx == 0,
1058})
1059inIdx += len(inPer)
1060ins = ins[len(inPer):]
1061}
1062outIdx += len(outPer)
1063outs = outs[len(outPer):]
1064}
1065}
1066
1067do := byteCount / gor
1068if do < r.o.minSplitSize {
1069do = r.o.minSplitSize
1070}
1071
1072exec := func(start, stop int) {
1073lstart, lstop := start, start+r.o.perRound
1074if lstop > stop {
1075lstop = stop
1076}
1077for lstart < stop {
1078if lstop-lstart >= minAvx2Size {
1079// Execute plan...
1080for _, p := range plan {
1081if p.first {
1082galMulSlicesAvx2(p.m, p.input, p.output, lstart, lstop)
1083} else {
1084galMulSlicesAvx2Xor(p.m, p.input, p.output, lstart, lstop)
1085}
1086}
1087lstart += (lstop - lstart) & avxSizeMask
1088if lstart == lstop {
1089lstop += r.o.perRound
1090if lstop > stop {
1091lstop = stop
1092}
1093continue
1094}
1095}
1096
1097for c := range inputs {
1098in := inputs[c][lstart:lstop]
1099for iRow := 0; iRow < len(outputs); iRow++ {
1100if c == 0 {
1101galMulSlice(matrixRows[iRow][c], in, outputs[iRow][lstart:lstop], &r.o)
1102} else {
1103galMulSliceXor(matrixRows[iRow][c], in, outputs[iRow][lstart:lstop], &r.o)
1104}
1105}
1106}
1107lstart = lstop
1108lstop += r.o.perRound
1109if lstop > stop {
1110lstop = stop
1111}
1112}
1113wg.Done()
1114}
1115if gor == 1 {
1116wg.Add(1)
1117exec(0, byteCount)
1118return
1119}
1120
1121// Make sizes divisible by 64
1122do = (do + 63) & (^63)
1123start := 0
1124for start < byteCount {
1125if start+do > byteCount {
1126do = byteCount - start
1127}
1128
1129wg.Add(1)
1130go exec(start, start+do)
1131start += do
1132}
1133wg.Wait()
1134}
1135
1136// Perform the same as codeSomeShards, but split the workload into
1137// several goroutines.
1138func (r *reedSolomon) codeSomeShardsGFNI(matrixRows, inputs, outputs [][]byte, byteCount int) {
1139var wg sync.WaitGroup
1140gor := r.o.maxGoroutines
1141
1142type state struct {
1143input [][]byte
1144output [][]byte
1145m []uint64
1146first bool
1147}
1148// Make a plan...
1149plan := make([]state, 0, ((len(inputs)+maxAvx2Inputs-1)/maxAvx2Inputs)*((len(outputs)+maxAvx2Outputs-1)/maxAvx2Outputs))
1150
1151// Flips between input first to output first.
1152// We put the smallest data load in the inner loop.
1153if len(inputs) > len(outputs) {
1154inIdx := 0
1155ins := inputs
1156for len(ins) > 0 {
1157inPer := ins
1158if len(inPer) > maxAvx2Inputs {
1159inPer = inPer[:maxAvx2Inputs]
1160}
1161outs := outputs
1162outIdx := 0
1163for len(outs) > 0 {
1164outPer := outs
1165if len(outPer) > maxAvx2Outputs {
1166outPer = outPer[:maxAvx2Outputs]
1167}
1168// Generate local matrix
1169m := genGFNIMatrix(matrixRows[outIdx:], len(inPer), inIdx, len(outPer), make([]uint64, len(inPer)*len(outPer)))
1170plan = append(plan, state{
1171input: inPer,
1172output: outPer,
1173m: m,
1174first: inIdx == 0,
1175})
1176outIdx += len(outPer)
1177outs = outs[len(outPer):]
1178}
1179inIdx += len(inPer)
1180ins = ins[len(inPer):]
1181}
1182} else {
1183outs := outputs
1184outIdx := 0
1185for len(outs) > 0 {
1186outPer := outs
1187if len(outPer) > maxAvx2Outputs {
1188outPer = outPer[:maxAvx2Outputs]
1189}
1190
1191inIdx := 0
1192ins := inputs
1193for len(ins) > 0 {
1194inPer := ins
1195if len(inPer) > maxAvx2Inputs {
1196inPer = inPer[:maxAvx2Inputs]
1197}
1198// Generate local matrix
1199m := genGFNIMatrix(matrixRows[outIdx:], len(inPer), inIdx, len(outPer), make([]uint64, len(inPer)*len(outPer)))
1200//fmt.Println("bytes:", len(inPer)*r.o.perRound, "out:", len(outPer)*r.o.perRound)
1201plan = append(plan, state{
1202input: inPer,
1203output: outPer,
1204m: m,
1205first: inIdx == 0,
1206})
1207inIdx += len(inPer)
1208ins = ins[len(inPer):]
1209}
1210outIdx += len(outPer)
1211outs = outs[len(outPer):]
1212}
1213}
1214
1215do := byteCount / gor
1216if do < r.o.minSplitSize {
1217do = r.o.minSplitSize
1218}
1219
1220exec := func(start, stop int) {
1221lstart, lstop := start, start+r.o.perRound
1222if lstop > stop {
1223lstop = stop
1224}
1225for lstart < stop {
1226if lstop-lstart >= minAvx2Size {
1227// Execute plan...
1228for _, p := range plan {
1229if p.first {
1230galMulSlicesGFNI(p.m, p.input, p.output, lstart, lstop)
1231} else {
1232galMulSlicesGFNIXor(p.m, p.input, p.output, lstart, lstop)
1233}
1234}
1235lstart += (lstop - lstart) & avxSizeMask
1236if lstart == lstop {
1237lstop += r.o.perRound
1238if lstop > stop {
1239lstop = stop
1240}
1241continue
1242}
1243}
1244
1245for c := range inputs {
1246in := inputs[c][lstart:lstop]
1247for iRow := 0; iRow < len(outputs); iRow++ {
1248if c == 0 {
1249galMulSlice(matrixRows[iRow][c], in, outputs[iRow][lstart:lstop], &r.o)
1250} else {
1251galMulSliceXor(matrixRows[iRow][c], in, outputs[iRow][lstart:lstop], &r.o)
1252}
1253}
1254}
1255lstart = lstop
1256lstop += r.o.perRound
1257if lstop > stop {
1258lstop = stop
1259}
1260}
1261wg.Done()
1262}
1263if gor == 1 {
1264wg.Add(1)
1265exec(0, byteCount)
1266return
1267}
1268
1269// Make sizes divisible by 64
1270do = (do + 63) & (^63)
1271start := 0
1272for start < byteCount {
1273if start+do > byteCount {
1274do = byteCount - start
1275}
1276
1277wg.Add(1)
1278go exec(start, start+do)
1279start += do
1280}
1281wg.Wait()
1282}
1283
1284// checkSomeShards is mostly the same as codeSomeShards,
1285// except this will check values and return
1286// as soon as a difference is found.
1287func (r *reedSolomon) checkSomeShards(matrixRows, inputs, toCheck [][]byte, byteCount int) bool {
1288if len(toCheck) == 0 {
1289return true
1290}
1291
1292outputs := AllocAligned(len(toCheck), byteCount)
1293r.codeSomeShards(matrixRows, inputs, outputs, byteCount)
1294
1295for i, calc := range outputs {
1296if !bytes.Equal(calc, toCheck[i]) {
1297return false
1298}
1299}
1300return true
1301}
1302
1303// ErrShardNoData will be returned if there are no shards,
1304// or if the length of all shards is zero.
1305var ErrShardNoData = errors.New("no shard data")
1306
1307// ErrShardSize is returned if shard length isn't the same for all
1308// shards.
1309var ErrShardSize = errors.New("shard sizes do not match")
1310
1311// checkShards will check if shards are the same size
1312// or 0, if allowed. An error is returned if this fails.
1313// An error is also returned if all shards are size 0.
1314func checkShards(shards [][]byte, nilok bool) error {
1315size := shardSize(shards)
1316if size == 0 {
1317return ErrShardNoData
1318}
1319for _, shard := range shards {
1320if len(shard) != size {
1321if len(shard) != 0 || !nilok {
1322return ErrShardSize
1323}
1324}
1325}
1326return nil
1327}
1328
1329// shardSize return the size of a single shard.
1330// The first non-zero size is returned,
1331// or 0 if all shards are size 0.
1332func shardSize(shards [][]byte) int {
1333for _, shard := range shards {
1334if len(shard) != 0 {
1335return len(shard)
1336}
1337}
1338return 0
1339}
1340
1341// Reconstruct will recreate the missing shards, if possible.
1342//
1343// Given a list of shards, some of which contain data, fills in the
1344// ones that don't have data.
1345//
1346// The length of the array must be equal to shards.
1347// You indicate that a shard is missing by setting it to nil or zero-length.
1348// If a shard is zero-length but has sufficient capacity, that memory will
1349// be used, otherwise a new []byte will be allocated.
1350//
1351// If there are too few shards to reconstruct the missing
1352// ones, ErrTooFewShards will be returned.
1353//
1354// The reconstructed shard set is complete, but integrity is not verified.
1355// Use the Verify function to check if data set is ok.
1356func (r *reedSolomon) Reconstruct(shards [][]byte) error {
1357return r.reconstruct(shards, false, nil)
1358}
1359
1360// ReconstructData will recreate any missing data shards, if possible.
1361//
1362// Given a list of shards, some of which contain data, fills in the
1363// data shards that don't have data.
1364//
1365// The length of the array must be equal to shards.
1366// You indicate that a shard is missing by setting it to nil or zero-length.
1367// If a shard is zero-length but has sufficient capacity, that memory will
1368// be used, otherwise a new []byte will be allocated.
1369//
1370// If there are too few shards to reconstruct the missing
1371// ones, ErrTooFewShards will be returned.
1372//
1373// As the reconstructed shard set may contain missing parity shards,
1374// calling the Verify function is likely to fail.
1375func (r *reedSolomon) ReconstructData(shards [][]byte) error {
1376return r.reconstruct(shards, true, nil)
1377}
1378
1379// ReconstructSome will recreate only requested data shards, if possible.
1380//
1381// Given a list of shards, some of which contain data, fills in the
1382// data shards indicated by true values in the "required" parameter.
1383// The length of "required" array must be equal to dataShards.
1384//
1385// The length of "shards" array must be equal to shards.
1386// You indicate that a shard is missing by setting it to nil or zero-length.
1387// If a shard is zero-length but has sufficient capacity, that memory will
1388// be used, otherwise a new []byte will be allocated.
1389//
1390// If there are too few shards to reconstruct the missing
1391// ones, ErrTooFewShards will be returned.
1392//
1393// As the reconstructed shard set may contain missing parity shards,
1394// calling the Verify function is likely to fail.
1395func (r *reedSolomon) ReconstructSome(shards [][]byte, required []bool) error {
1396return r.reconstruct(shards, true, required)
1397}
1398
1399// reconstruct will recreate the missing data totalShards, and unless
1400// dataOnly is true, also the missing parity totalShards
1401//
1402// The length of "shards" array must be equal to totalShards.
1403// You indicate that a shard is missing by setting it to nil.
1404//
1405// If there are too few totalShards to reconstruct the missing
1406// ones, ErrTooFewShards will be returned.
1407func (r *reedSolomon) reconstruct(shards [][]byte, dataOnly bool, required []bool) error {
1408if len(shards) != r.totalShards || required != nil && len(required) < r.dataShards {
1409return ErrTooFewShards
1410}
1411// Check arguments.
1412err := checkShards(shards, true)
1413if err != nil {
1414return err
1415}
1416
1417shardSize := shardSize(shards)
1418
1419// Quick check: are all of the shards present? If so, there's
1420// nothing to do.
1421numberPresent := 0
1422dataPresent := 0
1423missingRequired := 0
1424for i := 0; i < r.totalShards; i++ {
1425if len(shards[i]) != 0 {
1426numberPresent++
1427if i < r.dataShards {
1428dataPresent++
1429}
1430} else if required != nil && required[i] {
1431missingRequired++
1432}
1433}
1434if numberPresent == r.totalShards || dataOnly && dataPresent == r.dataShards ||
1435required != nil && missingRequired == 0 {
1436// Cool. All of the shards have data. We don't
1437// need to do anything.
1438return nil
1439}
1440
1441// More complete sanity check
1442if numberPresent < r.dataShards {
1443return ErrTooFewShards
1444}
1445
1446// Pull out an array holding just the shards that
1447// correspond to the rows of the submatrix. These shards
1448// will be the input to the decoding process that re-creates
1449// the missing data shards.
1450//
1451// Also, create an array of indices of the valid rows we do have
1452// and the invalid rows we don't have up until we have enough valid rows.
1453subShards := make([][]byte, r.dataShards)
1454validIndices := make([]int, r.dataShards)
1455invalidIndices := make([]int, 0)
1456subMatrixRow := 0
1457for matrixRow := 0; matrixRow < r.totalShards && subMatrixRow < r.dataShards; matrixRow++ {
1458if len(shards[matrixRow]) != 0 {
1459subShards[subMatrixRow] = shards[matrixRow]
1460validIndices[subMatrixRow] = matrixRow
1461subMatrixRow++
1462} else {
1463invalidIndices = append(invalidIndices, matrixRow)
1464}
1465}
1466
1467// Attempt to get the cached inverted matrix out of the tree
1468// based on the indices of the invalid rows.
1469dataDecodeMatrix := r.tree.GetInvertedMatrix(invalidIndices)
1470
1471// If the inverted matrix isn't cached in the tree yet we must
1472// construct it ourselves and insert it into the tree for the
1473// future. In this way the inversion tree is lazily loaded.
1474if dataDecodeMatrix == nil {
1475// Pull out the rows of the matrix that correspond to the
1476// shards that we have and build a square matrix. This
1477// matrix could be used to generate the shards that we have
1478// from the original data.
1479subMatrix, _ := newMatrix(r.dataShards, r.dataShards)
1480for subMatrixRow, validIndex := range validIndices {
1481for c := 0; c < r.dataShards; c++ {
1482subMatrix[subMatrixRow][c] = r.m[validIndex][c]
1483}
1484}
1485// Invert the matrix, so we can go from the encoded shards
1486// back to the original data. Then pull out the row that
1487// generates the shard that we want to decode. Note that
1488// since this matrix maps back to the original data, it can
1489// be used to create a data shard, but not a parity shard.
1490dataDecodeMatrix, err = subMatrix.Invert()
1491if err != nil {
1492return err
1493}
1494
1495// Cache the inverted matrix in the tree for future use keyed on the
1496// indices of the invalid rows.
1497err = r.tree.InsertInvertedMatrix(invalidIndices, dataDecodeMatrix, r.totalShards)
1498if err != nil {
1499return err
1500}
1501}
1502
1503// Re-create any data shards that were missing.
1504//
1505// The input to the coding is all of the shards we actually
1506// have, and the output is the missing data shards. The computation
1507// is done using the special decode matrix we just built.
1508outputs := make([][]byte, r.parityShards)
1509matrixRows := make([][]byte, r.parityShards)
1510outputCount := 0
1511
1512for iShard := 0; iShard < r.dataShards; iShard++ {
1513if len(shards[iShard]) == 0 && (required == nil || required[iShard]) {
1514if cap(shards[iShard]) >= shardSize {
1515shards[iShard] = shards[iShard][0:shardSize]
1516} else {
1517shards[iShard] = AllocAligned(1, shardSize)[0]
1518}
1519outputs[outputCount] = shards[iShard]
1520matrixRows[outputCount] = dataDecodeMatrix[iShard]
1521outputCount++
1522}
1523}
1524r.codeSomeShards(matrixRows, subShards, outputs[:outputCount], shardSize)
1525
1526if dataOnly {
1527// Exit out early if we are only interested in the data shards
1528return nil
1529}
1530
1531// Now that we have all of the data shards intact, we can
1532// compute any of the parity that is missing.
1533//
1534// The input to the coding is ALL of the data shards, including
1535// any that we just calculated. The output is whichever of the
1536// data shards were missing.
1537outputCount = 0
1538for iShard := r.dataShards; iShard < r.totalShards; iShard++ {
1539if len(shards[iShard]) == 0 && (required == nil || required[iShard]) {
1540if cap(shards[iShard]) >= shardSize {
1541shards[iShard] = shards[iShard][0:shardSize]
1542} else {
1543shards[iShard] = AllocAligned(1, shardSize)[0]
1544}
1545outputs[outputCount] = shards[iShard]
1546matrixRows[outputCount] = r.parity[iShard-r.dataShards]
1547outputCount++
1548}
1549}
1550r.codeSomeShards(matrixRows, shards[:r.dataShards], outputs[:outputCount], shardSize)
1551return nil
1552}
1553
1554// ErrShortData will be returned by Split(), if there isn't enough data
1555// to fill the number of shards.
1556var ErrShortData = errors.New("not enough data to fill the number of requested shards")
1557
1558// Split a data slice into the number of shards given to the encoder,
1559// and create empty parity shards if necessary.
1560//
1561// The data will be split into equally sized shards.
1562// If the data size isn't divisible by the number of shards,
1563// the last shard will contain extra zeros.
1564//
1565// If there is extra capacity on the provided data slice
1566// it will be used instead of allocating parity shards.
1567// It will be zeroed out.
1568//
1569// There must be at least 1 byte otherwise ErrShortData will be
1570// returned.
1571//
1572// The data will not be copied, except for the last shard, so you
1573// should not modify the data of the input slice afterwards.
1574func (r *reedSolomon) Split(data []byte) ([][]byte, error) {
1575if len(data) == 0 {
1576return nil, ErrShortData
1577}
1578if r.totalShards == 1 {
1579return [][]byte{data}, nil
1580}
1581
1582dataLen := len(data)
1583// Calculate number of bytes per data shard.
1584perShard := (len(data) + r.dataShards - 1) / r.dataShards
1585needTotal := r.totalShards * perShard
1586
1587if cap(data) > len(data) {
1588if cap(data) > needTotal {
1589data = data[:needTotal]
1590} else {
1591data = data[:cap(data)]
1592}
1593clear := data[dataLen:]
1594for i := range clear {
1595clear[i] = 0
1596}
1597}
1598
1599// Only allocate memory if necessary
1600var padding [][]byte
1601if len(data) < needTotal {
1602// calculate maximum number of full shards in `data` slice
1603fullShards := len(data) / perShard
1604padding = AllocAligned(r.totalShards-fullShards, perShard)
1605
1606if dataLen > perShard*fullShards {
1607// Copy partial shards
1608copyFrom := data[perShard*fullShards : dataLen]
1609for i := range padding {
1610if len(copyFrom) <= 0 {
1611break
1612}
1613copyFrom = copyFrom[copy(padding[i], copyFrom):]
1614}
1615}
1616}
1617
1618// Split into equal-length shards.
1619dst := make([][]byte, r.totalShards)
1620i := 0
1621for ; i < len(dst) && len(data) >= perShard; i++ {
1622dst[i] = data[:perShard:perShard]
1623data = data[perShard:]
1624}
1625
1626for j := 0; i+j < len(dst); j++ {
1627dst[i+j] = padding[0]
1628padding = padding[1:]
1629}
1630
1631return dst, nil
1632}
1633
1634// ErrReconstructRequired is returned if too few data shards are intact and a
1635// reconstruction is required before you can successfully join the shards.
1636var ErrReconstructRequired = errors.New("reconstruction required as one or more required data shards are nil")
1637
1638// Join the shards and write the data segment to dst.
1639//
1640// Only the data shards are considered.
1641// You must supply the exact output size you want.
1642//
1643// If there are to few shards given, ErrTooFewShards will be returned.
1644// If the total data size is less than outSize, ErrShortData will be returned.
1645// If one or more required data shards are nil, ErrReconstructRequired will be returned.
1646func (r *reedSolomon) Join(dst io.Writer, shards [][]byte, outSize int) error {
1647// Do we have enough shards?
1648if len(shards) < r.dataShards {
1649return ErrTooFewShards
1650}
1651shards = shards[:r.dataShards]
1652
1653// Do we have enough data?
1654size := 0
1655for _, shard := range shards {
1656if shard == nil {
1657return ErrReconstructRequired
1658}
1659size += len(shard)
1660
1661// Do we have enough data already?
1662if size >= outSize {
1663break
1664}
1665}
1666if size < outSize {
1667return ErrShortData
1668}
1669
1670// Copy data to dst
1671write := outSize
1672for _, shard := range shards {
1673if write < len(shard) {
1674_, err := dst.Write(shard[:write])
1675return err
1676}
1677n, err := dst.Write(shard)
1678if err != nil {
1679return err
1680}
1681write -= n
1682}
1683return nil
1684}
1685