Skip to content

Commit

Permalink
A precise boundary check has been introduced to QRinput_estimateVersi…
Browse files Browse the repository at this point in the history
…on(). (closes fukuchi#160)
  • Loading branch information
fukuchi committed Aug 28, 2020
1 parent 110884e commit 1fbb517
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 3 deletions.
13 changes: 10 additions & 3 deletions qrinput.c
Original file line number Diff line number Diff line change
Expand Up @@ -896,7 +896,11 @@ static int QRinput_estimateBitStreamSizeOfEntry(QRinput_List *entry, int version
} else {
l = QRspec_lengthIndicator(entry->mode, version);
m = 1 << l;
num = (entry->size + m - 1) / m;
if(entry->mode == QR_MODE_KANJI) {
num = (entry->size/2 + m - 1) / m;
} else {
num = (entry->size + m - 1) / m;
}

bits += num * (MODE_INDICATOR_SIZE + l);
}
Expand Down Expand Up @@ -927,9 +931,9 @@ STATIC_IN_RELEASE int QRinput_estimateBitStreamSize(QRinput *input, int version)
/**
* Estimate the required version number of the symbol.
* @param input input data
* @return required version number
* @return required version number or -1 for failure.
*/
static int QRinput_estimateVersion(QRinput *input)
STATIC_IN_RELEASE int QRinput_estimateVersion(QRinput *input)
{
int bits;
int version, prev;
Expand All @@ -939,6 +943,9 @@ static int QRinput_estimateVersion(QRinput *input)
prev = version;
bits = QRinput_estimateBitStreamSize(input, prev);
version = QRspec_getMinimumVersion((bits + 7) / 8, input->level);
if(prev == 0 && version > 1) {
version--;
}
} while (version > prev);

return version;
Expand Down
1 change: 1 addition & 0 deletions qrinput.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ extern int QRinput_mergeBitStream(QRinput *input, BitStream *bstream);
extern int QRinput_getBitStream(QRinput *input, BitStream *bstream);
extern int QRinput_estimateBitStreamSize(QRinput *input, int version);
extern int QRinput_splitEntry(QRinput_List *entry, int bytes);
extern int QRinput_estimateVersion(QRinput *input);
extern int QRinput_lengthOfCode(QRencodeMode mode, int version, int bits);
extern int QRinput_insertStructuredAppendHeader(QRinput *input, int size, int index, unsigned char parity);
#endif
Expand Down
29 changes: 29 additions & 0 deletions tests/test_qrencode.c
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,34 @@ static void test_encodeLongData(void)
testFinish();
}

static void test_encodeVer26Num(void)
{
char data[3284];
QRcode *qrcode;

testStart("Encoding 3283 digits number. (issue #160)");

memset(data, '0', 3283);
data[3283] = '\0';
qrcode = QRcode_encodeString(data, 0, QR_ECLEVEL_L, QR_MODE_8, 0);
assert_nonnull(qrcode, "(QRcode_encodeString) failed to encode 3283 digits number in level L.\n");
assert_equal(qrcode->version, 26, "version number is %d (26 expected)\n", qrcode->version);
if(qrcode != NULL) {
QRcode_free(qrcode);
}

QRinput *input;
QRinput_List *list;

input = QRinput_new2(0, QR_ECLEVEL_L);
Split_splitStringToQRinput(data, input, QR_MODE_8, 0);
list = input->head;
assert_equal(list->size, 3283, "chunk size is wrong. (%d, 3283 expected)\n", list->size);
QRinput_free(input);

testFinish();
}

static void test_01234567(void)
{
QRinput *stream;
Expand Down Expand Up @@ -961,6 +989,7 @@ int main(int argc, char **argv)
test_encodeNull8();
test_encodeEmpty8();
test_encodeLongData();
test_encodeVer26Num();
test_01234567();
test_invalid_input();
test_struct_example();
Expand Down
98 changes: 98 additions & 0 deletions tests/test_qrinput.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,50 @@
#include "../split.h"
#include "decoder.h"

/* taken from the Table 7 of JIS X0510:2018 (pp. 31-34) */
static int maxCharacterLengths[40][4] = {
{ 41, 25, 17, 10}, /* ver 1 */
{ 77, 47, 32, 20}, /* ver 2 */
{ 127, 77, 53, 32}, /* ver 3 */
{ 187, 114, 78, 48}, /* ver 4 */
{ 255, 154, 106, 65}, /* ver 5 */
{ 322, 195, 134, 82}, /* ver 6 */
{ 370, 224, 154, 95}, /* ver 7 */
{ 461, 279, 192, 118}, /* ver 8 */
{ 552, 335, 230, 141}, /* ver 9 */
{ 652, 395, 271, 167}, /* ver 10 */
{ 772, 468, 321, 198}, /* ver 11 */
{ 883, 535, 367, 226}, /* ver 12 */
{1022, 619, 425, 262}, /* ver 13 */
{1101, 667, 458, 282}, /* ver 14 */
{1250, 758, 520, 320}, /* ver 15 */
{1408, 854, 586, 361}, /* ver 16 */
{1548, 938, 644, 397}, /* ver 17 */
{1725, 1046, 718, 442}, /* ver 18 */
{1903, 1153, 792, 488}, /* ver 19 */
{2061, 1249, 858, 528}, /* ver 20 */
{2232, 1352, 929, 572}, /* ver 21 */
{2409, 1460, 1003, 618}, /* ver 22 */
{2620, 1588, 1091, 672}, /* ver 23 */
{2812, 1704, 1171, 721}, /* ver 24 */
{3057, 1853, 1273, 784}, /* ver 25 */
{3283, 1990, 1367, 842}, /* ver 26 */
{3517, 2132, 1465, 902}, /* ver 27 */
{3669, 2223, 1528, 940}, /* ver 28 */
{3909, 2369, 1628, 1002}, /* ver 29 */
{4158, 2520, 1732, 1066}, /* ver 30 */
{4417, 2677, 1840, 1132}, /* ver 31 */
{4686, 2840, 1952, 1201}, /* ver 32 */
{4965, 3009, 2068, 1273}, /* ver 33 */
{5253, 3183, 2188, 1347}, /* ver 34 */
{5529, 3351, 2303, 1417}, /* ver 35 */
{5836, 3537, 2431, 1496}, /* ver 36 */
{6153, 3729, 2563, 1577}, /* ver 37 */
{6479, 3927, 2699, 1661}, /* ver 38 */
{6743, 4087, 2809, 1729}, /* ver 39 */
{7089, 4296, 2953, 1817} /* ver 40 */
};

static int encodeAndCheckBStream(int mqr, int version, QRecLevel level, QRencodeMode mode, char *data, char *correct)
{
QRinput *input;
Expand Down Expand Up @@ -791,6 +835,59 @@ static void test_null_free(void)
testFinish();
}

static void fillCharacter(char *dest, char ch, int size)
{
memset(dest, ch, size);
dest[size] = '\0';
}

static void checkEstimatedVersion(int ver, int mode)
{
int estimatedVersion;
char data[7200];
QRinput *input;
QRencodeMode hint;
int size1, size2;
static char *modeStr[4] = {"numeric", "alphanumeric", "8 bit data", "kanji"};
static char ch[4] = {'0', 'A', 'a', '\x92'};

if(mode == QR_MODE_KANJI) {
hint = QR_MODE_KANJI;
size1 = maxCharacterLengths[ver - 1][mode] * 2;
size2 = size1 + 2;
} else {
hint = QR_MODE_8;
size1 = maxCharacterLengths[ver - 1][mode];
size2 = size1 + 1;
}

fillCharacter(data, ch[mode], size1);
input = QRinput_new2(0, QR_ECLEVEL_L);
Split_splitStringToQRinput(data, input, hint, 1);
estimatedVersion = QRinput_estimateVersion(input);
assert_equal(estimatedVersion, ver, "Estimated version %d is not equal to the expected version %d for %d %s sequence.\n", estimatedVersion, ver, maxCharacterLengths[ver - 1][mode], modeStr[mode]);
QRinput_free(input);

fillCharacter(data, ch[mode], size2);
input = QRinput_new2(0, QR_ECLEVEL_L);
Split_splitStringToQRinput(data, input, hint, 1);
estimatedVersion = QRinput_estimateVersion(input);
assert_equal(estimatedVersion, ver + 1, "Estimated version %d is not equal to the expected version %d for %d %s sequence.\n", estimatedVersion, ver, maxCharacterLengths[ver - 1][mode] + 1, modeStr[mode]);
QRinput_free(input);
}

static void test_estimateVersionBoundaryCheck(void)
{
testStart("Boundary check of estimateVersion");
for(int ver=1; ver < QRSPEC_VERSION_MAX; ver++) {
checkEstimatedVersion(ver, QR_MODE_NUM);
checkEstimatedVersion(ver, QR_MODE_AN);
checkEstimatedVersion(ver, QR_MODE_8);
checkEstimatedVersion(ver, QR_MODE_KANJI);
}
testFinish();
}

static void test_mqr_new(void)
{
QRinput *input;
Expand Down Expand Up @@ -976,6 +1073,7 @@ int main()
test_parity();
test_parity2();
test_null_free();
test_estimateVersionBoundaryCheck();

test_mqr_new();
test_mqr_setversion();
Expand Down

0 comments on commit 1fbb517

Please sign in to comment.