1 /**
2 Cyclic Redundancy Check (32-bit) implementation.
3 
4 $(SCRIPT inhibitQuickIndex = 1;)
5 
6 $(DIVC quickindex,
7 $(BOOKTABLE ,
8 $(TR $(TH Category) $(TH Functions)
9 )
10 $(TR $(TDNW Template API) $(TD $(MYREF CRC) $(MYREF CRC32) $(MYREF CRC64ECMA) $(MYREF CRC64ISO)
11 )
12 )
13 $(TR $(TDNW OOP API) $(TD $(MYREF CRC32Digest) $(MYREF CRC64ECMADigest) $(MYREF CRC64ISODigest))
14 )
15 $(TR $(TDNW Helpers) $(TD $(MYREF crcHexString) $(MYREF crc32Of) $(MYREF crc64ECMAOf) $(MYREF crc64ISOOf))
16 )
17 )
18 )
19 
20  *
21  * This module conforms to the APIs defined in $(D std.digest). To understand the
22  * differences between the template and the OOP API, see $(MREF std, digest).
23  *
24  * This module publicly imports $(MREF std, digest) and can be used as a stand-alone
25  * module.
26  *
27  * Note:
28  * CRCs are usually printed with the MSB first. When using
29  * $(REF toHexString, std,digest) the result will be in an unexpected
30  * order. Use $(REF toHexString, std,digest)'s optional order parameter
31  * to specify decreasing order for the correct result. The $(LREF crcHexString)
32  * alias can also be used for this purpose.
33  *
34  * License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
35  *
36  * Authors:   Pavel "EvilOne" Minayev, Alex Rønne Petersen, Johannes Pfau
37  *
38  * References:
39  *      $(LINK2 http://en.wikipedia.org/wiki/Cyclic_redundancy_check, Wikipedia on CRC)
40  *
41  * Source: $(PHOBOSSRC std/digest/_crc.d)
42  *
43  * Standards:
44  * Implements the 'common' IEEE CRC32 variant
45  * (LSB-first order, Initial value uint.max, complement result)
46  *
47  * CTFE:
48  * Digests do not work in CTFE
49  */
50 /*
51  * Copyright (c) 2001 - 2002
52  * Pavel "EvilOne" Minayev
53  * Copyright (c) 2012
54  * Alex Rønne Petersen
55  * Distributed under the Boost Software License, Version 1.0.
56  *    (See accompanying file LICENSE_1_0.txt or copy at
57  *          http://www.boost.org/LICENSE_1_0.txt)
58  */
59 module std.digest.crc;
60 
61 public import std.digest;
62 
63 version (unittest)
64     import std.exception;
65 
66 
67 ///
68 @safe unittest
69 {
70     //Template API
71     import std.digest.crc;
72 
73     ubyte[4] hash = crc32Of("The quick brown fox jumps over the lazy dog");
74     assert(crcHexString(hash) == "414FA339");
75 
76     //Feeding data
77     ubyte[1024] data;
78     CRC32 crc;
79     crc.put(data[]);
80     crc.start(); //Start again
81     crc.put(data[]);
82     hash = crc.finish();
83 }
84 
85 ///
86 @safe unittest
87 {
88     //OOP API
89     import std.digest.crc;
90 
91     auto crc = new CRC32Digest();
92     ubyte[] hash = crc.digest("The quick brown fox jumps over the lazy dog");
93     assert(crcHexString(hash) == "414FA339"); //352441c2
94 
95     //Feeding data
96     ubyte[1024] data;
97     crc.put(data[]);
98     crc.reset(); //Start again
99     crc.put(data[]);
100     hash = crc.finish();
101 }
102 
genTables(T)103 private T[256][8] genTables(T)(T polynomial)
104 {
105     T[256][8] res = void;
106 
107     foreach (i; 0 .. 0x100)
108     {
109         T crc = i;
110         foreach (_; 0 .. 8)
111             crc = (crc >> 1) ^ (-int(crc & 1) & polynomial);
112         res[0][i] = crc;
113     }
114 
115     foreach (i; 0 .. 0x100)
116     {
117         res[1][i] = (res[0][i] >> 8) ^ res[0][res[0][i] & 0xFF];
118         res[2][i] = (res[1][i] >> 8) ^ res[0][res[1][i] & 0xFF];
119         res[3][i] = (res[2][i] >> 8) ^ res[0][res[2][i] & 0xFF];
120         res[4][i] = (res[3][i] >> 8) ^ res[0][res[3][i] & 0xFF];
121         res[5][i] = (res[4][i] >> 8) ^ res[0][res[4][i] & 0xFF];
122         res[6][i] = (res[5][i] >> 8) ^ res[0][res[5][i] & 0xFF];
123         res[7][i] = (res[6][i] >> 8) ^ res[0][res[6][i] & 0xFF];
124     }
125     return res;
126 }
127 
128 @system unittest
129 {
130     auto tables = genTables(0xEDB88320);
131     assert(tables[0][0] == 0x00000000 && tables[0][$ - 1] == 0x2d02ef8d && tables[7][$ - 1] == 0x264b06e6);
132 }
133 
134 /**
135  * Template API CRC32 implementation.
136  * See $(D std.digest) for differences between template and OOP API.
137  */
138 alias CRC32 = CRC!(32, 0xEDB88320);
139 
140 /**
141  * Template API CRC64-ECMA implementation.
142  * See $(D std.digest.digest) for differences between template and OOP API.
143  */
144 alias CRC64ECMA = CRC!(64, 0xC96C5795D7870F42);
145 
146 /**
147  * Template API CRC64-ISO implementation.
148  * See $(D std.digest.digest) for differences between template and OOP API.
149  */
150 alias CRC64ISO = CRC!(64, 0xD800000000000000);
151 
152 /**
153  * Generic Template API used for CRC32 and CRC64 implementations.
154  *
155  * The N parameter indicate the size of the hash in bits.
156  * The parameter P specify the polynomial to be used for reduction.
157  *
158  * You may want to use the CRC32, CRC65ECMA and CRC64ISO aliases
159  * for convenience.
160  *
161  * See $(D std.digest.digest) for differences between template and OOP API.
162  */
163 struct CRC(uint N, ulong P) if (N == 32 || N == 64)
164 {
165     private:
166         static if (N == 32)
167         {
168             alias T = uint;
169         }
170         else
171         {
172             alias T = ulong;
173         }
174 
175         static immutable T[256][8] tables = genTables!T(P);
176 
177         /**
178          * Type of the finished CRC hash.
179          * ubyte[4] if N is 32, ubyte[8] if N is 64.
180          */
181         alias R = ubyte[T.sizeof];
182 
183         // magic initialization constants
184         T _state = T.max;
185 
186     public:
187         /**
188          * Use this to feed the digest with data.
189          * Also implements the $(REF isOutputRange, std,range,primitives)
190          * interface for $(D ubyte) and $(D const(ubyte)[]).
191          */
put(scope const (ubyte)[]data...)192         void put(scope const(ubyte)[] data...) @trusted pure nothrow @nogc
193         {
194             T crc = _state;
195             // process eight bytes at once
196             while (data.length >= 8)
197             {
198                 // Use byte-wise reads to support architectures without HW support
199                 // for unaligned reads. This can be optimized by compilers to a single
200                 // 32-bit read if unaligned reads are supported.
201                 // DMD is not able to do this optimization though, so explicitly
202                 // do unaligned reads for DMD's architectures.
203                 version (X86)
204                     enum hasLittleEndianUnalignedReads = true;
205                 else version (X86_64)
206                     enum hasLittleEndianUnalignedReads = true;
207                 else
208                     enum hasLittleEndianUnalignedReads = false; // leave decision to optimizer
209                 static if (hasLittleEndianUnalignedReads)
210                 {
211                     uint one = (cast(uint*) data.ptr)[0];
212                     uint two = (cast(uint*) data.ptr)[1];
213                 }
214                 else
215                 {
216                     uint one = (data.ptr[3] << 24 | data.ptr[2] << 16 | data.ptr[1] << 8 | data.ptr[0]);
217                     uint two = (data.ptr[7] << 24 | data.ptr[6] << 16 | data.ptr[5] << 8 | data.ptr[4]);
218                 }
219 
220                 static if (N == 32)
221                 {
222                     one ^= crc;
223                 }
224                 else
225                 {
226                     one ^= (crc & 0xffffffff);
227                     two ^= (crc >> 32);
228                 }
229 
230                 crc =
231                     tables[0][two >> 24] ^
232                     tables[1][(two >> 16) & 0xFF] ^
233                     tables[2][(two >>  8) & 0xFF] ^
234                     tables[3][two & 0xFF] ^
235                     tables[4][one >> 24] ^
236                     tables[5][(one >> 16) & 0xFF] ^
237                     tables[6][(one >>  8) & 0xFF] ^
238                     tables[7][one & 0xFF];
239 
240                 data = data[8 .. $];
241             }
242             // remaining 1 to 7 bytes
243             foreach (d; data)
244                 crc = (crc >> 8) ^ tables[0][(crc & 0xFF) ^ d];
245             _state = crc;
246         }
247 
248         /**
249          * Used to initialize the CRC32 digest.
250          *
251          * Note:
252          * For this CRC32 Digest implementation calling start after default construction
253          * is not necessary. Calling start is only necessary to reset the Digest.
254          *
255          * Generic code which deals with different Digest types should always call start though.
256          */
start()257         void start() @safe pure nothrow @nogc
258         {
259             this = CRC.init;
260         }
261 
262         /**
263          * Returns the finished CRC hash. This also calls $(LREF start) to
264          * reset the internal state.
265          */
finish()266         R finish() @safe pure nothrow @nogc
267         {
268             auto tmp = peek();
269             start();
270             return tmp;
271         }
272 
273         /**
274          * Works like $(D finish) but does not reset the internal state, so it's possible
275          * to continue putting data into this CRC after a call to peek.
276          */
peek()277         R peek() const @safe pure nothrow @nogc
278         {
279             import std.bitmanip : nativeToLittleEndian;
280             //Complement, LSB first / Little Endian, see http://rosettacode.org/wiki/CRC-32
281             return nativeToLittleEndian(~_state);
282         }
283 }
284 
285 ///
286 @safe unittest
287 {
288     //Simple example, hashing a string using crc32Of helper function
289     ubyte[4] hash32 = crc32Of("abc");
290     //Let's get a hash string
291     assert(crcHexString(hash32) == "352441C2");
292     // Repeat for CRC64
293     ubyte[8] hash64ecma = crc64ECMAOf("abc");
294     assert(crcHexString(hash64ecma) == "2CD8094A1A277627");
295     ubyte[8] hash64iso = crc64ISOOf("abc");
296     assert(crcHexString(hash64iso) == "3776C42000000000");
297 }
298 
299 ///
300 @safe unittest
301 {
302     ubyte[1024] data;
303     //Using the basic API
304     CRC32 hash32;
305     CRC64ECMA hash64ecma;
306     CRC64ISO hash64iso;
307     //Initialize data here...
308     hash32.put(data);
309     ubyte[4] result32 = hash32.finish();
310     hash64ecma.put(data);
311     ubyte[8] result64ecma = hash64ecma.finish();
312     hash64iso.put(data);
313     ubyte[8] result64iso = hash64iso.finish();
314 }
315 
316 ///
317 @safe unittest
318 {
319     //Let's use the template features:
320     //Note: When passing a CRC32 to a function, it must be passed by reference!
321     void doSomething(T)(ref T hash)
322     if (isDigest!T)
323     {
324       hash.put(cast(ubyte) 0);
325     }
326     CRC32 crc32;
327     crc32.start();
328     doSomething(crc32);
329     assert(crcHexString(crc32.finish()) == "D202EF8D");
330     // repeat for CRC64
331     CRC64ECMA crc64ecma;
332     crc64ecma.start();
333     doSomething(crc64ecma);
334     assert(crcHexString(crc64ecma.finish()) == "1FADA17364673F59");
335     CRC64ISO crc64iso;
336     crc64iso.start();
337     doSomething(crc64iso);
338     assert(crcHexString(crc64iso.finish()) == "6F90000000000000");
339 }
340 
341 @safe unittest
342 {
343     assert(isDigest!CRC32);
344     assert(isDigest!CRC64ECMA);
345     assert(isDigest!CRC64ISO);
346 }
347 
348 @system unittest
349 {
350     ubyte[4] digest;
351 
352     CRC32 crc;
353     crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz");
354     assert(crc.peek() == cast(ubyte[]) x"bd50274c");
355     crc.start();
356     crc.put(cast(ubyte[])"");
357     assert(crc.finish() == cast(ubyte[]) x"00000000");
358 
359     digest = crc32Of("");
360     assert(digest == cast(ubyte[]) x"00000000");
361 
362     //Test vector from http://rosettacode.org/wiki/CRC-32
363     assert(crcHexString(crc32Of("The quick brown fox jumps over the lazy dog")) == "414FA339");
364 
365     digest = crc32Of("a");
366     assert(digest == cast(ubyte[]) x"43beb7e8");
367 
368     digest = crc32Of("abc");
369     assert(digest == cast(ubyte[]) x"c2412435");
370 
371     digest = crc32Of("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
372     assert(digest == cast(ubyte[]) x"5f3f1a17");
373 
374     digest = crc32Of("message digest");
375     assert(digest == cast(ubyte[]) x"7f9d1520");
376 
377     digest = crc32Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
378     assert(digest == cast(ubyte[]) x"d2e6c21f");
379 
380     digest = crc32Of("1234567890123456789012345678901234567890"~
381                     "1234567890123456789012345678901234567890");
382     assert(digest == cast(ubyte[]) x"724aa97c");
383 
384     assert(crcHexString(cast(ubyte[4]) x"c3fcd3d7") == "D7D3FCC3");
385 }
386 
387 @system unittest
388 {
389     ubyte[8] digest;
390 
391     CRC64ECMA crc;
392     crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz");
393     assert(crc.peek() == cast(ubyte[]) x"2f121b7575789626");
394     crc.start();
395     crc.put(cast(ubyte[])"");
396     assert(crc.finish() == cast(ubyte[]) x"0000000000000000");
397     digest = crc64ECMAOf("");
398     assert(digest == cast(ubyte[]) x"0000000000000000");
399 
400     //Test vector from http://rosettacode.org/wiki/CRC-32
401     assert(crcHexString(crc64ECMAOf("The quick brown fox jumps over the lazy dog")) == "5B5EB8C2E54AA1C4");
402 
403     digest = crc64ECMAOf("a");
404     assert(digest == cast(ubyte[]) x"052b652e77840233");
405 
406     digest = crc64ECMAOf("abc");
407     assert(digest == cast(ubyte[]) x"2776271a4a09d82c");
408 
409     digest = crc64ECMAOf("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
410     assert(digest == cast(ubyte[]) x"4b7cdce3746c449f");
411 
412     digest = crc64ECMAOf("message digest");
413     assert(digest == cast(ubyte[]) x"6f9b8a3156c9bc5d");
414 
415     digest = crc64ECMAOf("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
416     assert(digest == cast(ubyte[]) x"2656b716e1bf0503");
417 
418     digest = crc64ECMAOf("1234567890123456789012345678901234567890"~
419                          "1234567890123456789012345678901234567890");
420     assert(digest == cast(ubyte[]) x"bd3eb7765d0a22ae");
421 
422     assert(crcHexString(cast(ubyte[8]) x"c3fcd3d7efbeadde") == "DEADBEEFD7D3FCC3");
423 }
424 
425 @system unittest
426 {
427     ubyte[8] digest;
428 
429     CRC64ISO crc;
430     crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz");
431     assert(crc.peek() == cast(ubyte[]) x"f0494ab780989b42");
432     crc.start();
433     crc.put(cast(ubyte[])"");
434     assert(crc.finish() == cast(ubyte[]) x"0000000000000000");
435     digest = crc64ISOOf("");
436     assert(digest == cast(ubyte[]) x"0000000000000000");
437 
438     //Test vector from http://rosettacode.org/wiki/CRC-32
439     assert(crcHexString(crc64ISOOf("The quick brown fox jumps over the lazy dog")) == "4EF14E19F4C6E28E");
440 
441     digest = crc64ISOOf("a");
442     assert(digest == cast(ubyte[]) x"0000000000002034");
443 
444     digest = crc64ISOOf("abc");
445     assert(digest == cast(ubyte[]) x"0000000020c47637");
446 
447     digest = crc64ISOOf("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
448     assert(digest == cast(ubyte[]) x"5173f717971365e5");
449 
450     digest = crc64ISOOf("message digest");
451     assert(digest == cast(ubyte[]) x"a2c355bbc0b93f86");
452 
453     digest = crc64ISOOf("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
454     assert(digest == cast(ubyte[]) x"598B258292E40084");
455 
456     digest = crc64ISOOf("1234567890123456789012345678901234567890"~
457                         "1234567890123456789012345678901234567890");
458     assert(digest == cast(ubyte[]) x"760cd2d3588bf809");
459 
460     assert(crcHexString(cast(ubyte[8]) x"c3fcd3d7efbeadde") == "DEADBEEFD7D3FCC3");
461 }
462 
463 /**
464  * This is a convenience alias for $(REF digest, std,digest) using the
465  * CRC32 implementation.
466  *
467  * Params:
468  *      data = $(D InputRange) of $(D ElementType) implicitly convertible to
469  *             $(D ubyte), $(D ubyte[]) or $(D ubyte[num]) or one or more arrays
470  *             of any type.
471  *
472  * Returns:
473  *      CRC32 of data
474  */
475 //simple alias doesn't work here, hope this gets inlined...
crc32Of(T...)476 ubyte[4] crc32Of(T...)(T data)
477 {
478     return digest!(CRC32, T)(data);
479 }
480 
481 ///
482 @system unittest
483 {
484     ubyte[] data = [4,5,7,25];
485     assert(data.crc32Of == [167, 180, 199, 131]);
486 
487     import std.utf : byChar;
488     assert("hello"d.byChar.crc32Of == [134, 166, 16, 54]);
489 
490     ubyte[4] hash = "abc".crc32Of();
491     assert(hash == digest!CRC32("ab", "c"));
492 
493     import std.range : iota;
494     enum ubyte S = 5, F = 66;
495     assert(iota(S, F).crc32Of == [59, 140, 234, 154]);
496 }
497 
498 /**
499  * This is a convenience alias for $(REF digest, std,digest) using the
500  * CRC64-ECMA implementation.
501  *
502  * Params:
503  *      data = $(D InputRange) of $(D ElementType) implicitly convertible to
504  *             $(D ubyte), $(D ubyte[]) or $(D ubyte[num]) or one or more arrays
505  *             of any type.
506  *
507  * Returns:
508  *      CRC64-ECMA of data
509  */
510 //simple alias doesn't work here, hope this gets inlined...
crc64ECMAOf(T...)511 ubyte[8] crc64ECMAOf(T...)(T data)
512 {
513     return digest!(CRC64ECMA, T)(data);
514 }
515 
516 ///
517 @system unittest
518 {
519     ubyte[] data = [4,5,7,25];
520     assert(data.crc64ECMAOf == [58, 142, 220, 214, 118, 98, 105, 69]);
521 
522     import std.utf : byChar;
523     assert("hello"d.byChar.crc64ECMAOf == [177, 55, 185, 219, 229, 218, 30, 155]);
524 
525     ubyte[8] hash = "abc".crc64ECMAOf();
526     assert("abc".crc64ECMAOf == [39, 118, 39, 26, 74, 9, 216, 44]);
527     assert(hash == digest!CRC64ECMA("ab", "c"));
528 
529     import std.range : iota;
530     enum ubyte S = 5, F = 66;
531     assert(iota(S, F).crc64ECMAOf == [6, 184, 91, 238, 46, 213, 127, 188]);
532 }
533 
534 /**
535  * This is a convenience alias for $(REF digest, std,digest,digest) using the
536  * CRC64-ISO implementation.
537  *
538  * Params:
539  *      data = $(D InputRange) of $(D ElementType) implicitly convertible to
540  *             $(D ubyte), $(D ubyte[]) or $(D ubyte[num]) or one or more arrays
541  *             of any type.
542  *
543  * Returns:
544  *      CRC64-ISO of data
545  */
546 //simple alias doesn't work here, hope this gets inlined...
crc64ISOOf(T...)547 ubyte[8] crc64ISOOf(T...)(T data)
548 {
549     return digest!(CRC64ISO, T)(data);
550 }
551 
552 ///
553 @system unittest
554 {
555     ubyte[] data = [4,5,7,25];
556     assert(data.crc64ISOOf == [0, 0, 0, 80, 137, 232, 203, 120]);
557 
558     import std.utf : byChar;
559     assert("hello"d.byChar.crc64ISOOf == [0, 0, 16, 216, 226, 238, 62, 60]);
560 
561     ubyte[8] hash = "abc".crc64ISOOf();
562     assert("abc".crc64ISOOf == [0, 0, 0, 0, 32, 196, 118, 55]);
563     assert(hash == digest!CRC64ISO("ab", "c"));
564 
565     import std.range : iota;
566     enum ubyte S = 5, F = 66;
567 
568     assert(iota(S, F).crc64ISOOf == [21, 185, 116, 95, 219, 11, 54, 7]);
569 }
570 
571 /**
572  * producing the usual CRC32 string output.
573  */
574 public alias crcHexString = toHexString!(Order.decreasing);
575 ///ditto
576 public alias crcHexString = toHexString!(Order.decreasing, 16);
577 
578 /**
579  * OOP API CRC32 implementation.
580  * See $(D std.digest) for differences between template and OOP API.
581  *
582  * This is an alias for $(D $(REF WrapperDigest, std,digest)!CRC32), see
583  * there for more information.
584  */
585 alias CRC32Digest = WrapperDigest!CRC32;
586 
587 /**
588  * OOP API CRC64-ECMA implementation.
589  * See $(D std.digest.digest) for differences between template and OOP API.
590  *
591  * This is an alias for $(D $(REF WrapperDigest, std,digest,digest)!CRC64ECMA),
592  * see there for more information.
593  */
594 alias CRC64ECMADigest = WrapperDigest!CRC64ECMA;
595 
596 /**
597  * OOP API CRC64-ISO implementation.
598  * See $(D std.digest.digest) for differences between template and OOP API.
599  *
600  * This is an alias for $(D $(REF WrapperDigest, std,digest,digest)!CRC64ISO),
601  * see there for more information.
602  */
603 alias CRC64ISODigest = WrapperDigest!CRC64ISO;
604 
605 ///
606 @safe unittest
607 {
608     //Simple example, hashing a string using Digest.digest helper function
609     auto crc = new CRC32Digest();
610     ubyte[] hash = crc.digest("abc");
611     //Let's get a hash string
612     assert(crcHexString(hash) == "352441C2");
613 }
614 
615 ///
616 @system unittest
617 {
618      //Let's use the OOP features:
test(Digest dig)619     void test(Digest dig)
620     {
621       dig.put(cast(ubyte) 0);
622     }
623     auto crc = new CRC32Digest();
624     test(crc);
625 
626     //Let's use a custom buffer:
627     ubyte[4] buf;
628     ubyte[] result = crc.finish(buf[]);
629     assert(crcHexString(result) == "D202EF8D");
630 }
631 
632 ///
633 @safe unittest
634 {
635     //Simple example
636     auto hash = new CRC32Digest();
637     hash.put(cast(ubyte) 0);
638     ubyte[] result = hash.finish();
639 }
640 
641 ///
642 @system unittest
643 {
644     //using a supplied buffer
645     ubyte[4] buf;
646     auto hash = new CRC32Digest();
647     hash.put(cast(ubyte) 0);
648     ubyte[] result = hash.finish(buf[]);
649     //The result is now in result (and in buf. If you pass a buffer which is bigger than
650     //necessary, result will have the correct length, but buf will still have it's original
651     //length)
652 }
653 
654 @system unittest
655 {
656     import std.range;
657 
658     auto crc = new CRC32Digest();
659 
660     crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz");
661     assert(crc.peek() == cast(ubyte[]) x"bd50274c");
662     crc.reset();
663     crc.put(cast(ubyte[])"");
664     assert(crc.finish() == cast(ubyte[]) x"00000000");
665 
666     crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz");
667     ubyte[20] result;
668     auto result2 = crc.finish(result[]);
669     assert(result[0 .. 4] == result2 && result2 == cast(ubyte[]) x"bd50274c");
670 
671     debug
672         assertThrown!Error(crc.finish(result[0 .. 3]));
673 
674     assert(crc.length == 4);
675 
676     assert(crc.digest("") == cast(ubyte[]) x"00000000");
677 
678     assert(crc.digest("a") == cast(ubyte[]) x"43beb7e8");
679 
680     assert(crc.digest("abc") == cast(ubyte[]) x"c2412435");
681 
682     assert(crc.digest("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")
683            == cast(ubyte[]) x"5f3f1a17");
684 
685     assert(crc.digest("message digest") == cast(ubyte[]) x"7f9d1520");
686 
687     assert(crc.digest("abcdefghijklmnopqrstuvwxyz")
688            == cast(ubyte[]) x"bd50274c");
689 
690     assert(crc.digest("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
691            == cast(ubyte[]) x"d2e6c21f");
692 
693     assert(crc.digest("1234567890123456789012345678901234567890",
694                                    "1234567890123456789012345678901234567890")
695            == cast(ubyte[]) x"724aa97c");
696 
697     ubyte[] onemilliona = new ubyte[1000000];
698     onemilliona[] = 'a';
699     auto digest = crc32Of(onemilliona);
700     assert(digest == cast(ubyte[]) x"BCBF25DC");
701 
702     auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000);
703     digest = crc32Of(oneMillionRange);
704     assert(digest == cast(ubyte[]) x"BCBF25DC");
705 }
706