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:
- Check every read's return count.
freadreturns items read; short reads mean truncation. - Validate before use. Magic, version, record counts, field ranges โ reject anything outside the format you defined.
- 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.)