An x86-64 operating system built from xv6, now largely rewritten: SMP, a copy-on-write root filesystem (SchFS), PCI/USB/virtio drivers, a TTF rasteriser, and a Linux-numbered syscall ABI. No history before this point -- the tree was never under version control. This commit exists so the rename that follows has something to undo to.
354 lines
12 KiB
C
354 lines
12 KiB
C
// Host harness for kernel/core/qr.c.
|
|
//
|
|
// The QR encoder cannot be unit-tested against itself in any meaningful way: a
|
|
// symbol with a mistyped table entry is still a plausible-looking grid of black
|
|
// squares. The only real test is whether a decoder reads it back, so this builds
|
|
// the same encoder on the host and writes a PNG to scan.
|
|
//
|
|
// make qrtest a sample crash report
|
|
// build/tools/qrtest out.png "text" arbitrary payload
|
|
//
|
|
// It also self-checks the parts that *can* be checked mechanically -- the
|
|
// version table's internal consistency, and the Reed-Solomon remainder against
|
|
// a known vector -- so a transcription error is caught before anyone reaches
|
|
// for a phone.
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define QR_HOST
|
|
#include "../kernel/core/qr.c"
|
|
|
|
// --- Minimal PNG writer ----------------------------------------------------
|
|
//
|
|
// Uncompressed (stored) deflate blocks, so there is no zlib dependency for a
|
|
// tool whose entire job is to produce one small image. A stored block costs
|
|
// five bytes of header per 65535 bytes of data, which for a 41-module symbol is
|
|
// irrelevant.
|
|
|
|
static unsigned long crc_table[256];
|
|
|
|
static void
|
|
crc_init(void)
|
|
{
|
|
for (int n = 0; n < 256; n++) {
|
|
unsigned long c = (unsigned long)n;
|
|
for (int k = 0; k < 8; k++)
|
|
c = (c & 1) ? 0xEDB88320UL ^ (c >> 1) : c >> 1;
|
|
crc_table[n] = c;
|
|
}
|
|
}
|
|
|
|
static unsigned long
|
|
crc(const unsigned char *buf, int len)
|
|
{
|
|
unsigned long c = 0xFFFFFFFFUL;
|
|
for (int n = 0; n < len; n++)
|
|
c = crc_table[(c ^ buf[n]) & 0xFF] ^ (c >> 8);
|
|
return c ^ 0xFFFFFFFFUL;
|
|
}
|
|
|
|
static void
|
|
be32(unsigned char *p, unsigned long v)
|
|
{
|
|
p[0] = (unsigned char)(v >> 24); p[1] = (unsigned char)(v >> 16);
|
|
p[2] = (unsigned char)(v >> 8); p[3] = (unsigned char)v;
|
|
}
|
|
|
|
static void
|
|
put_chunk(FILE *f, const char *type, const unsigned char *data, int len)
|
|
{
|
|
unsigned char hdr[4];
|
|
be32(hdr, (unsigned long)len);
|
|
fwrite(hdr, 1, 4, f);
|
|
|
|
unsigned char *buf = malloc((size_t)len + 4);
|
|
memcpy(buf, type, 4);
|
|
if (len)
|
|
memcpy(buf + 4, data, (size_t)len);
|
|
fwrite(buf, 1, (size_t)len + 4, f);
|
|
|
|
be32(hdr, crc(buf, len + 4));
|
|
fwrite(hdr, 1, 4, f);
|
|
free(buf);
|
|
}
|
|
|
|
// Write the symbol as a PNG, `scale` pixels per module, with the 4-module quiet
|
|
// zone the standard requires. The quiet zone is not decoration: without it many
|
|
// decoders will not find the symbol at all.
|
|
static int
|
|
write_png(const char *path, const struct qr *q, int scale, int quiet)
|
|
{
|
|
int mods = q->size + 2 * quiet;
|
|
int w = mods * scale, h = w;
|
|
|
|
// Greyscale, 8-bit, one filter byte per row.
|
|
int stride = w + 1;
|
|
unsigned char *raw = malloc((size_t)stride * h);
|
|
for (int y = 0; y < h; y++) {
|
|
unsigned char *row = raw + (size_t)y * stride;
|
|
row[0] = 0; // filter: none
|
|
int my = y / scale - quiet;
|
|
for (int x = 0; x < w; x++) {
|
|
int mx = x / scale - quiet;
|
|
int dark = (mx >= 0 && my >= 0 && mx < q->size && my < q->size)
|
|
? q->mod[my][mx] : 0;
|
|
row[1 + x] = dark ? 0x00 : 0xFF;
|
|
}
|
|
}
|
|
int rawlen = stride * h;
|
|
|
|
// zlib stream: header, stored deflate blocks, adler32.
|
|
int cap = rawlen + (rawlen / 65535 + 1) * 5 + 6;
|
|
unsigned char *z = malloc((size_t)cap);
|
|
int zn = 0;
|
|
z[zn++] = 0x78; z[zn++] = 0x01;
|
|
int off = 0;
|
|
while (off < rawlen) {
|
|
int n = rawlen - off;
|
|
if (n > 65535) n = 65535;
|
|
z[zn++] = (off + n >= rawlen) ? 1 : 0;
|
|
z[zn++] = (unsigned char)(n & 0xFF);
|
|
z[zn++] = (unsigned char)(n >> 8);
|
|
z[zn++] = (unsigned char)(~n & 0xFF);
|
|
z[zn++] = (unsigned char)((~n >> 8) & 0xFF);
|
|
memcpy(z + zn, raw + off, (size_t)n);
|
|
zn += n;
|
|
off += n;
|
|
}
|
|
unsigned long a = 1, b = 0;
|
|
for (int i = 0; i < rawlen; i++) {
|
|
a = (a + raw[i]) % 65521;
|
|
b = (b + a) % 65521;
|
|
}
|
|
be32(z + zn, (b << 16) | a);
|
|
zn += 4;
|
|
|
|
FILE *f = fopen(path, "wb");
|
|
if (!f) { perror(path); return -1; }
|
|
fwrite("\x89PNG\r\n\x1a\n", 1, 8, f);
|
|
|
|
unsigned char ihdr[13];
|
|
be32(ihdr, (unsigned long)w);
|
|
be32(ihdr + 4, (unsigned long)h);
|
|
ihdr[8] = 8; ihdr[9] = 0; ihdr[10] = 0; ihdr[11] = 0; ihdr[12] = 0;
|
|
put_chunk(f, "IHDR", ihdr, 13);
|
|
put_chunk(f, "IDAT", z, zn);
|
|
put_chunk(f, "IEND", NULL, 0);
|
|
fclose(f);
|
|
|
|
free(raw);
|
|
free(z);
|
|
return 0;
|
|
}
|
|
|
|
// --- Self-checks -----------------------------------------------------------
|
|
|
|
static int failures;
|
|
|
|
static void
|
|
check(int ok, const char *what)
|
|
{
|
|
printf(" [%s] %s\n", ok ? "ok" : "FAIL", what);
|
|
if (!ok) failures++;
|
|
}
|
|
|
|
// The version table is the one part of qr.c that is transcription rather than
|
|
// reasoning, so check what can be checked: block sizes must divide evenly, and
|
|
// the symbol must have room for exactly the codewords claimed.
|
|
static void
|
|
check_tables(void)
|
|
{
|
|
printf("version table\n");
|
|
for (int v = 1; v <= QR_MAX_VERSION; v++) {
|
|
int total = vinfo[v].total;
|
|
int data = total - vinfo[v].ecc * vinfo[v].blocks;
|
|
char msg[128];
|
|
|
|
snprintf(msg, sizeof msg, "v%d: %d data codewords divide into %d blocks",
|
|
v, data, vinfo[v].blocks);
|
|
check(data > 0 && data % vinfo[v].blocks == 0, msg);
|
|
|
|
// Count the modules a symbol of this version actually leaves for data,
|
|
// by building one and asking. If the table's codeword count disagrees
|
|
// with the geometry, one of the two is wrong.
|
|
struct qr q;
|
|
memset(&q, 0, sizeof q);
|
|
q.size = 17 + 4 * v;
|
|
draw_function_patterns(&q);
|
|
int free_modules = 0;
|
|
for (int y = 0; y < q.size; y++)
|
|
for (int x = 0; x < q.size; x++)
|
|
if (!q.fn[y][x]) free_modules++;
|
|
|
|
snprintf(msg, sizeof msg,
|
|
"v%d: %d free modules holds %d codewords (%d spare bits)",
|
|
v, free_modules, total, free_modules - total * 8);
|
|
check(free_modules >= total * 8 && free_modules - total * 8 < 8, msg);
|
|
}
|
|
}
|
|
|
|
// A known Reed-Solomon vector. "HELLO WORLD" as a version 1-M byte-mode symbol
|
|
// has data codewords 0x40 0xB3 0x42 ... ; rather than depend on remembering the
|
|
// whole thing, check the standard's own worked example: the generator
|
|
// polynomial for degree 10 begins with the coefficients below.
|
|
static void
|
|
check_rs(void)
|
|
{
|
|
printf("reed-solomon\n");
|
|
unsigned char g[30];
|
|
|
|
rs_divisor(10, g);
|
|
// Generator for 10 EC codewords, highest power first, leading 1 implicit.
|
|
static const unsigned char want10[10] = {
|
|
216, 194, 159, 111, 199, 94, 95, 113, 157, 193
|
|
};
|
|
check(memcmp(g, want10, 10) == 0, "degree-10 generator polynomial");
|
|
|
|
rs_divisor(7, g);
|
|
static const unsigned char want7[7] = { 127, 122, 154, 164, 11, 68, 117 };
|
|
check(memcmp(g, want7, 7) == 0, "degree-7 generator polynomial");
|
|
|
|
// Multiplication is the primitive everything else rests on.
|
|
check(gmul(0, 5) == 0 && gmul(1, 5) == 5, "gf(256) identity and zero");
|
|
check(gmul(0x80, 0x02) == 0x1D, "gf(256) reduction by 0x11D");
|
|
}
|
|
|
|
static void
|
|
check_encode(void)
|
|
{
|
|
printf("encode\n");
|
|
struct qr q;
|
|
|
|
check(qr_encode(&q, (const unsigned char *)"HELLO", 5) == 0, "short payload encodes");
|
|
check(q.size == 21, "and picks version 1");
|
|
|
|
unsigned char big[QR_MAX_BYTES];
|
|
memset(big, 'A', sizeof big);
|
|
check(qr_encode(&q, big, QR_MAX_BYTES) == 0, "maximum payload encodes");
|
|
check(q.size == 41, "and picks version 6");
|
|
check(qr_encode(&q, big, QR_MAX_BYTES + 1) == -1, "one byte over is refused");
|
|
|
|
// Every function module must still be a function module after masking --
|
|
// i.e. masking must not have touched the finders.
|
|
qr_encode(&q, (const unsigned char *)"test", 4);
|
|
check(q.mod[0][0] && q.mod[6][6] && !q.mod[5][5], "finder pattern intact");
|
|
check(q.mod[q.size - 8][8] == 1, "dark module set");
|
|
// Row 6 alternates with the *even* coordinates dark, so the first timing
|
|
// module clear of the separator (x=8) is dark and x=9 is light. Asserting
|
|
// this the other way round was the one self-check that failed on the first
|
|
// run, and the test was what was wrong -- worth keeping the orientation
|
|
// written down.
|
|
check(q.mod[6][8] == 1 && q.mod[6][9] == 0, "horizontal timing pattern");
|
|
check(q.mod[8][6] == 1 && q.mod[9][6] == 0, "vertical timing pattern");
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
crc_init();
|
|
|
|
// -q suppresses the terminal rendering. It is on by default because a
|
|
// phone can read the symbol straight off the terminal without anyone
|
|
// opening a file, but it is 40 lines of escape codes and sometimes in the
|
|
// way.
|
|
int quiet = 0;
|
|
if (argc > 1 && strcmp(argv[1], "-q") == 0) {
|
|
quiet = 1;
|
|
argc--; argv++;
|
|
}
|
|
|
|
// -m dumps the raw module matrix and nothing else, for comparison against
|
|
// an independent encoder. This is the check that actually matters: a symbol
|
|
// can pass every self-check above and still be unreadable if the zigzag,
|
|
// the masking or the block interleave is subtly wrong, and none of those
|
|
// have a plausible-looking failure mode -- they all just produce noise that
|
|
// resembles a QR code. tools/qrcheck.py drives this.
|
|
// -f dumps the function-pattern map alongside the modules, so a checker can
|
|
// run the zigzag backwards and recover the codewords. Knowing *which*
|
|
// codeword two encoders disagree about is the difference between a bug you
|
|
// can see and one you can only stare at.
|
|
if (argc > 2 && strcmp(argv[1], "-f") == 0) {
|
|
struct qr q;
|
|
int n = (int)strlen(argv[2]);
|
|
if (n > QR_MAX_BYTES || qr_encode(&q, (const unsigned char *)argv[2], n) != 0)
|
|
return 1;
|
|
for (int y = 0; y < q.size; y++) {
|
|
for (int x = 0; x < q.size; x++)
|
|
putchar(q.fn[y][x] ? '1' : '0');
|
|
putchar('\n');
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (argc > 2 && strcmp(argv[1], "-m") == 0) {
|
|
struct qr q;
|
|
int n = (int)strlen(argv[2]);
|
|
if (n > QR_MAX_BYTES || qr_encode(&q, (const unsigned char *)argv[2], n) != 0)
|
|
return 1;
|
|
for (int y = 0; y < q.size; y++) {
|
|
for (int x = 0; x < q.size; x++)
|
|
putchar(q.mod[y][x] ? '1' : '0');
|
|
putchar('\n');
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char *out = argc > 1 ? argv[1] : "build/qr.png";
|
|
const char *text = argc > 2 ? argv[2] :
|
|
"SCHUNIX PANIC\n"
|
|
"build 8f2a91c4\n"
|
|
"trap 14 err 4 cpu 0\n"
|
|
"rip ffffffff80104e2a\n"
|
|
"cr2 0000000000000018\n"
|
|
"pid 7 schinit";
|
|
|
|
check_tables();
|
|
check_rs();
|
|
check_encode();
|
|
|
|
struct qr q;
|
|
int len = (int)strlen(text);
|
|
if (len > QR_MAX_BYTES) {
|
|
fprintf(stderr, "qrtest: payload is %d bytes, max %d\n", len, QR_MAX_BYTES);
|
|
return 1;
|
|
}
|
|
if (qr_encode(&q, (const unsigned char *)text, len) != 0) {
|
|
fprintf(stderr, "qrtest: encode failed\n");
|
|
return 1;
|
|
}
|
|
|
|
if (write_png(out, &q, 8, 4) != 0)
|
|
return 1;
|
|
|
|
printf("\n%d bytes -> version %d (%dx%d modules) -> %s\n",
|
|
len, (q.size - 17) / 4, q.size, q.size, out);
|
|
|
|
// Also dump it to the terminal, where a phone can read it directly off the
|
|
// screen without opening the file. Two spaces per module because a terminal
|
|
// cell is about twice as tall as it is wide, and a squashed symbol will not
|
|
// scan.
|
|
if (!quiet) {
|
|
printf("\n");
|
|
for (int i = 0; i < 2; i++) {
|
|
printf(" ");
|
|
for (int x = 0; x < q.size + 8; x++) printf("\033[107m \033[0m");
|
|
printf("\n");
|
|
}
|
|
for (int y = 0; y < q.size; y++) {
|
|
printf(" \033[107m \033[0m");
|
|
for (int x = 0; x < q.size; x++)
|
|
printf("%s \033[0m", q.mod[y][x] ? "\033[40m" : "\033[107m");
|
|
printf("\033[107m \033[0m\n");
|
|
}
|
|
for (int i = 0; i < 2; i++) {
|
|
printf(" ");
|
|
for (int x = 0; x < q.size + 8; x++) printf("\033[107m \033[0m");
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
printf("\n%s\n", failures ? "SELF-CHECKS FAILED" : "self-checks passed");
|
|
return failures ? 1 : 0;
|
|
}
|