blob: c606f4c11086387838eb5a47922278212b996c5d [file] [log] [blame]
/**
Cyclic Redundancy Check (32-bit) implementation.
$(SCRIPT inhibitQuickIndex = 1;)
$(DIVC quickindex,
$(BOOKTABLE ,
$(TR $(TH Category) $(TH Functions)
)
$(TR $(TDNW Template API) $(TD $(MYREF CRC) $(MYREF CRC32) $(MYREF CRC64ECMA) $(MYREF CRC64ISO)
)
)
$(TR $(TDNW OOP API) $(TD $(MYREF CRC32Digest) $(MYREF CRC64ECMADigest) $(MYREF CRC64ISODigest))
)
$(TR $(TDNW Helpers) $(TD $(MYREF crcHexString) $(MYREF crc32Of) $(MYREF crc64ECMAOf) $(MYREF crc64ISOOf))
)
)
)
*
* This module conforms to the APIs defined in $(D std.digest). To understand the
* differences between the template and the OOP API, see $(MREF std, digest).
*
* This module publicly imports $(MREF std, digest) and can be used as a stand-alone
* module.
*
* Note:
* CRCs are usually printed with the MSB first. When using
* $(REF toHexString, std,digest) the result will be in an unexpected
* order. Use $(REF toHexString, std,digest)'s optional order parameter
* to specify decreasing order for the correct result. The $(LREF crcHexString)
* alias can also be used for this purpose.
*
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
*
* Authors: Pavel "EvilOne" Minayev, Alex Rønne Petersen, Johannes Pfau
*
* References:
* $(LINK2 http://en.wikipedia.org/wiki/Cyclic_redundancy_check, Wikipedia on CRC)
*
* Source: $(PHOBOSSRC std/digest/_crc.d)
*
* Standards:
* Implements the 'common' IEEE CRC32 variant
* (LSB-first order, Initial value uint.max, complement result)
*
* CTFE:
* Digests do not work in CTFE
*/
/*
* Copyright (c) 2001 - 2002
* Pavel "EvilOne" Minayev
* Copyright (c) 2012
* Alex Rønne Petersen
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*/
module std.digest.crc;
public import std.digest;
version (unittest)
import std.exception;
///
@safe unittest
{
//Template API
import std.digest.crc;
ubyte[4] hash = crc32Of("The quick brown fox jumps over the lazy dog");
assert(crcHexString(hash) == "414FA339");
//Feeding data
ubyte[1024] data;
CRC32 crc;
crc.put(data[]);
crc.start(); //Start again
crc.put(data[]);
hash = crc.finish();
}
///
@safe unittest
{
//OOP API
import std.digest.crc;
auto crc = new CRC32Digest();
ubyte[] hash = crc.digest("The quick brown fox jumps over the lazy dog");
assert(crcHexString(hash) == "414FA339"); //352441c2
//Feeding data
ubyte[1024] data;
crc.put(data[]);
crc.reset(); //Start again
crc.put(data[]);
hash = crc.finish();
}
private T[256][8] genTables(T)(T polynomial)
{
T[256][8] res = void;
foreach (i; 0 .. 0x100)
{
T crc = i;
foreach (_; 0 .. 8)
crc = (crc >> 1) ^ (-int(crc & 1) & polynomial);
res[0][i] = crc;
}
foreach (i; 0 .. 0x100)
{
res[1][i] = (res[0][i] >> 8) ^ res[0][res[0][i] & 0xFF];
res[2][i] = (res[1][i] >> 8) ^ res[0][res[1][i] & 0xFF];
res[3][i] = (res[2][i] >> 8) ^ res[0][res[2][i] & 0xFF];
res[4][i] = (res[3][i] >> 8) ^ res[0][res[3][i] & 0xFF];
res[5][i] = (res[4][i] >> 8) ^ res[0][res[4][i] & 0xFF];
res[6][i] = (res[5][i] >> 8) ^ res[0][res[5][i] & 0xFF];
res[7][i] = (res[6][i] >> 8) ^ res[0][res[6][i] & 0xFF];
}
return res;
}
@system unittest
{
auto tables = genTables(0xEDB88320);
assert(tables[0][0] == 0x00000000 && tables[0][$ - 1] == 0x2d02ef8d && tables[7][$ - 1] == 0x264b06e6);
}
/**
* Template API CRC32 implementation.
* See $(D std.digest) for differences between template and OOP API.
*/
alias CRC32 = CRC!(32, 0xEDB88320);
/**
* Template API CRC64-ECMA implementation.
* See $(D std.digest.digest) for differences between template and OOP API.
*/
alias CRC64ECMA = CRC!(64, 0xC96C5795D7870F42);
/**
* Template API CRC64-ISO implementation.
* See $(D std.digest.digest) for differences between template and OOP API.
*/
alias CRC64ISO = CRC!(64, 0xD800000000000000);
/**
* Generic Template API used for CRC32 and CRC64 implementations.
*
* The N parameter indicate the size of the hash in bits.
* The parameter P specify the polynomial to be used for reduction.
*
* You may want to use the CRC32, CRC65ECMA and CRC64ISO aliases
* for convenience.
*
* See $(D std.digest.digest) for differences between template and OOP API.
*/
struct CRC(uint N, ulong P) if (N == 32 || N == 64)
{
private:
static if (N == 32)
{
alias T = uint;
}
else
{
alias T = ulong;
}
static immutable T[256][8] tables = genTables!T(P);
/**
* Type of the finished CRC hash.
* ubyte[4] if N is 32, ubyte[8] if N is 64.
*/
alias R = ubyte[T.sizeof];
// magic initialization constants
T _state = T.max;
public:
/**
* Use this to feed the digest with data.
* Also implements the $(REF isOutputRange, std,range,primitives)
* interface for $(D ubyte) and $(D const(ubyte)[]).
*/
void put(scope const(ubyte)[] data...) @trusted pure nothrow @nogc
{
T crc = _state;
// process eight bytes at once
while (data.length >= 8)
{
// Use byte-wise reads to support architectures without HW support
// for unaligned reads. This can be optimized by compilers to a single
// 32-bit read if unaligned reads are supported.
// DMD is not able to do this optimization though, so explicitly
// do unaligned reads for DMD's architectures.
version (X86)
enum hasLittleEndianUnalignedReads = true;
else version (X86_64)
enum hasLittleEndianUnalignedReads = true;
else
enum hasLittleEndianUnalignedReads = false; // leave decision to optimizer
static if (hasLittleEndianUnalignedReads)
{
uint one = (cast(uint*) data.ptr)[0];
uint two = (cast(uint*) data.ptr)[1];
}
else
{
uint one = (data.ptr[3] << 24 | data.ptr[2] << 16 | data.ptr[1] << 8 | data.ptr[0]);
uint two = (data.ptr[7] << 24 | data.ptr[6] << 16 | data.ptr[5] << 8 | data.ptr[4]);
}
static if (N == 32)
{
one ^= crc;
}
else
{
one ^= (crc & 0xffffffff);
two ^= (crc >> 32);
}
crc =
tables[0][two >> 24] ^
tables[1][(two >> 16) & 0xFF] ^
tables[2][(two >> 8) & 0xFF] ^
tables[3][two & 0xFF] ^
tables[4][one >> 24] ^
tables[5][(one >> 16) & 0xFF] ^
tables[6][(one >> 8) & 0xFF] ^
tables[7][one & 0xFF];
data = data[8 .. $];
}
// remaining 1 to 7 bytes
foreach (d; data)
crc = (crc >> 8) ^ tables[0][(crc & 0xFF) ^ d];
_state = crc;
}
/**
* Used to initialize the CRC32 digest.
*
* Note:
* For this CRC32 Digest implementation calling start after default construction
* is not necessary. Calling start is only necessary to reset the Digest.
*
* Generic code which deals with different Digest types should always call start though.
*/
void start() @safe pure nothrow @nogc
{
this = CRC.init;
}
/**
* Returns the finished CRC hash. This also calls $(LREF start) to
* reset the internal state.
*/
R finish() @safe pure nothrow @nogc
{
auto tmp = peek();
start();
return tmp;
}
/**
* Works like $(D finish) but does not reset the internal state, so it's possible
* to continue putting data into this CRC after a call to peek.
*/
R peek() const @safe pure nothrow @nogc
{
import std.bitmanip : nativeToLittleEndian;
//Complement, LSB first / Little Endian, see http://rosettacode.org/wiki/CRC-32
return nativeToLittleEndian(~_state);
}
}
///
@safe unittest
{
//Simple example, hashing a string using crc32Of helper function
ubyte[4] hash32 = crc32Of("abc");
//Let's get a hash string
assert(crcHexString(hash32) == "352441C2");
// Repeat for CRC64
ubyte[8] hash64ecma = crc64ECMAOf("abc");
assert(crcHexString(hash64ecma) == "2CD8094A1A277627");
ubyte[8] hash64iso = crc64ISOOf("abc");
assert(crcHexString(hash64iso) == "3776C42000000000");
}
///
@safe unittest
{
ubyte[1024] data;
//Using the basic API
CRC32 hash32;
CRC64ECMA hash64ecma;
CRC64ISO hash64iso;
//Initialize data here...
hash32.put(data);
ubyte[4] result32 = hash32.finish();
hash64ecma.put(data);
ubyte[8] result64ecma = hash64ecma.finish();
hash64iso.put(data);
ubyte[8] result64iso = hash64iso.finish();
}
///
@safe unittest
{
//Let's use the template features:
//Note: When passing a CRC32 to a function, it must be passed by reference!
void doSomething(T)(ref T hash)
if (isDigest!T)
{
hash.put(cast(ubyte) 0);
}
CRC32 crc32;
crc32.start();
doSomething(crc32);
assert(crcHexString(crc32.finish()) == "D202EF8D");
// repeat for CRC64
CRC64ECMA crc64ecma;
crc64ecma.start();
doSomething(crc64ecma);
assert(crcHexString(crc64ecma.finish()) == "1FADA17364673F59");
CRC64ISO crc64iso;
crc64iso.start();
doSomething(crc64iso);
assert(crcHexString(crc64iso.finish()) == "6F90000000000000");
}
@safe unittest
{
assert(isDigest!CRC32);
assert(isDigest!CRC64ECMA);
assert(isDigest!CRC64ISO);
}
@system unittest
{
ubyte[4] digest;
CRC32 crc;
crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz");
assert(crc.peek() == cast(ubyte[]) x"bd50274c");
crc.start();
crc.put(cast(ubyte[])"");
assert(crc.finish() == cast(ubyte[]) x"00000000");
digest = crc32Of("");
assert(digest == cast(ubyte[]) x"00000000");
//Test vector from http://rosettacode.org/wiki/CRC-32
assert(crcHexString(crc32Of("The quick brown fox jumps over the lazy dog")) == "414FA339");
digest = crc32Of("a");
assert(digest == cast(ubyte[]) x"43beb7e8");
digest = crc32Of("abc");
assert(digest == cast(ubyte[]) x"c2412435");
digest = crc32Of("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
assert(digest == cast(ubyte[]) x"5f3f1a17");
digest = crc32Of("message digest");
assert(digest == cast(ubyte[]) x"7f9d1520");
digest = crc32Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
assert(digest == cast(ubyte[]) x"d2e6c21f");
digest = crc32Of("1234567890123456789012345678901234567890"~
"1234567890123456789012345678901234567890");
assert(digest == cast(ubyte[]) x"724aa97c");
assert(crcHexString(cast(ubyte[4]) x"c3fcd3d7") == "D7D3FCC3");
}
@system unittest
{
ubyte[8] digest;
CRC64ECMA crc;
crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz");
assert(crc.peek() == cast(ubyte[]) x"2f121b7575789626");
crc.start();
crc.put(cast(ubyte[])"");
assert(crc.finish() == cast(ubyte[]) x"0000000000000000");
digest = crc64ECMAOf("");
assert(digest == cast(ubyte[]) x"0000000000000000");
//Test vector from http://rosettacode.org/wiki/CRC-32
assert(crcHexString(crc64ECMAOf("The quick brown fox jumps over the lazy dog")) == "5B5EB8C2E54AA1C4");
digest = crc64ECMAOf("a");
assert(digest == cast(ubyte[]) x"052b652e77840233");
digest = crc64ECMAOf("abc");
assert(digest == cast(ubyte[]) x"2776271a4a09d82c");
digest = crc64ECMAOf("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
assert(digest == cast(ubyte[]) x"4b7cdce3746c449f");
digest = crc64ECMAOf("message digest");
assert(digest == cast(ubyte[]) x"6f9b8a3156c9bc5d");
digest = crc64ECMAOf("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
assert(digest == cast(ubyte[]) x"2656b716e1bf0503");
digest = crc64ECMAOf("1234567890123456789012345678901234567890"~
"1234567890123456789012345678901234567890");
assert(digest == cast(ubyte[]) x"bd3eb7765d0a22ae");
assert(crcHexString(cast(ubyte[8]) x"c3fcd3d7efbeadde") == "DEADBEEFD7D3FCC3");
}
@system unittest
{
ubyte[8] digest;
CRC64ISO crc;
crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz");
assert(crc.peek() == cast(ubyte[]) x"f0494ab780989b42");
crc.start();
crc.put(cast(ubyte[])"");
assert(crc.finish() == cast(ubyte[]) x"0000000000000000");
digest = crc64ISOOf("");
assert(digest == cast(ubyte[]) x"0000000000000000");
//Test vector from http://rosettacode.org/wiki/CRC-32
assert(crcHexString(crc64ISOOf("The quick brown fox jumps over the lazy dog")) == "4EF14E19F4C6E28E");
digest = crc64ISOOf("a");
assert(digest == cast(ubyte[]) x"0000000000002034");
digest = crc64ISOOf("abc");
assert(digest == cast(ubyte[]) x"0000000020c47637");
digest = crc64ISOOf("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
assert(digest == cast(ubyte[]) x"5173f717971365e5");
digest = crc64ISOOf("message digest");
assert(digest == cast(ubyte[]) x"a2c355bbc0b93f86");
digest = crc64ISOOf("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
assert(digest == cast(ubyte[]) x"598B258292E40084");
digest = crc64ISOOf("1234567890123456789012345678901234567890"~
"1234567890123456789012345678901234567890");
assert(digest == cast(ubyte[]) x"760cd2d3588bf809");
assert(crcHexString(cast(ubyte[8]) x"c3fcd3d7efbeadde") == "DEADBEEFD7D3FCC3");
}
/**
* This is a convenience alias for $(REF digest, std,digest) using the
* CRC32 implementation.
*
* Params:
* data = $(D InputRange) of $(D ElementType) implicitly convertible to
* $(D ubyte), $(D ubyte[]) or $(D ubyte[num]) or one or more arrays
* of any type.
*
* Returns:
* CRC32 of data
*/
//simple alias doesn't work here, hope this gets inlined...
ubyte[4] crc32Of(T...)(T data)
{
return digest!(CRC32, T)(data);
}
///
@system unittest
{
ubyte[] data = [4,5,7,25];
assert(data.crc32Of == [167, 180, 199, 131]);
import std.utf : byChar;
assert("hello"d.byChar.crc32Of == [134, 166, 16, 54]);
ubyte[4] hash = "abc".crc32Of();
assert(hash == digest!CRC32("ab", "c"));
import std.range : iota;
enum ubyte S = 5, F = 66;
assert(iota(S, F).crc32Of == [59, 140, 234, 154]);
}
/**
* This is a convenience alias for $(REF digest, std,digest) using the
* CRC64-ECMA implementation.
*
* Params:
* data = $(D InputRange) of $(D ElementType) implicitly convertible to
* $(D ubyte), $(D ubyte[]) or $(D ubyte[num]) or one or more arrays
* of any type.
*
* Returns:
* CRC64-ECMA of data
*/
//simple alias doesn't work here, hope this gets inlined...
ubyte[8] crc64ECMAOf(T...)(T data)
{
return digest!(CRC64ECMA, T)(data);
}
///
@system unittest
{
ubyte[] data = [4,5,7,25];
assert(data.crc64ECMAOf == [58, 142, 220, 214, 118, 98, 105, 69]);
import std.utf : byChar;
assert("hello"d.byChar.crc64ECMAOf == [177, 55, 185, 219, 229, 218, 30, 155]);
ubyte[8] hash = "abc".crc64ECMAOf();
assert("abc".crc64ECMAOf == [39, 118, 39, 26, 74, 9, 216, 44]);
assert(hash == digest!CRC64ECMA("ab", "c"));
import std.range : iota;
enum ubyte S = 5, F = 66;
assert(iota(S, F).crc64ECMAOf == [6, 184, 91, 238, 46, 213, 127, 188]);
}
/**
* This is a convenience alias for $(REF digest, std,digest,digest) using the
* CRC64-ISO implementation.
*
* Params:
* data = $(D InputRange) of $(D ElementType) implicitly convertible to
* $(D ubyte), $(D ubyte[]) or $(D ubyte[num]) or one or more arrays
* of any type.
*
* Returns:
* CRC64-ISO of data
*/
//simple alias doesn't work here, hope this gets inlined...
ubyte[8] crc64ISOOf(T...)(T data)
{
return digest!(CRC64ISO, T)(data);
}
///
@system unittest
{
ubyte[] data = [4,5,7,25];
assert(data.crc64ISOOf == [0, 0, 0, 80, 137, 232, 203, 120]);
import std.utf : byChar;
assert("hello"d.byChar.crc64ISOOf == [0, 0, 16, 216, 226, 238, 62, 60]);
ubyte[8] hash = "abc".crc64ISOOf();
assert("abc".crc64ISOOf == [0, 0, 0, 0, 32, 196, 118, 55]);
assert(hash == digest!CRC64ISO("ab", "c"));
import std.range : iota;
enum ubyte S = 5, F = 66;
assert(iota(S, F).crc64ISOOf == [21, 185, 116, 95, 219, 11, 54, 7]);
}
/**
* producing the usual CRC32 string output.
*/
public alias crcHexString = toHexString!(Order.decreasing);
///ditto
public alias crcHexString = toHexString!(Order.decreasing, 16);
/**
* OOP API CRC32 implementation.
* See $(D std.digest) for differences between template and OOP API.
*
* This is an alias for $(D $(REF WrapperDigest, std,digest)!CRC32), see
* there for more information.
*/
alias CRC32Digest = WrapperDigest!CRC32;
/**
* OOP API CRC64-ECMA implementation.
* See $(D std.digest.digest) for differences between template and OOP API.
*
* This is an alias for $(D $(REF WrapperDigest, std,digest,digest)!CRC64ECMA),
* see there for more information.
*/
alias CRC64ECMADigest = WrapperDigest!CRC64ECMA;
/**
* OOP API CRC64-ISO implementation.
* See $(D std.digest.digest) for differences between template and OOP API.
*
* This is an alias for $(D $(REF WrapperDigest, std,digest,digest)!CRC64ISO),
* see there for more information.
*/
alias CRC64ISODigest = WrapperDigest!CRC64ISO;
///
@safe unittest
{
//Simple example, hashing a string using Digest.digest helper function
auto crc = new CRC32Digest();
ubyte[] hash = crc.digest("abc");
//Let's get a hash string
assert(crcHexString(hash) == "352441C2");
}
///
@system unittest
{
//Let's use the OOP features:
void test(Digest dig)
{
dig.put(cast(ubyte) 0);
}
auto crc = new CRC32Digest();
test(crc);
//Let's use a custom buffer:
ubyte[4] buf;
ubyte[] result = crc.finish(buf[]);
assert(crcHexString(result) == "D202EF8D");
}
///
@safe unittest
{
//Simple example
auto hash = new CRC32Digest();
hash.put(cast(ubyte) 0);
ubyte[] result = hash.finish();
}
///
@system unittest
{
//using a supplied buffer
ubyte[4] buf;
auto hash = new CRC32Digest();
hash.put(cast(ubyte) 0);
ubyte[] result = hash.finish(buf[]);
//The result is now in result (and in buf. If you pass a buffer which is bigger than
//necessary, result will have the correct length, but buf will still have it's original
//length)
}
@system unittest
{
import std.range;
auto crc = new CRC32Digest();
crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz");
assert(crc.peek() == cast(ubyte[]) x"bd50274c");
crc.reset();
crc.put(cast(ubyte[])"");
assert(crc.finish() == cast(ubyte[]) x"00000000");
crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz");
ubyte[20] result;
auto result2 = crc.finish(result[]);
assert(result[0 .. 4] == result2 && result2 == cast(ubyte[]) x"bd50274c");
debug
assertThrown!Error(crc.finish(result[0 .. 3]));
assert(crc.length == 4);
assert(crc.digest("") == cast(ubyte[]) x"00000000");
assert(crc.digest("a") == cast(ubyte[]) x"43beb7e8");
assert(crc.digest("abc") == cast(ubyte[]) x"c2412435");
assert(crc.digest("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")
== cast(ubyte[]) x"5f3f1a17");
assert(crc.digest("message digest") == cast(ubyte[]) x"7f9d1520");
assert(crc.digest("abcdefghijklmnopqrstuvwxyz")
== cast(ubyte[]) x"bd50274c");
assert(crc.digest("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
== cast(ubyte[]) x"d2e6c21f");
assert(crc.digest("1234567890123456789012345678901234567890",
"1234567890123456789012345678901234567890")
== cast(ubyte[]) x"724aa97c");
ubyte[] onemilliona = new ubyte[1000000];
onemilliona[] = 'a';
auto digest = crc32Of(onemilliona);
assert(digest == cast(ubyte[]) x"BCBF25DC");
auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000);
digest = crc32Of(oneMillionRange);
assert(digest == cast(ubyte[]) x"BCBF25DC");
}