202 lines
6.1 KiB
C#
202 lines
6.1 KiB
C#
|
|
using System;
|
||
|
|
using System.Collections.Generic;
|
||
|
|
|
||
|
|
namespace fec
|
||
|
|
{
|
||
|
|
public class ReedSolomonTest
|
||
|
|
{
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Try encoding and decoding with a lot of shards.
|
||
|
|
*/
|
||
|
|
public void testBigEncodeDecode() {
|
||
|
|
Random random = new Random(0);
|
||
|
|
for (int k = 0; k < 1000; k++)
|
||
|
|
{
|
||
|
|
int dataCount = 64;
|
||
|
|
int parityCount = 64;
|
||
|
|
int shardSize = 200;
|
||
|
|
byte [] [] dataShards = new byte [dataCount] [];
|
||
|
|
for (var j = 0; j < dataShards.Length; j++)
|
||
|
|
{
|
||
|
|
var shard = dataShards[j] = new byte[shardSize];
|
||
|
|
for (int i = 0; i < shard.Length; i++) {
|
||
|
|
shard[i] = (byte) random.Next(256);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
runEncodeDecode(dataCount, parityCount, dataShards);
|
||
|
|
}
|
||
|
|
|
||
|
|
Console.WriteLine("测试完成");
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Encodes a set of data shards, and then tries decoding
|
||
|
|
* using all possible subsets of the encoded shards.
|
||
|
|
*
|
||
|
|
* Uses 5+5 coding, so there must be 5 input data shards.
|
||
|
|
*/
|
||
|
|
private void runEncodeDecode(int dataCount, int parityCount, byte[][] dataShards) {
|
||
|
|
|
||
|
|
int totalCount = dataCount + parityCount;
|
||
|
|
int shardLength = dataShards[0].Length;
|
||
|
|
|
||
|
|
// Make the list of data and parity shards.
|
||
|
|
// assertEquals(dataCount, dataShards.length);
|
||
|
|
int dataLength = dataShards[0].Length;
|
||
|
|
byte [] [] allShards = new byte [totalCount] [];
|
||
|
|
for (int i = 0; i < dataCount; i++) {
|
||
|
|
byte[] temp = new byte[dataLength];
|
||
|
|
Array.Copy(dataShards[i],0,temp,0,dataLength);
|
||
|
|
allShards[i] = temp;
|
||
|
|
}
|
||
|
|
for (int i = dataCount; i < totalCount; i++) {
|
||
|
|
allShards[i] = new byte [dataLength];
|
||
|
|
}
|
||
|
|
|
||
|
|
// Encode.
|
||
|
|
ReedSolomon codec = ReedSolomon.create(dataCount, parityCount);
|
||
|
|
codec.encodeParity(allShards, 0, dataLength);
|
||
|
|
|
||
|
|
// Make a copy to decode with.
|
||
|
|
byte [] [] testShards = new byte [totalCount] [];
|
||
|
|
bool [] shardPresent = new bool [totalCount];
|
||
|
|
for (int i = 0; i < totalCount; i++) {
|
||
|
|
byte[] temp = new byte[shardLength];
|
||
|
|
Array.Copy(allShards[i],0,temp,0,shardLength);
|
||
|
|
testShards[i] = temp;
|
||
|
|
shardPresent[i] = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Decode with 0, 1, ..., 5 shards missing.
|
||
|
|
for (int numberMissing = 0; numberMissing < parityCount + 1; numberMissing++) {
|
||
|
|
tryAllSubsetsMissing(codec, allShards, testShards, shardPresent, numberMissing);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private void tryAllSubsetsMissing(ReedSolomon codec,
|
||
|
|
byte [] [] allShards, byte [] [] testShards,
|
||
|
|
bool [] shardPresent, int numberMissing) {
|
||
|
|
int shardLength = allShards[0].Length;
|
||
|
|
List<int []> subsets = allSubsets(numberMissing, 0, 10);
|
||
|
|
foreach (var subset in subsets) {
|
||
|
|
// Get rid of the shards specified by this subset.
|
||
|
|
foreach (var missingShard in subset)
|
||
|
|
{
|
||
|
|
clearBytes(testShards[missingShard]);
|
||
|
|
shardPresent[missingShard] = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Reconstruct the missing shards
|
||
|
|
codec.decodeMissing(testShards, shardPresent, 0, shardLength);
|
||
|
|
|
||
|
|
// Check the results. After checking, the contents of testShards
|
||
|
|
// is ready for the next test, the next time through the loop.
|
||
|
|
checkShards(allShards, testShards);
|
||
|
|
|
||
|
|
// Put the "present" flags back
|
||
|
|
for (int i = 0; i < codec.getTotalShardCount(); i++) {
|
||
|
|
shardPresent[i] = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
private void assertTrue(bool isParityCorrect)
|
||
|
|
{
|
||
|
|
throw new NotImplementedException();
|
||
|
|
}
|
||
|
|
|
||
|
|
private void assertFalse(bool isParityCorrect)
|
||
|
|
{
|
||
|
|
throw new NotImplementedException();
|
||
|
|
}
|
||
|
|
|
||
|
|
private void clearBytes(byte [] data) {
|
||
|
|
for (int i = 0; i < data.Length; i++) {
|
||
|
|
data[i] = 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private void checkShards(byte[][] expectedShards, byte[][] actualShards) {
|
||
|
|
assertEquals(expectedShards.Length, actualShards.Length);
|
||
|
|
for (int i = 0; i < expectedShards.Length; i++) {
|
||
|
|
assertArrayEquals(expectedShards[i], actualShards[i]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private void assertArrayEquals(byte[] expectedShard, byte[] actualShard)
|
||
|
|
{
|
||
|
|
|
||
|
|
var len1 = expectedShard.Length;
|
||
|
|
var len2 = actualShard.Length;
|
||
|
|
if (len1 != len2)
|
||
|
|
{
|
||
|
|
throw new NotImplementedException();
|
||
|
|
}
|
||
|
|
for (var i = 0; i < len1; i++)
|
||
|
|
{
|
||
|
|
if (expectedShard[i] != actualShard[i])
|
||
|
|
{
|
||
|
|
throw new NotImplementedException();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private void assertEquals(int expectedShardsLength, int actualShardsLength)
|
||
|
|
{
|
||
|
|
if (expectedShardsLength != actualShardsLength)
|
||
|
|
{
|
||
|
|
throw new NotImplementedException();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Returns a list of arrays with all possible sets of
|
||
|
|
* unique values where (min <= n < max).
|
||
|
|
*
|
||
|
|
* This is NOT EFFICIENT, because it allocates lots of
|
||
|
|
* temporary arrays, but it's OK for these tests.
|
||
|
|
*
|
||
|
|
* To avoid duplicates that are in a different order,
|
||
|
|
* each subset is generated with elements in increasing
|
||
|
|
* order.
|
||
|
|
*
|
||
|
|
* Given (n=2, min=1, max=4), returns:
|
||
|
|
* [1, 2]
|
||
|
|
* [1, 3]
|
||
|
|
* [1, 4]
|
||
|
|
* [2, 3]
|
||
|
|
* [2, 4]
|
||
|
|
* [3, 4]
|
||
|
|
*/
|
||
|
|
private List<int []> allSubsets(int n, int min, int max) {
|
||
|
|
List<int []> result = new List<int[]>();
|
||
|
|
if (n == 0) {
|
||
|
|
result.Add(new int [0]);
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
for (int i = min; i < max - n; i++) {
|
||
|
|
int [] prefix = { i };
|
||
|
|
foreach (var suffix in allSubsets(n - 1, i + 1, max))
|
||
|
|
{
|
||
|
|
result.Add(appendIntArrays(prefix, suffix));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
private int [] appendIntArrays(int [] a, int [] b) {
|
||
|
|
int [] result = new int[a.Length + b.Length];
|
||
|
|
Array.Copy(a, 0, result, 0, a.Length);
|
||
|
|
Array.Copy(b, 0, result, a.Length, b.Length);
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|