/// <summary> /// Encode the length of the packets that are in the data BufferChunk array and place the /// encoded length over GF16 in the 2 first byte of the checksum packets that are in /// the result BufferChunk array /// </summary> /// <param name="data">The BufferChunk array containing the data packets to get the size</param> /// <param name="checksum">The number of checksum packets to generate. /// This number is actually also the number of column of the Vandermonde /// encoding matrix</param> /// <param name="encode">The Vandermonde encoding matrix (size min should be /// Vandermonde #column: checksum, Vandermonde #row: data.Length) /// </param> /// <param name="result">The BufferChunk checksum packets array of containing the encoded length (over GF16) /// in the first 2 bytes</param> private static void EncodeRSLength(BufferChunk[] data, BufferChunk[] result, UInt16[,] encode) { // Get the length once to avoid having the overhead of getting it again inside a inner (performance) // Note that this value is also used outside of this method, so we could have had a parameter too, // but I prefer to get it here to avoid inconsistencies int dataPackets = data.Length; int checksum = result.Length; // Note: The size of the Vandermonde matrix used to encode the length is // Vandermonde #column: checksum, Vandermonde #row: dataPackets // TODO: Validate if Vandermonde # max column >= checksum and // Vandermonde #row >= dataPackets // TODO: Validate that there are no null entries in data and result // TODO: Validate that all the checksum packet have at least 2 bytes // Length encoding for (int encodeColumn = 0; encodeColumn < checksum; encodeColumn++) { UInt16 encodeValue = 0; for (int dataPacket = 0; dataPacket < dataPackets; dataPacket++) { // TODO: performance - lengths of BC don't change between loops, store once? JVE UInt16 dataValue = GF16.Multiply((UInt16)data[dataPacket].Length, encode[encodeColumn, dataPacket]); encodeValue = GF16.Add(encodeValue, dataValue); } result[encodeColumn] += encodeValue; } }
/// <summary> /// Decode to retrieve the missing data packet(s) (null entries) in the data BufferChunk array and place the /// decoded result over GF16 in order in the recovery BufferChunk array /// </summary> /// <param name="data">The data BufferChunk array</param> /// <param name="checksum">The number of checksum packet(s) that were used to encode</param> /// <param name="decode">The decoding GF16 matrix</param> /// <param name="recovery">The recovery BufferChunk array to place the recovered result</param> private static void DecodeRSData(BufferChunk[] data, int checksum, GF16[,] decode, BufferChunk[] recovery) { // Important! For now I assume the following in the firstMatrix: // - The array is always of size data + checksum // - #checksum not null entries = #packet lost // - recovery.length is exactly the number of data packet lost // - checksum param is # checksum packets that were used to encode // Reconstruct data // Get the length once to avoid having the overhead of getting it again inside a inner (performance) // Note that this value is also used outside of this method, so we could have had a parameter too, // but I prefer to get it here to avoid inconsitencies int dataLength = data.Length; // Scan column after column int recoveryColumn = 0; for (int dataColumn = 0; dataColumn < dataLength - checksum; dataColumn++) { // recover only the missing column if (data[dataColumn] == null) { int recoveryRowLength = recovery[recoveryColumn].Length; // For each column fill out the output mtx row after row for (int recoveryRow = 0; recoveryRow < recoveryRowLength; recoveryRow += 2) { int imRowIndex = 0; // nnDataColumn - not null data index for (int nnDataColumn = 0; nnDataColumn < dataLength; nnDataColumn++) { // Perform the core operation mult with add in GF16 // TODO: We could crunch the column so we avoid this test if (data[nnDataColumn] != null) { if (recoveryRow < data[nnDataColumn].Length) { UInt16 currentValue = GF16.Multiply(data[nnDataColumn].GetUInt16(recoveryRow), decode[dataColumn, imRowIndex].Value); currentValue = GF16.Add(recovery[recoveryColumn].GetUInt16(recoveryRow), currentValue); recovery[recoveryColumn].SetUInt16(recoveryRow, currentValue); } imRowIndex++; } } } recoveryColumn++; } } }
/// <summary> /// Create a Vandermonde matrix of size row x column over GF16 /// </summary> /// <remarks> /// The Vandermonde matrix is typically used to create the encoding matrix where: /// - The number of Columns of the matrix correspond to number of checksum /// packets. /// - The number of Rows of the matrix correspond to number of data packets. /// </remarks> /// <param name="columns">The number of columns of the Vandermonde matrix</param> /// <param name="rows">The number of rows of the Vandermode matrix</param> /// <returns></returns> public static UInt16[,] CreateVandermondeMatrix(int columns, int rows) { // TODO: Add input validation // maxChecksumPackets will be the max number of Columns of the encoding static matrix // maxDataPackets will be the max number of Rows of the encoding static matrix UInt16[,] vandermondeMtx = new UInt16[columns, rows]; // Creation of the Vandermonde Matrix over GF16 with the following // (2^column)^row // As an example, a 5 x 3 Vandermonde Matrix over GF16 (5 data packets, 3 checksum packets) // would give the following: // // 1^0 2^0 4^0 // 1^1 2^1 4^1 // 1^2 2^2 4^2 // 1^3 2^3 4^3 // 1^4 2^4 4^4 // // Which gives: // // 1 1 1 // 1 2 4 // 1 4 16 // 1 8 64 // 1 16 256 for (int col = 0; col < columns; col++) { // multFactor is the number to multiply to get the value in the next row // for a given column of the Vandermonde matrix UInt16 multFactor = GF16.Power(2, (uint)col); for (int row = 0; row < rows; row++) { if (row == 0) { // Special case the first row (power of zero) vandermondeMtx[col, row] = 1; } else { // Each element of the Vandermonde matrix is calculated as (2^column)^row over GF16 // This algorithm uses the previous row to compute the next one to improve // the performances (instead of recalculating (2^column)^row) vandermondeMtx[col, row] = GF16.Multiply(vandermondeMtx[col, row - 1], multFactor); } } } return(vandermondeMtx); }
/// <summary> /// Encode the packets that are in the data BufferChunk array and place the /// encoded result over GF16 in the checksum packets that are in /// the result BufferChunk array /// </summary> /// <param name="data">The BufferChunk array containing the data packets</param> /// <param name="checksum">The number of checksum packets to generate. /// This number is actually also the number of column of the Vandermonde /// encoding matrix</param> /// <param name="checksum">The BufferChunk checksum packets array of containing the encoded data (over GF16) /// after the first 2 bytes (the first 2 bytes are used to encode the length)</param> /// <param name="encode">The Vandermonde encoding matrix (size min should be /// Vandermonde #column: checksum, Vandermonde #row: data.Length) /// </param> /// <param name="checksumRowsInt16">The number of row (in int 16 chunks) of the checksum packets</param> private static void EncodeRSData(BufferChunk[] data, BufferChunk[] checksum, UInt16[,] encode, int maxDataLength) { // Note: The size of the Vandermonde matrix used to encode the length is // Vandermonde #column: checksumLength, Vandermonde #row: dataPackets // TODO: Validate if Vandermonde # max column >= checksum and // Vandermonde #row >= dataPackets // TODO: Validate that there are no null entries in data and checksum // Get the length once to avoid having the overhead of getting it again inside a inner (performance) int dataPackets = data.Length; int checksumLength = checksum.Length; // Note that we scan column after column, so we generate the checksum packets // one after the other for (int checksumColumn = 0; checksumColumn < checksumLength; checksumColumn++) { // For each column fill out the checksum mtx row after row for (int checksumRow = 0; checksumRow < maxDataLength; checksumRow += 2) { for (int encodeRow = 0; encodeRow < dataPackets; encodeRow++) { // If we pass the size of the current data packet, so no operation are required // if ((checksumRow) < data[encodeRow].Length) { UInt16 currentValue = GF16.Multiply(data[encodeRow].GetUInt16(checksumRow), encode[checksumColumn, encodeRow]); currentValue = GF16.Add(checksum[checksumColumn].GetUInt16(checksumRow), currentValue); checksum[checksumColumn].SetUInt16(checksumRow, currentValue); } } } } }
/// <summary> /// Decode to retrieve the length of the missing data packet(s) (null entries) in the /// data BufferChunk array and set the length of the packets in the recovery BufferChunk array. /// We have to do that because every data packet could have a different length. /// </summary> /// <param name="data">The data BufferChunk array</param> /// <param name="checksum">The number of checksum packet(s) that were used to encode</param> /// <param name="decode">The decoding GF16 matrix</param> /// <param name="recovery">The recovery BufferChunk array to set the length of the recovered packets</param> private static void DecodeRSLength(BufferChunk[] data, int checksum, GF16[,] decode, BufferChunk[] recovery) { // Important! For now I assume the following in the firstMatrix: // - The array is always of size data + checksum // - #checksum not null entries = #packet lost // - recovery.length is exactly the number of data packet lost // - checksum param is # checksum packets that were used to encode // Get the length once to avoid having the overhead of getting it again inside a inner (performance) // Note that this value is also used outside of this method, so we could have had a parameter too, // but I prefer to get it here to avoid inconsitencies int dataLength = data.Length; int recoveryColumn = 0; for (int dataColumn = 0; dataColumn < dataLength - checksum; dataColumn++) { // recover only the missing column if (data[dataColumn] == null) { // Inverted matrix row index int imRowIndex = 0; // length of the recovered buffer UInt16 currentLength = 0; for (int dataIndex = 0; dataIndex < dataLength; dataIndex++) { // Perform the core operation mult with add in GF16 // TODO: We could crunch the column so we avoid this test if (data[dataIndex] != null) { UInt16 length = 0; if (dataIndex < dataLength - checksum) // For the data part, we get the length of the BufferChunk { length = (UInt16)data[dataIndex].Length; } else // For the checksum part, the encoded length is inside the first 2 bytes { length = data[dataIndex].GetUInt16(0); } UInt16 currentValue = GF16.Multiply(length, decode[dataColumn, imRowIndex].Value); currentLength = GF16.Add(currentLength, currentValue); imRowIndex++; } // if } // for column (do elementary operations) BufferChunk bc = recovery[recoveryColumn]; bc.Reset(bc.Index, currentLength); // Reset all of the recovery packets so they have no data // TODO - Temporary workaround for column based approach - JVE 7/6/2004 bc.Clear(); recoveryColumn++; } // if } // for dataColumn }
/// <summary> /// Overload the * operator /// </summary> /// <param name="a">First operande</param> /// <param name="b">Second operande</param> /// <returns>Multiplication of the 2 Galois Fields</returns> public static GF16 operator *(GF16 a, GF16 b) { return(GF16.Multiply(a.Value, b.Value)); }