Skip to main content

The Robust Reader

intermediate17 min readLesson 131 of 148

Checksums, bounds, and failure as a first-class return value: reading files that may be truncated, corrupted, or hostile.

Assume the file is lying

A robust reader treats every byte as suspect:

  1. Check every read's return count. fread returns items read; short reads mean truncation.
  2. Validate before use. Magic, version, record counts, field ranges โ€” reject anything outside the format you defined.
  3. Checksum the payload. A checksum cannot prove integrity, but it catches the corruption that actually happens (torn writes, bit rot, truncated transfers).
/* FNV-1a over a file's bytes */
unsigned long file_checksum(FILE *f, long size) {
    unsigned long h = 2166136261UL;
    int c;
    while (size-- > 0 && (c = fgetc(f)) != EOF)
        h = (h ^ (unsigned long)c) * 16777619UL;
    return h;
}

Store the checksum with the data (header or trailer); the reader recomputes and compares. Mismatch = reject the file, don't guess.

Failure is a return value

C's file functions signal through return codes โ€” NULL from fopen, item-count from fread, EOF from fgetc. The robust shape is one error path:

int load(const char *path, Doc *out) {
    FILE *f = fopen(path, "rb");
    if (!f) return -1;                       /* errno says why */
    /* ... every step checks and bails ... */
    fclose(f);
    return 0;
}

fclose on the success path and every bail path โ€” the leak in file form. (The pattern that finally mechanizes this discipline, RAII / __attribute__((cleanup)), is outside ISO C; the honest habit is a single goto cleanup exit or scrupulous pairing.)

Now practice

Robust Reader GymChecksummed binary blobs that reject corruption, and a log parser that never trusts a line.1 challenge ยท ยท ~26 min