Skip to content

Commit b8c0e74

Browse files
committed
Reworked the shift-register-trick section
1 parent d9443e4 commit b8c0e74

File tree

1 file changed

+79
-59
lines changed

1 file changed

+79
-59
lines changed

README.md

Lines changed: 79 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ brute force search:
240240
^ 1 => 11101001 (0xe9 != 0x3b)
241241
^ 1 => 01110101 (0x75 != 0x3b)
242242
^ 1 => 00111011 (0x3b == 0x3b) !!! found our bit-error
243-
^- note no CRC repeats
243+
244244
corrected message:
245245
= 01101000 01101001 00100001 00000000 => 00111011 (0x3b == 0x3b)
246246
```
@@ -264,83 +264,103 @@ There are a couple implementation tricks worth noting in ramcrc32bd:
264264
makes sense to only search for more bit-errors when a solution with
265265
fewer bit-errors can't be found.
266266

267-
This means ramcrc32bd should read quite quickly in the common case of
268-
few/none bit-errors. Though this does risk reads sort of slowing down
269-
as bit-errors develop.
267+
By trying fewer bit-errors first, ramcrc32bd should return quickly in
268+
the common case of few/no bit-errors.
270269

271-
2. We don't actually need to permute the message for every bit-flip.
270+
Though this does risk degraded performance over time as bit-errors
271+
develop.
272272

273-
CRCs are pretty nifty in that they're linear. The CRC of the xor
274-
of two messages is equivalent to the xor of the two CRCS:
273+
2. We don't actually need to permute the message to try every bit-flip.
275274

276-
```
277-
crc(a xor b):
278-
= 01101000 01101001 01100001 00000000
279-
^ 1
280-
-------------------------------------
281-
= 01101000 01101001 00100001 00000000 => 00111011 (0x3b == 0x3b)
275+
First note that since CRCs are a glorified remainder operation,
276+
shifting a message (multiplying by $x$ in GF(2)) and then calculating
277+
the CRC is equivalent to shifting the CRC and then calculating the
278+
remainder:
282279

283-
crc(a) xor crc(b):
284-
= 01101000 01101001 01100001 00000000 => 11111100 (0xfc)
285-
^ 1 => ^ 11000111 (0xc7)
286-
----------
287-
= 00111011 (0x3b == 0x3b)
280+
```
281+
crc(a << 1):
282+
= 00111001 10110100 00110110 00000000 => 01000010 (0x42)
283+
s 01110011 01101000 01101100 00000000 => 10000100 (0x84)
284+
s 11100110 11010000 11011000 00000000 => 00001111 (0x0f)
285+
286+
(crc(a) << 1) % p:
287+
= 00111001 10110100 00110110 00000000 => 01000010 (0x42)
288+
s 0 10000100
289+
^ 0 00000000
290+
= 10000100 (0x84)
291+
s 1 00001000
292+
^ 1 00000111
293+
= 00001111 (0x0f)
288294
```
289295

290-
This means we can quickly check the effect of a bit-flip by xoring our
291-
CRC with the CRC of the bit flip.
292-
293-
If this wasn't convenient enough, shifting (multiplying by 2) is also
294-
preserved over CRCs:
296+
We can use this to quickly iterate through all CRCs that represent a
297+
single bit:
295298

296299
```
297-
crc(a << 1):
298-
= 1 => 11100000 (0xe0)
299-
s 1 => 11000111 (0xc7)
300-
301-
crc(crc(a) << 1):
302-
= 1 => 11100000 (0xe0)
303-
s 1 11000000
304-
^ 1 00000111
305-
------------
306-
= 0 11000111 (0xc7)
300+
a = (a << 1) % p:
301+
= 00000001 => 00000001 (0x01)
302+
s 00000010 => 00000010 (0x02)
303+
s 00000100 => 00000100 (0x04)
304+
s 00001000 => 00001000 (0x08)
305+
s 00010000 => 00010000 (0x10)
306+
s 00100000 => 00100000 (0x20)
307+
s 01000000 => 01000000 (0x40)
308+
s 10000000 => 10000000 (0x80)
309+
s (1)00000000 => 00001110 (0x0e)
310+
s (1) 00000000 => 00011100 (0x1c)
311+
s (1) 00000000 => 00111000 (0x38)
312+
s (1) 00000000 => 01110000 (0x70)
313+
s (1) 00000000 => 11100000 (0xe0)
314+
s (1) 00000000 => 11000111 (0xc7)
315+
s (1) 00000000 => 10001001 (0x89)
316+
s (1) 00000000 => 00010101 (0x15)
307317
```
308318

309-
So testing every bit-flip requires only a single register that we
310-
repeatedly shift and xor until we find a CRC match (or don't).
319+
Combining this with the fact that CRCs are linear, i.e. the CRC of the
320+
xor of two messages (addition in GF(2)) is equivalent to the xor of
321+
two CRCs:
311322

312-
Once we find a match, we can use the number of shifts to figure out
313-
which bit we needed to flip in the original message:
323+
```
324+
crc(a ^ b):
325+
= 01100001 01100100 01100100 00000000
326+
^ 00011001 00001011 00010110 00000000
327+
= 01111000 01101111 01110010 00000000 => 01011001 (0x59)
328+
329+
crc(a) ^ crc(b):
330+
= 01100001 01100100 01100100 00000000 => 00110100 (0x34)
331+
^ 00011001 00001011 00010110 00000000 => ^ 01101101 (0x6d)
332+
= 01011001 (0x59)
333+
```
334+
335+
And we can quickly test the affect of every possible bit-flip by
336+
shifting a single register per simulated bit-flip and xoring it
337+
into our original CRC:
314338

315339
```
316340
fancy brute force search:
317341
= 01101000 01101001 01100001 00000000 => 11111100 (0xfc != 0x3b)
318-
s 0 00000001 => 11111101 (0xfd != 0x3b)
319-
s 0 00000010 => 11111110 (0xfe != 0x3b)
320-
s 0 00000100 => 11111000 (0xf8 != 0x3b)
321-
s 0 00001000 => 11110100 (0xf4 != 0x3b)
322-
s 0 00010000 => 11101100 (0xec != 0x3b)
323-
s 0 00100000 => 11011100 (0xdc != 0x3b)
324-
s 0 01000000 => 10111100 (0xbc != 0x3b)
325-
s 0 10000000 => 01111100 (0x7c != 0x3b)
326-
s 1 00000000
327-
^ 1 00000111
328-
= (1)00000111 => 11111011 (0xfb != 0x3b)
329-
s (1) 00001110 => 11110010 (0xf2 != 0x3b)
330-
s (1)0 00011100 => 11100000 (0xe0 != 0x3b)
331-
s (1) 0 00111000 => 11000100 (0xc4 != 0x3b)
332-
s (1) 0 01110000 => 10001100 (0x8c != 0x3b)
333-
s (1) 0 11100000 => 00011100 (0x1c != 0x3b)
334-
s 1 11000000
335-
^ 1 00000111
336-
= (1) 0 11000111 => 00111011 (0x3b == 0x3b) !!! found our bit-error
337-
^- note no CRC repeats
342+
^ 00000001 => 11111101 (0xfd != 0x3b)
343+
^ 00000010 => 11111110 (0xfe != 0x3b)
344+
^ 00000100 => 11111000 (0xf8 != 0x3b)
345+
^ 00001000 => 11110100 (0xf4 != 0x3b)
346+
^ 00010000 => 11101100 (0xec != 0x3b)
347+
^ 00100000 => 11011100 (0xdc != 0x3b)
348+
^ 01000000 => 10111100 (0xbc != 0x3b)
349+
^ 10000000 => 01111100 (0x7c != 0x3b)
350+
^ (1)00000111 => 11111011 (0xfb != 0x3b)
351+
^ (1) 00001110 => 11110010 (0xf2 != 0x3b)
352+
^ (1) 00011100 => 11100000 (0xe0 != 0x3b)
353+
^ (1) 00111000 => 11000100 (0xc4 != 0x3b)
354+
^ (1) 01110000 => 10001100 (0x8c != 0x3b)
355+
^ (1) 11100000 => 00011100 (0x1c != 0x3b)
356+
^ (1) 11000111 => 00111011 (0x3b == 0x3b) !!! found our bit-error
357+
338358
corrected message:
339359
= 01101000 01101001 00100001 00000000 => 00111011 (0x3b == 0x3b)
340360
```
341361

342-
Still $O(n^e)$, but limited only by your CPU's shift, xor, and
343-
branching hardware. No memory accesses required.
362+
The end result is still $O(n^e)$, but limited only by your CPU's
363+
shift, xor, and branching hardware. No memory accesses required.
344364

345365
See [ramcrc32bd_read][ramcrc32bd_read] for an implementation of this.
346366

0 commit comments

Comments
 (0)