From 7dbfabd3883876d30e2a2a80294b818a74b741bc Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 8 Oct 2018 18:01:14 +0000 Subject: [PATCH] Reordered functions and sections in C code for better flow for human readers, without changing functionality. --- c/qrcodegen.c | 183 +++++++++++++++++++++++++------------------------- c/qrcodegen.h | 74 ++++++++++---------- 2 files changed, 131 insertions(+), 126 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index a2d0517..8f6de55 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -187,6 +187,99 @@ testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[] +/*---- Low-level QR Code encoding functions ----*/ + +// Public function - see documentation comment in header file. +bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, + enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]) { + return qrcodegen_encodeSegmentsAdvanced(segs, len, ecl, + qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, -1, true, tempBuffer, qrcode); +} + + +// Public function - see documentation comment in header file. +bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl, + int minVersion, int maxVersion, int mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]) { + assert(segs != NULL || len == 0); + assert(qrcodegen_VERSION_MIN <= minVersion && minVersion <= maxVersion && maxVersion <= qrcodegen_VERSION_MAX); + assert(0 <= (int)ecl && (int)ecl <= 3 && -1 <= (int)mask && (int)mask <= 7); + + // Find the minimal version number to use + int version, dataUsedBits; + for (version = minVersion; ; version++) { + int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available + dataUsedBits = getTotalBits(segs, len, version); + if (dataUsedBits != -1 && 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 + qrcode[0] = 0; // Set size to invalid value for safety + return false; + } + } + assert(dataUsedBits != -1); + + // Increase the error correction level while the data still fits in the current version number + for (int i = (int)qrcodegen_Ecc_MEDIUM; i <= (int)qrcodegen_Ecc_HIGH; i++) { // From low to high + if (boostEcl && dataUsedBits <= getNumDataCodewords(version, (enum qrcodegen_Ecc)i) * 8) + ecl = (enum qrcodegen_Ecc)i; + } + + // Concatenate all segments to create the data bit string + memset(qrcode, 0, qrcodegen_BUFFER_LEN_FOR_VERSION(version) * sizeof(qrcode[0])); + int bitLen = 0; + for (size_t i = 0; i < len; i++) { + const struct qrcodegen_Segment *seg = &segs[i]; + appendBitsToBuffer((int)seg->mode, 4, qrcode, &bitLen); + appendBitsToBuffer(seg->numChars, numCharCountBits(seg->mode, version), qrcode, &bitLen); + for (int j = 0; j < seg->bitLength; j++) + appendBitsToBuffer((seg->data[j >> 3] >> (7 - (j & 7))) & 1, 1, qrcode, &bitLen); + } + assert(bitLen == dataUsedBits); + + // Add terminator and pad up to a byte if applicable + int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; + assert(bitLen <= dataCapacityBits); + int terminatorBits = dataCapacityBits - bitLen; + if (terminatorBits > 4) + terminatorBits = 4; + appendBitsToBuffer(0, terminatorBits, qrcode, &bitLen); + appendBitsToBuffer(0, (8 - bitLen % 8) % 8, qrcode, &bitLen); + assert(bitLen % 8 == 0); + + // Pad with alternating bytes until data capacity is reached + for (uint8_t padByte = 0xEC; bitLen < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + appendBitsToBuffer(padByte, 8, qrcode, &bitLen); + + // Draw function and data codeword modules + addEccAndInterleave(qrcode, version, ecl, tempBuffer); + initializeFunctionModules(version, qrcode); + drawCodewords(tempBuffer, getNumRawDataModules(version) / 8, qrcode); + drawWhiteFunctionModules(qrcode, version); + initializeFunctionModules(version, tempBuffer); + + // Handle masking + if (mask == qrcodegen_Mask_AUTO) { // Automatically choose best mask + long minPenalty = LONG_MAX; + for (int i = 0; i < 8; i++) { + enum qrcodegen_Mask msk = (enum qrcodegen_Mask)i; + drawFormatBits(ecl, msk, qrcode); + applyMask(tempBuffer, qrcode, msk); + long penalty = getPenaltyScore(qrcode); + if (penalty < minPenalty) { + mask = msk; + minPenalty = penalty; + } + applyMask(tempBuffer, qrcode, msk); // Undoes the mask due to XOR + } + } + assert(0 <= (int)mask && (int)mask <= 7); + drawFormatBits(ecl, mask, qrcode); + applyMask(tempBuffer, qrcode, mask); + return true; +} + + + /*---- Error correction code generation functions ----*/ // Appends error correction bytes to each block of the given data array, then interleaves @@ -851,96 +944,6 @@ struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]) { } -// Public function - see documentation comment in header file. -bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, - enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]) { - return qrcodegen_encodeSegmentsAdvanced(segs, len, ecl, - qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, -1, true, tempBuffer, qrcode); -} - - -// Public function - see documentation comment in header file. -bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl, - int minVersion, int maxVersion, int mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]) { - assert(segs != NULL || len == 0); - assert(qrcodegen_VERSION_MIN <= minVersion && minVersion <= maxVersion && maxVersion <= qrcodegen_VERSION_MAX); - assert(0 <= (int)ecl && (int)ecl <= 3 && -1 <= (int)mask && (int)mask <= 7); - - // Find the minimal version number to use - int version, dataUsedBits; - for (version = minVersion; ; version++) { - int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available - dataUsedBits = getTotalBits(segs, len, version); - if (dataUsedBits != -1 && 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 - qrcode[0] = 0; // Set size to invalid value for safety - return false; - } - } - assert(dataUsedBits != -1); - - // Increase the error correction level while the data still fits in the current version number - for (int i = (int)qrcodegen_Ecc_MEDIUM; i <= (int)qrcodegen_Ecc_HIGH; i++) { // From low to high - if (boostEcl && dataUsedBits <= getNumDataCodewords(version, (enum qrcodegen_Ecc)i) * 8) - ecl = (enum qrcodegen_Ecc)i; - } - - // Concatenate all segments to create the data bit string - memset(qrcode, 0, qrcodegen_BUFFER_LEN_FOR_VERSION(version) * sizeof(qrcode[0])); - int bitLen = 0; - for (size_t i = 0; i < len; i++) { - const struct qrcodegen_Segment *seg = &segs[i]; - appendBitsToBuffer((int)seg->mode, 4, qrcode, &bitLen); - appendBitsToBuffer(seg->numChars, numCharCountBits(seg->mode, version), qrcode, &bitLen); - for (int j = 0; j < seg->bitLength; j++) - appendBitsToBuffer((seg->data[j >> 3] >> (7 - (j & 7))) & 1, 1, qrcode, &bitLen); - } - assert(bitLen == dataUsedBits); - - // Add terminator and pad up to a byte if applicable - int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; - assert(bitLen <= dataCapacityBits); - int terminatorBits = dataCapacityBits - bitLen; - if (terminatorBits > 4) - terminatorBits = 4; - appendBitsToBuffer(0, terminatorBits, qrcode, &bitLen); - appendBitsToBuffer(0, (8 - bitLen % 8) % 8, qrcode, &bitLen); - assert(bitLen % 8 == 0); - - // Pad with alternating bytes until data capacity is reached - for (uint8_t padByte = 0xEC; bitLen < dataCapacityBits; padByte ^= 0xEC ^ 0x11) - appendBitsToBuffer(padByte, 8, qrcode, &bitLen); - - // Draw function and data codeword modules - addEccAndInterleave(qrcode, version, ecl, tempBuffer); - initializeFunctionModules(version, qrcode); - drawCodewords(tempBuffer, getNumRawDataModules(version) / 8, qrcode); - drawWhiteFunctionModules(qrcode, version); - initializeFunctionModules(version, tempBuffer); - - // Handle masking - if (mask == qrcodegen_Mask_AUTO) { // Automatically choose best mask - long minPenalty = LONG_MAX; - for (int i = 0; i < 8; i++) { - enum qrcodegen_Mask msk = (enum qrcodegen_Mask)i; - drawFormatBits(ecl, msk, qrcode); - applyMask(tempBuffer, qrcode, msk); - long penalty = getPenaltyScore(qrcode); - if (penalty < minPenalty) { - mask = msk; - minPenalty = penalty; - } - applyMask(tempBuffer, qrcode, msk); // Undoes the mask due to XOR - } - } - assert(0 <= (int)mask && (int)mask <= 7); - drawFormatBits(ecl, mask, qrcode); - applyMask(tempBuffer, qrcode, mask); - return true; -} - - // Calculates the number of bits needed to encode the given segments at the given version. // Returns a non-negative number if successful. Otherwise returns -1 if a segment has too // many characters to fit its length field, or the total bits exceeds INT16_MAX. diff --git a/c/qrcodegen.h b/c/qrcodegen.h index 13e8eab..30f9db2 100644 --- a/c/qrcodegen.h +++ b/c/qrcodegen.h @@ -145,7 +145,7 @@ struct qrcodegen_Segment { -/*---- Functions to generate QR Codes ----*/ +/*---- Functions (high level) to generate QR Codes ----*/ /* * Encodes the given text string to a QR Code, returning true if encoding succeeded. @@ -192,6 +192,43 @@ bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcod enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl); +/*---- Functions (low level) to generate QR Codes ----*/ + +/* + * Renders a QR Code representing the given segments at the given error correction level. + * The smallest possible QR Code version is automatically chosen for the output. Returns true if + * QR Code creation succeeded, or false if the data is too long to fit in any version. The ECC level + * of the result may be higher than the ecl argument if it can be done without increasing the version. + * This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and byte) to encode text in less space. + * This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary(). + * To save memory, the segments' data buffers can alias/overlap tempBuffer, and will + * result in them being clobbered, but the QR Code output will still be correct. + * But the qrcode array must not overlap tempBuffer or any segment's data buffer. + */ +bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, + enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]); + + +/* + * Renders a QR Code representing the given segments with the given encoding parameters. + * Returns true if QR Code creation succeeded, or false if the data is too long to fit in the range of versions. + * The smallest possible QR Code version within the given range is automatically + * chosen for the output. Iff boostEcl is true, then the ECC level of the result + * may be higher than the ecl argument if it can be done without increasing the + * version. The mask number is either between 0 to 7 (inclusive) to force that + * mask, or -1 to automatically choose an appropriate mask (which may be slow). + * This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and byte) to encode text in less space. + * This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary(). + * To save memory, the segments' data buffers can alias/overlap tempBuffer, and will + * result in them being clobbered, but the QR Code output will still be correct. + * But the qrcode array must not overlap tempBuffer or any segment's data buffer. + */ +bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl, + int minVersion, int maxVersion, int mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]); + + /* * Tests whether the given string can be encoded as a segment in alphanumeric mode. * A string is encodable iff each character is in the following set: 0 to 9, A to Z @@ -250,41 +287,6 @@ struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t bu struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]); -/* - * Renders a QR Code representing the given segments at the given error correction level. - * The smallest possible QR Code version is automatically chosen for the output. Returns true if - * QR Code creation succeeded, or false if the data is too long to fit in any version. The ECC level - * of the result may be higher than the ecl argument if it can be done without increasing the version. - * This function allows the user to create a custom sequence of segments that switches - * between modes (such as alphanumeric and byte) to encode text in less space. - * This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary(). - * To save memory, the segments' data buffers can alias/overlap tempBuffer, and will - * result in them being clobbered, but the QR Code output will still be correct. - * But the qrcode array must not overlap tempBuffer or any segment's data buffer. - */ -bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, - enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]); - - -/* - * Renders a QR Code representing the given segments with the given encoding parameters. - * Returns true if QR Code creation succeeded, or false if the data is too long to fit in the range of versions. - * The smallest possible QR Code version within the given range is automatically - * chosen for the output. Iff boostEcl is true, then the ECC level of the result - * may be higher than the ecl argument if it can be done without increasing the - * version. The mask number is either between 0 to 7 (inclusive) to force that - * mask, or -1 to automatically choose an appropriate mask (which may be slow). - * This function allows the user to create a custom sequence of segments that switches - * between modes (such as alphanumeric and byte) to encode text in less space. - * This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary(). - * To save memory, the segments' data buffers can alias/overlap tempBuffer, and will - * result in them being clobbered, but the QR Code output will still be correct. - * But the qrcode array must not overlap tempBuffer or any segment's data buffer. - */ -bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl, - int minVersion, int maxVersion, int mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]); - - /*---- Functions to extract raw data from QR Codes ----*/ /*