upm_guru_kcp/Runtime/csharp-kcp/base-kcp/fec/FecExpansion.cs

188 lines
7.4 KiB
C#
Raw Permalink Normal View History

2023-08-30 05:50:21 +00:00
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);
}
}
}