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.