Replace the pre-optimisation gas estimates with freshly measured values
after the GF256-table / uint256-grid / Yul-assembly rewrite:
Fixed mask: v1 byte ~930 k (was ~2.24 M), v5 ~3.3–3.6 M (was ~8.9 M)
Auto mask: v1 ~5.3 M, v3 ~11 M, v5 ~15.7 M (all now below 30 M limit)
SVG render: ~2.36 M for version-1 with 4-module border
Also adds a MASK_AUTO table, SVG row, and corrected scaling guide.
Co-authored-by: okwme <964052+okwme@users.noreply.github.com>
1. Pre-computed lookup tables as bytes constant
- GF256_EXP / GF256_LOG (256 bytes each) stored in bytecode;
replaces 8-iteration Russian-peasant GF(256) multiply with
3 table lookups in _gf256Mul.
- ALPHA_MAP (59 bytes) replaces 11-branch if-else chain in
_alphanumericCharIndex with 2 range checks + 1 lookup.
- _ECPB_* / _NECB_* ECC tables (4 x 41 bytes each) stored as
library constants; eliminates heap allocation per call.
2. uint256 row packing for the internal module grid
- Grid kept as uint256[] rows (bit x of rows[y] = module(x,y))
throughout _buildQrCode; converted to packed bytes only by
_gridToBytes at the end.
- _fillRect: 1 OR per row instead of width*height bit sets.
- _applyMask: per-row 256-bit XOR computed analytically from
9 precomputed 256-bit constants (_MASK0_EVEN/ODD, _MP0-2,
_MPA/B/6/C/D) — O(1) per row for all 8 masks.
- _drawCodewords: no y*size multiplication per module access.
3. Yul inline assembly for hot paths
- _reedSolomonComputeRemainder: full inner loop in assembly with
GF(256) multiply inlined via memory-resident log/exp tables;
eliminates bounds checks and function-call overhead.
- _getPenaltyScore N2/N4: vectorised 256-bit row operations +
inline popcnt64/popcnt256 reduce O(n^2) module reads to O(n)
word operations.
All 22 tests pass with byte-exact output matching the C reference.
Co-authored-by: okwme <964052+okwme@users.noreply.github.com>