188 lines
7.4 KiB
C#
188 lines
7.4 KiB
C#
using System;
|
|
using DotNetty.Buffers;
|
|
|
|
namespace fec
|
|
{
|
|
public static class FecExpansion
|
|
{
|
|
private static readonly InputOutputByteBufTableCodingLoop LOOP = new InputOutputByteBufTableCodingLoop();
|
|
|
|
|
|
public static void encodeParity(this ReedSolomon reedSolomon, IByteBuffer[] shards, int offset, int byteCount)
|
|
{
|
|
// Check arguments.
|
|
reedSolomon.checkBuffersAndSizes(shards, offset, byteCount);
|
|
|
|
// Build the array of output buffers.
|
|
IByteBuffer[] outputs = new IByteBuffer [reedSolomon.ParityShardCount];
|
|
Array.Copy(shards, reedSolomon.DataShardCount, outputs, 0, reedSolomon.ParityShardCount);
|
|
// System.arraycopy(shards, dataShardCount, outputs, 0, parityShardCount);
|
|
|
|
// Do the coding.
|
|
LOOP.codeSomeShards(
|
|
reedSolomon.ParityRows,
|
|
shards, reedSolomon.DataShardCount,
|
|
outputs, reedSolomon.ParityShardCount,
|
|
offset, byteCount);
|
|
}
|
|
|
|
|
|
/**
|
|
* Checks the consistency of arguments passed to public methods.
|
|
*/
|
|
private static void checkBuffersAndSizes(this ReedSolomon reedSolomon, IByteBuffer[] shards, int offset,
|
|
int byteCount)
|
|
{
|
|
// The number of buffers should be equal to the number of
|
|
// data shards plus the number of parity shards.
|
|
if (shards.Length != reedSolomon.TotalShardCount)
|
|
{
|
|
throw new Exception("wrong number of shards: " + shards.Length);
|
|
}
|
|
|
|
// All of the shard buffers should be the same length.
|
|
int shardLength = shards[0].ReadableBytes;
|
|
for (int i = 1; i < shards.Length; i++)
|
|
{
|
|
if (shards[i].ReadableBytes != shardLength)
|
|
{
|
|
throw new Exception("Shards are different sizes");
|
|
}
|
|
}
|
|
|
|
// The offset and byteCount must be non-negative and fit in the buffers.
|
|
if (offset < 0)
|
|
{
|
|
throw new Exception("offset is negative: " + offset);
|
|
}
|
|
|
|
if (byteCount < 0)
|
|
{
|
|
throw new Exception("byteCount is negative: " + byteCount);
|
|
}
|
|
|
|
if (shardLength < offset + byteCount)
|
|
{
|
|
throw new Exception("buffers to small: " + byteCount + offset);
|
|
}
|
|
}
|
|
|
|
|
|
public static void decodeMissing(this ReedSolomon reedSolomon, IByteBuffer[] shards,
|
|
bool[] shardPresent,
|
|
int offset,
|
|
int byteCount)
|
|
{
|
|
// Check arguments.
|
|
reedSolomon.checkBuffersAndSizes(shards, offset, byteCount);
|
|
|
|
// Quick check: are all of the shards present? If so, there's
|
|
// nothing to do.
|
|
int numberPresent = 0;
|
|
for (int i = 0; i < reedSolomon.TotalShardCount; i++)
|
|
{
|
|
if (shardPresent[i])
|
|
{
|
|
numberPresent += 1;
|
|
}
|
|
}
|
|
|
|
if (numberPresent == reedSolomon.TotalShardCount)
|
|
{
|
|
// Cool. All of the shards data data. We don't
|
|
// need to do anything.
|
|
return;
|
|
}
|
|
|
|
// More complete sanity check
|
|
if (numberPresent < reedSolomon.DataShardCount)
|
|
{
|
|
throw new Exception("Not enough shards present");
|
|
}
|
|
|
|
// Pull out the rows of the matrix that correspond to the
|
|
// shards that we have and build a square matrix. This
|
|
// matrix could be used to generate the shards that we have
|
|
// from the original data.
|
|
//
|
|
// Also, pull out an array holding just the shards that
|
|
// correspond to the rows of the submatrix. These shards
|
|
// will be the input to the decoding process that re-creates
|
|
// the missing data shards.
|
|
Matrix subMatrix = new Matrix(reedSolomon.DataShardCount, reedSolomon.DataShardCount);
|
|
IByteBuffer[] subShards = new IByteBuffer[reedSolomon.DataShardCount];
|
|
{
|
|
int subMatrixRow = 0;
|
|
for (int matrixRow = 0;
|
|
matrixRow < reedSolomon.TotalShardCount && subMatrixRow < reedSolomon.DataShardCount;
|
|
matrixRow++)
|
|
{
|
|
if (shardPresent[matrixRow])
|
|
{
|
|
for (int c = 0; c < reedSolomon.DataShardCount; c++)
|
|
{
|
|
subMatrix.set(subMatrixRow, c, reedSolomon.Matrix.get(matrixRow, c));
|
|
}
|
|
|
|
subShards[subMatrixRow] = shards[matrixRow];
|
|
subMatrixRow += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Invert the matrix, so we can go from the encoded shards
|
|
// back to the original data. Then pull out the row that
|
|
// generates the shard that we want to decode. Note that
|
|
// since this matrix maps back to the orginal data, it can
|
|
// be used to create a data shard, but not a parity shard.
|
|
Matrix dataDecodeMatrix = subMatrix.invert();
|
|
|
|
// Re-create any data shards that were missing.
|
|
//
|
|
// The input to the coding is all of the shards we actually
|
|
// have, and the output is the missing data shards. The computation
|
|
// is done using the special decode matrix we just built.
|
|
IByteBuffer[] outputs = new IByteBuffer[reedSolomon.ParityShardCount];
|
|
byte[][] matrixRows = new byte [reedSolomon.ParityShardCount][];
|
|
int outputCount = 0;
|
|
for (int iShard = 0; iShard < reedSolomon.DataShardCount; iShard++)
|
|
{
|
|
if (!shardPresent[iShard])
|
|
{
|
|
outputs[outputCount] = shards[iShard];
|
|
matrixRows[outputCount] = dataDecodeMatrix.getRow(iShard);
|
|
outputCount += 1;
|
|
}
|
|
}
|
|
|
|
LOOP.codeSomeShards(
|
|
matrixRows,
|
|
subShards, reedSolomon.DataShardCount,
|
|
outputs, outputCount,
|
|
offset, byteCount);
|
|
|
|
// Now that we have all of the data shards intact, we can
|
|
// compute any of the parity that is missing.
|
|
//
|
|
// The input to the coding is ALL of the data shards, including
|
|
// any that we just calculated. The output is whichever of the
|
|
// data shards were missing.
|
|
outputCount = 0;
|
|
for (int iShard = reedSolomon.DataShardCount; iShard < reedSolomon.TotalShardCount; iShard++)
|
|
{
|
|
if (!shardPresent[iShard])
|
|
{
|
|
outputs[outputCount] = shards[iShard];
|
|
matrixRows[outputCount] = reedSolomon.ParityRows[iShard - reedSolomon.DataShardCount];
|
|
outputCount += 1;
|
|
}
|
|
}
|
|
|
|
LOOP.codeSomeShards(
|
|
matrixRows,
|
|
shards, reedSolomon.DataShardCount,
|
|
outputs, outputCount,
|
|
offset, byteCount);
|
|
}
|
|
}
|
|
} |