From cb6835fecbfbebdcc723e5156c70a7a1dd569273 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Thu, 20 Apr 2017 05:36:03 +0000 Subject: [PATCH] Updated C code to consolidate logic for finding minimum fitting version number, also tweaked arithmetic and control flow. --- c/qrcodegen.c | 78 +++++++++++++++++++++++++-------------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index c363c6a..08c3ef1 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -31,6 +31,7 @@ /*---- Forward declarations for private functions ----*/ +static int fitVersionToData(int minVersion, int maxVersion, enum qrcodegen_Ecc ecl, int dataLen, int dataBitLen, int ver1To9LenBits, int ver10To26LenBits, int ver27To40LenBits); static void encodeQrCodeTail(uint8_t dataAndQrcode[], int bitLen, uint8_t tempBuffer[], int version, enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, bool boostEcl); static long getPenaltyScore(const uint8_t qrcode[], int qrsize); static void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen); @@ -128,31 +129,14 @@ int qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode[ return qrcodegen_encodeBinary(tempBuffer, (size_t)textLen, qrcode, ecl, minVersion, maxVersion, mask, boostEcl); } - int version; - int dataUsedBits = -1; - int dataCapacityBits = -1; - int lengthBits = -1; - for (version = minVersion; ; version++) { - if (version <= 9) - lengthBits = isNumeric ? 10 : 9; - else if (version <= 26) - lengthBits = isNumeric ? 12 : 11; - else - lengthBits = isNumeric ? 14 : 13; - if (textLen < (1 << lengthBits)) { - dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available - dataUsedBits = checkedAdd(4 + lengthBits, textBits); - if (0 <= dataUsedBits && dataUsedBits <= dataCapacityBits) - break; // This version number is found to be suitable - } - if (version >= maxVersion) // All versions in the range could not fit the given data - return 0; - } - assert(dataUsedBits >= 0 && dataCapacityBits >= 0); - + int version = fitVersionToData(minVersion, maxVersion, ecl, textLen, textBits, + (isNumeric ? 10 : 9), (isNumeric ? 12 : 11), (isNumeric ? 14 : 13)); + if (version == 0) + return 0; memset(qrcode, 0, qrcodegen_BUFFER_LEN_FOR_VERSION(version) * sizeof(qrcode[0])); int bitLen = 0; appendBitsToBuffer(isNumeric ? 1 : 2, 4, qrcode, &bitLen); + int lengthBits = (version <= 9 ? 9 : (version <= 26 ? 11 : 13)) + (isNumeric ? 1 : 0); appendBitsToBuffer((unsigned int)textLen, lengthBits, qrcode, &bitLen); if (isNumeric) { int accumData = 0; @@ -193,25 +177,13 @@ int qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcode enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl) { assert(qrcodegen_VERSION_MIN <= minVersion && minVersion <= maxVersion && maxVersion <= qrcodegen_VERSION_MAX); assert(0 <= (int)ecl && (int)ecl <= 3 && -1 <= (int)mask && (int)mask <= 7); - if (dataLen > INT16_MAX) + if (dataLen > INT16_MAX / 8) return 0; - // Now dataLen <= INT_MAX, since int has at least 16 bits - - int version; - int dataUsedBits = -1; - int dataCapacityBits = -1; - for (version = minVersion; ; version++) { - if ((version <= 9 && dataLen < (1U << 8)) || dataLen < (1U << 16)) { - dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available - dataUsedBits = checkedAdd(4 + (version <= 9 ? 8 : 16), checkedMultiply((int)dataLen, 8)); - if (0 <= dataUsedBits && dataUsedBits <= dataCapacityBits) - break; // This version number is found to be suitable - } - if (version >= maxVersion) // All versions in the range could not fit the given data - return 0; - } - assert(dataUsedBits >= 0 && dataCapacityBits >= 0); + // Now dataLen * 8 <= 65535 <= INT_MAX + int version = fitVersionToData(minVersion, maxVersion, ecl, (int)dataLen, (int)dataLen * 8, 8, 16, 16); + if (version == 0) + return 0; memset(qrcode, 0, qrcodegen_BUFFER_LEN_FOR_VERSION(version) * sizeof(qrcode[0])); int bitLen = 0; appendBitsToBuffer(4, 4, qrcode, &bitLen); @@ -223,6 +195,34 @@ int qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcode } +static int fitVersionToData(int minVersion, int maxVersion, enum qrcodegen_Ecc ecl, + int dataLen, int dataBitLen, int ver1To9LenBits, int ver10To26LenBits, int ver27To40LenBits) { + + assert(qrcodegen_VERSION_MIN <= minVersion && minVersion <= maxVersion && maxVersion <= qrcodegen_VERSION_MAX); + assert(0 <= (int)ecl && (int)ecl <= 3); + assert(dataLen >= 0 && dataBitLen >= 0); + assert(1 <= ver1To9LenBits && ver1To9LenBits <= 16); + assert(1 <= ver10To26LenBits && ver10To26LenBits <= 16); + assert(1 <= ver27To40LenBits && ver27To40LenBits <= 16); + + for (int version = minVersion; ; version++) { + int lengthBits; + if (version <= 9) lengthBits = ver1To9LenBits; + else if (version <= 26) lengthBits = ver10To26LenBits; + else lengthBits = ver27To40LenBits; + + if (dataLen >= (1L << lengthBits)) + continue; + int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available + int dataUsedBits = checkedAdd(4 + lengthBits, dataBitLen); + if (0 <= dataUsedBits && dataUsedBits <= dataCapacityBits) + return version; // This version number is found to be suitable + if (version >= maxVersion) // All versions in the range could not fit the given data + return 0; + } +} + + // Given data codewords in dataAndQrcode already padded to the length specified by the // version and ECC level, this function adds ECC bytes, interleaves blocks, renders the // QR Code symbol back to the array dataAndQrcode, and handles automatic mask selection.