summaryrefslogtreecommitdiff
path: root/docs/handbook/genqrcode/architecture.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/handbook/genqrcode/architecture.md')
-rw-r--r--docs/handbook/genqrcode/architecture.md948
1 files changed, 948 insertions, 0 deletions
diff --git a/docs/handbook/genqrcode/architecture.md b/docs/handbook/genqrcode/architecture.md
new file mode 100644
index 0000000000..f03715bcba
--- /dev/null
+++ b/docs/handbook/genqrcode/architecture.md
@@ -0,0 +1,948 @@
+# genqrcode / libqrencode — Architecture
+
+## High-Level Architecture
+
+libqrencode is a layered C library that transforms input data into a QR Code bitmap through a pipeline of distinct modules. The architecture separates concerns cleanly: input management, bit stream encoding, error correction, frame construction, module placement, and masking are each handled by dedicated source files.
+
+```
+User Input
+ │
+ ▼
+┌─────────────────────────────────────────┐
+│ split.c — Input Splitter │
+│ Automatic mode detection & optimization│
+└─────────────┬───────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────┐
+│ qrinput.c — Input Data Manager │
+│ QRinput linked list, mode encoders, │
+│ bit stream construction, padding │
+└─────────────┬───────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────┐
+│ bitstream.c — Bit Stream Class │
+│ Dynamic bit array with append ops │
+└─────────────┬───────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────┐
+│ qrencode.c — Core Encoder │
+│ QRRawCode / MQRRawCode, FrameFiller, │
+│ QRcode_encodeMask, interleaving │
+│ │
+│ ┌──────────┐ ┌──────────────────────┐ │
+│ │ rsecc.c │ │ qrspec.c / mqrspec.c │ │
+│ │ RS ECC │ │ Spec tables & frames │ │
+│ └──────────┘ └──────────────────────┘ │
+└─────────────┬───────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────┐
+│ mask.c / mmask.c — Masking │
+│ 8 patterns (QR) / 4 patterns (MQR), │
+│ penalty evaluation, mask selection │
+└─────────────┬───────────────────────────┘
+ │
+ ▼
+ QRcode struct
+ (version, width, data[])
+```
+
+---
+
+## Module Dependency Graph
+
+Each `.c` file includes specific headers. Here is the complete include dependency:
+
+```
+qrencode.c
+├── qrencode.h
+├── qrspec.h
+├── mqrspec.h
+├── bitstream.h
+├── qrinput.h
+├── rsecc.h
+├── split.h
+├── mask.h
+└── mmask.h
+
+qrinput.c
+├── qrencode.h
+├── qrspec.h
+├── mqrspec.h
+├── bitstream.h
+└── qrinput.h
+
+split.c
+├── qrencode.h
+├── qrinput.h
+├── qrspec.h
+└── split.h
+
+mask.c
+├── qrencode.h
+├── qrspec.h
+└── mask.h
+
+mmask.c
+├── qrencode.h
+├── mqrspec.h
+└── mmask.h
+
+qrspec.c
+├── qrspec.h
+└── qrinput.h
+
+mqrspec.c
+└── mqrspec.h
+
+rsecc.c
+└── rsecc.h
+
+bitstream.c
+└── bitstream.h
+
+qrenc.c (CLI tool)
+└── qrencode.h
+```
+
+**Key insight:** `qrencode.c` is the integration point that pulls together all modules. The CLI tool (`qrenc.c`) only depends on `qrencode.h`, using exclusively the public API.
+
+---
+
+## Detailed Module Descriptions
+
+### `qrencode.h` — Public API Header
+
+This is the **only header that external consumers include**. It defines:
+
+- `QRencodeMode` enum — All encoding modes (`QR_MODE_NUM`, `QR_MODE_AN`, `QR_MODE_8`, `QR_MODE_KANJI`, `QR_MODE_ECI`, `QR_MODE_FNC1FIRST`, `QR_MODE_FNC1SECOND`, plus internal `QR_MODE_NUL`, `QR_MODE_STRUCTURE`)
+- `QRecLevel` enum — Error correction levels (`QR_ECLEVEL_L` through `QR_ECLEVEL_H`)
+- `QRcode` struct — Output symbol (version, width, data array)
+- `QRcode_List` struct — Singly-linked list of QRcode for structured append
+- `QRinput` — Opaque typedef for input object
+- `QRinput_Struct` — Opaque typedef for structured input set
+- Version constants: `QRSPEC_VERSION_MAX` (40), `MQRSPEC_VERSION_MAX` (4)
+- All `QRinput_*`, `QRcode_*`, and utility function declarations
+
+The header uses `extern "C"` guards for C++ compatibility.
+
+### `qrencode_inner.h` — Test-Only Internal Header
+
+Exposes internal types and functions for the test suite:
+
+```c
+typedef struct {
+ int dataLength;
+ int eccLength;
+ unsigned char *data;
+ unsigned char *ecc;
+} RSblock;
+
+typedef struct {
+ int version;
+ int dataLength;
+ int eccLength;
+ unsigned char *datacode;
+ unsigned char *ecccode;
+ int b1;
+ int blocks;
+ RSblock *rsblock;
+ int count;
+} QRRawCode;
+
+typedef struct {
+ int version;
+ int dataLength;
+ int eccLength;
+ unsigned char *datacode;
+ unsigned char *ecccode;
+ RSblock *rsblock;
+ int oddbits;
+ int count;
+} MQRRawCode;
+```
+
+Exposed functions:
+- `QRraw_new()`, `QRraw_getCode()`, `QRraw_free()`
+- `MQRraw_new()`, `MQRraw_getCode()`, `MQRraw_free()`
+- `FrameFiller_test()`, `FrameFiller_testMQR()`
+- `QRcode_encodeMask()`, `QRcode_encodeMaskMQR()`
+- `QRcode_new()`
+
+### `qrencode.c` — Core Encoding Engine
+
+This is the **central orchestrator** of the entire encoding process. It contains:
+
+#### RSblock and QRRawCode
+
+The `RSblock` struct represents one Reed-Solomon block:
+```c
+typedef struct {
+ int dataLength;
+ int eccLength;
+ unsigned char *data;
+ unsigned char *ecc;
+} RSblock;
+```
+
+`QRRawCode` manages all RS blocks for a full QR Code:
+```c
+typedef struct {
+ int version;
+ int dataLength;
+ int eccLength;
+ unsigned char *datacode; // merged data byte stream
+ unsigned char *ecccode; // merged ECC byte stream
+ int b1; // number of type-1 blocks
+ int blocks; // total block count
+ RSblock *rsblock; // array of RS blocks
+ int count; // iteration counter for getCode()
+} QRRawCode;
+```
+
+`QRraw_new()` creates a QRRawCode from a QRinput:
+1. Calls `QRinput_getByteStream()` to convert input to a padded byte stream
+2. Retrieves the ECC specification via `QRspec_getEccSpec()`
+3. Initializes RS blocks via `RSblock_init()`, which calls `RSECC_encode()` for each block
+4. Sets up interleaving state (b1 records the count of type-1 blocks)
+
+`QRraw_getCode()` implements data/ECC interleaving:
+```c
+STATIC_IN_RELEASE unsigned char QRraw_getCode(QRRawCode *raw)
+{
+ int col, row;
+ unsigned char ret;
+
+ if(raw->count < raw->dataLength) {
+ row = raw->count % raw->blocks;
+ col = raw->count / raw->blocks;
+ if(col >= raw->rsblock[0].dataLength) {
+ row += raw->b1;
+ }
+ ret = raw->rsblock[row].data[col];
+ } else if(raw->count < raw->dataLength + raw->eccLength) {
+ row = (raw->count - raw->dataLength) % raw->blocks;
+ col = (raw->count - raw->dataLength) / raw->blocks;
+ ret = raw->rsblock[row].ecc[col];
+ } else {
+ return 0;
+ }
+ raw->count++;
+ return ret;
+}
+```
+
+This interleaves by cycling through blocks row-by-row: the first byte from each block, then the second byte from each block, and so on. When type-1 blocks are exhausted (their data is shorter), it shifts to type-2 blocks by adding `b1` to the row index.
+
+#### FrameFiller
+
+The `FrameFiller` struct manages the zigzag placement of data modules:
+
+```c
+typedef struct {
+ int width;
+ unsigned char *frame;
+ int x, y; // current position
+ int dir; // direction: -1 (upward) or 1 (downward)
+ int bit; // 0 or 1 within a column pair
+ int mqr; // flag for Micro QR mode
+} FrameFiller;
+```
+
+`FrameFiller_set()` initializes the filler at the bottom-right corner `(width-1, width-1)` with direction `-1` (upward).
+
+`FrameFiller_next()` implements the QR Code module placement algorithm:
+- Modules are placed in 2-column strips from right to left
+- Within each strip, columns alternate right-left
+- Direction alternates between upward and downward
+- Column 6 (the vertical timing pattern) is skipped in full QR mode
+- Modules already marked with bit 7 set (`0x80`) — function patterns — are skipped
+
+#### QRcode_encodeMask()
+
+The main encoding function for full QR Codes:
+
+1. Validates input (not MQR, version in range, valid EC level)
+2. Creates `QRRawCode` from input
+3. Creates base frame via `QRspec_newFrame()`
+4. Places data bits using `FrameFiller_next()`:
+ - Data modules: `*p = ((bit & code) != 0)` — only LSB set
+ - ECC modules: `*p = 0x02 | ((bit & code) != 0)` — bit 1 also set
+ - Remainder bits: `*p = 0x02`
+5. Applies masking:
+ - `mask == -2`: debug mode, no masking
+ - `mask < 0` (normal): `Mask_mask()` evaluates all 8 patterns
+ - `mask >= 0`: `Mask_makeMask()` applies specific mask
+
+#### QRcode_encodeMaskMQR()
+
+The Micro QR variant:
+- Validates MQR mode, version 1–4, EC level L through Q
+- Uses `MQRRawCode` instead of `QRRawCode`
+- Handles `oddbits` — the last data byte may have fewer than 8 significant bits
+- Uses `MMask_mask()` / `MMask_makeMask()` for the 4-pattern MQR masking
+- Sets FrameFiller's `mqr` flag to 1 (no column-6 skip)
+
+#### High-Level Encoding Functions
+
+`QRcode_encodeInput()` simply dispatches based on the `mqr` flag:
+```c
+QRcode *QRcode_encodeInput(QRinput *input)
+{
+ if(input->mqr) {
+ return QRcode_encodeMaskMQR(input, -1);
+ } else {
+ return QRcode_encodeMask(input, -1);
+ }
+}
+```
+
+`QRcode_encodeStringReal()` is the shared implementation for `QRcode_encodeString()` and `QRcode_encodeStringMQR()`:
+1. Creates a `QRinput` (MQR or standard)
+2. Calls `Split_splitStringToQRinput()` for automatic mode optimization
+3. Calls `QRcode_encodeInput()`
+
+For MQR, `QRcode_encodeStringMQR()` tries each version from the minimum up to 4:
+```c
+for(i = version; i <= MQRSPEC_VERSION_MAX; i++) {
+ QRcode *code = QRcode_encodeStringReal(string, i, level, 1, hint, casesensitive);
+ if(code != NULL) return code;
+}
+```
+
+#### Structured Append
+
+`QRcode_encodeInputStructured()` encodes each `QRinput` in a `QRinput_Struct` and builds a `QRcode_List` linked list.
+
+`QRcode_encodeStringStructured()` and variants auto-split the input, insert structured append headers, encode each part, and return the linked list.
+
+---
+
+### `qrinput.h` / `qrinput.c` — Input Data Management
+
+This module manages the input data pipeline from raw user data to a padded byte stream ready for RS encoding.
+
+#### Internal Structures
+
+```c
+// A linked list entry for one data chunk
+struct _QRinput_List {
+ QRencodeMode mode; // encoding mode
+ int size; // data size in bytes
+ unsigned char *data; // data chunk
+ BitStream *bstream; // encoded bit stream (created during encoding)
+ QRinput_List *next; // next chunk
+};
+
+// The main input object
+struct _QRinput {
+ int version;
+ QRecLevel level;
+ QRinput_List *head; // first data chunk
+ QRinput_List *tail; // last data chunk
+ int mqr; // 1 if Micro QR mode
+ int fnc1; // FNC1 mode flag
+ unsigned char appid; // FNC1 application ID
+};
+
+// Structured append management
+struct _QRinput_Struct {
+ int size; // number of symbols
+ int parity; // parity byte
+ QRinput_InputList *head;
+ QRinput_InputList *tail;
+};
+```
+
+#### Mode Encoding Functions
+
+Each encoding mode has three functions in `qrinput.c`:
+
+1. **Check function** — Validates input data for the mode
+2. **Estimate function** — Estimates bit count without actually encoding
+3. **Encode function** — Produces the actual bit stream
+
+##### Numeric Mode (`QRinput_encodeModeNum`)
+
+Encodes digits in groups:
+- 3 digits → 10 bits (values 000–999)
+- 2 remaining digits → 7 bits
+- 1 remaining digit → 4 bits
+
+```c
+int QRinput_estimateBitsModeNum(int size)
+{
+ int w = size / 3;
+ int bits = w * 10;
+ switch(size - w * 3) {
+ case 1: bits += 4; break;
+ case 2: bits += 7; break;
+ }
+ return bits;
+}
+```
+
+##### Alphanumeric Mode (`QRinput_encodeModeAn`)
+
+Uses the 45-character lookup table `QRinput_anTable[128]`:
+- Pairs → 11 bits (value = c1 × 45 + c2)
+- Odd character → 6 bits
+
+The table maps: 0-9 → 0-9, A-Z → 10-35, space → 36, $ → 37, % → 38, * → 39, + → 40, - → 41, . → 42, / → 43, : → 44.
+
+The lookup macro:
+```c
+#define QRinput_lookAnTable(__c__) \
+ ((__c__ & 0x80)?-1:QRinput_anTable[(int)__c__])
+```
+
+##### 8-Bit Mode (`QRinput_encodeMode8`)
+
+Each byte → 8 bits, using `BitStream_appendBytes()`.
+
+##### Kanji Mode (`QRinput_encodeModeKanji`)
+
+Shift-JIS double-byte characters are compressed:
+1. If code ≤ 0x9FFC: subtract 0x8140
+2. If code > 0x9FFC: subtract 0xC140
+3. High byte × 0xC0 + low byte → 13-bit value
+
+Validation in `QRinput_checkModeKanji()`:
+```c
+if(val < 0x8140 || (val > 0x9ffc && val < 0xe040) || val > 0xebbf) {
+ return -1;
+}
+```
+
+##### ECI Mode (`QRinput_encodeModeECI`)
+
+ECI indicator encoding (per JIS X0510:2004, Table 4):
+- 0–127: 1 byte (8 bits)
+- 128–16383: 2 bytes (16 bits, prefix 0x8000)
+- 16384–999999: 3 bytes (24 bits, prefix 0xC0000)
+
+##### Structured Append Header
+
+20-bit header: 4 bits mode indicator + 4 bits symbol count + 4 bits symbol index + 8 bits parity.
+
+##### FNC1 Second Position
+
+4-bit mode indicator + 8-bit application ID.
+
+#### Bit Stream Construction Pipeline
+
+The conversion from input chunks to a byte stream follows this call chain:
+
+```
+QRinput_getByteStream()
+ └── QRinput_getBitStream()
+ ├── QRinput_convertData() [for standard QR]
+ │ ├── QRinput_estimateVersion()
+ │ │ └── QRinput_estimateBitStreamSize()
+ │ │ └── QRinput_estimateBitStreamSizeOfEntry() × N
+ │ └── QRinput_createBitStream()
+ │ └── QRinput_encodeBitStream() × N
+ │ └── QRinput_encodeMode{Num,An,8,Kanji,...}()
+ ├── QRinput_appendPaddingBit() [for standard QR]
+ └── QRinput_appendPaddingBitMQR() [for Micro QR]
+ └── BitStream_toByte()
+```
+
+`QRinput_convertData()` handles version auto-selection with a convergence loop:
+```c
+for(;;) {
+ BitStream_reset(bstream);
+ bits = QRinput_createBitStream(input, bstream);
+ ver = QRspec_getMinimumVersion((bits + 7) / 8, input->level);
+ if(ver > QRinput_getVersion(input)) {
+ QRinput_setVersion(input, ver);
+ } else {
+ break;
+ }
+}
+```
+
+`QRinput_encodeBitStream()` handles entry splitting when data exceeds `QRspec_maximumWords()`:
+```c
+if(words != 0 && entry->size > words) {
+ st1 = QRinput_List_newEntry(entry->mode, words, entry->data);
+ st2 = QRinput_List_newEntry(entry->mode, entry->size - words, &entry->data[words]);
+ QRinput_encodeBitStream(st1, bstream, version, mqr);
+ QRinput_encodeBitStream(st2, bstream, version, mqr);
+}
+```
+
+Padding appends:
+1. Terminator (up to 4 zero bits)
+2. Byte-alignment zeros
+3. Alternating pad codewords: `0xEC`, `0x11`, `0xEC`, `0x11`, ...
+
+---
+
+### `bitstream.h` / `bitstream.c` — Binary Sequence Class
+
+A dynamic bit array class used throughout the encoding pipeline.
+
+```c
+typedef struct {
+ size_t length; // current number of bits
+ size_t datasize; // allocated buffer size
+ unsigned char *data; // one byte per bit (0 or 1)
+} BitStream;
+```
+
+**Critical design choice:** Each bit occupies one byte in memory. This simplifies bit manipulation at the cost of memory. The buffer starts at `DEFAULT_BUFSIZE` (128) and doubles on demand via `BitStream_expand()`.
+
+Key operations:
+
+| Function | Description |
+|---|---|
+| `BitStream_new()` | Allocate with 128-byte initial buffer |
+| `BitStream_append(dst, src)` | Append another BitStream |
+| `BitStream_appendNum(bs, bits, num)` | Append `bits` bits of integer `num` |
+| `BitStream_appendBytes(bs, size, data)` | Append `size` bytes (8 bits each) |
+| `BitStream_toByte(bs)` | Pack bit array into byte array |
+| `BitStream_free(bs)` | Free all memory |
+
+Macros:
+```c
+#define BitStream_size(__bstream__) (__bstream__->length)
+#define BitStream_reset(__bstream__) (__bstream__->length = 0)
+```
+
+`BitStream_toByte()` packs the 1-byte-per-bit representation into a proper byte array:
+```c
+for(i = 0; i < bytes; i++) {
+ v = 0;
+ for(j = 0; j < 8; j++) {
+ v = (unsigned char)(v << 1);
+ v |= *p;
+ p++;
+ }
+ data[i] = v;
+}
+```
+
+---
+
+### `qrspec.h` / `qrspec.c` — QR Code Specification Tables
+
+Contains all specification-derived data tables and frame construction for full QR Codes.
+
+#### Capacity Table
+
+```c
+typedef struct {
+ int width;
+ int words;
+ int remainder;
+ int ec[4];
+} QRspec_Capacity;
+
+static const QRspec_Capacity qrspecCapacity[QRSPEC_VERSION_MAX + 1];
+```
+
+Sourced from Table 1 (p.13) and Tables 12–16 (pp.30–36) of JIS X0510:2004.
+
+#### Length Indicator Table
+
+```c
+static const int lengthTableBits[4][3] = {
+ {10, 12, 14}, // Numeric
+ { 9, 11, 13}, // Alphanumeric
+ { 8, 16, 16}, // 8-bit
+ { 8, 10, 12} // Kanji
+};
+```
+
+Three version ranges: 1–9, 10–26, 27–40.
+
+#### ECC Block Specification Table
+
+```c
+static const int eccTable[QRSPEC_VERSION_MAX+1][4][2];
+```
+
+Each entry `eccTable[version][level]` gives `{type1_blocks, type2_blocks}`. Combined with `QRspec_getEccSpec()` to produce the 5-element spec array:
+
+```c
+void QRspec_getEccSpec(int version, QRecLevel level, int spec[5])
+```
+
+Where `spec` = `{num_type1_blocks, type1_data_codes, ecc_codes_per_block, num_type2_blocks, type2_data_codes}`.
+
+Accessor macros:
+```c
+#define QRspec_rsBlockNum(__spec__) (__spec__[0] + __spec__[3])
+#define QRspec_rsBlockNum1(__spec__) (__spec__[0])
+#define QRspec_rsDataCodes1(__spec__) (__spec__[1])
+#define QRspec_rsEccCodes1(__spec__) (__spec__[2])
+#define QRspec_rsBlockNum2(__spec__) (__spec__[3])
+#define QRspec_rsDataCodes2(__spec__) (__spec__[4])
+#define QRspec_rsEccCodes2(__spec__) (__spec__[2])
+```
+
+Note: type-1 and type-2 blocks share the same ECC code count.
+
+#### Alignment Pattern Table
+
+```c
+static const int alignmentPattern[QRSPEC_VERSION_MAX+1][2];
+```
+
+From Table 1 in Appendix E (p.71). Stores the second and third alignment pattern positions; remaining positions are interpolated.
+
+`QRspec_putAlignmentPattern()` places all alignment markers, computing positions from the stored two values and the inter-pattern distance.
+
+#### Version Information Pattern
+
+```c
+static const unsigned int versionPattern[QRSPEC_VERSION_MAX - 6];
+```
+
+BCH-encoded version information for versions 7–40. From Appendix D (p.68).
+
+#### Format Information
+
+```c
+static const unsigned int formatInfo[4][8];
+```
+
+BCH-encoded format information indexed by `[level][mask]`.
+
+#### Frame Creation (`QRspec_createFrame`)
+
+Builds the initial symbol frame for a given version:
+
+1. Allocates `width × width` bytes, zeroed
+2. Places **3 finder patterns** (7×7, `0xC1`/`0xC0` pattern) at corners
+3. Places **separators** (1-module-wide white border around finders, `0xC0`)
+4. Masks **format information area** (9+8 modules around finder, `0x84`)
+5. Places **timing patterns** (alternating `0x91`/`0x90` along row 6 and column 6)
+6. Places **alignment patterns** (5×5, `0xA1`/`0xA0` pattern)
+7. For versions ≥ 7: places **version information** (6×3 blocks, `0x88`/`0x89`)
+8. Sets the **dark module** at position `(8, width-8)` to `0x81`
+
+All function pattern modules have bit 7 set (`0x80`), so the FrameFiller skips them during data placement.
+
+---
+
+### `mqrspec.h` / `mqrspec.c` — Micro QR Specification Tables
+
+Parallel to `qrspec.c` but for Micro QR (versions M1–M4).
+
+```c
+typedef struct {
+ int width;
+ int ec[4];
+} MQRspec_Capacity;
+
+static const MQRspec_Capacity mqrspecCapacity[MQRSPEC_VERSION_MAX + 1] = {
+ { 0, {0, 0, 0, 0}},
+ { 11, {2, 0, 0, 0}}, // M1
+ { 13, {5, 6, 0, 0}}, // M2
+ { 15, {6, 8, 0, 0}}, // M3
+ { 17, {8, 10, 14, 0}} // M4
+};
+```
+
+Notable difference: `MQRspec_getDataLengthBit()` returns data capacity in **bits** (not bytes), because Micro QR symbols can have non-byte-aligned data areas:
+```c
+int MQRspec_getDataLengthBit(int version, QRecLevel level)
+{
+ int w = mqrspecCapacity[version].width - 1;
+ int ecc = mqrspecCapacity[version].ec[level];
+ if(ecc == 0) return 0;
+ return w * w - 64 - ecc * 8;
+}
+```
+
+The data length in bytes rounds up: `(bits + 4) / 8`.
+
+#### Micro QR Frame Creation
+
+`MQRspec_createFrame()` is simpler than the full QR version:
+- Only **1 finder pattern** (top-left)
+- No alignment patterns
+- No version information
+- Timing patterns run along one row and one column from the finder
+
+#### Format Info Type Table
+
+```c
+static const int typeTable[MQRSPEC_VERSION_MAX + 1][3] = {
+ {-1, -1, -1},
+ { 0, -1, -1}, // M1: only error detection
+ { 1, 2, -1}, // M2: L, M
+ { 3, 4, -1}, // M3: L, M
+ { 5, 6, 7} // M4: L, M, Q
+};
+```
+
+Maps `(version, level)` → format info table index. Returns -1 for unsupported combinations.
+
+---
+
+### `rsecc.h` / `rsecc.c` — Reed-Solomon Error Correction
+
+Contains the GF(2^8) arithmetic and RS encoding for QR Codes.
+
+Single public function:
+```c
+int RSECC_encode(size_t data_length, size_t ecc_length,
+ const unsigned char *data, unsigned char *ecc);
+```
+
+Internal state:
+- `alpha[256]` — Power-to-element mapping (logarithm table)
+- `aindex[256]` — Element-to-power mapping (antilogarithm table)
+- `generator[29][31]` — Cached generator polynomials for ECC lengths 2–30
+- `generatorInitialized[29]` — Whether each generator has been computed
+
+Lazy initialization via `RSECC_init()` and `generator_init()`, protected by `RSECC_mutex` when pthreads are available.
+
+The primitive polynomial is `0x11d` = $x^8 + x^4 + x^3 + x^2 + 1$, per JIS X0510:2004 p.37.
+
+See [reed-solomon.md](reed-solomon.md) for detailed implementation analysis.
+
+---
+
+### `split.h` / `split.c` — Input String Splitter
+
+Automatically parses an input string and splits it into optimal encoding mode segments.
+
+Entry point:
+```c
+int Split_splitStringToQRinput(const char *string, QRinput *input,
+ QRencodeMode hint, int casesensitive);
+```
+
+Key functions:
+- `Split_identifyMode()` — Classifies a character: digit → `QR_MODE_NUM`, AN table match → `QR_MODE_AN`, Shift-JIS → `QR_MODE_KANJI`, else → `QR_MODE_8`
+- `Split_eatNum()` — Consumes a run of numeric characters, considers switching to AN or 8-bit if more efficient
+- `Split_eatAn()` — Consumes alphanumeric characters, embedded digit runs tested for mode-switch optimization
+- `Split_eatKanji()` — Consumes pairs of Kanji bytes
+- `Split_eat8()` — Consumes 8-bit characters, tests for switching to NUM or AN when sub-runs are encountered
+
+The optimization logic compares bit costs: each `Split_eat*` function calculates `dif` — the bit savings of staying in the current mode vs. switching. Example from `Split_eatNum()`:
+
+```c
+dif = QRinput_estimateBitsModeNum(run) + 4 + ln
+ + QRinput_estimateBitsMode8(1)
+ - QRinput_estimateBitsMode8(run + 1);
+if(dif > 0) {
+ return Split_eat8(string, input, hint);
+}
+```
+
+Case conversion (when `casesensitive=0`) is handled by `dupAndToUpper()`, which converts lowercase to uppercase while preserving Kanji double-byte sequences.
+
+---
+
+### `mask.h` / `mask.c` — QR Code Masking
+
+Implements the 8 mask patterns for full QR Code and the penalty evaluation algorithm.
+
+#### Mask Patterns
+
+All 8 patterns are defined via the `MASKMAKER` macro:
+
+```c
+#define MASKMAKER(__exp__) \
+ int x, y;\
+ int b = 0;\
+ for(y = 0; y < width; y++) {\
+ for(x = 0; x < width; x++) {\
+ if(*s & 0x80) {\
+ *d = *s;\
+ } else {\
+ *d = *s ^ ((__exp__) == 0);\
+ }\
+ b += (int)(*d & 1);\
+ s++; d++;\
+ }\
+ }\
+ return b;
+```
+
+The 8 mask functions:
+
+| Pattern | Function | Condition (dark if true) |
+|---|---|---|
+| 0 | `Mask_mask0` | `(x+y) % 2 == 0` |
+| 1 | `Mask_mask1` | `y % 2 == 0` |
+| 2 | `Mask_mask2` | `x % 3 == 0` |
+| 3 | `Mask_mask3` | `(x+y) % 3 == 0` |
+| 4 | `Mask_mask4` | `((y/2)+(x/3)) % 2 == 0` |
+| 5 | `Mask_mask5` | `(x*y)%2 + (x*y)%3 == 0` |
+| 6 | `Mask_mask6` | `((x*y)%2 + (x*y)%3) % 2 == 0` |
+| 7 | `Mask_mask7` | `((x*y)%3 + (x+y)%2) % 2 == 0` |
+
+Function pointer array: `static MaskMaker *maskMakers[8]`
+
+#### Penalty Evaluation
+
+`Mask_mask()` tries all 8 masks and selects the one with the lowest penalty:
+
+```c
+for(i = 0; i < maskNum; i++) {
+ penalty = 0;
+ blacks = maskMakers[i](width, frame, mask);
+ blacks += Mask_writeFormatInformation(width, mask, i, level);
+ bratio = (200 * blacks + w2) / w2 / 2;
+ penalty = (abs(bratio - 50) / 5) * N4;
+ penalty += Mask_evaluateSymbol(width, mask);
+ if(penalty < minPenalty) {
+ minPenalty = penalty;
+ memcpy(bestMask, mask, w2);
+ }
+}
+```
+
+Penalty constants from JIS X0510:2004, Section 8.8.2:
+```c
+#define N1 (3) // Run penalty base
+#define N2 (3) // 2×2 block penalty
+#define N3 (40) // Finder-like pattern penalty
+#define N4 (10) // Proportion penalty per 5% deviation
+```
+
+See [masking-algorithms.md](masking-algorithms.md) for detailed penalty calculation analysis.
+
+---
+
+### `mmask.h` / `mmask.c` — Micro QR Masking
+
+Implements 4 mask patterns for Micro QR Code with a different evaluation algorithm.
+
+The 4 patterns:
+
+| Pattern | Condition |
+|---|---|
+| 0 | `y % 2 == 0` |
+| 1 | `((y/2)+(x/3)) % 2 == 0` |
+| 2 | `((x*y)%2 + (x*y)%3) % 2 == 0` |
+| 3 | `((x+y)%2 + (x*y)%3) % 2 == 0` |
+
+The Micro QR evaluation in `MMask_evaluateSymbol()` uses a completely different approach from full QR:
+```c
+STATIC_IN_RELEASE int MMask_evaluateSymbol(int width, unsigned char *frame)
+{
+ int x, y;
+ unsigned char *p;
+ int sum1 = 0, sum2 = 0;
+
+ p = frame + width * (width - 1);
+ for(x = 1; x < width; x++) {
+ sum1 += (p[x] & 1);
+ }
+
+ p = frame + width * 2 - 1;
+ for(y = 1; y < width; y++) {
+ sum2 += (*p & 1);
+ p += width;
+ }
+
+ return (sum1 <= sum2)?(sum1 * 16 + sum2):(sum2 * 16 + sum1);
+}
+```
+
+Instead of penalties, it counts dark modules on the bottom row and right column, then selects the mask with the **highest** score (not lowest).
+
+---
+
+## Data Flow: Complete Encoding Pipeline
+
+Here is the detailed function call chain for encoding a string:
+
+```
+QRcode_encodeString("Hello", 0, QR_ECLEVEL_M, QR_MODE_8, 1)
+ │
+ └── QRcode_encodeStringReal("Hello", 0, QR_ECLEVEL_M, 0, QR_MODE_8, 1)
+ │
+ ├── QRinput_new2(0, QR_ECLEVEL_M)
+ │ └── allocate QRinput {version=0, level=M, mqr=0}
+ │
+ ├── Split_splitStringToQRinput("Hello", input, QR_MODE_8, 1)
+ │ └── Split_splitString("Hello", input, QR_MODE_8)
+ │ ├── Split_identifyMode("H") → QR_MODE_AN
+ │ └── Split_eatAn("Hello", ...)
+ │ └── QRinput_append(input, QR_MODE_AN, 5, "Hello")
+ │
+ └── QRcode_encodeInput(input)
+ │
+ └── QRcode_encodeMask(input, -1)
+ │
+ ├── QRraw_new(input)
+ │ ├── QRinput_getByteStream(input)
+ │ │ ├── QRinput_convertData() — version auto-select
+ │ │ ├── QRinput_createBitStream() — encode each chunk
+ │ │ ├── QRinput_appendPaddingBit() — terminator + padding
+ │ │ └── BitStream_toByte() — pack bits to bytes
+ │ │
+ │ ├── QRspec_getEccSpec(version, level, spec)
+ │ └── RSblock_init() → RSECC_encode() per block
+ │
+ ├── QRspec_newFrame(version)
+ │ └── QRspec_createFrame() — finder, timing, alignment
+ │
+ ├── FrameFiller placement loop
+ │ └── FrameFiller_next() × (dataLength + eccLength) × 8
+ │
+ └── Mask_mask(width, frame, level)
+ ├── maskMakers[0..7]() — apply each pattern
+ ├── Mask_writeFormatInformation() — embed format info
+ ├── Mask_evaluateSymbol() — penalty calculation
+ │ ├── Mask_calcN2() — 2×2 blocks
+ │ ├── Mask_calcRunLengthH() — horizontal runs
+ │ ├── Mask_calcRunLengthV() — vertical runs
+ │ └── Mask_calcN1N3() — run + finder penalties
+ └── select minimum penalty mask
+```
+
+---
+
+## STATIC_IN_RELEASE Pattern
+
+The codebase uses the `STATIC_IN_RELEASE` macro to control visibility:
+
+```c
+// When WITH_TESTS is defined:
+#define STATIC_IN_RELEASE
+
+// When WITH_TESTS is not defined:
+#define STATIC_IN_RELEASE static
+```
+
+Functions marked `STATIC_IN_RELEASE` (like `QRraw_new`, `QRcode_encodeMask`, `Mask_evaluateSymbol`) are `static` in release builds and externally visible in test builds. Similarly, `#ifdef WITH_TESTS` blocks expose additional test-only functions.
+
+---
+
+## Memory Management
+
+The library follows a consistent pattern:
+- All allocation uses `malloc()`/`calloc()`/`realloc()`
+- Every `_new()` function has a corresponding `_free()` function
+- On allocation failure, functions return `NULL` and set `errno`
+- `QRcode_free()` frees both the `QRcode` struct and its internal `data` array
+- `QRinput_free()` walks the linked list and frees each entry
+- `BitStream_free()` frees the data buffer and the struct
+
+No memory pools or custom allocators are used.
+
+---
+
+## Error Handling
+
+Errors are reported via return values and `errno`:
+
+| Error | Meaning |
+|---|---|
+| `EINVAL` | Invalid argument (bad version, level, mode, or data) |
+| `ENOMEM` | Memory allocation failure |
+| `ERANGE` | Input data too large for any supported version |
+
+Functions returning pointers return `NULL` on error. Functions returning `int` return `-1` on error and `0` on success. The only exception is `QRraw_getCode()` which returns `0` when the code stream is exhausted.