The OpenD Programming Language

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