SchOS/kernel/core/qr.c
Nexoniarz a1c965da1e include: one folder per subsystem, and includes that name it
Sixty-five headers sat side by side in include/, so `#include "irq.h"`
said nothing about where irq.h was or what it belonged to, and errno.h,
stat.h and fcntl.h at the top shadowed the system's for any host tool
built with -I./include. They are in folders now -- arch/ core/ fs/
dev/ gfx/ net/ uapi/ (and ldls/ for the Linux driver layer) -- and
every include names the folder, as Linux's do: "core/proc.h".

Done mechanically and checked the hard way: the kernel built from this
tree disassembles identically to the one built from the tree before
it. The full image boots, and userland is the same set of programs.
Paths in docs and comments follow; docs/guides/layout.md has a table
of what is where, and tools/fix-includes.py rewrites old-style
includes from the folders in the tree, for code written before this.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
2026-10-23 15:05:12 +02:00

456 lines
14 KiB
C

// QR encoder. See include/gfx/qr.h for why it exists and why it is this small.
//
// Freestanding on purpose: no allocation, no floating point, no libc beyond
// memset/memmove. It has to run in panic context, where the heap may be exactly
// what is broken. Every buffer here is either on the caller's struct or a
// bounded local.
//
// The structure follows ISO/IEC 18004 in the order the standard describes it,
// which is also the only order in which any of it can be checked:
//
// 1. encode the payload into data codewords
// 2. append Reed-Solomon error correction, per block
// 3. interleave the blocks
// 4. draw the function patterns (finders, timing, alignment, format area)
// 5. lay the codeword bits into the remaining modules in a zigzag
// 6. try all 8 masks, score each, keep the best
//
// Verified by scanning the output with a phone, which is the only test that
// matters and is also literally the feature.
#ifdef QR_HOST
#include <string.h>
#else
#include "core/types.h"
#include "core/defs.h"
#endif
#include "gfx/qr.h"
// ---------------------------------------------------------------------------
// Version table
// ---------------------------------------------------------------------------
//
// Per version, at error correction level L: total codewords, error correction
// codewords per block, and number of blocks. These are from the standard's
// tables and cannot be derived, so they are the one part of this file that has
// to be right by transcription rather than by reasoning.
//
// A useful check on them: total - ecc*blocks must be divisible by blocks for
// every row below, because levels 1..6 at L happen to have equally sized
// blocks. qr_encode() asserts exactly that at runtime, which is what turns a
// mistyped number here into a visible failure instead of an unscannable symbol.
static const struct {
short total; // total codewords in the symbol
unsigned char ecc; // EC codewords per block
unsigned char blocks; // number of RS blocks
} vinfo[QR_MAX_VERSION + 1] = {
{ 0, 0, 0 }, // version 0 does not exist; keeps indexing natural
{ 26, 7, 1 },
{ 44, 10, 1 },
{ 70, 15, 1 },
{ 100, 20, 1 },
{ 134, 26, 1 },
{ 172, 18, 2 },
};
int
qr_capacity(int version)
{
if (version < 1 || version > QR_MAX_VERSION)
return 0;
// Data codewords, less the two bytes of framing: a 4-bit mode indicator, an
// 8-bit character count (versions 1-9), and a 4-bit terminator come to
// exactly two codewords' worth of overhead.
int data = vinfo[version].total - vinfo[version].ecc * vinfo[version].blocks;
return data - 2;
}
// ---------------------------------------------------------------------------
// GF(256) and Reed-Solomon
// ---------------------------------------------------------------------------
// Multiply in GF(256) modulo x^8 + x^4 + x^3 + x^2 + 1 (0x11D).
//
// Done by shift-and-add rather than log/exp tables. Two 256-byte tables plus
// their initialisation is more code than this loop, and this needs no init step
// at all -- which matters when the caller is a panic handler that cannot assume
// any particular earlier function ran.
static unsigned char
gmul(unsigned char x, unsigned char y)
{
unsigned char z = 0;
for (int i = 7; i >= 0; i--) {
z = (unsigned char)((z << 1) ^ ((z >> 7) * 0x1D));
z ^= (unsigned char)(((y >> i) & 1) * x);
}
return z;
}
// Coefficients of the degree-`degree` generator polynomial, highest power
// first, with the leading 1 left implicit.
static void
rs_divisor(int degree, unsigned char *result)
{
memset(result, 0, degree);
result[degree - 1] = 1; // start at the monomial x^0
// Multiply by (x - r) for r = 1, a, a^2, ... Each pass shifts the polynomial
// up one degree and folds in the new root.
unsigned char root = 1;
for (int i = 0; i < degree; i++) {
for (int j = 0; j < degree; j++) {
result[j] = gmul(result[j], root);
if (j + 1 < degree)
result[j] ^= result[j + 1];
}
root = gmul(root, 0x02);
}
}
// The remainder of data[] divided by the generator: the EC codewords.
static void
rs_remainder(const unsigned char *data, int len,
const unsigned char *gen, int degree, unsigned char *result)
{
memset(result, 0, degree);
for (int i = 0; i < len; i++) {
unsigned char factor = data[i] ^ result[0];
memmove(&result[0], &result[1], degree - 1);
result[degree - 1] = 0;
for (int j = 0; j < degree; j++)
result[j] ^= gmul(gen[j], factor);
}
}
// ---------------------------------------------------------------------------
// Module access
// ---------------------------------------------------------------------------
static void
set_fn(struct qr *q, int x, int y, int dark)
{
if (x < 0 || y < 0 || x >= q->size || y >= q->size)
return;
q->mod[y][x] = (unsigned char)(dark != 0);
q->fn[y][x] = 1;
}
static int
absi(int v)
{
return v < 0 ? -v : v;
}
static int
cheb(int dx, int dy)
{
int ax = absi(dx), ay = absi(dy);
return ax > ay ? ax : ay;
}
// ---------------------------------------------------------------------------
// Function patterns
// ---------------------------------------------------------------------------
// A finder plus its separator, as one 9x9 stamp centred on (cx, cy).
//
// Writing it by Chebyshev distance rather than as a literal bitmap is not
// cleverness for its own sake: the concentric rings *are* the definition -- dark
// 3x3 core, light ring, dark ring, light separator -- and the edge stamps run
// off the symbol, which set_fn() clips.
static void
draw_finder(struct qr *q, int cx, int cy)
{
for (int dy = -4; dy <= 4; dy++)
for (int dx = -4; dx <= 4; dx++) {
int d = cheb(dx, dy);
set_fn(q, cx + dx, cy + dy, d != 2 && d != 4);
}
}
// The 5x5 alignment pattern: dark 3x3 outline with a light ring inside.
static void
draw_alignment(struct qr *q, int cx, int cy)
{
for (int dy = -2; dy <= 2; dy++)
for (int dx = -2; dx <= 2; dx++)
set_fn(q, cx + dx, cy + dy, cheb(dx, dy) != 1);
}
// The 15-bit format information, twice, plus the always-dark module.
//
// Called once with the real mask at the end, and once early with mask 0 purely
// to *reserve* these modules -- the zigzag below skips anything marked as a
// function pattern, so the reservation has to exist before data is placed.
static void
draw_format(struct qr *q, int mask)
{
// 0b01 is error correction level L. Five data bits, then a BCH(15,5) check
// computed with generator 0x537, then XOR 0x5412 so an all-zero format never
// produces an all-zero pattern.
int data = (1 << 3) | mask;
int rem = data;
for (int i = 0; i < 10; i++)
rem = (rem << 1) ^ ((rem >> 9) * 0x537);
int bits = ((data << 10) | rem) ^ 0x5412;
int n = q->size;
// First copy: down the left of the top-right finder, then left along the top
// of the bottom-left one. The 6/7 and 7/8 jogs skip the timing pattern.
for (int i = 0; i <= 5; i++)
set_fn(q, 8, i, (bits >> i) & 1);
set_fn(q, 8, 7, (bits >> 6) & 1);
set_fn(q, 8, 8, (bits >> 7) & 1);
set_fn(q, 7, 8, (bits >> 8) & 1);
for (int i = 9; i < 15; i++)
set_fn(q, 14 - i, 8, (bits >> i) & 1);
// Second copy, so the format survives damage to either corner.
for (int i = 0; i < 8; i++)
set_fn(q, n - 1 - i, 8, (bits >> i) & 1);
for (int i = 8; i < 15; i++)
set_fn(q, 8, n - 15 + i, (bits >> i) & 1);
set_fn(q, 8, n - 8, 1); // the dark module, always set, always here
}
static void
draw_function_patterns(struct qr *q)
{
int n = q->size;
// Timing patterns first: the finders overwrite their ends, which is correct.
for (int i = 0; i < n; i++) {
set_fn(q, 6, i, i % 2 == 0);
set_fn(q, i, 6, i % 2 == 0);
}
draw_finder(q, 3, 3);
draw_finder(q, n - 4, 3);
draw_finder(q, 3, n - 4);
// For versions 2-6 the alignment centres are {6, n-7}, and three of those
// four combinations collide with a finder. Only the bottom-right survives,
// which is why there is no position table here. Version 7 would need one.
if (q->size > 21)
draw_alignment(q, n - 7, n - 7);
draw_format(q, 0); // reserve; overwritten once a mask is chosen
}
// ---------------------------------------------------------------------------
// Masking
// ---------------------------------------------------------------------------
static int
mask_bit(int mask, int x, int y)
{
switch (mask) {
case 0: return (x + y) % 2 == 0;
case 1: return y % 2 == 0;
case 2: return x % 3 == 0;
case 3: return (x + y) % 3 == 0;
case 4: return (x / 3 + y / 2) % 2 == 0;
case 5: return x * y % 2 + x * y % 3 == 0;
case 6: return (x * y % 2 + x * y % 3) % 2 == 0;
case 7: return ((x + y) % 2 + x * y % 3) % 2 == 0;
}
return 0;
}
static void
apply_mask(struct qr *q, int mask)
{
for (int y = 0; y < q->size; y++)
for (int x = 0; x < q->size; x++)
if (!q->fn[y][x])
q->mod[y][x] ^= (unsigned char)mask_bit(mask, x, y);
}
// The standard's four penalty rules. Lower is better.
//
// A decoder will read any legal mask, so this is not correctness -- it is scan
// reliability, which is the whole point when the reader is a handheld camera
// pointed at a screen. Rule 3 in particular exists to stop the data accidentally
// containing something that looks like a finder pattern.
static int
penalty(const struct qr *q)
{
int n = q->size, score = 0, dark = 0;
for (int y = 0; y < n; y++) {
for (int x = 0; x < n; x++)
dark += q->mod[y][x];
// Rule 1, both directions: runs of five or more identical modules.
for (int dir = 0; dir < 2; dir++) {
int run = 1;
int prev = dir ? q->mod[0][y] : q->mod[y][0];
for (int i = 1; i < n; i++) {
int cur = dir ? q->mod[i][y] : q->mod[y][i];
if (cur == prev) {
run++;
} else {
if (run >= 5) score += 3 + (run - 5);
run = 1;
prev = cur;
}
}
if (run >= 5) score += 3 + (run - 5);
}
}
// Rule 2: every 2x2 block of one colour.
for (int y = 0; y + 1 < n; y++)
for (int x = 0; x + 1 < n; x++) {
int c = q->mod[y][x];
if (c == q->mod[y][x + 1] && c == q->mod[y + 1][x] && c == q->mod[y + 1][x + 1])
score += 3;
}
// Rule 3: the finder-lookalike 1011101 with four light modules on either
// side, in either direction.
for (int dir = 0; dir < 2; dir++)
for (int a = 0; a < n; a++) {
unsigned bits = 0;
for (int b = 0; b < n; b++) {
int m = dir ? q->mod[b][a] : q->mod[a][b];
bits = ((bits << 1) | (unsigned)m) & 0x7FF;
if (b >= 10 && (bits == 0x05D || bits == 0x5D0))
score += 40;
}
}
// Rule 4: deviation from an even split of dark and light, in 5% steps.
int total = n * n;
int k = (absi(dark * 20 - total * 10) + total - 1) / total;
score += k * 10;
return score;
}
// ---------------------------------------------------------------------------
// Encode
// ---------------------------------------------------------------------------
static void
put_bits(unsigned char *buf, int *pos, unsigned val, int n)
{
for (int i = n - 1; i >= 0; i--) {
buf[*pos >> 3] |= (unsigned char)(((val >> i) & 1) << (7 - (*pos & 7)));
(*pos)++;
}
}
int
qr_encode(struct qr *q, const unsigned char *data, int len)
{
int v;
if (len < 0)
return -1;
for (v = 1; v <= QR_MAX_VERSION; v++)
if (len <= qr_capacity(v))
break;
if (v > QR_MAX_VERSION)
return -1;
int total = vinfo[v].total;
int ecclen = vinfo[v].ecc;
int blocks = vinfo[v].blocks;
int datacw = total - ecclen * blocks;
// The table claims equal-sized blocks for every version this file supports.
// If that is ever untrue the interleave below is silently wrong, so check it
// rather than trust it.
if (datacw % blocks != 0)
return -1;
int blocklen = datacw / blocks;
// --- 1. payload -> data codewords ---------------------------------------
unsigned char cw[172]; // >= max total codewords
memset(cw, 0, sizeof(cw));
int pos = 0;
put_bits(cw, &pos, 0x4, 4); // byte mode
put_bits(cw, &pos, (unsigned)len, 8);
for (int i = 0; i < len; i++)
put_bits(cw, &pos, data[i], 8);
// Terminator, up to four zero bits, then round up to a whole codeword.
int spare = datacw * 8 - pos;
put_bits(cw, &pos, 0, spare < 4 ? spare : 4);
if (pos & 7)
put_bits(cw, &pos, 0, 8 - (pos & 7));
// Fill the rest with the standard alternating pad bytes. They are specified
// values, not arbitrary filler: a decoder uses the character count to know
// where the data ends, but the pad pattern keeps the symbol from having large
// uniform regions that mask selection would then have to fight.
for (int i = pos / 8; i < datacw; i++)
cw[i] = (i - pos / 8) % 2 == 0 ? 0xEC : 0x11;
// --- 2 and 3. error correction, then interleave -------------------------
unsigned char gen[30];
unsigned char ec[4][30];
rs_divisor(ecclen, gen);
for (int b = 0; b < blocks; b++)
rs_remainder(&cw[b * blocklen], blocklen, gen, ecclen, ec[b]);
unsigned char out[172];
int o = 0;
for (int i = 0; i < blocklen; i++)
for (int b = 0; b < blocks; b++)
out[o++] = cw[b * blocklen + i];
for (int i = 0; i < ecclen; i++)
for (int b = 0; b < blocks; b++)
out[o++] = ec[b][i];
// --- 4. function patterns ------------------------------------------------
memset(q, 0, sizeof(*q));
q->size = 17 + 4 * v;
draw_function_patterns(q);
// --- 5. zigzag placement -------------------------------------------------
//
// Two-module-wide columns walked from the right edge leftwards, alternating
// upward and downward. Column 6 is skipped entirely because it is the vertical
// timing pattern -- without that step every subsequent column lands one place
// out and the symbol is quietly unreadable.
int bit = 0;
int nbits = o * 8;
for (int right = q->size - 1; right >= 1; right -= 2) {
if (right == 6)
right = 5;
for (int vert = 0; vert < q->size; vert++) {
for (int j = 0; j < 2; j++) {
int x = right - j;
int upward = ((right + 1) & 2) == 0;
int y = upward ? q->size - 1 - vert : vert;
if (!q->fn[y][x] && bit < nbits) {
q->mod[y][x] = (unsigned char)((out[bit >> 3] >> (7 - (bit & 7))) & 1);
bit++;
}
}
}
}
// --- 6. pick a mask ------------------------------------------------------
int best = 0, bestscore = 0;
for (int m = 0; m < 8; m++) {
apply_mask(q, m);
draw_format(q, m);
int s = penalty(q);
if (m == 0 || s < bestscore) {
bestscore = s;
best = m;
}
apply_mask(q, m); // XOR again to undo; masking is involutive
}
apply_mask(q, best);
draw_format(q, best);
return 0;
}