networking · intermediate · ~25 min
Bit-by-bit polynomial division — the heart of every CRC.
Compute the CRC-8 (polynomial 0x07, initial value 0x00, no XOR-out) of a byte stream:
unsigned char crc8(const unsigned char *data, size_t len);
The algorithm:
crc = 0.b in data:crc ^= b.crc is set, crc = (crc << 1) ^ 0x07; else crc = crc << 1. Mask to 8 bits if needed.crc.crc8("123456789", 9) -> 0xF4
crc8("", 0) -> 0x00
crc8("\x01", 1) -> 0x07
crc8("A", 1) -> 0xC0 // (one byte 'A' = 0x41)
Every 1-Wire temperature sensor, every USB Power Delivery negotiator, and many CAN bus frames carry a CRC-8 that you can compute yourself with this exact loop. Real systems use lookup tables for speed — but the loop form is the reference you compare against.
CRC checksums are everywhere: 1-Wire / I²C / CAN / SMBus all use CRC-8, and TCP/Ethernet use larger CRCs with the same core algorithm. Writing one by hand demystifies what looks like a magic value in protocol specs.
Byte buffer + length.
The CRC-8 byte.
No lookup tables; bit-by-bit loop.
#include <stddef.h>
unsigned char crc8(const unsigned char *data, size_t len) { /* TODO */ return 0; }
Forgetting to mask crc back to 8 bits after the shift. Using the wrong polynomial. Initialising with 0xFF (some CRC-8 variants do; this one uses 0x00).
Empty buffer → 0; single byte; ASCII string "123456789" is the canonical check value (0xF4).
O(8 * len).
Solve this exercise in the browser editor — compile and run against the test harness, no setup required.