diff options
Diffstat (limited to 'docs/handbook/genqrcode/architecture.md')
| -rw-r--r-- | docs/handbook/genqrcode/architecture.md | 948 |
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. |
