Added test cases for C function appendErrorCorrection().

This commit is contained in:
Project Nayuki 2017-05-06 16:54:44 +00:00
parent d11707d06a
commit a712ccc230
2 changed files with 75 additions and 4 deletions

View File

@ -43,8 +43,11 @@ static int numTestCases = 0;
// Prototypes of private functions under test // Prototypes of private functions under test
extern const int8_t ECC_CODEWORDS_PER_BLOCK[4][41];
extern const int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41];
int getTextProperties(const char *text, bool *isNumeric, bool *isAlphanumeric, int *textBits); int getTextProperties(const char *text, bool *isNumeric, bool *isAlphanumeric, int *textBits);
void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen); void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen);
void appendErrorCorrection(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]);
int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl); int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl);
int getNumRawDataModules(int version); int getNumRawDataModules(int version);
void calcReedSolomonGenerator(int degree, uint8_t result[]); void calcReedSolomonGenerator(int degree, uint8_t result[]);
@ -158,6 +161,73 @@ static void testAppendBitsToBuffer(void) {
} }
// Ported from the Java version of the code.
static uint8_t *appendErrorCorrectionReference(const uint8_t *data, int version, enum qrcodegen_Ecc ecl) {
// Calculate parameter numbers
int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[(int)ecl][version];
int blockEccLen = ECC_CODEWORDS_PER_BLOCK[(int)ecl][version];
int rawCodewords = getNumRawDataModules(version) / 8;
int numShortBlocks = numBlocks - rawCodewords % numBlocks;
int shortBlockLen = rawCodewords / numBlocks;
// Split data into blocks and append ECC to each block
uint8_t **blocks = malloc(numBlocks * sizeof(uint8_t*));
uint8_t *generator = malloc(blockEccLen * sizeof(uint8_t));
calcReedSolomonGenerator(blockEccLen, generator);
for (int i = 0, k = 0; i < numBlocks; i++) {
uint8_t *block = malloc((shortBlockLen + 1) * sizeof(uint8_t));
int blockDataLen = shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1);
memcpy(block, &data[k], blockDataLen * sizeof(uint8_t));
calcReedSolomonRemainder(&data[k], blockDataLen, generator, blockEccLen, &block[shortBlockLen + 1 - blockEccLen]);
k += blockDataLen;
blocks[i] = block;
}
free(generator);
// Interleave (not concatenate) the bytes from every block into a single sequence
uint8_t *result = malloc(rawCodewords * sizeof(uint8_t));
for (int i = 0, k = 0; i < shortBlockLen + 1; i++) {
for (int j = 0; j < numBlocks; j++) {
// Skip the padding byte in short blocks
if (i != shortBlockLen - blockEccLen || j >= numShortBlocks) {
result[k] = blocks[j][i];
k++;
}
}
}
for (int i = 0; i < numBlocks; i++)
free(blocks[i]);
free(blocks);
return result;
}
static void testAppendErrorCorrection(void) {
for (int version = 1; version <= 40; version++) {
for (int ecl = 0; ecl < 4; ecl++) {
int dataLen = getNumDataCodewords(version, (enum qrcodegen_Ecc)ecl);
uint8_t *pureData = malloc(dataLen * sizeof(uint8_t));
for (int i = 0; i < dataLen; i++)
pureData[i] = rand() % 256;
uint8_t *expectOutput = appendErrorCorrectionReference(pureData, version, (enum qrcodegen_Ecc)ecl);
int dataAndEccLen = getNumRawDataModules(version) / 8;
uint8_t *paddedData = malloc(dataAndEccLen * sizeof(uint8_t));
memcpy(paddedData, pureData, dataLen * sizeof(uint8_t));
uint8_t *actualOutput = malloc(dataAndEccLen * sizeof(uint8_t));
appendErrorCorrection(paddedData, version, (enum qrcodegen_Ecc)ecl, actualOutput);
assert(memcmp(actualOutput, expectOutput, dataAndEccLen * sizeof(uint8_t)) == 0);
free(pureData);
free(expectOutput);
free(paddedData);
free(actualOutput);
numTestCases++;
}
}
}
static void testGetNumDataCodewords(void) { static void testGetNumDataCodewords(void) {
int cases[][3] = { int cases[][3] = {
{ 3, 1, 44}, { 3, 1, 44},
@ -492,6 +562,7 @@ int main(void) {
srand(time(NULL)); srand(time(NULL));
testGetTextProperties(); testGetTextProperties();
testAppendBitsToBuffer(); testAppendBitsToBuffer();
testAppendErrorCorrection();
testGetNumDataCodewords(); testGetNumDataCodewords();
testGetNumRawDataModules(); testGetNumRawDataModules();
testCalcReedSolomonGenerator(); testCalcReedSolomonGenerator();

View File

@ -60,7 +60,7 @@ static void encodeQrCodeTail(uint8_t dataAndQrcode[], int bitLen, uint8_t tempBu
int version, enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, bool boostEcl); int version, enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, bool boostEcl);
testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen); testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen);
static void appendErrorCorrection(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]); testable void appendErrorCorrection(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]);
testable int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl); testable int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl);
testable int getNumRawDataModules(int version); testable int getNumRawDataModules(int version);
@ -90,7 +90,7 @@ testable void setModuleBounded(uint8_t qrcode[], int x, int y, bool isBlack);
static const char *ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; static const char *ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:";
// For generating error correction codes. // For generating error correction codes.
static const int8_t ECC_CODEWORDS_PER_BLOCK[4][41] = { testable const int8_t ECC_CODEWORDS_PER_BLOCK[4][41] = {
// Version: (note that index 0 is for padding, and is set to an illegal value) // Version: (note that index 0 is for padding, and is set to an illegal value)
//0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
{-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Low {-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Low
@ -100,7 +100,7 @@ static const int8_t ECC_CODEWORDS_PER_BLOCK[4][41] = {
}; };
// For generating error correction codes. // For generating error correction codes.
const int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41] = { testable const int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41] = {
// Version: (note that index 0 is for padding, and is set to an illegal value) // Version: (note that index 0 is for padding, and is set to an illegal value)
//0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level //0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
{-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low {-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low
@ -356,7 +356,7 @@ testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[]
// from the blocks and stores them in the result array. data[0 : rawCodewords - totalEcc] contains // from the blocks and stores them in the result array. data[0 : rawCodewords - totalEcc] contains
// the input data. data[rawCodewords - totalEcc : rawCodewords] is used as a temporary work area // the input data. data[rawCodewords - totalEcc : rawCodewords] is used as a temporary work area
// and will be clobbered by this function. The final answer is stored in result[0 : rawCodewords]. // and will be clobbered by this function. The final answer is stored in result[0 : rawCodewords].
static void appendErrorCorrection(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]) { testable void appendErrorCorrection(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]) {
// Calculate parameter numbers // Calculate parameter numbers
assert(0 <= (int)ecl && (int)ecl < 4 && qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); assert(0 <= (int)ecl && (int)ecl < 4 && qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX);
int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[(int)ecl][version]; int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[(int)ecl][version];