Skip to content

Commit

Permalink
Allow zero parity shards (#161)
Browse files Browse the repository at this point in the history
  • Loading branch information
shawnz authored Mar 8, 2021
1 parent ab26eb4 commit 0e7f9a6
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 6 deletions.
18 changes: 15 additions & 3 deletions reedsolomon.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,9 @@ type reedSolomon struct {
}

// ErrInvShardNum will be returned by New, if you attempt to create
// an Encoder where either data or parity shards is zero or less.
var ErrInvShardNum = errors.New("cannot create Encoder with zero or less data/parity shards")
// an Encoder with less than one data shard or less than zero parity
// shards.
var ErrInvShardNum = errors.New("cannot create Encoder with less than one data shard or less than zero parity shards")

// ErrMaxShardNum will be returned by New, if you attempt to create an
// Encoder where data and parity shards are bigger than the order of
Expand Down Expand Up @@ -249,14 +250,18 @@ func New(dataShards, parityShards int, opts ...Option) (Encoder, error) {
for _, opt := range opts {
opt(&r.o)
}
if dataShards <= 0 || parityShards <= 0 {
if dataShards <= 0 || parityShards < 0 {
return nil, ErrInvShardNum
}

if dataShards+parityShards > 256 {
return nil, ErrMaxShardNum
}

if parityShards == 0 {
return &r, nil
}

var err error
switch {
case r.o.fastOneParity && parityShards == 1:
Expand Down Expand Up @@ -423,6 +428,10 @@ func (r *reedSolomon) Update(shards [][]byte, newDatashards [][]byte) error {
}

func (r *reedSolomon) updateParityShards(matrixRows, oldinputs, newinputs, outputs [][]byte, outputCount, byteCount int) {
if len(outputs) == 0 {
return
}

if r.o.maxGoroutines > 1 && byteCount > r.o.minSplitSize {
r.updateParityShardsP(matrixRows, oldinputs, newinputs, outputs, outputCount, byteCount)
return
Expand Down Expand Up @@ -612,6 +621,9 @@ func (r *reedSolomon) codeSomeShardsP(matrixRows, inputs, outputs [][]byte, outp
// except this will check values and return
// as soon as a difference is found.
func (r *reedSolomon) checkSomeShards(matrixRows, inputs, toCheck [][]byte, outputCount, byteCount int) bool {
if len(toCheck) == 0 {
return true
}
if r.o.maxGoroutines > 1 && byteCount > r.o.minSplitSize {
return r.checkSomeShardsP(matrixRows, inputs, toCheck, outputCount, byteCount)
}
Expand Down
24 changes: 22 additions & 2 deletions reedsolomon_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,9 @@ func TestEncoding(t *testing.T) {

// matrix sizes to test.
// note that par1 matric will fail on some combinations.
var testSizes = [][2]int{{1, 1}, {1, 2}, {3, 3}, {3, 1}, {5, 3}, {8, 4}, {10, 30}, {12, 10}, {14, 7}, {41, 17}, {49, 1}}
var testSizes = [][2]int{
{1, 0}, {3, 0}, {5, 0}, {8, 0}, {10, 0}, {12, 0}, {14, 0}, {41, 0}, {49, 0},
{1, 1}, {1, 2}, {3, 3}, {3, 1}, {5, 3}, {8, 4}, {10, 30}, {12, 10}, {14, 7}, {41, 17}, {49, 1}}
var testDataSizes = []int{10, 100, 1000, 10001, 100003, 1000055}
var testDataSizesShort = []int{10, 10001, 100003}

Expand Down Expand Up @@ -220,6 +222,22 @@ func testEncoding(t *testing.T, o ...Option) {
if !ok {
t.Fatal("Verification failed")
}

if parity == 0 {
// Check that Reconstruct and ReconstructData do nothing
err = r.ReconstructData(shards)
if err != nil {
t.Fatal(err)
}
err = r.Reconstruct(shards)
if err != nil {
t.Fatal(err)
}

// Skip integrity checks
return
}

// Delete one in data
idx := rng.Intn(data)
want := shards[idx]
Expand Down Expand Up @@ -1434,10 +1452,12 @@ func TestNew(t *testing.T) {
{127, 127, nil},
{128, 128, nil},
{255, 1, nil},
{255, 0, nil},
{1, 0, nil},
{256, 256, ErrMaxShardNum},

{0, 1, ErrInvShardNum},
{1, 0, ErrInvShardNum},
{1, -1, ErrInvShardNum},
{256, 1, ErrMaxShardNum},

// overflow causes r.Shards to be negative
Expand Down
81 changes: 80 additions & 1 deletion streaming_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,84 @@ func TestStreamEncodingConcurrent(t *testing.T) {
}
}

func TestStreamZeroParity(t *testing.T) {
perShard := 10 << 20
if testing.Short() {
perShard = 50000
}
r, err := NewStream(10, 0, testOptions()...)
if err != nil {
t.Fatal(err)
}
rand.Seed(0)
input := randomBytes(10, perShard)
data := toBuffers(input)

err = r.Encode(toReaders(data), []io.Writer{})
if err != nil {
t.Fatal(err)
}
// Reset Data
data = toBuffers(input)

all := toReaders(data)
ok, err := r.Verify(all)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("Verification failed")
}
// Reset Data
data = toBuffers(input)

// Check that Reconstruct does nothing
all = toReaders(data)
err = r.Reconstruct(all, nilWriters(10))
if err != nil {
t.Fatal(err)
}
}

func TestStreamZeroParityConcurrent(t *testing.T) {
perShard := 10 << 20
if testing.Short() {
perShard = 50000
}
r, err := NewStreamC(10, 0, true, true, testOptions()...)
if err != nil {
t.Fatal(err)
}
rand.Seed(0)
input := randomBytes(10, perShard)
data := toBuffers(input)

err = r.Encode(toReaders(data), []io.Writer{})
if err != nil {
t.Fatal(err)
}
// Reset Data
data = toBuffers(input)

all := toReaders(data)
ok, err := r.Verify(all)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("Verification failed")
}
// Reset Data
data = toBuffers(input)

// Check that Reconstruct does nothing
all = toReaders(data)
err = r.Reconstruct(all, nilWriters(10))
if err != nil {
t.Fatal(err)
}
}

func randomBuffer(length int) *bytes.Buffer {
b := make([]byte, length)
fillRandom(b)
Expand Down Expand Up @@ -605,10 +683,11 @@ func TestNewStream(t *testing.T) {
err error
}{
{127, 127, nil},
{1, 0, nil},
{256, 256, ErrMaxShardNum},

{0, 1, ErrInvShardNum},
{1, 0, ErrInvShardNum},
{1, -1, ErrInvShardNum},
{257, 1, ErrMaxShardNum},

// overflow causes r.Shards to be negative
Expand Down

0 comments on commit 0e7f9a6

Please sign in to comment.