From fb544495e737eb1a38594030b0e1807731b8588f Mon Sep 17 00:00:00 2001 From: Project Nayuki <me@nayuki.io> Date: Fri, 8 Sep 2017 07:06:22 +0000 Subject: [PATCH] Added C functions to make numeric and alphanumeric segments, added test cases. --- c/qrcodegen-test.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++ c/qrcodegen.c | 64 +++++++++++++++++++++++++++++++++ c/qrcodegen.h | 14 ++++++++ 3 files changed, 166 insertions(+) diff --git a/c/qrcodegen-test.c b/c/qrcodegen-test.c index 1e8a8e3..d92ea29 100644 --- a/c/qrcodegen-test.c +++ b/c/qrcodegen-test.c @@ -911,6 +911,92 @@ static void testMakeBytes(void) { } +static void testMakeNumeric(void) { + { + struct qrcodegen_Segment seg = qrcodegen_makeNumeric("", NULL); + assert(seg.mode == qrcodegen_Mode_NUMERIC); + assert(seg.numChars == 0); + assert(seg.bitLength == 0); + numTestCases++; + } + { + uint8_t buf[1]; + struct qrcodegen_Segment seg = qrcodegen_makeNumeric("9", buf); + assert(seg.numChars == 1); + assert(seg.bitLength == 4); + assert(seg.data[0] == 0x90); + numTestCases++; + } + { + uint8_t buf[1]; + struct qrcodegen_Segment seg = qrcodegen_makeNumeric("81", buf); + assert(seg.numChars == 2); + assert(seg.bitLength == 7); + assert(seg.data[0] == 0xA2); + numTestCases++; + } + { + uint8_t buf[2]; + struct qrcodegen_Segment seg = qrcodegen_makeNumeric("673", buf); + assert(seg.numChars == 3); + assert(seg.bitLength == 10); + assert(seg.data[0] == 0xA8); + assert(seg.data[1] == 0x40); + numTestCases++; + } + { + uint8_t buf[5]; + struct qrcodegen_Segment seg = qrcodegen_makeNumeric("3141592653", buf); + assert(seg.numChars == 10); + assert(seg.bitLength == 34); + assert(seg.data[0] == 0x4E); + assert(seg.data[1] == 0x89); + assert(seg.data[2] == 0xF4); + assert(seg.data[3] == 0x24); + assert(seg.data[4] == 0xC0); + numTestCases++; + } +} + + +static void testMakeAlphanumeric(void) { + { + struct qrcodegen_Segment seg = qrcodegen_makeAlphanumeric("", NULL); + assert(seg.mode == qrcodegen_Mode_ALPHANUMERIC); + assert(seg.numChars == 0); + assert(seg.bitLength == 0); + numTestCases++; + } + { + uint8_t buf[1]; + struct qrcodegen_Segment seg = qrcodegen_makeAlphanumeric("A", buf); + assert(seg.numChars == 1); + assert(seg.bitLength == 6); + assert(seg.data[0] == 0x28); + numTestCases++; + } + { + uint8_t buf[2]; + struct qrcodegen_Segment seg = qrcodegen_makeAlphanumeric("%:", buf); + assert(seg.numChars == 2); + assert(seg.bitLength == 11); + assert(seg.data[0] == 0xDB); + assert(seg.data[1] == 0x40); + numTestCases++; + } + { + uint8_t buf[3]; + struct qrcodegen_Segment seg = qrcodegen_makeAlphanumeric("Q R", buf); + assert(seg.numChars == 3); + assert(seg.bitLength == 17); + assert(seg.data[0] == 0x96); + assert(seg.data[1] == 0xCD); + assert(seg.data[2] == 0x80); + numTestCases++; + } +} + + static void testMakeEci(void) { { uint8_t buf[1]; @@ -964,6 +1050,8 @@ int main(void) { testCalcSegmentBufferSize(); testCalcSegmentBitLength(); testMakeBytes(); + testMakeNumeric(); + testMakeAlphanumeric(); testMakeEci(); printf("All %d test cases passed\n", numTestCases); return EXIT_SUCCESS; diff --git a/c/qrcodegen.c b/c/qrcodegen.c index b8b7523..b5c7e90 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -948,6 +948,70 @@ struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, u } +struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[]) { + struct qrcodegen_Segment result; + size_t len = strlen(digits); + result.mode = qrcodegen_Mode_NUMERIC; + int bitLen = calcSegmentBitLength(result.mode, len); + assert(bitLen != -1); + result.numChars = (int)len; + if (bitLen > 0) + memset(buf, 0, ((size_t)bitLen + 7) / 8 * sizeof(buf[0])); + result.bitLength = 0; + + unsigned int accumData = 0; + int accumCount = 0; + for (; *digits != '\0'; digits++) { + char c = *digits; + assert('0' <= c && c <= '9'); + accumData = accumData * 10 + (c - '0'); + accumCount++; + if (accumCount == 3) { + appendBitsToBuffer(accumData, 10, buf, &result.bitLength); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 or 2 digits remaining + appendBitsToBuffer(accumData, accumCount * 3 + 1, buf, &result.bitLength); + assert(result.bitLength == bitLen); + result.data = buf; + return result; +} + + +struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t buf[]) { + struct qrcodegen_Segment result; + size_t len = strlen(text); + result.mode = qrcodegen_Mode_ALPHANUMERIC; + int bitLen = calcSegmentBitLength(result.mode, len); + assert(bitLen != -1); + result.numChars = (int)len; + if (bitLen > 0) + memset(buf, 0, ((size_t)bitLen + 7) / 8 * sizeof(buf[0])); + result.bitLength = 0; + + unsigned int accumData = 0; + int accumCount = 0; + for (; *text != '\0'; text++) { + const char *temp = strchr(ALPHANUMERIC_CHARSET, *text); + assert(temp != NULL); + accumData = accumData * 45 + (temp - ALPHANUMERIC_CHARSET); + accumCount++; + if (accumCount == 2) { + appendBitsToBuffer(accumData, 11, buf, &result.bitLength); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 character remaining + appendBitsToBuffer(accumData, 6, buf, &result.bitLength); + assert(result.bitLength == bitLen); + result.data = buf; + return result; +} + + struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]) { struct qrcodegen_Segment result; result.mode = qrcodegen_Mode_ECI; diff --git a/c/qrcodegen.h b/c/qrcodegen.h index 0fbbe92..f2f7977 100644 --- a/c/qrcodegen.h +++ b/c/qrcodegen.h @@ -185,6 +185,20 @@ size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, uint8_t buf[]); +/* + * Returns a segment representing the given string of decimal digits encoded in numeric mode. + */ +struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[]); + + +/* + * Returns a segment representing the given text string encoded in alphanumeric mode. + * The characters allowed are: 0 to 9, A to Z (uppercase only), space, + * dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ +struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t buf[]); + + /* * Returns a segment representing an Extended Channel Interpretation * (ECI) designator with the given assignment value.