You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@bookkeeper.apache.org by GitBox <gi...@apache.org> on 2018/01/07 05:46:08 UTC

[GitHub] sijie closed pull request #916: Issue 915: Part 1 - Copy pulsar-checksum module as circe-checksum

sijie closed pull request #916: Issue 915: Part 1 - Copy pulsar-checksum module as circe-checksum
URL: https://github.com/apache/bookkeeper/pull/916
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/NOTICE b/NOTICE
index 1102436af..52de65bc5 100644
--- a/NOTICE
+++ b/NOTICE
@@ -4,3 +4,8 @@ Copyright 2011-2017 The Apache Software Foundation
 This product includes software developed at
 The Apache Software Foundation (http://www.apache.org/).
 
+This product contains a modified version of 'Circe', a high-performance
+hash algorithm framework & library from Trevor Robinson:
+
+  * LICENSE: Apache License 2.0
+  * HOMEPAGE: https://github.com/trevorr/circe
diff --git a/bookkeeper-dist/src/main/resources/NOTICE-all.bin.txt b/bookkeeper-dist/src/main/resources/NOTICE-all.bin.txt
index ba657ccd7..ac64c97b0 100644
--- a/bookkeeper-dist/src/main/resources/NOTICE-all.bin.txt
+++ b/bookkeeper-dist/src/main/resources/NOTICE-all.bin.txt
@@ -47,3 +47,9 @@ This project includes:
   Twitter Util under The Apache Software License, Version 2.0
   Vertx under The Apache Software License, Version 2.0
   ZooKeeper under The Apache Software License, Version 2.0
+
+This product contains a modified version of 'Circe', a high-performance
+hash algorithm framework & library from Trevor Robinson:
+
+  * LICENSE: Apache License 2.0
+  * HOMEPAGE: https://github.com/trevorr/circe
diff --git a/bookkeeper-dist/src/main/resources/NOTICE-server.bin.txt b/bookkeeper-dist/src/main/resources/NOTICE-server.bin.txt
index 73e7fdb47..bd0ec23a3 100644
--- a/bookkeeper-dist/src/main/resources/NOTICE-server.bin.txt
+++ b/bookkeeper-dist/src/main/resources/NOTICE-server.bin.txt
@@ -35,3 +35,9 @@ This project includes:
   The Netty Project under The Apache Software License, Version 2.0
   Vertx under The Apache Software License, Version 2.0
   ZooKeeper under Apache License, Version 2.0
+
+This product contains a modified version of 'Circe', a high-performance
+hash algorithm framework & library from Trevor Robinson:
+
+  * LICENSE: Apache License 2.0
+  * HOMEPAGE: https://github.com/trevorr/circe
diff --git a/buildtools/src/main/resources/bookkeeper/suppressions.xml b/buildtools/src/main/resources/bookkeeper/suppressions.xml
index e28dd403c..c45e2fa2b 100644
--- a/buildtools/src/main/resources/bookkeeper/suppressions.xml
+++ b/buildtools/src/main/resources/bookkeeper/suppressions.xml
@@ -25,4 +25,7 @@
     <suppress checks=".*" files=".+[\\/]generated[\\/].+\.java" />
     <suppress checks=".*" files=".+[\\/]generated-sources[\\/].+\.java" />
     <suppress checks=".*" files=".+[\\/]generated-test-sources[\\/].+\.java" />
+
+    <!-- suppress all checks in the copied code -->
+    <suppress checks=".*" files=".+[\\/]com[\\/]scurrilous[\\/]circe[\\/].+\.java" />
 </suppressions>
diff --git a/circe-checksum/pom.xml b/circe-checksum/pom.xml
new file mode 100644
index 000000000..001fd7c7b
--- /dev/null
+++ b/circe-checksum/pom.xml
@@ -0,0 +1,206 @@
+<?xml version="1.0"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<project
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+  xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.bookkeeper</groupId>
+    <artifactId>bookkeeper</artifactId>
+    <version>4.7.0-SNAPSHOT</version>
+    <relativePath>..</relativePath>
+  </parent>
+
+  <artifactId>circe-checksum</artifactId>
+  <packaging>nar</packaging>
+  <name>Apache BookKeeper :: Circe Checksum Library</name>
+  <description>Circe Checksum Library</description>
+
+  <properties>
+    <nar.runtime>dynamic</nar.runtime>
+    <nar.cpp.optionSet>-msse4.2 -mpclmul</nar.cpp.optionSet>
+  </properties>
+
+  <dependencies>
+
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+      <version>${guava.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.testng</groupId>
+      <artifactId>testng</artifactId>
+      <version>6.8.5</version>
+    </dependency>
+    
+    <dependency>
+      <groupId>com.googlecode.jmockit</groupId>
+      <artifactId>jmockit</artifactId>
+      <version>1.7</version>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+        <version>${maven-checkstyle-plugin.version}</version>
+        <dependencies>
+          <dependency>
+            <groupId>com.puppycrawl.tools</groupId>
+            <artifactId>checkstyle</artifactId>
+            <version>${puppycrawl.checkstyle.version}</version>
+          </dependency>
+          <dependency>
+            <groupId>org.apache.bookkeeper</groupId>
+            <artifactId>buildtools</artifactId>
+            <version>${project.parent.version}</version>
+          </dependency>
+        </dependencies>
+        <configuration>
+          <configLocation>bookkeeper/checkstyle.xml</configLocation>
+          <suppressionsLocation>bookkeeper/suppressions.xml</suppressionsLocation>
+          <consoleOutput>true</consoleOutput>
+          <failOnViolation>true</failOnViolation>
+          <includeResources>false</includeResources>
+          <includeTestSourceDirectory>true</includeTestSourceDirectory>
+        </configuration>
+        <executions>
+          <execution>
+            <id>checkstyle</id>
+            <phase>validate</phase>
+            <goals>
+              <goal>check</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>com.github.maven-nar</groupId>
+        <artifactId>nar-maven-plugin</artifactId>
+        <version>3.1.0</version>
+        <extensions>true</extensions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <version>2.4.1</version>
+        <configuration>
+          <descriptors>
+            <descriptor>src/main/assembly/assembly.xml</descriptor>
+          </descriptors>
+          <appendAssemblyId>false</appendAssemblyId>
+        </configuration>
+        <executions>
+          <execution>
+            <id>make-assembly</id>
+            <phase>package</phase>
+            <goals>
+              <goal>single</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <profiles>
+    <profile>
+      <id>mac</id>
+      <activation>
+        <os>
+          <name>Mac OS X</name>
+        </os>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>com.github.maven-nar</groupId>
+            <artifactId>nar-maven-plugin</artifactId>
+            <version>3.1.0</version>
+            <extensions>true</extensions>
+            <configuration>
+              <runtime>${nar.runtime}</runtime>
+              <output>circe-checksum</output>
+              <libraries>
+                <library>
+                  <type>jni</type>
+                  <narSystemPackage>com.scurrilous.circe.checksum</narSystemPackage>
+                </library>
+              </libraries>
+              <cpp>
+                <optionSet>${nar.cpp.optionSet}</optionSet>
+                <exceptions>false</exceptions>
+                <rtti>false</rtti>
+                <optimize>full</optimize>
+              </cpp>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+    <profile>
+      <id>Linux</id>
+      <activation>
+        <os>
+          <name>Linux</name>
+        </os>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>com.github.maven-nar</groupId>
+            <artifactId>nar-maven-plugin</artifactId>
+            <version>3.1.0</version>
+            <extensions>true</extensions>
+            <configuration>
+              <runtime>${nar.runtime}</runtime>
+              <output>circe-checksum</output>
+              <libraries>
+                <library>
+                  <type>jni</type>
+                  <narSystemPackage>com.scurrilous.circe.checksum</narSystemPackage>
+                </library>
+              </libraries>
+              <cpp>
+                <optionSet>${nar.cpp.optionSet}</optionSet>
+                <exceptions>false</exceptions>
+                <rtti>false</rtti>
+                <optimize>full</optimize>
+              </cpp>
+              <linker>
+                <libSet>rt</libSet>
+              </linker>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+
+</project>
diff --git a/circe-checksum/src/main/assembly/assembly.xml b/circe-checksum/src/main/assembly/assembly.xml
new file mode 100644
index 000000000..bded0cab8
--- /dev/null
+++ b/circe-checksum/src/main/assembly/assembly.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<assembly>
+  <id>all</id>
+  <formats>
+    <format>jar</format>
+  </formats>
+
+  <includeBaseDirectory>false</includeBaseDirectory>
+  <dependencySets>
+    <dependencySet>
+      <excludes>
+        <exclude>org.slf4j:slf4j-api</exclude>
+        <exclude>io.netty</exclude>
+        <exclude>*:nar:*</exclude>
+      </excludes>
+    </dependencySet>
+  </dependencySets>
+  <fileSets>
+    <fileSet>
+      <directory>${project.build.directory}/nar/${project.artifactId}-${project.version}-${os.arch}-MacOSX-gpp-jni/lib/${os.arch}-MacOSX-gpp/jni
+      </directory>
+      <outputDirectory>lib</outputDirectory>
+      <includes>
+        <include>lib*</include>
+      </includes>
+    </fileSet>
+    <fileSet>
+      <directory>${project.build.directory}/nar/${project.artifactId}-${project.version}-${os.arch}-Linux-gpp-jni/lib/${os.arch}-Linux-gpp/jni
+      </directory>
+      <outputDirectory>lib</outputDirectory>
+      <includes>
+        <include>lib*</include>
+      </includes>
+    </fileSet>
+    <fileSet>
+      <directory>${project.build.directory}/nar/${project.artifactId}-${project.version}-${os.arch}-${os.name}-gpp-jni/lib/${os.arch}-${os.name}-gpp/jni
+      </directory>
+      <outputDirectory>lib</outputDirectory>
+      <includes>
+        <include>lib*</include>
+      </includes>
+    </fileSet>
+    <fileSet>
+      <directory>${project.build.directory}/classes</directory>
+      <outputDirectory></outputDirectory>
+      <includes>
+        <include>**/*</include>
+      </includes>
+    </fileSet>
+  </fileSets>
+</assembly>
diff --git a/circe-checksum/src/main/circe/cpp/crc32c_sse42.cpp b/circe-checksum/src/main/circe/cpp/crc32c_sse42.cpp
new file mode 100644
index 000000000..904d43718
--- /dev/null
+++ b/circe-checksum/src/main/circe/cpp/crc32c_sse42.cpp
@@ -0,0 +1,217 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+#include "../include/crc32c_sse42.hpp"
+
+#include <assert.h>
+#include <nmmintrin.h> // SSE4.2
+#include <wmmintrin.h> // PCLMUL
+
+#ifdef _MSC_VER
+# include <intrin.h>
+#else
+# include <cpuid.h>
+#endif
+
+//#define CRC32C_DEBUG
+#define CRC32C_PCLMULQDQ
+
+#ifdef CRC32C_DEBUG
+# include <stdio.h>
+# define DEBUG_PRINTF1(fmt, v1) printf(fmt, v1)
+# define DEBUG_PRINTF2(fmt, v1, v2) printf(fmt, v1, v2)
+# define DEBUG_PRINTF3(fmt, v1, v2, v3) printf(fmt, v1, v2, v3)
+# define DEBUG_PRINTF4(fmt, v1, v2, v3, v4) printf(fmt, v1, v2, v3, v4)
+#else
+# define DEBUG_PRINTF1(fmt, v1)
+# define DEBUG_PRINTF2(fmt, v1, v2)
+# define DEBUG_PRINTF3(fmt, v1, v2, v3)
+# define DEBUG_PRINTF4(fmt, v1, v2, v3, v4)
+#endif
+
+static bool initialized = false;
+static bool has_sse42 = false;
+static bool has_pclmulqdq = false;
+
+bool crc32c_initialize() {
+    if (!initialized) {
+        const uint32_t cpuid_ecx_sse42 = (1 << 20);
+        const uint32_t cpuid_ecx_pclmulqdq = (1 << 1);
+
+#ifdef _MSC_VER
+        int CPUInfo[4] = {};
+        __cpuid(CPUInfo, 1);
+        has_sse42 = (CPUInfo[2] & cpuid_ecx_sse42) != 0;
+        has_pclmulqdq = (CPUInfo[2] & cpuid_ecx_pclmulqdq) != 0;
+#else
+        unsigned int eax, ebx, ecx, edx;
+        if (__get_cpuid(1, &eax, &ebx, &ecx, &edx)) {
+            has_sse42 = (ecx & cpuid_ecx_sse42) != 0;
+            has_pclmulqdq = (ecx & cpuid_ecx_pclmulqdq) != 0;
+        }
+#endif
+        DEBUG_PRINTF1("has_sse42 = %d\n", has_sse42);
+        DEBUG_PRINTF1("has_pclmulqdq = %d\n", has_pclmulqdq);
+        initialized = true;
+    }
+    return has_sse42;
+}
+
+#include "gf2.hpp"
+
+chunk_config::chunk_config(size_t words, const chunk_config* next) : words(words), next(next) {
+    assert(words > 0);
+    assert(!next || next->words < words);
+    const size_t loop_bytes = loops() * 8;
+    make_shift_table(loop_bytes, shift1);
+    make_shift_table(loop_bytes * 2, shift2);
+}
+
+void chunk_config::make_shift_table(size_t bytes, uint32_t table[256]) {
+    bitmatrix<32, 32> op;
+    op.lower_shift();
+    op[0] = 0x82f63b78; // reversed CRC-32C polynomial
+    bitmatrix<32, 32> m;
+    pow(m, op, bytes * 8);
+    for (unsigned int i = 0; i < 256; ++i)
+        table[i] = (const bitvector<32>) mul(m, bitvector<32>(i));
+}
+
+static uint32_t crc32c_chunk(uint32_t crc, const void *buf, const chunk_config& config) {
+    DEBUG_PRINTF3("  crc32c_chunk(crc = 0x%08x, buf = %p, config.words = " SIZE_T_FORMAT ")", crc, buf, config.words);
+
+    const uint64_t *pq = (const uint64_t*) buf;
+    uint64_t crc0 = config.extra() > 1 ? _mm_crc32_u64(crc, *pq++) : crc;
+    uint64_t crc1 = 0;
+    uint64_t crc2 = 0;
+    const size_t loops = config.loops();
+    for (unsigned int i = 0; i < loops; ++i, ++pq) {
+        crc1 = _mm_crc32_u64(crc1, pq[1 * loops]);
+        crc2 = _mm_crc32_u64(crc2, pq[2 * loops]);
+        crc0 = _mm_crc32_u64(crc0, pq[0 * loops]);
+    }
+    pq += 2 * loops;
+    uint64_t tmp = *pq++;
+# ifdef CRC32C_PCLMULQDQ
+    if (has_pclmulqdq) {
+        __m128i k = _mm_set_epi64x(config.shift1[1], config.shift2[1]);
+        __m128i mul1 = _mm_clmulepi64_si128(_mm_cvtsi64_si128((int64_t) crc1), k, 0x10);
+        __m128i mul0 = _mm_clmulepi64_si128(_mm_cvtsi64_si128((int64_t) crc0), k, 0x00);
+        tmp ^= (uint64_t) _mm_cvtsi128_si64(mul1);
+        tmp ^= (uint64_t) _mm_cvtsi128_si64(mul0);
+    } else
+# endif
+    {
+        tmp ^= config.shift1[crc1 & 0xff];
+        tmp ^= ((uint64_t) config.shift1[(crc1 >> 8) & 0xff]) << 8;
+        tmp ^= ((uint64_t) config.shift1[(crc1 >> 16) & 0xff]) << 16;
+        tmp ^= ((uint64_t) config.shift1[(crc1 >> 24) & 0xff]) << 24;
+
+        tmp ^= config.shift2[crc0 & 0xff];
+        tmp ^= ((uint64_t) config.shift2[(crc0 >> 8) & 0xff]) << 8;
+        tmp ^= ((uint64_t) config.shift2[(crc0 >> 16) & 0xff]) << 16;
+        tmp ^= ((uint64_t) config.shift2[(crc0 >> 24) & 0xff]) << 24;
+    }
+    crc2 = _mm_crc32_u64(crc2, tmp);
+    if (config.extra() > 2) // only if words is divisible by 3
+        crc2 = _mm_crc32_u64(crc2, *pq);
+    crc = (uint32_t) crc2;
+
+    DEBUG_PRINTF1(" = 0x%08x\n", crc);
+    return crc;
+}
+
+static uint32_t crc32c_words(uint32_t crc, const void *buf, size_t count) {
+    DEBUG_PRINTF3("  crc32c_words(crc = 0x%08x, buf = %p, count = " SIZE_T_FORMAT ")", crc, buf, count);
+
+    const uint64_t *pq = (const uint64_t*) buf;
+    size_t loops = (count + 7) / 8;
+    assert(loops > 0);
+    switch (count & 7) {
+    case 0:
+        do {
+            crc = (uint32_t) _mm_crc32_u64(crc, *pq++);
+    case 7: crc = (uint32_t) _mm_crc32_u64(crc, *pq++);
+    case 6: crc = (uint32_t) _mm_crc32_u64(crc, *pq++);
+    case 5: crc = (uint32_t) _mm_crc32_u64(crc, *pq++);
+    case 4: crc = (uint32_t) _mm_crc32_u64(crc, *pq++);
+    case 3: crc = (uint32_t) _mm_crc32_u64(crc, *pq++);
+    case 2: crc = (uint32_t) _mm_crc32_u64(crc, *pq++);
+    case 1: crc = (uint32_t) _mm_crc32_u64(crc, *pq++);
+        } while (--loops > 0);
+    }
+
+    DEBUG_PRINTF1(" = 0x%08x\n", crc);
+    return crc;
+}
+
+static uint32_t crc32c_bytes(uint32_t crc, const void *buf, size_t count) {
+    DEBUG_PRINTF3("  crc32c_bytes(crc = 0x%08x, buf = %p, count = " SIZE_T_FORMAT ")", crc, buf, count);
+
+    const uint8_t *pc = (const uint8_t*) buf;
+    size_t loops = (count + 7) / 8;
+    assert(loops > 0);
+    switch (count & 7) {
+    case 0:
+        do {
+            crc = (uint32_t) _mm_crc32_u8(crc, *pc++);
+    case 7: crc = (uint32_t) _mm_crc32_u8(crc, *pc++);
+    case 6: crc = (uint32_t) _mm_crc32_u8(crc, *pc++);
+    case 5: crc = (uint32_t) _mm_crc32_u8(crc, *pc++);
+    case 4: crc = (uint32_t) _mm_crc32_u8(crc, *pc++);
+    case 3: crc = (uint32_t) _mm_crc32_u8(crc, *pc++);
+    case 2: crc = (uint32_t) _mm_crc32_u8(crc, *pc++);
+    case 1: crc = (uint32_t) _mm_crc32_u8(crc, *pc++);
+        } while (--loops > 0);
+    }
+
+    DEBUG_PRINTF1(" = 0x%08x\n", crc);
+    return crc;
+}
+
+uint32_t crc32c(uint32_t init, const void *buf, size_t len, const chunk_config* config) {
+    DEBUG_PRINTF3("crc32c(init = 0x%08x, buf = %p, len = " SIZE_T_FORMAT ")\n", init, buf, len);
+
+    uint32_t crc = ~init;
+    const char *pc = (const char*) buf;
+    if (len >= 24) {
+        if ((uintptr_t) pc & 7) {
+            size_t unaligned = 8 - ((uintptr_t) pc & 7);
+            crc = crc32c_bytes(crc, pc, unaligned);
+            pc += unaligned;
+            len -= unaligned;
+        }
+        size_t words = len / 8;
+        while (config) {
+            while (words >= config->words) {
+                crc = crc32c_chunk(crc, pc, *config);
+                pc += config->words * 8;
+                words -= config->words;
+            }
+            config = config->next;
+        }
+        if (words > 0) {
+            crc = crc32c_words(crc, pc, words);
+            pc += words * 8;
+        }
+        len &= 7;
+    }
+    if (len)
+        crc = crc32c_bytes(crc, pc, len);
+    crc = ~crc;
+
+    DEBUG_PRINTF1("crc = 0x%08x\n", crc);
+    return crc;
+}
diff --git a/circe-checksum/src/main/circe/cpp/crc32c_sse42_jni.cpp b/circe-checksum/src/main/circe/cpp/crc32c_sse42_jni.cpp
new file mode 100644
index 000000000..4a8b116f2
--- /dev/null
+++ b/circe-checksum/src/main/circe/cpp/crc32c_sse42_jni.cpp
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+#include "com_scurrilous_circe_crc_Sse42Crc32C.h"
+#include "../include/crc32c_sse42.hpp"
+#include <new>
+
+extern "C"
+JNIEXPORT jboolean JNICALL Java_com_scurrilous_circe_crc_Sse42Crc32C_nativeSupported
+(JNIEnv *, jclass) {
+    return crc32c_initialize();
+}
+
+extern "C"
+JNIEXPORT jint JNICALL Java_com_scurrilous_circe_crc_Sse42Crc32C_nativeArray
+(JNIEnv *env, jclass, jint current, jbyteArray input, jint index, jint length, jlong config) {
+    const char *buf = (const char *) env->GetPrimitiveArrayCritical(input, 0);
+    jint crc = (jint) crc32c((uint32_t) current, buf + index, (size_t) length, (const chunk_config*) config);
+    env->ReleasePrimitiveArrayCritical(input, (void*) buf, 0);
+    return crc;
+}
+
+extern "C"
+JNIEXPORT jint JNICALL Java_com_scurrilous_circe_crc_Sse42Crc32C_nativeDirectBuffer
+(JNIEnv *env, jclass, jint current, jobject input, jint offset, jint length, jlong config) {
+    const char *address = (const char *) env->GetDirectBufferAddress(input);
+    if (!address)
+        return 0;
+    return (jint) crc32c((uint32_t) current, address + offset, (size_t) length, (const chunk_config*) config);
+}
+
+extern "C"
+JNIEXPORT jint JNICALL Java_com_scurrilous_circe_crc_Sse42Crc32C_nativeUnsafe
+(JNIEnv *, jclass, jint current, jlong address, jlong length, jlong config) {
+    return (jint) crc32c((uint32_t) current, (const void *) address, (size_t) length, (const chunk_config*) config);
+}
+
+extern "C"
+JNIEXPORT jlong JNICALL Java_com_scurrilous_circe_crc_Sse42Crc32C_allocConfig
+  (JNIEnv *env, jclass, jintArray chunkWords) {
+    chunk_config* configs = 0;
+    jsize len = env->GetArrayLength(chunkWords);
+    const jint *arr = (const jint *) env->GetPrimitiveArrayCritical(chunkWords, 0);
+    if (len < 1 || arr[0] < chunk_config::min_words)
+        goto fail;
+    for (jsize i = 1; i < len; ++i) {
+        if (arr[i] < chunk_config::min_words
+            || arr[i] >= arr[i - 1]) // chunk words must be strictly decreasing
+            goto fail;
+    }
+    configs = (chunk_config*) ::operator new[](sizeof(chunk_config) * (size_t) len, std::nothrow);
+    if (configs) {
+        for (jsize i = len; i > 0; --i) {
+            new(&configs[i - 1]) chunk_config((size_t) arr[i - 1], i < len ? &configs[i] : 0);
+        }
+    }
+fail:
+    env->ReleasePrimitiveArrayCritical(chunkWords, (void*) arr, 0);
+    return (jlong) configs;
+}
+
+extern "C"
+JNIEXPORT void JNICALL Java_com_scurrilous_circe_crc_Sse42Crc32C_freeConfig
+  (JNIEnv *, jclass, jlong config) {
+    delete[] (const chunk_config*) config;
+}
diff --git a/circe-checksum/src/main/circe/cpp/gf2.hpp b/circe-checksum/src/main/circe/cpp/gf2.hpp
new file mode 100644
index 000000000..ef963d35d
--- /dev/null
+++ b/circe-checksum/src/main/circe/cpp/gf2.hpp
@@ -0,0 +1,174 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+#include "../include/int_types.h"
+#include <algorithm> // std::swap
+
+#ifdef _MSC_VER
+#pragma warning(disable:4146) // unary minus operator applied to unsigned type, result still unsigned
+#endif
+
+// Type trait for unsigned integers of at least N bytes
+template <unsigned int N>
+struct uint_bytes {
+    enum { is_defined = 0 };
+};
+
+template <class T>
+struct defined_uint_bytes {
+    enum { is_defined = 1 };
+    typedef T type;
+};
+
+template <> struct uint_bytes<1> : defined_uint_bytes<uint8_t> {};
+template <> struct uint_bytes<2> : defined_uint_bytes<uint16_t> {};
+template <> struct uint_bytes<3> : defined_uint_bytes<uint32_t> {};
+template <> struct uint_bytes<4> : defined_uint_bytes<uint32_t> {};
+template <> struct uint_bytes<5> : defined_uint_bytes<uint64_t> {};
+template <> struct uint_bytes<6> : defined_uint_bytes<uint64_t> {};
+template <> struct uint_bytes<7> : defined_uint_bytes<uint64_t> {};
+template <> struct uint_bytes<8> : defined_uint_bytes<uint64_t> {};
+
+// Type trait for unsigned integers of at least N bits
+template <unsigned int N>
+struct uint_bits : uint_bytes<(N + 7) / 8> {
+    enum { bits = 8 * sizeof(typename uint_bits::type) };
+};
+
+// Bit vector of N bits; currently just exposes an unsigned integer
+template <unsigned int N>
+class bitvector {
+    typedef typename uint_bits<N>::type type;
+    type value;
+public:
+    bitvector() {}
+    bitvector(type value) : value(value) {}
+    operator type&() { return value; }
+    operator type() const { return value; }
+};
+
+// Bit matrix of M columns by N rows
+template <unsigned int M, unsigned int N = M>
+class bitmatrix {
+    typedef bitvector<M> row;
+    row value[N];
+public:
+    bitmatrix() {
+    }
+    explicit bitmatrix(bool b) {
+        if (b)
+            identity();
+        else
+            null();
+    }
+    void null() {
+        for (unsigned int i = 0; i < N; ++i)
+            value[i] = 0;
+    }
+    void identity() {
+        for (unsigned int i = 0; i < N; ++i)
+            value[i] = i < M ? (const row) 1 << i : 0;
+    }
+    void lower_shift() {
+        for (unsigned int i = 0; i < N; ++i)
+            value[i] = i > 0 && i <= M ? (const row) 1 << (i - 1) : 0;
+    }
+    void upper_shift() {
+        for (unsigned int i = 0; i < N; ++i)
+            value[i] = i + 1 < M ? (const row) 1 << (i + 1) : 0;
+    }
+    operator bitvector<M>*() { return value; }
+    operator const bitvector<M>*() const { return value; }
+};
+
+/*
+ * Multiplies MxN matrix A by N-row vector B in GF(2).
+ *
+ * For M,N = 3:
+ *
+ *     | a b c |      | x |       | ax + by + cz |
+ * A = | d e f |, B = | y |, AB = | dx + ey + fz |
+ *     | g h i |      | z |       | gx + hy + iz |
+ *
+ * In GF(2), addition corresponds to XOR and multiplication to AND:
+ *
+ *      | (a & x) ^ (b & y) ^ (c & z) |
+ * AB = | (d & x) ^ (e & y) ^ (f & z) |
+ *      | (g & x) ^ (h & y) ^ (i & z) |
+ *
+ * Trading variable names for [row,column] indices:
+ *
+ * AB = (A[,0] & B[0]) ^ (A[,1] & B[1]) ^ (A[,2] & B[2]) ^ ...
+ *
+ * Assuming columns are represented as words and rows as bit offsets,
+ * all rows of AB can be calculated in parallel:
+ *
+ * AB = (A[0] & -((B >> 0) & 1) ^ (A[1] & -((B >> 1) & 1) ^ ...
+ */
+template <unsigned int M, unsigned int N>
+bitvector<M> mul(const bitmatrix<M, N>& a, const bitvector<N> b) {
+    bitvector<M> result(0);
+    for (unsigned int i = 0; i < N; ++i)
+        result ^= a[i] & -((b >> i) & 1);
+    return result;
+}
+
+/*
+ * Multiplies MxN matrix A by NxP matrix B in GF(2).
+ *
+ * For M,N,P = 3:
+ *
+ *     | a b c |      | j k l |       | (aj + bm + cp) (ak + bn + cq) (al + bo + cr) |
+ * A = | d e f |, B = | m n o |, AB = | (dj + em + fp) (dk + en + fq) (dl + eo + fr) |
+ *     | g h i |      | p q r |       | (gj + hm + ip) (gk + hn + iq) (gl + ho + ir) |
+ */
+template <unsigned int M, unsigned int N, unsigned int P>
+void mul(bitmatrix<M, P>& result, const bitmatrix<M, N>& a, const bitmatrix<N, P>& b) {
+    for (unsigned int i = 0; i < P; i++)
+        result[i] = mul(a, b[i]);
+}
+
+/*
+ * Squares an NxN matrix in GF(2).
+ */
+template <unsigned int N>
+void sqr(bitmatrix<N, N>& result, const bitmatrix<N, N>& a) {
+    mul(result, a, a);
+}
+
+/*
+ * Raises an NxN matrix to the power n in GF(2) by squaring.
+ */
+template <unsigned int N>
+void pow(bitmatrix<N, N>& result, const bitmatrix<N, N>& a, uint64_t n) {
+    result.identity();
+    if (n > 0) {
+        bitmatrix<N, N> square = a;
+        bitmatrix<N, N> temp;
+        bitmatrix<N, N> *ptemp = &temp, *psquare = &square, *presult = &result;
+        for (;;) {
+            if (n & 1) {
+                mul(*ptemp, *presult, *psquare);
+                std::swap(ptemp, presult);
+            }
+            if (!(n >>= 1))
+                break;
+            sqr(*ptemp, *psquare);
+            std::swap(ptemp, psquare);
+        }
+        if (presult != &result)
+            result = *presult;
+    }
+}
diff --git a/circe-checksum/src/main/circe/include/crc32c_sse42.hpp b/circe-checksum/src/main/circe/include/crc32c_sse42.hpp
new file mode 100644
index 000000000..629f35e20
--- /dev/null
+++ b/circe-checksum/src/main/circe/include/crc32c_sse42.hpp
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+#include "int_types.h"
+
+bool crc32c_initialize();
+
+class chunk_config {
+public:
+    enum { min_words = 4 };
+
+    const size_t words;
+    const chunk_config* const next;
+    uint32_t shift1[256];
+    uint32_t shift2[256];
+
+    chunk_config(size_t words, const chunk_config* next = 0);
+
+    size_t loops() const { return (words - 1) / 3; }
+    size_t extra() const { return (words - 1) % 3 + 1; }
+
+private:
+    chunk_config& operator=(const chunk_config&);
+
+    static void make_shift_table(size_t bytes, uint32_t table[256]);
+};
+
+uint32_t crc32c(uint32_t init, const void *buf, size_t len, const chunk_config* config);
diff --git a/circe-checksum/src/main/circe/include/int_types.h b/circe-checksum/src/main/circe/include/int_types.h
new file mode 100644
index 000000000..16c5c8548
--- /dev/null
+++ b/circe-checksum/src/main/circe/include/int_types.h
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+#include <stddef.h> // size_t
+
+#if defined(_MSC_VER) && _MSC_VER < 1600 // stdint.h added in MSVC 2010
+
+typedef __int8 int8_t;
+typedef __int16 int16_t;
+typedef __int32 int32_t;
+typedef __int64 int64_t;
+typedef unsigned __int8 uint8_t;
+typedef unsigned __int16 uint16_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+
+#else
+
+# include <stdint.h>
+
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER < 1900 // MSVC 2015
+
+# define SIZE_T_FORMAT "%Iu"
+
+#else
+
+# define SIZE_T_FORMAT "%zu"
+
+#endif
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/CommonHashes.java b/circe-checksum/src/main/java/com/scurrilous/circe/CommonHashes.java
new file mode 100644
index 000000000..cb6ef6612
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/CommonHashes.java
@@ -0,0 +1,188 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe;
+
+import com.scurrilous.circe.params.CrcParameters;
+import com.scurrilous.circe.params.MurmurHash3Parameters;
+import com.scurrilous.circe.params.MurmurHash3Variant;
+import com.scurrilous.circe.params.SimpleHashParameters;
+import com.scurrilous.circe.params.SipHash24Parameters;
+
+/**
+ * Static methods to obtain commonly-used hash functions. Note that a suitable
+ * provider JAR must be made available on the class path. This class does not
+ * have direct access to specific implementations; it simply constructs the hash
+ * parameters and uses the {@link Hashes} class to search for a provider.
+ */
+public final class CommonHashes {
+
+    private CommonHashes() {
+    }
+
+    /**
+     * Returns an incremental stateless hash function implementing the
+     * {@linkplain CrcParameters#CRC32 CRC-32} checksum algorithm.
+     * 
+     * @return a CRC-32 stateless incremental integer hash
+     * @throws UnsupportedOperationException if no provider is available
+     */
+    public static IncrementalIntHash crc32() {
+        return Hashes.getIncrementalInt(CrcParameters.CRC32);
+    }
+
+    /**
+     * Returns an incremental stateless hash function implementing the
+     * {@linkplain CrcParameters#CRC32C CRC-32C} checksum algorithm.
+     * 
+     * @return a CRC-32C stateless incremental integer hash
+     * @throws UnsupportedOperationException if no provider is available
+     */
+    public static IncrementalIntHash crc32c() {
+        return Hashes.getIncrementalInt(CrcParameters.CRC32C);
+    }
+
+    /**
+     * Returns an incremental stateless hash function implementing the
+     * {@linkplain CrcParameters#CRC64 CRC-64} checksum algorithm.
+     * 
+     * @return a CRC-64 stateless incremental long integer hash
+     * @throws UnsupportedOperationException if no provider is available
+     */
+    public static IncrementalLongHash crc64() {
+        return Hashes.getIncrementalLong(CrcParameters.CRC64);
+    }
+
+    /**
+     * Returns a hash function implementing the MurmurHash3 algorithm, 32-bit
+     * x86 variant, with a seed of 0.
+     * 
+     * @return a MurmurHash3 32-bit/x86 stateless integer hash
+     * @throws UnsupportedOperationException if no provider is available
+     */
+    public static StatelessIntHash murmur3_32() {
+        return Hashes.getStatelessInt(new MurmurHash3Parameters(MurmurHash3Variant.X86_32));
+    }
+
+    /**
+     * Returns a hash function implementing the MurmurHash3 algorithm, 32-bit
+     * x86 variant, with the given seed value
+     * 
+     * @param seed the 32-bit seed value
+     * @return a MurmurHash3 32-bit/x86 stateless integer hash
+     * @throws UnsupportedOperationException if no provider is available
+     */
+    public static StatelessIntHash murmur3_32(int seed) {
+        return Hashes.getStatelessInt(new MurmurHash3Parameters(MurmurHash3Variant.X86_32, seed));
+    }
+
+    /**
+     * Returns a hash function implementing the MurmurHash3 algorithm, 128-bit
+     * x64 variant, with a seed of 0.
+     * 
+     * @return a MurmurHash3 128-bit/x64 stateful hash
+     * @throws UnsupportedOperationException if no provider is available
+     */
+    public static StatefulHash murmur3_128() {
+        return Hashes.createStateful(new MurmurHash3Parameters(MurmurHash3Variant.X64_128));
+    }
+
+    /**
+     * Returns a hash function implementing the MurmurHash3 algorithm, 128-bit
+     * x64 variant, with the given seed value.
+     * 
+     * @param seed the 32-bit seed value
+     * @return a MurmurHash3 128-bit/x64 stateful hash
+     * @throws UnsupportedOperationException if no provider is available
+     */
+    public static StatefulHash murmur3_128(int seed) {
+        return Hashes.createStateful(new MurmurHash3Parameters(MurmurHash3Variant.X64_128, seed));
+    }
+
+    /**
+     * Returns a hash function implementing the SipHash-2-4 algorithm (64 bits)
+     * with the default seed,
+     * {@code 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F}.
+     * 
+     * @return a SipHash-2-4 stateless long integer hash
+     * @throws UnsupportedOperationException if no provider is available
+     */
+    public static StatelessLongHash sipHash24() {
+        return Hashes.getStatelessLong(new SipHash24Parameters());
+    }
+
+    /**
+     * Returns a hash function implementing the SipHash-2-4 algorithm (64 bits)
+     * with the given seed value.
+     * 
+     * @param seedLow the low-order 64 bits of the seed
+     * @param seedHigh the high-order 64 bits of the seed
+     * @return a SipHash-2-4 stateless long integer hash
+     * @throws UnsupportedOperationException if no provider is available
+     */
+    public static StatelessLongHash sipHash24(long seedLow, long seedHigh) {
+        return Hashes.getStatelessLong(new SipHash24Parameters(seedLow, seedHigh));
+    }
+
+    /**
+     * Returns a hash function implementing the MD5 algorithm (128 bits).
+     * 
+     * @return an MD5 stateful hash
+     * @throws UnsupportedOperationException if no provider is available
+     */
+    public static StatefulHash md5() {
+        return Hashes.createStateful(SimpleHashParameters.MD5);
+    }
+
+    /**
+     * Returns a hash function implementing the SHA-1 algorithm (160 bits).
+     * 
+     * @return a SHA-1 stateful hash
+     * @throws UnsupportedOperationException if no provider is available
+     */
+    public static StatefulHash sha1() {
+        return Hashes.createStateful(SimpleHashParameters.SHA1);
+    }
+
+    /**
+     * Returns a hash function implementing the SHA-256 algorithm (256 bits).
+     * 
+     * @return a SHA-256 stateful hash
+     * @throws UnsupportedOperationException if no provider is available
+     */
+    public static StatefulHash sha256() {
+        return Hashes.createStateful(SimpleHashParameters.SHA256);
+    }
+
+    /**
+     * Returns a hash function implementing the SHA-384 algorithm (384 bits).
+     * 
+     * @return a SHA-384 stateful hash
+     * @throws UnsupportedOperationException if no provider is available
+     */
+    public static StatefulHash sha384() {
+        return Hashes.createStateful(SimpleHashParameters.SHA384);
+    }
+
+    /**
+     * Returns a hash function implementing the SHA-512 algorithm (512 bits).
+     * 
+     * @return a SHA-512 stateful hash
+     * @throws UnsupportedOperationException if no provider is available
+     */
+    public static StatefulHash sha512() {
+        return Hashes.createStateful(SimpleHashParameters.SHA512);
+    }
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/Hash.java b/circe-checksum/src/main/java/com/scurrilous/circe/Hash.java
new file mode 100644
index 000000000..1072e2d81
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/Hash.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe;
+
+/**
+ * Abstract hash function. Each actual hash function is provided using a
+ * {@linkplain StatefulHash stateful} derived interface. Hash functions with an
+ * output length that fits within an {@code int} or a {@code long} are also
+ * generally provided using a {@linkplain StatelessHash stateless} derived
+ * interface. Given a stateless hash object, a method is provided for obtaining
+ * a new corresponding stateful object.
+ */
+public interface Hash {
+
+    /**
+     * Returns the canonical name of this hash algorithm.
+     * 
+     * @return the name of this hash algorithm
+     */
+    String algorithm();
+
+    /**
+     * Returns the length in bytes of the output of this hash function.
+     * 
+     * @return the hash length in bytes
+     */
+    int length();
+
+    /**
+     * Returns whether this hash function supports unsafe access to arbitrary
+     * memory addresses using methods such as
+     * {@link StatefulHash#update(long, long)},
+     * {@link StatelessIntHash#calculate(long, long)}, or
+     * {@link IncrementalIntHash#resume(int, long, long)}. Such functions are
+     * generally implemented in native code.
+     * 
+     * @return true if unsafe access is supported, false if not
+     */
+    boolean supportsUnsafe();
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/HashParameters.java b/circe-checksum/src/main/java/com/scurrilous/circe/HashParameters.java
new file mode 100644
index 000000000..5e6a444f5
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/HashParameters.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe;
+
+/**
+ * Base interface implemented by classes describing parameters for a particular
+ * family of hash algorithms.
+ */
+public interface HashParameters {
+
+    /**
+     * Returns the canonical name of the hash algorithm.
+     * 
+     * @return the name of the hash algorithm
+     */
+    String algorithm();
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/HashProvider.java b/circe-checksum/src/main/java/com/scurrilous/circe/HashProvider.java
new file mode 100644
index 000000000..219006e8c
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/HashProvider.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe;
+
+import java.util.EnumSet;
+
+/**
+ * Interface used to obtain instances of various kinds of hash algorithms.
+ */
+public interface HashProvider {
+
+    /**
+     * Returns information about the available implementations corresponding to
+     * the given hash algorithm parameters.
+     * 
+     * @param params the hash algorithm parameters
+     * @return a set of flags indicating the level of support
+     */
+    EnumSet<HashSupport> querySupport(HashParameters params);
+
+    /**
+     * Creates a stateful hash function using the given parameters.
+     * 
+     * @param params the hash algorithm parameters
+     * @return a stateful hash function
+     * @throws UnsupportedOperationException if this provider cannot support the
+     *             given parameters
+     */
+    StatefulHash createStateful(HashParameters params);
+
+    /**
+     * Requests a stateless, int-width hash function with the given parameters.
+     * Because not all stateless hash functions are incremental, this method may
+     * be able to return implementations not supported by or more optimized than
+     * {@link #getIncrementalInt}.
+     * 
+     * @param params the hash algorithm parameters
+     * @return a stateless int-width hash function
+     * @throws UnsupportedOperationException if this provider cannot support the
+     *             given parameters
+     */
+    StatelessIntHash getStatelessInt(HashParameters params);
+
+    /**
+     * Requests a stateless, long-width hash function with the given parameters.
+     * Because not all stateless hash functions are incremental, this method may
+     * be able to return implementations not supported by or more optimized than
+     * {@link #getIncrementalLong}.
+     * <p>
+     * Note that this method may return a less efficient hash function than
+     * {@link #getStatelessInt} for hashes of 32 bits or less.
+     * 
+     * @param params the hash algorithm parameters
+     * @return a stateless long-width hash function
+     * @throws UnsupportedOperationException if this provider cannot support the
+     *             given parameters
+     */
+    StatelessLongHash getStatelessLong(HashParameters params);
+
+    /**
+     * Requests an incremental, stateless, int-width hash function with the
+     * given parameters. Note that although an algorithm may be available in
+     * incremental form, some potentially more optimized implementations may not
+     * support that form, and therefore cannot be provided be this method.
+     * 
+     * @param params the hash algorithm parameters
+     * @return a stateful int-width hash function
+     * @throws UnsupportedOperationException if this provider cannot support the
+     *             given parameters
+     */
+    IncrementalIntHash getIncrementalInt(HashParameters params);
+
+    /**
+     * Requests an incremental, stateless, long-width hash function with the
+     * given parameters. Note that although an algorithm may be available in
+     * incremental form, some potentially more optimized implementations may not
+     * support that form, and therefore cannot be provided be this method.
+     * <p>
+     * Also note that this method may return a less efficient hash function than
+     * {@link #getIncrementalInt} for hashes of 32 bits or less.
+     * 
+     * @param params the hash algorithm parameters
+     * @return a stateful long-width hash function
+     * @throws UnsupportedOperationException if this provider cannot support the
+     *             given parameters
+     */
+    IncrementalLongHash getIncrementalLong(HashParameters params);
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/HashProviders.java b/circe-checksum/src/main/java/com/scurrilous/circe/HashProviders.java
new file mode 100644
index 000000000..7196af5c4
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/HashProviders.java
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.ServiceLoader;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * Static utility methods for discovering {@link HashProvider} instances.
+ */
+public final class HashProviders {
+
+    static final Collection<HashProvider> ALL_PROVIDERS = getAllProviders();
+
+    private static Collection<HashProvider> getAllProviders() {
+        final ServiceLoader<HashProvider> loader = ServiceLoader.load(HashProvider.class);
+        final LinkedList<HashProvider> providers = new LinkedList<>();
+        for (final HashProvider provider : loader)
+            providers.add(provider);
+        return Collections.unmodifiableList(new ArrayList<>(providers));
+    }
+
+    private HashProviders() {
+    }
+
+    /**
+     * Returns an iterator over all known {@link HashProvider} instances.
+     * 
+     * @return an iterator over all HashProviders
+     */
+    public static Iterator<HashProvider> iterator() {
+        return ALL_PROVIDERS.iterator();
+    }
+
+    /**
+     * Returns the best hash provider supporting at least a stateful
+     * implementation of a hash function with the given parameters.
+     * 
+     * @param params the parameters defining the hash function
+     * @return the best hash provider for the given parameters
+     * @throws UnsupportedOperationException if no provider supports the
+     *             parameters
+     */
+    public static HashProvider best(HashParameters params) {
+        return best(params, EnumSet.of(HashSupport.STATEFUL));
+    }
+
+    /**
+     * Returns the best hash provider supporting at least the given flags for a
+     * hash function with the given parameters.
+     * 
+     * @param params the parameters defining the hash function
+     * @param required the required support flags for a provider to be
+     *            considered
+     * @return the best hash provider for the given parameters
+     * @throws UnsupportedOperationException if no provider supports the
+     *             parameters
+     */
+    public static HashProvider best(HashParameters params, EnumSet<HashSupport> required) {
+        HashProvider result = null;
+        EnumSet<HashSupport> resultSupport = null;
+        for (final HashProvider provider : ALL_PROVIDERS) {
+            final EnumSet<HashSupport> support = provider.querySupport(params);
+            if (support.containsAll(required) &&
+                    (result == null || HashSupport.compare(support, resultSupport) < 0)) {
+                result = provider;
+                resultSupport = support;
+            }
+        }
+        if (result == null)
+            throw new UnsupportedOperationException();
+        return result;
+    }
+
+    /**
+     * Returns a map of hash providers supporting at least a stateful
+     * implementation of a hash function with the given parameters.
+     * 
+     * @param params the parameters defining the hash function
+     * @return a sorted map of hash support flags to hash providers
+     */
+    public static SortedMap<EnumSet<HashSupport>, HashProvider> search(HashParameters params) {
+        return search(params, EnumSet.of(HashSupport.STATEFUL));
+    }
+
+    /**
+     * Returns a map of hash providers supporting at least the given flags for a
+     * hash function with the given parameters.
+     * 
+     * @param params the parameters defining the hash function
+     * @param required the required support flags for a provider to be included
+     * @return a sorted map of hash support flags to hash providers
+     */
+    public static SortedMap<EnumSet<HashSupport>, HashProvider> search(HashParameters params,
+            EnumSet<HashSupport> required) {
+        final SortedMap<EnumSet<HashSupport>, HashProvider> result = new TreeMap<>(
+                new HashSupport.SetComparator());
+        for (final HashProvider provider : ALL_PROVIDERS) {
+            final EnumSet<HashSupport> support = provider.querySupport(params);
+            if (support.containsAll(required))
+                result.put(support, provider);
+        }
+        return result;
+    }
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/HashSupport.java b/circe-checksum/src/main/java/com/scurrilous/circe/HashSupport.java
new file mode 100644
index 000000000..34a203c39
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/HashSupport.java
@@ -0,0 +1,179 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe;
+
+import java.util.Comparator;
+import java.util.EnumSet;
+import java.util.Iterator;
+
+/**
+ * Flags indicating the support available for some set of hash algorithm.
+ */
+public enum HashSupport {
+    /**
+     * Indicates that the hash algorithm is available in hardware-accelerated
+     * native code as an {@link IncrementalIntHash} or
+     * {@link IncrementalLongHash}, depending on which of {@link #INT_SIZED} or
+     * {@link #LONG_SIZED} is set.
+     */
+    HARDWARE_INCREMENTAL(10),
+    /**
+     * Indicates that the hash algorithm is available in hardware-accelerated
+     * native code.
+     */
+    HARDWARE(20),
+    /**
+     * Indicates that the hash algorithm is available in native code as a
+     * {@link IncrementalIntHash} or {@link IncrementalLongHash}, depending on
+     * which of {@link #INT_SIZED} or {@link #LONG_SIZED} is set.
+     */
+    NATIVE_INCREMENTAL(30),
+    /**
+     * Indicates that the hash algorithm is available in native code.
+     */
+    NATIVE(40),
+    /**
+     * Indicates that the incremental hash algorithm supports unsafe memory
+     * access via {@link IncrementalIntHash#resume(int, long, long)} or
+     * {@link IncrementalLongHash#resume(long, long, long)}, depending on which
+     * of {@link #INT_SIZED} or {@link #LONG_SIZED} is set.
+     */
+    UNSAFE_INCREMENTAL(50),
+    /**
+     * Indicates that the stateful hash algorithm unsafe memory access via
+     * {@link StatefulHash#update(long, long)}. If {@link #INT_SIZED} is also
+     * set, the function returned by {@link StatefulIntHash#asStateless()} also
+     * supports {@link StatelessIntHash#calculate(long, long)}. Similarly, if
+     * {@link #LONG_SIZED} is also set, the function returned by
+     * {@link StatefulLongHash#asStateless()} also supports
+     * {@link StatelessLongHash#calculate(long, long)}.
+     */
+    UNSAFE(60),
+    /**
+     * Indicates that the hash algorithm is available as a
+     * {@link IncrementalIntHash} or {@link IncrementalLongHash}, depending on
+     * which of {@link #INT_SIZED} or {@link #LONG_SIZED} is set.
+     */
+    STATELESS_INCREMENTAL(70),
+    /**
+     * Indicates that the hash algorithm is available as an incremental stateful
+     * hash function, for which {@link StatefulHash#supportsIncremental()}
+     * returns {@code true}. This flag is implied by
+     * {@link #STATELESS_INCREMENTAL}.
+     */
+    INCREMENTAL(80),
+    /**
+     * Indicates that the hash algorithm is available as a
+     * {@link StatefulIntHash} and {@link StatelessIntHash}.
+     */
+    INT_SIZED(90),
+    /**
+     * Indicates that the hash algorithm is available as a
+     * {@link StatefulLongHash} and {@link StatelessLongHash}.
+     */
+    LONG_SIZED(90),
+    /**
+     * Indicates that the hash algorithm is available as a {@link StatefulHash}.
+     * If this flag is not set, the algorithm is not supported at all.
+     */
+    STATEFUL(100);
+
+    /**
+     * The minimum priority value, indicating the highest priority. All flags
+     * have a priority value greater than this.
+     */
+    public static final int MIN_PRIORITY = 0;
+
+    /**
+     * The maximum priority value, indicating the lowest priority. All flags
+     * have a priority value less than this.
+     */
+    public static final int MAX_PRIORITY = 110;
+
+    private final int priority;
+
+    private HashSupport(int priority) {
+        this.priority = priority;
+    }
+
+    /**
+     * Returns the relative priority of a hash algorithm support flag, which is
+     * an indicator of its performance and flexibility. Lower values indicate
+     * higher priority.
+     * 
+     * @return the priority of this flag (currently between 10 and 90)
+     */
+    public int getPriority() {
+        return priority;
+    }
+
+    /**
+     * Returns the {@linkplain #getPriority() priority} of the highest-priority
+     * hash algorithm support flag in the given set of flags. If the set is
+     * empty, {@link #MAX_PRIORITY} is returned.
+     * 
+     * @param set a set of hash algorithm support flags
+     * @return the highest priority (lowest value) in the set, or
+     *         {@link #MAX_PRIORITY} if empty
+     */
+    public static int getMaxPriority(EnumSet<HashSupport> set) {
+        if (set.isEmpty())
+            return MAX_PRIORITY;
+        return set.iterator().next().getPriority();
+    }
+
+    /**
+     * Compares the given sets of hash algorithm support flags for priority
+     * order. The set with the highest priority flag without a flag of matching
+     * priority in the other set has higher priority.
+     * 
+     * @param set1 the first set to be compared
+     * @param set2 the second set to be compared
+     * @return a negative integer, zero, or a positive integer if the first set
+     *         has priority higher than, equal to, or lower than the second
+     */
+    public static int compare(EnumSet<HashSupport> set1, EnumSet<HashSupport> set2) {
+        // assumes iterators return flags in priority order
+        final Iterator<HashSupport> i1 = set1.iterator();
+        final Iterator<HashSupport> i2 = set2.iterator();
+        int floor = MIN_PRIORITY;
+        while (i1.hasNext() || i2.hasNext()) {
+            int p1, p2;
+            do {
+                p1 = i1.hasNext() ? i1.next().getPriority() : MAX_PRIORITY;
+            } while (p1 == floor);
+            do {
+                p2 = i2.hasNext() ? i2.next().getPriority() : MAX_PRIORITY;
+            } while (p2 == floor);
+            if (p1 < p2)
+                return -1;
+            if (p1 > p2)
+                return 1;
+            floor = p1;
+        }
+        return 0;
+    }
+
+    /**
+     * {@link Comparator} for {@link EnumSet EnumSets} for hash support flags.
+     */
+    static final class SetComparator implements Comparator<EnumSet<HashSupport>> {
+        @Override
+        public int compare(EnumSet<HashSupport> o1, EnumSet<HashSupport> o2) {
+            return HashSupport.compare(o1, o2);
+        }
+    }
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/Hashes.java b/circe-checksum/src/main/java/com/scurrilous/circe/Hashes.java
new file mode 100644
index 000000000..786ead5f3
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/Hashes.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe;
+
+import java.util.EnumSet;
+
+import com.scurrilous.circe.HashParameters;
+import com.scurrilous.circe.HashProviders;
+import com.scurrilous.circe.HashSupport;
+import com.scurrilous.circe.IncrementalIntHash;
+import com.scurrilous.circe.IncrementalLongHash;
+import com.scurrilous.circe.StatefulHash;
+import com.scurrilous.circe.StatelessIntHash;
+import com.scurrilous.circe.StatelessLongHash;
+
+/**
+ * Static methods to obtain various forms of abstract hash functions. Each
+ * method uses {@link HashProviders#best} to find the best provider for the
+ * given parameters and hash interface, and then calls the corresponding method
+ * on that provider.
+ */
+public final class Hashes {
+
+    private Hashes() {
+    }
+
+    /**
+     * Creates a stateful hash function using the given parameters.
+     * 
+     * @param params the hash algorithm parameters
+     * @return a stateful hash function
+     * @throws UnsupportedOperationException if no provider supports the
+     *             parameters
+     */
+    public static StatefulHash createStateful(HashParameters params) {
+        return HashProviders.best(params).createStateful(params);
+    }
+
+    /**
+     * Requests a stateless, int-width hash function with the given parameters.
+     * Because not all stateless hash functions are incremental, this method may
+     * be able to return implementations not supported by or more optimized than
+     * {@link #getIncrementalInt}.
+     * 
+     * @param params the hash algorithm parameters
+     * @return a stateless int-width hash function
+     * @throws UnsupportedOperationException if no provider supports the
+     *             parameters as a {@link StatelessIntHash}
+     */
+    public static StatelessIntHash getStatelessInt(HashParameters params) {
+        return HashProviders.best(params, EnumSet.of(HashSupport.INT_SIZED))
+                .getStatelessInt(params);
+    }
+
+    /**
+     * Requests a stateless, long-width hash function with the given parameters.
+     * Because not all stateless hash functions are incremental, this method may
+     * be able to return implementations not supported by or more optimized than
+     * {@link #getIncrementalLong}.
+     * <p>
+     * Note that this method may return a less efficient hash function than
+     * {@link #getStatelessInt} for hashes of 32 bits or less.
+     * 
+     * @param params the hash algorithm parameters
+     * @return a stateless long-width hash function
+     * @throws UnsupportedOperationException if no provider supports the
+     *             parameters as a {@link StatelessLongHash}
+     */
+    public static StatelessLongHash getStatelessLong(HashParameters params) {
+        return HashProviders.best(params, EnumSet.of(HashSupport.LONG_SIZED)).getStatelessLong(
+                params);
+    }
+
+    /**
+     * Requests an incremental, stateless, int-width hash function with the
+     * given parameters. Note that although an algorithm may be available in
+     * incremental form, some potentially more optimized implementations may not
+     * support that form, and therefore cannot be provided be this method.
+     * 
+     * @param params the hash algorithm parameters
+     * @return a stateful int-width hash function
+     * @throws UnsupportedOperationException if no provider supports the
+     *             parameters as an {@link IncrementalIntHash}
+     */
+    public static IncrementalIntHash getIncrementalInt(HashParameters params) {
+        return HashProviders.best(params,
+                EnumSet.of(HashSupport.INT_SIZED, HashSupport.STATELESS_INCREMENTAL))
+                .getIncrementalInt(params);
+    }
+
+    /**
+     * Requests an incremental, stateless, long-width hash function with the
+     * given parameters. Note that although an algorithm may be available in
+     * incremental form, some potentially more optimized implementations may not
+     * support that form, and therefore cannot be provided be this method.
+     * <p>
+     * Also note that this method may return a less efficient hash function than
+     * {@link #getIncrementalInt} for hashes of 32 bits or less.
+     * 
+     * @param params the hash algorithm parameters
+     * @return a stateful long-width hash function
+     * @throws UnsupportedOperationException if no provider supports the
+     *             parameters as an {@link IncrementalLongHash}
+     */
+    public static IncrementalLongHash getIncrementalLong(HashParameters params) {
+        return HashProviders.best(params,
+                EnumSet.of(HashSupport.LONG_SIZED, HashSupport.STATELESS_INCREMENTAL))
+                .getIncrementalLong(params);
+    }
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/IncrementalIntHash.java b/circe-checksum/src/main/java/com/scurrilous/circe/IncrementalIntHash.java
new file mode 100644
index 000000000..46320c77b
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/IncrementalIntHash.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Incremental stateless integer hash function, which has the property that its
+ * output is the same as (or easily derivable from) its state. Specifically, for
+ * any sequence M partitioned arbitrarily into two subsequences M<sub>1</sub>
+ * M<sub>2</sub>:
+ * 
+ * <pre>
+ * h(M) = h'(h(M<sub>1</sub>), M<sub>2</sub>)
+ * </pre>
+ * 
+ * where h corresponds to the {@link StatelessIntHash#calculate calculate}
+ * function and h' corresponds to the {@link #resume} function.
+ * <p>
+ * Note that stateful hash functions obtained from incremental stateless hash
+ * functions are also {@linkplain StatefulHash#supportsIncremental incremental}.
+ */
+public interface IncrementalIntHash extends StatelessIntHash {
+
+    /**
+     * Evaluates this hash function as if the entire given input array were
+     * appended to the previously hashed input.
+     * 
+     * @param current the hash output for input hashed so far
+     * @param input the input array
+     * @return the output of the hash function for the concatenated input
+     */
+    int resume(int current, byte[] input);
+
+    /**
+     * Evaluates this hash function as if the given range of the given input
+     * array were appended to the previously hashed input.
+     * 
+     * @param current the hash output for input hashed so far
+     * @param input the input array
+     * @param index the starting index of the first input byte
+     * @param length the length of the input range
+     * @return the output of the hash function for the concatenated input
+     * @throws IndexOutOfBoundsException if index is negative or
+     *             {@code index + length} is greater than the array length
+     */
+    int resume(int current, byte[] input, int index, int length);
+
+    /**
+     * Evaluates this hash function as if the remaining contents of the given
+     * input buffer were appended to the previously hashed input. This method
+     * leaves the buffer position at the limit.
+     * 
+     * @param current the hash output for input hashed so far
+     * @param input the input buffer
+     * @return the output of the hash function for the concatenated input
+     */
+    int resume(int current, ByteBuffer input);
+
+    /**
+     * Evaluates this hash function as if the memory with the given address and
+     * length were appended to the previously hashed input. The arguments are
+     * generally not checked in any way and will likely lead to a VM crash or
+     * undefined results if invalid.
+     * 
+     * @param current the hash output for input hashed so far
+     * @param address the base address of the input
+     * @param length the length of the input
+     * @return the output of the hash function for the concatenated input
+     * @throws UnsupportedOperationException if this function does not support
+     *             unsafe memory access
+     * @see #supportsUnsafe()
+     */
+    int resume(int current, long address, long length);
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/IncrementalLongHash.java b/circe-checksum/src/main/java/com/scurrilous/circe/IncrementalLongHash.java
new file mode 100644
index 000000000..382346df2
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/IncrementalLongHash.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Incremental stateless long integer hash function, which has the property that
+ * its output is the same as (or easily derivable from) its state. Specifically,
+ * for any sequence M partitioned arbitrarily into two subsequences
+ * M<sub>1</sub> M<sub>2</sub>:
+ * 
+ * <pre>
+ * h(M) = h'(h(M<sub>1</sub>), M<sub>2</sub>)
+ * </pre>
+ * 
+ * where h corresponds to the {@link StatelessLongHash#calculate calculate}
+ * function and h' corresponds to the {@link #resume} function.
+ * <p>
+ * Note that stateful hash functions obtained from incremental stateless hash
+ * functions are also {@linkplain StatefulHash#supportsIncremental incremental}.
+ */
+public interface IncrementalLongHash extends StatelessLongHash {
+
+    /**
+     * Evaluates this hash function as if the entire given input array were
+     * appended to the previously hashed input.
+     * 
+     * @param current the hash output for input hashed so far
+     * @param input the input array
+     * @return the output of the hash function for the concatenated input
+     */
+    long resume(long current, byte[] input);
+
+    /**
+     * Evaluates this hash function as if the given range of the given input
+     * array were appended to the previously hashed input.
+     * 
+     * @param current the hash output for input hashed so far
+     * @param input the input array
+     * @param index the starting index of the first input byte
+     * @param length the length of the input range
+     * @return the output of the hash function for the concatenated input
+     * @throws IndexOutOfBoundsException if index is negative or
+     *             {@code index + length} is greater than the array length
+     */
+    long resume(long current, byte[] input, int index, int length);
+
+    /**
+     * Evaluates this hash function as if the remaining contents of the given
+     * input buffer were appended to the previously hashed input. This method
+     * leaves the buffer position at the limit.
+     * 
+     * @param current the hash output for input hashed so far
+     * @param input the input buffer
+     * @return the output of the hash function for the concatenated input
+     */
+    long resume(long current, ByteBuffer input);
+
+    /**
+     * Evaluates this hash function as if the memory with the given address and
+     * length were appended to the previously hashed input. The arguments are
+     * generally not checked in any way and will likely lead to a VM crash or
+     * undefined results if invalid.
+     * 
+     * @param current the hash output for input hashed so far
+     * @param address the base address of the input
+     * @param length the length of the input
+     * @return the output of the hash function for the concatenated input
+     * @throws UnsupportedOperationException if this function does not support
+     *             unsafe memory access
+     * @see #supportsUnsafe()
+     */
+    long resume(long current, long address, long length);
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/StatefulHash.java b/circe-checksum/src/main/java/com/scurrilous/circe/StatefulHash.java
new file mode 100644
index 000000000..94d355a11
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/StatefulHash.java
@@ -0,0 +1,171 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Represents a stateful hash function, which can accumulate input from multiple
+ * method calls and provide output in various forms. Stateful hash functions
+ * should not be used concurrently from multiple threads.
+ * <p>
+ * Stateful hash functions can be incremental or non-incremental. Incremental
+ * hashing allows calling the {@link #update} methods to continue hashing using
+ * accumulated state after calling any of the output methods (such as
+ * {@link #getBytes}). Non-incremental hash functions <i>require</i> calling
+ * {@link #reset} to reinitialize the state between calling an output method and
+ * an update method.
+ */
+public interface StatefulHash extends Hash {
+
+    /**
+     * Returns a new instance of this stateful hash function reset to the
+     * initial state.
+     * 
+     * @return a new instance of this hash in the initial state
+     */
+    StatefulHash createNew();
+
+    /**
+     * Returns whether this hash function supports incremental hashing.
+     * Incremental hashing allows calling the {@link #update} methods to
+     * continue hashing using accumulated state after calling any of the output
+     * methods (such as {@link #getBytes}). Non-incremental hash functions
+     * require calling {@link #reset} to reinitialize the state between calling
+     * an output method and an update method.
+     * 
+     * @return true if incremental hashing is supported, false if not
+     */
+    boolean supportsIncremental();
+
+    /**
+     * Resets this hash function to its initial state. Resetting the state is
+     * required for non-incremental hash functions after any output methods have
+     * been called.
+     */
+    void reset();
+
+    /**
+     * Updates the state of this hash function with the entire given input
+     * array.
+     * 
+     * @param input the input array
+     * @throws IllegalStateException if this hash function is not incremental
+     *             but an output method has been called without an intervening
+     *             call to {@link #reset}
+     */
+    void update(byte[] input);
+
+    /**
+     * Updates the state of this hash function with the given range of the given
+     * input array.
+     * 
+     * @param input the input array
+     * @param index the starting index of the first input byte
+     * @param length the length of the input range
+     * @throws IllegalArgumentException if length is negative
+     * @throws IndexOutOfBoundsException if index is negative or
+     *             {@code index + length} is greater than the array length
+     * @throws IllegalStateException if this hash function is not incremental
+     *             but an output method has been called without an intervening
+     *             call to {@link #reset}
+     */
+    void update(byte[] input, int index, int length);
+
+    /**
+     * Updates the state of this hash function with the remaining contents of
+     * the given input buffer. This method leaves the buffer position at the
+     * limit.
+     * 
+     * @param input the input buffer
+     * @throws IllegalStateException if this hash function is not incremental
+     *             but an output method has been called without an intervening
+     *             call to {@link #reset}
+     */
+    void update(ByteBuffer input);
+
+    /**
+     * Updates the state of this hash function with memory with the given
+     * address and length. The arguments are generally not checked in any way
+     * and will likely lead to a VM crash or undefined results if invalid.
+     * 
+     * @param address the base address of the input
+     * @param length the length of the input
+     * @throws UnsupportedOperationException if this function does not support
+     *             unsafe memory access
+     * @throws IllegalStateException if this hash function is not incremental
+     *             but an output method has been called without an intervening
+     *             call to {@link #reset}
+     * @see #supportsUnsafe()
+     */
+    void update(long address, long length);
+
+    /**
+     * Returns a new byte array containing the output of this hash function. The
+     * caller may freely modify the contents of the array.
+     * 
+     * @return a new byte array containing the hash output
+     */
+    byte[] getBytes();
+
+    /**
+     * Writes the output of this hash function into the given byte array at the
+     * given offset.
+     * 
+     * @param output the destination array for the output
+     * @param index the starting index of the first output byte
+     * @param maxLength the maximum number of bytes to write
+     * @return the number of bytes written
+     * @throws IllegalArgumentException if {@code maxLength} is negative
+     * @throws IndexOutOfBoundsException if {@code index} is negative or if
+     *             {@code index + maxLength} is greater than the array length
+     */
+    int getBytes(byte[] output, int index, int maxLength);
+
+    /**
+     * Returns the first byte of the output of this hash function.
+     * 
+     * @return the first output byte
+     */
+    byte getByte();
+
+    /**
+     * Returns the first two bytes of the output of this hash function as a
+     * little-endian {@code short}. If the output is less than two bytes, the
+     * remaining bytes are set to 0.
+     * 
+     * @return the first two output bytes as a short
+     */
+    short getShort();
+
+    /**
+     * Returns the first four bytes of the output of this hash function as a
+     * little-endian {@code int}. If the output is less than four bytes, the
+     * remaining bytes are set to 0.
+     * 
+     * @return the first four output bytes as an int
+     */
+    int getInt();
+
+    /**
+     * Returns the first eight bytes of the output of this hash function as a
+     * little-endian {@code long}. If the output is less than eight bytes, the
+     * remaining bytes are set to 0.
+     * 
+     * @return the first eight output bytes as a long
+     */
+    long getLong();
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/StatefulIntHash.java b/circe-checksum/src/main/java/com/scurrilous/circe/StatefulIntHash.java
new file mode 100644
index 000000000..ba6bcf17d
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/StatefulIntHash.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe;
+
+/**
+ * Interface implemented by stateful hash functions with an output length of 4
+ * bytes or less. Such functions can provide a corresponding stateless
+ * implementation.
+ */
+public interface StatefulIntHash extends StatefulHash {
+
+    /**
+     * Returns an instance of stateless version of this hash function.
+     * 
+     * @return the stateless version of this hash function
+     */
+    StatelessIntHash asStateless();
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/StatefulLongHash.java b/circe-checksum/src/main/java/com/scurrilous/circe/StatefulLongHash.java
new file mode 100644
index 000000000..076401b5a
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/StatefulLongHash.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe;
+
+/**
+ * Interface implemented by stateless hash functions with an output length
+ * greater than 4 bytes and less than or equal to 8 bytes.
+ */
+public interface StatefulLongHash extends StatefulHash {
+
+    /**
+     * Returns an instance of stateless version of this hash function.
+     * 
+     * @return the stateless version of this hash function
+     */
+    StatelessLongHash asStateless();
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/StatelessHash.java b/circe-checksum/src/main/java/com/scurrilous/circe/StatelessHash.java
new file mode 100644
index 000000000..8d8534702
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/StatelessHash.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe;
+
+/**
+ * Base interface for stateless hash functions that immediately return the hash
+ * value corresponding to a given input. Stateless hash functions may be used
+ * concurrently by multiple threads without any synchronization overhead.
+ */
+public interface StatelessHash extends Hash {
+
+    /**
+     * Returns a new instance of stateful version of this hash function.
+     * 
+     * @return the stateful version of this hash function
+     */
+    StatefulHash createStateful();
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/StatelessIntHash.java b/circe-checksum/src/main/java/com/scurrilous/circe/StatelessIntHash.java
new file mode 100644
index 000000000..3ddb7c47a
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/StatelessIntHash.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Interface implemented by stateless hash functions with an output length of 4
+ * bytes or less.
+ */
+public interface StatelessIntHash extends StatelessHash {
+
+    /**
+     * Returns a new instance of stateful version of this hash function.
+     * 
+     * @return the stateful version of this hash function
+     */
+    @Override
+    StatefulIntHash createStateful();
+
+    /**
+     * Evaluates this hash function for the entire given input array.
+     * 
+     * @param input the input array
+     * @return the output of the hash function
+     */
+    int calculate(byte[] input);
+
+    /**
+     * Evaluates this hash function for the given range of the given input
+     * array.
+     * 
+     * @param input the input array
+     * @param index the starting index of the first input byte
+     * @param length the length of the input range
+     * @return the output of the hash function
+     * @throws IndexOutOfBoundsException if index is negative or
+     *             {@code index + length} is greater than the array length
+     */
+    int calculate(byte[] input, int index, int length);
+
+    /**
+     * Evaluates this hash function with the remaining contents of the given
+     * input buffer. This method leaves the buffer position at the limit.
+     * 
+     * @param input the input buffer
+     * @return the output of the hash function
+     */
+    int calculate(ByteBuffer input);
+
+    /**
+     * Evaluates this hash function for the memory with the given address and
+     * length. The arguments are generally not checked in any way and will
+     * likely lead to a VM crash or undefined results if invalid.
+     * 
+     * @param address the base address of the input
+     * @param length the length of the input
+     * @return the output of the hash function
+     * @throws UnsupportedOperationException if this function does not support
+     *             unsafe memory access
+     * @see #supportsUnsafe()
+     */
+    int calculate(long address, long length);
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/StatelessLongHash.java b/circe-checksum/src/main/java/com/scurrilous/circe/StatelessLongHash.java
new file mode 100644
index 000000000..ddaef380c
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/StatelessLongHash.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Interface implemented by stateless hash functions with an output length
+ * greater than 4 bytes and less than or equal to 8 bytes.
+ */
+public interface StatelessLongHash extends StatelessHash {
+
+    /**
+     * Returns a new instance of stateful version of this hash function.
+     * 
+     * @return the stateful version of this hash function
+     */
+    @Override
+    StatefulLongHash createStateful();
+
+    /**
+     * Evaluates this hash function for the entire given input array.
+     * 
+     * @param input the input array
+     * @return the output of the hash function
+     */
+    long calculate(byte[] input);
+
+    /**
+     * Evaluates this hash function for the given range of the given input
+     * array.
+     * 
+     * @param input the input array
+     * @param index the starting index of the first input byte
+     * @param length the length of the input range
+     * @return the output of the hash function
+     * @throws IndexOutOfBoundsException if index is negative or
+     *             {@code index + length} is greater than the array length
+     */
+    long calculate(byte[] input, int index, int length);
+
+    /**
+     * Evaluates this hash function with the remaining contents of the given
+     * input buffer. This method leaves the buffer position at the limit.
+     * 
+     * @param input the input buffer
+     * @return the output of the hash function
+     */
+    long calculate(ByteBuffer input);
+
+    /**
+     * Evaluates this hash function for the memory with the given address and
+     * length. The arguments are generally not checked in any way and will
+     * likely lead to a VM crash or undefined results if invalid.
+     * 
+     * @param address the base address of the input
+     * @param length the length of the input
+     * @return the output of the hash function
+     * @throws UnsupportedOperationException if this function does not support
+     *             unsafe memory access
+     * @see #supportsUnsafe()
+     */
+    long calculate(long address, long length);
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/crc/AbstractIntCrc.java b/circe-checksum/src/main/java/com/scurrilous/circe/crc/AbstractIntCrc.java
new file mode 100644
index 000000000..f7fafbeee
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/crc/AbstractIntCrc.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.crc;
+
+import com.scurrilous.circe.impl.AbstractIncrementalIntHash;
+
+/**
+ * Base implementation of int-width CRC functions.
+ */
+abstract class AbstractIntCrc extends AbstractIncrementalIntHash {
+
+    private final String algorithm;
+    protected final int bitWidth;
+    private final int initial;
+    private final int xorOut;
+
+    AbstractIntCrc(String algorithm, int bitWidth, int initial, int xorOut) {
+        if (bitWidth < 1 || bitWidth > 32)
+            throw new IllegalArgumentException("invalid CRC width");
+        this.algorithm = algorithm;
+        this.bitWidth = bitWidth;
+        this.initial = initial;
+        this.xorOut = xorOut;
+    }
+
+    @Override
+    public String algorithm() {
+        return algorithm;
+    }
+
+    @Override
+    public int length() {
+        return (bitWidth + 7) / 8;
+    }
+
+    @Override
+    protected int initial() {
+        return initial ^ xorOut;
+    }
+
+    @Override
+    protected int resumeUnchecked(int current, byte[] input, int index, int length) {
+        return resumeRaw(current ^ xorOut, input, index, length) ^ xorOut;
+    }
+
+    protected abstract int resumeRaw(int crc, byte[] input, int index, int length);
+
+    protected final int reflect(int value) {
+        return Integer.reverse(value) >>> (32 - bitWidth);
+    }
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/crc/AbstractLongCrc.java b/circe-checksum/src/main/java/com/scurrilous/circe/crc/AbstractLongCrc.java
new file mode 100644
index 000000000..e7c5e4bb2
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/crc/AbstractLongCrc.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.crc;
+
+import com.scurrilous.circe.impl.AbstractIncrementalLongHash;
+
+/**
+ * Base implementation of long-width CRC functions.
+ */
+abstract class AbstractLongCrc extends AbstractIncrementalLongHash {
+
+    private final String algorithm;
+    protected final int bitWidth;
+    private final long initial;
+    private final long xorOut;
+
+    AbstractLongCrc(String algorithm, int bitWidth, long initial, long xorOut) {
+        if (bitWidth < 1 || bitWidth > 64)
+            throw new IllegalArgumentException("invalid CRC width");
+        this.algorithm = algorithm;
+        this.bitWidth = bitWidth;
+        this.initial = initial;
+        this.xorOut = xorOut;
+    }
+
+    @Override
+    public String algorithm() {
+        return algorithm;
+    }
+
+    @Override
+    public int length() {
+        return (bitWidth + 7) / 8;
+    }
+
+    @Override
+    protected long initial() {
+        return initial ^ xorOut;
+    }
+
+    @Override
+    protected long resumeUnchecked(long current, byte[] input, int index, int length) {
+        return resumeRaw(current ^ xorOut, input, index, length) ^ xorOut;
+    }
+
+    protected abstract long resumeRaw(long crc, byte[] input, int index, int length);
+
+    protected final long reflect(long value) {
+        return Long.reverse(value) >>> (64 - bitWidth);
+    }
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/crc/JavaCrc32.java b/circe-checksum/src/main/java/com/scurrilous/circe/crc/JavaCrc32.java
new file mode 100644
index 000000000..afe22efc7
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/crc/JavaCrc32.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.crc;
+
+import java.util.zip.CRC32;
+
+import com.scurrilous.circe.StatefulHash;
+import com.scurrilous.circe.StatefulIntHash;
+import com.scurrilous.circe.StatelessIntHash;
+import com.scurrilous.circe.impl.AbstractStatefulHash;
+import com.scurrilous.circe.impl.AbstractStatelessIntHash;
+import com.scurrilous.circe.params.CrcParameters;
+
+/**
+ * Wraps {@link CRC32} in a {@link StatefulIntHash}.
+ */
+final class JavaCrc32 extends AbstractStatefulHash implements StatefulIntHash {
+
+    private static final String ALGORITHM = CrcParameters.CRC32.algorithm();
+    private static final int LENGTH = 4;
+
+    private final CRC32 impl = new CRC32();
+
+    @Override
+    public String algorithm() {
+        return ALGORITHM;
+    }
+
+    @Override
+    public int length() {
+        return LENGTH;
+    }
+
+    @Override
+    public StatefulHash createNew() {
+        return new JavaCrc32();
+    }
+
+    @Override
+    public boolean supportsIncremental() {
+        return true;
+    }
+
+    @Override
+    public void reset() {
+        impl.reset();
+    }
+
+    @Override
+    protected void updateUnchecked(byte[] input, int index, int length) {
+        impl.update(input, index, length);
+    }
+
+    @Override
+    public int getInt() {
+        return (int) impl.getValue();
+    }
+
+    @Override
+    public long getLong() {
+        return impl.getValue();
+    }
+
+    @Override
+    public StatelessIntHash asStateless() {
+        return new AbstractStatelessIntHash() {
+            @Override
+            public String algorithm() {
+                return ALGORITHM;
+            }
+
+            @Override
+            public int length() {
+                return LENGTH;
+            }
+
+            @Override
+            public StatefulIntHash createStateful() {
+                return new JavaCrc32();
+            }
+
+            @Override
+            protected int calculateUnchecked(byte[] input, int index, int length) {
+                final CRC32 crc32 = new CRC32();
+                crc32.update(input, index, length);
+                return (int) crc32.getValue();
+            }
+        };
+    }
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/crc/NormalByteCrc.java b/circe-checksum/src/main/java/com/scurrilous/circe/crc/NormalByteCrc.java
new file mode 100644
index 000000000..88706d6df
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/crc/NormalByteCrc.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.crc;
+
+/**
+ * Implements a "normal" MSB-first byte-width CRC function using a lookup table.
+ */
+final class NormalByteCrc extends AbstractIntCrc {
+
+    private final byte[] table = new byte[256];
+
+    NormalByteCrc(String algorithm, int bitWidth, int poly, int init, int xorOut) {
+        super(algorithm, bitWidth, init, xorOut);
+        if (bitWidth > 8)
+            throw new IllegalArgumentException("invalid CRC width");
+
+        final int widthMask = (1 << bitWidth) - 1;
+        final int shpoly = poly << (8 - bitWidth);
+        for (int i = 0; i < 256; ++i) {
+            int crc = i;
+            for (int j = 0; j < 8; ++j)
+                crc = (crc & 0x80) != 0 ? (crc << 1) ^ shpoly : crc << 1;
+            table[i] = (byte) ((crc >> (8 - bitWidth)) & widthMask);
+        }
+    }
+
+    @Override
+    protected int resumeRaw(int crc, byte[] input, int index, int length) {
+        for (int i = 0; i < length; ++i)
+            crc = table[(crc << (8 - bitWidth)) ^ (input[index + i] & 0xff)] & 0xff;
+        return crc;
+    }
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/crc/NormalIntCrc.java b/circe-checksum/src/main/java/com/scurrilous/circe/crc/NormalIntCrc.java
new file mode 100644
index 000000000..56362207a
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/crc/NormalIntCrc.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.crc;
+
+/**
+ * Implements a "normal" MSB-first int-width CRC function using a lookup table.
+ * Does not support bit-widths less than 8.
+ */
+final class NormalIntCrc extends AbstractIntCrc {
+
+    private final int widthMask;
+    private final int[] table = new int[256];
+
+    NormalIntCrc(String algorithm, int bitWidth, int poly, int init, int xorOut) {
+        super(algorithm, bitWidth, init, xorOut);
+        if (bitWidth < 8)
+            throw new IllegalArgumentException("invalid CRC width");
+
+        widthMask = bitWidth < 32 ? ((1 << bitWidth) - 1) : ~0;
+        final int top = 1 << (bitWidth - 1);
+        for (int i = 0; i < 256; ++i) {
+            int crc = i << (bitWidth - 8);
+            for (int j = 0; j < 8; ++j)
+                crc = (crc & top) != 0 ? (crc << 1) ^ poly : crc << 1;
+            table[i] = crc & widthMask;
+        }
+    }
+
+    @Override
+    protected int resumeRaw(int crc, byte[] input, int index, int length) {
+        for (int i = 0; i < length; ++i)
+            crc = table[((crc >>> (bitWidth - 8)) ^ input[index + i]) & 0xff] ^ (crc << 8);
+        return crc & widthMask;
+    }
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/crc/NormalLongCrc.java b/circe-checksum/src/main/java/com/scurrilous/circe/crc/NormalLongCrc.java
new file mode 100644
index 000000000..4a7284753
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/crc/NormalLongCrc.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.crc;
+
+/**
+ * Implements a "normal" MSB-first long-width CRC function using a lookup table.
+ * Does not support bit-widths less than 8.
+ */
+final class NormalLongCrc extends AbstractLongCrc {
+
+    private final long widthMask;
+    private final long[] table = new long[256];
+
+    NormalLongCrc(String algorithm, int bitWidth, long poly, long init, long xorOut) {
+        super(algorithm, bitWidth, init, xorOut);
+        if (bitWidth < 8)
+            throw new IllegalArgumentException("invalid CRC width");
+
+        widthMask = bitWidth < 64 ? ((1L << bitWidth) - 1) : ~0L;
+        final long top = 1L << (bitWidth - 1);
+        for (int i = 0; i < 256; ++i) {
+            long crc = (long) i << (bitWidth - 8);
+            for (int j = 0; j < 8; ++j)
+                crc = (crc & top) != 0 ? (crc << 1) ^ poly : crc << 1;
+            table[i] = crc & widthMask;
+        }
+    }
+
+    @Override
+    protected long resumeRaw(long crc, byte[] input, int index, int length) {
+        for (int i = 0; i < length; ++i)
+            crc = table[(int) ((crc >>> (bitWidth - 8)) ^ input[index + i]) & 0xff] ^ (crc << 8);
+        return crc & widthMask;
+    }
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/crc/ReflectedIntCrc.java b/circe-checksum/src/main/java/com/scurrilous/circe/crc/ReflectedIntCrc.java
new file mode 100644
index 000000000..0b190fa2b
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/crc/ReflectedIntCrc.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.crc;
+
+/**
+ * Implements a "reflected" LSB-first int-width CRC function using a lookup
+ * table.
+ */
+final class ReflectedIntCrc extends AbstractIntCrc {
+
+    private final int[] table = new int[256];
+
+    ReflectedIntCrc(String algorithm, int width, int poly, int init, int xorOut) {
+        super(algorithm, width, init, xorOut);
+
+        poly = reflect(poly);
+        for (int i = 0; i < 256; ++i) {
+            int crc = i;
+            for (int j = 0; j < 8; ++j)
+                crc = (crc & 1) != 0 ? (crc >>> 1) ^ poly : crc >>> 1;
+            table[i] = crc;
+        }
+    }
+
+    @Override
+    protected int initial() {
+        return reflect(super.initial());
+    }
+
+    @Override
+    protected int resumeRaw(int crc, byte[] input, int index, int length) {
+        for (int i = 0; i < length; ++i)
+            crc = table[(crc ^ input[index + i]) & 0xff] ^ (crc >>> 8);
+        return crc;
+    }
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/crc/ReflectedLongCrc.java b/circe-checksum/src/main/java/com/scurrilous/circe/crc/ReflectedLongCrc.java
new file mode 100644
index 000000000..951fbec6f
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/crc/ReflectedLongCrc.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.crc;
+
+/**
+ * Implements a "reflected" LSB-first long-width CRC function using a lookup
+ * table.
+ */
+final class ReflectedLongCrc extends AbstractLongCrc {
+
+    private final long[] table = new long[256];
+
+    ReflectedLongCrc(String algorithm, int width, long poly, long init, long xorOut) {
+        super(algorithm, width, init, xorOut);
+
+        poly = reflect(poly);
+        for (int i = 0; i < 256; ++i) {
+            long crc = i;
+            for (int j = 0; j < 8; ++j)
+                crc = (crc & 1) != 0 ? (crc >>> 1) ^ poly : crc >>> 1;
+            table[i] = crc;
+        }
+    }
+
+    @Override
+    protected long resumeRaw(long crc, byte[] input, int index, int length) {
+        crc = reflect(crc);
+        for (int i = 0; i < length; ++i)
+            crc = table[(int) (crc ^ input[index + i]) & 0xff] ^ (crc >>> 8);
+        return crc;
+    }
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/crc/Sse42Crc32C.java b/circe-checksum/src/main/java/com/scurrilous/circe/crc/Sse42Crc32C.java
new file mode 100644
index 000000000..28a989d2c
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/crc/Sse42Crc32C.java
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.crc;
+
+import static com.scurrilous.circe.utils.NativeUtils.loadLibraryFromJar;
+import static com.scurrilous.circe.utils.NativeUtils.libType;
+
+import java.nio.ByteBuffer;
+import com.scurrilous.circe.IncrementalIntHash;
+import com.scurrilous.circe.impl.AbstractIncrementalIntHash;
+import com.scurrilous.circe.params.CrcParameters;
+
+/**
+ * Implementation of CRC-32C using the SSE 4.2 CRC instruction.
+ */
+public final class Sse42Crc32C extends AbstractIncrementalIntHash implements IncrementalIntHash {
+
+    private static final boolean SUPPORTED = checkSupported();
+
+    private static boolean checkSupported() {
+        try {
+            loadLibraryFromJar("/lib/libcirce-checksum." + libType());
+            return nativeSupported();
+        } catch (final Exception | UnsatisfiedLinkError e) {
+            return false;
+        }
+    }
+
+    /**
+     * Returns whether SSE 4.2 CRC-32C is supported on this system.
+     * 
+     * @return true if this class is supported, false if not
+     */
+    public static boolean isSupported() {
+        return SUPPORTED;
+    }
+
+    private final long config;
+
+    Sse42Crc32C() {
+        config = 0;
+    }
+
+    public Sse42Crc32C(int chunkWords[]) {
+        if (chunkWords.length == 0) {
+            config = 0;
+        } else {
+            config = allocConfig(chunkWords);
+            if (config == 0)
+                throw new RuntimeException("CRC32C configuration allocation failed");
+        }
+    }
+
+    @Override
+    protected void finalize() {
+        if (config != 0)
+            freeConfig(config);
+    }
+
+    @Override
+    public String algorithm() {
+        return CrcParameters.CRC32C.algorithm();
+    }
+
+    @Override
+    public int length() {
+        return 4;
+    }
+
+    @Override
+    public boolean supportsUnsafe() {
+        return true;
+    }
+
+    @Override
+    public int calculate(long address, long length) {
+        return nativeUnsafe(initial(), address, length, config);
+    }
+
+    @Override
+    public int resume(int current, ByteBuffer input) {
+        if (input.isDirect()) {
+            final int result = nativeDirectBuffer(current, input, input.position(), input.remaining(), config);
+            input.position(input.limit());
+            return result;
+        }
+
+        return super.resume(current, input);
+    }
+
+    @Override
+    public int resume(int current, long address, long length) {
+        return nativeUnsafe(current, address, length, config);
+    }
+
+    @Override
+    protected int initial() {
+        return 0;
+    }
+
+    @Override
+    protected int resumeUnchecked(int current, byte[] input, int index, int length) {
+        return nativeArray(current, input, index, length, config);
+    }
+
+    private static native boolean nativeSupported();
+
+    private static native int nativeArray(int current, byte[] input, int index, int length, long config);
+
+    private static native int nativeDirectBuffer(int current, ByteBuffer input, int offset, int length, long config);
+
+    private static native int nativeUnsafe(int current, long address, long length, long config);
+
+    private static native long allocConfig(int[] chunkWords);
+
+    private static native void freeConfig(long config);
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/crc/StandardCrcProvider.java b/circe-checksum/src/main/java/com/scurrilous/circe/crc/StandardCrcProvider.java
new file mode 100644
index 000000000..b54fad642
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/crc/StandardCrcProvider.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.crc;
+
+import java.util.EnumSet;
+
+import com.scurrilous.circe.Hash;
+import com.scurrilous.circe.HashSupport;
+import com.scurrilous.circe.StatelessHash;
+import com.scurrilous.circe.impl.AbstractHashProvider;
+import com.scurrilous.circe.params.CrcParameters;
+
+/**
+ * Provides pure Java and JDK-supplied CRC implementations.
+ */
+public final class StandardCrcProvider extends AbstractHashProvider<CrcParameters> {
+
+    /**
+     * Constructs a new {@link StandardCrcProvider}.
+     */
+    public StandardCrcProvider() {
+        super(CrcParameters.class);
+    }
+
+    @Override
+    protected EnumSet<HashSupport> querySupportTyped(CrcParameters params) {
+        final EnumSet<HashSupport> result = EnumSet.of(HashSupport.STATEFUL,
+                HashSupport.INCREMENTAL, HashSupport.STATELESS_INCREMENTAL, HashSupport.LONG_SIZED);
+        if (params.bitWidth() <= 32)
+            result.add(HashSupport.INT_SIZED);
+        if (params.equals(CrcParameters.CRC32))
+            result.add(HashSupport.NATIVE);
+        return result;
+    }
+
+    @Override
+    protected Hash get(CrcParameters params, EnumSet<HashSupport> required) {
+        if (!required.contains(HashSupport.STATELESS_INCREMENTAL) &&
+                params.equals(CrcParameters.CRC32))
+            return new JavaCrc32();
+        if (required.contains(HashSupport.NATIVE))
+            throw new UnsupportedOperationException();
+        return getCacheable(params, required);
+    }
+
+    @Override
+    protected StatelessHash createCacheable(CrcParameters params, EnumSet<HashSupport> required) {
+        final int bitWidth = params.bitWidth();
+        if (bitWidth > 32 || (required.contains(HashSupport.LONG_SIZED) && bitWidth >= 8)) {
+            if (required.contains(HashSupport.INT_SIZED))
+                throw new UnsupportedOperationException();
+            if (params.reflected())
+                return new ReflectedLongCrc(params.algorithm(), bitWidth, params.polynomial(),
+                        params.initial(), params.xorOut());
+            else
+                return new NormalLongCrc(params.algorithm(), bitWidth, params.polynomial(),
+                        params.initial(), params.xorOut());
+        } else {
+            if (params.reflected())
+                return new ReflectedIntCrc(params.algorithm(), bitWidth, (int) params.polynomial(),
+                        (int) params.initial(), (int) params.xorOut());
+            else if (bitWidth > 8)
+                return new NormalIntCrc(params.algorithm(), bitWidth, (int) params.polynomial(),
+                        (int) params.initial(), (int) params.xorOut());
+            return new NormalByteCrc(params.algorithm(), bitWidth, (int) params.polynomial(),
+                    (int) params.initial(), (int) params.xorOut());
+        }
+    }
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/crc/package-info.java b/circe-checksum/src/main/java/com/scurrilous/circe/crc/package-info.java
new file mode 100644
index 000000000..4e4f0fe01
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/crc/package-info.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+/**
+ * Provides various implementations of <a
+ * href="http://en.wikipedia.org/wiki/Cyclic_redundancy_check">cyclic redundancy
+ * check</a> (CRC) error-detecting codes. CRC values are based on the remainder
+ * of a polynomial division of the input data. They are particularly well-suited
+ * to detecting burst errors in data sent over telecommunications channels or
+ * retrieved from storage media.
+ */
+package com.scurrilous.circe.crc;
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/impl/AbstractHashProvider.java b/circe-checksum/src/main/java/com/scurrilous/circe/impl/AbstractHashProvider.java
new file mode 100644
index 000000000..fb09624ab
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/impl/AbstractHashProvider.java
@@ -0,0 +1,193 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.impl;
+
+import java.util.EnumSet;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+
+import com.scurrilous.circe.Hash;
+import com.scurrilous.circe.HashParameters;
+import com.scurrilous.circe.HashProvider;
+import com.scurrilous.circe.HashSupport;
+import com.scurrilous.circe.IncrementalIntHash;
+import com.scurrilous.circe.IncrementalLongHash;
+import com.scurrilous.circe.StatefulHash;
+import com.scurrilous.circe.StatefulIntHash;
+import com.scurrilous.circe.StatefulLongHash;
+import com.scurrilous.circe.StatelessHash;
+import com.scurrilous.circe.StatelessIntHash;
+import com.scurrilous.circe.StatelessLongHash;
+
+/**
+ * Base implementation for hash function providers.
+ * 
+ * @param <P> base supported hash parameters type
+ */
+public abstract class AbstractHashProvider<P extends HashParameters> implements HashProvider {
+
+    private final Class<P> parametersClass;
+
+    /**
+     * Constructs a new {@link AbstractHashProvider} with the given base
+     * parameters class.
+     * 
+     * @param parametersClass the base hash parameters class supported
+     */
+    protected AbstractHashProvider(Class<P> parametersClass) {
+        this.parametersClass = parametersClass;
+    }
+
+    @Override
+    public final EnumSet<HashSupport> querySupport(HashParameters params) {
+        if (!parametersClass.isAssignableFrom(params.getClass()))
+            return EnumSet.noneOf(HashSupport.class);
+        return querySupportTyped(parametersClass.cast(params));
+    }
+
+    /**
+     * Implemented by subclasses to provide information about the available
+     * implementations corresponding to the given hash algorithm parameters.
+     * Called by {@link #querySupport} if the hash parameters match the base
+     * type supported by this provider.
+     * 
+     * @param params the hash algorithm parameters
+     * @return a set of flags indicating the level of support
+     */
+    protected abstract EnumSet<HashSupport> querySupportTyped(P params);
+
+    /**
+     * Requests a hash function using the given parameters and support flags.
+     * This method is only responsible for checking support flags returned by
+     * {@link #querySupportTyped}.
+     * <p>
+     * To support caching of stateless hash functions, call
+     * {@link #getCacheable} from this method and implement
+     * {@link #createCacheable}.
+     * 
+     * @param params the hash algorithm parameters
+     * @param required the required hash support flags
+     * @return a hash function
+     * @throws UnsupportedOperationException if this provider cannot support the
+     *             given parameters
+     */
+    protected abstract Hash get(P params, EnumSet<HashSupport> required);
+
+    /**
+     * Called by implementations that support caching of stateless hash
+     * functions when a cached instance is desired. If a cached instance is not
+     * available, this method calls {@link #createCacheable} to create one,
+     * which is then cached (if caching is available).
+     * 
+     * @param params the hash algorithm parameters
+     * @param required the required hash support flags
+     * @return a hash function
+     * @throws UnsupportedOperationException if this provider cannot support the
+     *             given parameters
+     */
+    protected final Hash getCacheable(final P params, final EnumSet<HashSupport> required) {
+        if (HashCacheLoader.hasCache()) {
+            final HashCache cache = HashCacheLoader.getCache();
+            try {
+                return cache.get(params, required, new Callable<Hash>() {
+                    @Override
+                    public Hash call() throws Exception {
+                        return createCacheable(params, required);
+                    }
+                });
+            } catch (ExecutionException e) {
+                final Throwable cause = e.getCause();
+                if (cause instanceof RuntimeException)
+                    throw (RuntimeException) cause;
+                throw new UnsupportedOperationException(e);
+            }
+        }
+        return createCacheable(params, required);
+    }
+
+    /**
+     * Called by {@link #getCacheable} to create new cacheable stateless hash
+     * functions. The default implementation simply throws
+     * {@link UnsupportedOperationException}.
+     * 
+     * @param params the hash algorithm parameters
+     * @param required the required hash support flags
+     * @return a stateless hash function
+     * @throws UnsupportedOperationException if this provider cannot support the
+     *             given parameters
+     */
+    protected StatelessHash createCacheable(P params, EnumSet<HashSupport> required) {
+        throw new UnsupportedOperationException();
+    }
+
+    private Hash castAndGet(HashParameters params, EnumSet<HashSupport> required) {
+        if (!parametersClass.isAssignableFrom(params.getClass()))
+            throw new UnsupportedOperationException();
+        return get(parametersClass.cast(params), required);
+    }
+
+    @Override
+    public StatefulHash createStateful(HashParameters params) {
+        final Hash hash = castAndGet(params, EnumSet.of(HashSupport.STATEFUL));
+        if (hash instanceof StatefulHash)
+            return (StatefulHash) hash;
+        if (hash instanceof StatelessHash)
+            return ((StatelessHash) hash).createStateful();
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public StatelessIntHash getStatelessInt(HashParameters params) {
+        final Hash hash = castAndGet(params, EnumSet.of(HashSupport.INT_SIZED));
+        if (hash instanceof StatelessIntHash)
+            return (StatelessIntHash) hash;
+        if (hash instanceof StatefulIntHash)
+            return ((StatefulIntHash) hash).asStateless();
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public StatelessLongHash getStatelessLong(HashParameters params) {
+        final Hash hash = castAndGet(params, EnumSet.of(HashSupport.LONG_SIZED));
+        if (hash instanceof StatelessLongHash)
+            return (StatelessLongHash) hash;
+        if (hash instanceof StatefulLongHash)
+            return ((StatefulLongHash) hash).asStateless();
+        if (hash instanceof StatelessIntHash)
+            return new IntStatelessLongHash((StatelessIntHash) hash);
+        if (hash instanceof StatefulIntHash)
+            return new IntStatelessLongHash(((StatefulIntHash) hash).asStateless());
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public IncrementalIntHash getIncrementalInt(HashParameters params) {
+        final Hash hash = castAndGet(params,
+                EnumSet.of(HashSupport.INT_SIZED, HashSupport.STATELESS_INCREMENTAL));
+        if (hash instanceof IncrementalIntHash)
+            return (IncrementalIntHash) hash;
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public IncrementalLongHash getIncrementalLong(HashParameters params) {
+        final Hash hash = castAndGet(params,
+                EnumSet.of(HashSupport.LONG_SIZED, HashSupport.STATELESS_INCREMENTAL));
+        if (hash instanceof IncrementalLongHash)
+            return (IncrementalLongHash) hash;
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/impl/AbstractIncrementalIntHash.java b/circe-checksum/src/main/java/com/scurrilous/circe/impl/AbstractIncrementalIntHash.java
new file mode 100644
index 000000000..094cf2288
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/impl/AbstractIncrementalIntHash.java
@@ -0,0 +1,114 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.impl;
+
+import java.nio.ByteBuffer;
+
+import com.scurrilous.circe.IncrementalIntHash;
+import com.scurrilous.circe.StatefulIntHash;
+
+/**
+ * Base implementation for incremental stateless integer hash functions.
+ */
+public abstract class AbstractIncrementalIntHash implements IncrementalIntHash {
+
+    @Override
+    public boolean supportsUnsafe() {
+        return false;
+    }
+
+    @Override
+    public StatefulIntHash createStateful() {
+        return new IncrementalIntStatefulHash(this);
+    }
+
+    @Override
+    public int calculate(byte[] input) {
+        return resume(initial(), input);
+    }
+
+    @Override
+    public int calculate(byte[] input, int index, int length) {
+        return resume(initial(), input, index, length);
+    }
+
+    @Override
+    public int calculate(ByteBuffer input) {
+        return resume(initial(), input);
+    }
+
+    @Override
+    public int calculate(long address, long length) {
+        return resume(initial(), address, length);
+    }
+
+    @Override
+    public int resume(int current, byte[] input) {
+        return resumeUnchecked(current, input, 0, input.length);
+    }
+
+    @Override
+    public int resume(int current, byte[] input, int index, int length) {
+        if (length < 0)
+            throw new IllegalArgumentException();
+        if (index < 0 || index + length > input.length)
+            throw new IndexOutOfBoundsException();
+        return resumeUnchecked(current, input, index, length);
+    }
+
+    @Override
+    public int resume(int current, ByteBuffer input) {
+        final byte[] array;
+        final int index;
+        final int length = input.remaining();
+        if (input.hasArray()) {
+            array = input.array();
+            index = input.arrayOffset() + input.position();
+            input.position(input.limit());
+        } else {
+            array = new byte[length];
+            index = 0;
+            input.get(array);
+        }
+        return resumeUnchecked(current, array, index, length);
+    }
+
+    @Override
+    public int resume(int current, long address, long length) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * The initial state of the hash function, which is the same as the output
+     * value for an empty input sequence.
+     * 
+     * @return the initial hash state/output
+     */
+    protected abstract int initial();
+
+    /**
+     * Evaluates this hash function as if the given range of the given input
+     * array were appended to the previously hashed input. The index and length
+     * parameters have already been validated.
+     * 
+     * @param current the hash output for input hashed so far
+     * @param input the input array
+     * @param index the starting index of the first input byte
+     * @param length the length of the input range
+     * @return the output of the hash function for the concatenated input
+     */
+    protected abstract int resumeUnchecked(int current, byte[] input, int index, int length);
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/impl/AbstractIncrementalLongHash.java b/circe-checksum/src/main/java/com/scurrilous/circe/impl/AbstractIncrementalLongHash.java
new file mode 100644
index 000000000..9996f8eb4
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/impl/AbstractIncrementalLongHash.java
@@ -0,0 +1,114 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.impl;
+
+import java.nio.ByteBuffer;
+
+import com.scurrilous.circe.IncrementalLongHash;
+import com.scurrilous.circe.StatefulLongHash;
+
+/**
+ * Base implementation for incremental stateless long integer hash functions.
+ */
+public abstract class AbstractIncrementalLongHash implements IncrementalLongHash {
+
+    @Override
+    public boolean supportsUnsafe() {
+        return false;
+    }
+
+    @Override
+    public StatefulLongHash createStateful() {
+        return new IncrementalLongStatefulHash(this);
+    }
+
+    @Override
+    public long calculate(byte[] input) {
+        return resume(initial(), input);
+    }
+
+    @Override
+    public long calculate(byte[] input, int index, int length) {
+        return resume(initial(), input, index, length);
+    }
+
+    @Override
+    public long calculate(ByteBuffer input) {
+        return resume(initial(), input);
+    }
+
+    @Override
+    public long calculate(long address, long length) {
+        return resume(initial(), address, length);
+    }
+
+    @Override
+    public long resume(long current, byte[] input) {
+        return resumeUnchecked(current, input, 0, input.length);
+    }
+
+    @Override
+    public long resume(long current, byte[] input, int index, int length) {
+        if (length < 0)
+            throw new IllegalArgumentException();
+        if (index < 0 || index + length > input.length)
+            throw new IndexOutOfBoundsException();
+        return resumeUnchecked(current, input, index, length);
+    }
+
+    @Override
+    public long resume(long current, ByteBuffer input) {
+        final byte[] array;
+        final int index;
+        final int length = input.remaining();
+        if (input.hasArray()) {
+            array = input.array();
+            index = input.arrayOffset() + input.position();
+            input.position(input.limit());
+        } else {
+            array = new byte[length];
+            index = 0;
+            input.get(array);
+        }
+        return resumeUnchecked(current, array, index, length);
+    }
+
+    @Override
+    public long resume(long current, long address, long length) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * The initial state of the hash function, which is the same as the output
+     * value for an empty input sequence.
+     * 
+     * @return the initial hash state/output
+     */
+    protected abstract long initial();
+
+    /**
+     * Evaluates this hash function as if the given range of the given input
+     * array were appended to the previously hashed input. The index and length
+     * parameters have already been validated.
+     * 
+     * @param current the hash output for input hashed so far
+     * @param input the input array
+     * @param index the starting index of the first input byte
+     * @param length the length of the input range
+     * @return the output of the hash function for the concatenated input
+     */
+    protected abstract long resumeUnchecked(long current, byte[] input, int index, int length);
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/impl/AbstractStatefulHash.java b/circe-checksum/src/main/java/com/scurrilous/circe/impl/AbstractStatefulHash.java
new file mode 100644
index 000000000..e3edcec6e
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/impl/AbstractStatefulHash.java
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.impl;
+
+import java.nio.ByteBuffer;
+
+import com.scurrilous.circe.StatefulHash;
+
+/**
+ * Base implementation for stateful hash functions.
+ */
+public abstract class AbstractStatefulHash implements StatefulHash {
+
+    @Override
+    public boolean supportsUnsafe() {
+        return false;
+    }
+
+    @Override
+    public void update(byte[] input) {
+        updateUnchecked(input, 0, input.length);
+    }
+
+    @Override
+    public void update(byte[] input, int index, int length) {
+        if (length < 0)
+            throw new IllegalArgumentException();
+        if (index < 0 || index + length > input.length)
+            throw new IndexOutOfBoundsException();
+        updateUnchecked(input, index, length);
+    }
+
+    @Override
+    public void update(ByteBuffer input) {
+        final byte[] array;
+        final int index;
+        final int length = input.remaining();
+        if (input.hasArray()) {
+            array = input.array();
+            index = input.arrayOffset() + input.position();
+            input.position(input.limit());
+        } else {
+            // convert to unsafe access if possible
+            if (input.isDirect() && supportsUnsafe()) {
+                long address = DirectByteBufferAccessLoader.getAddress(input);
+                if (address != 0) {
+                    address += input.position();
+                    input.position(input.limit());
+                    update(address, length);
+                    return;
+                }
+            }
+
+            array = new byte[length];
+            index = 0;
+            input.get(array);
+        }
+        updateUnchecked(array, index, length);
+    }
+
+    @Override
+    public void update(long address, long length) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Updates the state of this hash function with the given range of the given
+     * input array. The index and length parameters have already been validated.
+     * 
+     * @param input the input array
+     * @param index the starting index of the first input byte
+     * @param length the length of the input range
+     */
+    protected abstract void updateUnchecked(byte[] input, int index, int length);
+
+    @Override
+    public byte[] getBytes() {
+        final byte[] array = new byte[length()];
+        writeBytes(array, 0, array.length);
+        return array;
+    }
+
+    @Override
+    public int getBytes(byte[] output, int index, int maxLength) {
+        if (maxLength < 0)
+            throw new IllegalArgumentException();
+        if (index < 0 || index + maxLength > output.length)
+            throw new IndexOutOfBoundsException();
+        final int length = Math.min(maxLength, length());
+        writeBytes(output, index, length);
+        return length;
+    }
+
+    /**
+     * Writes the output of this hash function into the given range of the given
+     * byte array. The inputs have already been validated.
+     * 
+     * @param output the destination array for the output
+     * @param index the starting index of the first output byte
+     * @param length the number of bytes to write
+     */
+    protected void writeBytes(byte[] output, int index, int length) {
+        long temp = getLong();
+        for (int i = 0; i < length; ++i) {
+            output[index + i] = (byte) temp;
+            temp >>>= 8;
+        }
+    }
+
+    @Override
+    public byte getByte() {
+        return (byte) getInt();
+    }
+
+    @Override
+    public short getShort() {
+        return (short) getInt();
+    }
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/impl/AbstractStatelessIntHash.java b/circe-checksum/src/main/java/com/scurrilous/circe/impl/AbstractStatelessIntHash.java
new file mode 100644
index 000000000..222869744
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/impl/AbstractStatelessIntHash.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.impl;
+
+import java.nio.ByteBuffer;
+
+import com.scurrilous.circe.StatelessIntHash;
+
+/**
+ * Base implementation for stateless (but not incremental) integer hash
+ * functions.
+ */
+public abstract class AbstractStatelessIntHash implements StatelessIntHash {
+
+    @Override
+    public boolean supportsUnsafe() {
+        return false;
+    }
+
+    @Override
+    public int calculate(byte[] input) {
+        return calculateUnchecked(input, 0, input.length);
+    }
+
+    @Override
+    public int calculate(byte[] input, int index, int length) {
+        if (length < 0)
+            throw new IllegalArgumentException();
+        if (index < 0 || index + length > input.length)
+            throw new IndexOutOfBoundsException();
+        return calculateUnchecked(input, index, length);
+    }
+
+    @Override
+    public int calculate(ByteBuffer input) {
+        final byte[] array;
+        final int index;
+        final int length = input.remaining();
+        if (input.hasArray()) {
+            array = input.array();
+            index = input.arrayOffset() + input.position();
+            input.position(input.limit());
+        } else {
+            array = new byte[length];
+            index = 0;
+            input.get(array);
+        }
+        return calculateUnchecked(array, index, length);
+    }
+
+    @Override
+    public int calculate(long address, long length) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Evaluates this hash function for the given range of the given input
+     * array. The index and length parameters have already been validated.
+     * 
+     * @param input the input array
+     * @param index the starting index of the first input byte
+     * @param length the length of the input range
+     * @return the output of the hash function
+     */
+    protected abstract int calculateUnchecked(byte[] input, int index, int length);
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/impl/AbstractStatelessLongHash.java b/circe-checksum/src/main/java/com/scurrilous/circe/impl/AbstractStatelessLongHash.java
new file mode 100644
index 000000000..8688b9984
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/impl/AbstractStatelessLongHash.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.impl;
+
+import java.nio.ByteBuffer;
+
+import com.scurrilous.circe.StatelessLongHash;
+
+/**
+ * Base implementation for stateless (but not incremental) long integer hash
+ * functions.
+ */
+public abstract class AbstractStatelessLongHash implements StatelessLongHash {
+
+    @Override
+    public boolean supportsUnsafe() {
+        return false;
+    }
+
+    @Override
+    public long calculate(byte[] input) {
+        return calculateUnchecked(input, 0, input.length);
+    }
+
+    @Override
+    public long calculate(byte[] input, int index, int length) {
+        if (length < 0)
+            throw new IllegalArgumentException();
+        if (index < 0 || index + length > input.length)
+            throw new IndexOutOfBoundsException();
+        return calculateUnchecked(input, index, length);
+    }
+
+    @Override
+    public long calculate(ByteBuffer input) {
+        final byte[] array;
+        final int index;
+        final int length = input.remaining();
+        if (input.hasArray()) {
+            array = input.array();
+            index = input.arrayOffset() + input.position();
+            input.position(input.limit());
+        } else {
+            array = new byte[length];
+            index = 0;
+            input.get(array);
+        }
+        return calculateUnchecked(array, index, length);
+    }
+
+    @Override
+    public long calculate(long address, long length) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Evaluates this hash function for the given range of the given input
+     * array. The index and length parameters have already been validated.
+     * 
+     * @param input the input array
+     * @param index the starting index of the first input byte
+     * @param length the length of the input range
+     * @return the output of the hash function
+     */
+    protected abstract long calculateUnchecked(byte[] input, int index, int length);
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/impl/DirectByteBufferAccess.java b/circe-checksum/src/main/java/com/scurrilous/circe/impl/DirectByteBufferAccess.java
new file mode 100644
index 000000000..fdc5e5e6f
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/impl/DirectByteBufferAccess.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+package com.scurrilous.circe.impl;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Service used to provide the native memory address of direct byte buffers.
+ */
+public interface DirectByteBufferAccess {
+
+    /**
+     * Returns the native memory address of the given direct byte buffer, or 0
+     * if the buffer is not direct or if obtaining the address is not supported.
+     *
+     * @param buffer the direct byte buffer for which to obtain the address
+     * @return the native memory address or 0
+     */
+    long getAddress(ByteBuffer buffer);
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/impl/DirectByteBufferAccessLoader.java b/circe-checksum/src/main/java/com/scurrilous/circe/impl/DirectByteBufferAccessLoader.java
new file mode 100644
index 000000000..5f457d82d
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/impl/DirectByteBufferAccessLoader.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.impl;
+
+import java.nio.ByteBuffer;
+import java.util.Iterator;
+import java.util.ServiceLoader;
+
+/**
+ * Provides access to a singleton {@link DirectByteBufferAccess} implementation,
+ * if one is available.
+ */
+public final class DirectByteBufferAccessLoader {
+
+    private static final DirectByteBufferAccess INSTANCE;
+
+    static {
+        final Iterator<DirectByteBufferAccess> iterator = ServiceLoader.load(
+                DirectByteBufferAccess.class).iterator();
+        INSTANCE = iterator.hasNext() ? iterator.next() : null;
+    }
+
+    /**
+     * Returns the native memory address of the given direct byte buffer, or 0
+     * if the buffer is not direct or if obtaining the address is not supported.
+     * 
+     * @param buffer the direct byte buffer for which to obtain the address
+     * @return the native memory address or 0
+     */
+    public static long getAddress(ByteBuffer buffer) {
+        return INSTANCE != null ? INSTANCE.getAddress(buffer) : 0;
+    }
+
+    private DirectByteBufferAccessLoader() {
+    }
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/impl/HashCache.java b/circe-checksum/src/main/java/com/scurrilous/circe/impl/HashCache.java
new file mode 100644
index 000000000..e8d367e56
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/impl/HashCache.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.impl;
+
+import java.util.EnumSet;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+
+import com.scurrilous.circe.Hash;
+import com.scurrilous.circe.HashParameters;
+import com.scurrilous.circe.HashSupport;
+
+/**
+ * Interface implemented by hash function caches.
+ */
+public interface HashCache {
+
+    /**
+     * Requests a cached hash function with the given parameters and required
+     * support flags. If no matching function is cached, the given loader is
+     * called to obtain one to cache.
+     * 
+     * @param params the hash algorithm parameters
+     * @param required the required hash support flags
+     * @param loader a cache loader that creates the function if not cached
+     * @return a hash with the given parameters and support flags
+     * @throws ExecutionException if the loader throws an exception
+     */
+    Hash get(HashParameters params, EnumSet<HashSupport> required, Callable<Hash> loader)
+            throws ExecutionException;
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/impl/HashCacheLoader.java b/circe-checksum/src/main/java/com/scurrilous/circe/impl/HashCacheLoader.java
new file mode 100644
index 000000000..55c7db43c
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/impl/HashCacheLoader.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.impl;
+
+import java.util.Iterator;
+import java.util.ServiceLoader;
+
+/**
+ * Provides access to a singleton hash function cache, if an implementation is
+ * available.
+ */
+public final class HashCacheLoader {
+
+    private static final HashCache HASH_CACHE;
+
+    static {
+        final Iterator<HashCache> iterator = ServiceLoader.load(HashCache.class).iterator();
+        HASH_CACHE = iterator.hasNext() ? iterator.next() : null;
+    }
+
+    /**
+     * Returns whether a hash function cache is available.
+     * 
+     * @return true if a cache is available, false if {@link #getCache} will
+     *         throw an exception
+     */
+    public static boolean hasCache() {
+        return HASH_CACHE != null;
+    }
+
+    /**
+     * Returns the single hash function cache.
+     * 
+     * @return the single hash cache
+     * @throws UnsupportedOperationException if no hash cache is available
+     */
+    public static HashCache getCache() {
+        if (HASH_CACHE == null)
+            throw new UnsupportedOperationException();
+        return HASH_CACHE;
+    }
+
+    private HashCacheLoader() {
+    }
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/impl/IncrementalIntStatefulHash.java b/circe-checksum/src/main/java/com/scurrilous/circe/impl/IncrementalIntStatefulHash.java
new file mode 100644
index 000000000..22f57b779
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/impl/IncrementalIntStatefulHash.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.impl;
+
+import java.nio.ByteBuffer;
+
+import com.scurrilous.circe.StatefulHash;
+import com.scurrilous.circe.StatefulIntHash;
+import com.scurrilous.circe.StatelessIntHash;
+
+class IncrementalIntStatefulHash extends AbstractStatefulHash implements StatefulIntHash {
+
+    final AbstractIncrementalIntHash stateless;
+    int current;
+
+    IncrementalIntStatefulHash(AbstractIncrementalIntHash stateless) {
+        this.stateless = stateless;
+    }
+
+    @Override
+    public StatelessIntHash asStateless() {
+        return stateless;
+    }
+
+    @Override
+    public String algorithm() {
+        return stateless.algorithm();
+    }
+
+    @Override
+    public int length() {
+        return stateless.length();
+    }
+
+    @Override
+    public boolean supportsUnsafe() {
+        return stateless.supportsUnsafe();
+    }
+
+    @Override
+    public StatefulHash createNew() {
+        return new IncrementalIntStatefulHash(stateless);
+    }
+
+    @Override
+    public boolean supportsIncremental() {
+        return true;
+    }
+
+    @Override
+    public void reset() {
+        current = stateless.initial();
+    }
+
+    @Override
+    public void update(ByteBuffer input) {
+        current = stateless.resume(current, input);
+    }
+
+    @Override
+    public void update(long address, long length) {
+        current = stateless.resume(current, address, length);
+    }
+
+    @Override
+    protected void updateUnchecked(byte[] input, int index, int length) {
+        current = stateless.resumeUnchecked(current, input, index, length);
+    }
+
+    @Override
+    public int getInt() {
+        return current;
+    }
+
+    @Override
+    public long getLong() {
+        return current;
+    }
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/impl/IncrementalLongStatefulHash.java b/circe-checksum/src/main/java/com/scurrilous/circe/impl/IncrementalLongStatefulHash.java
new file mode 100644
index 000000000..9d7f7f89c
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/impl/IncrementalLongStatefulHash.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.impl;
+
+import java.nio.ByteBuffer;
+
+import com.scurrilous.circe.StatefulHash;
+import com.scurrilous.circe.StatefulLongHash;
+import com.scurrilous.circe.StatelessLongHash;
+
+class IncrementalLongStatefulHash extends AbstractStatefulHash implements StatefulLongHash {
+
+    final AbstractIncrementalLongHash stateless;
+    long current;
+
+    IncrementalLongStatefulHash(AbstractIncrementalLongHash stateless) {
+        this.stateless = stateless;
+    }
+
+    @Override
+    public StatelessLongHash asStateless() {
+        return stateless;
+    }
+
+    @Override
+    public String algorithm() {
+        return stateless.algorithm();
+    }
+
+    @Override
+    public int length() {
+        return stateless.length();
+    }
+
+    @Override
+    public boolean supportsUnsafe() {
+        return stateless.supportsUnsafe();
+    }
+
+    @Override
+    public StatefulHash createNew() {
+        return new IncrementalLongStatefulHash(stateless);
+    }
+
+    @Override
+    public boolean supportsIncremental() {
+        return true;
+    }
+
+    @Override
+    public void reset() {
+        current = stateless.initial();
+    }
+
+    @Override
+    public void update(ByteBuffer input) {
+        current = stateless.resume(current, input);
+    }
+
+    @Override
+    public void update(long address, long length) {
+        current = stateless.resume(current, address, length);
+    }
+
+    @Override
+    protected void updateUnchecked(byte[] input, int index, int length) {
+        current = stateless.resumeUnchecked(current, input, index, length);
+    }
+
+    @Override
+    public int getInt() {
+        return (int) current;
+    }
+
+    @Override
+    public long getLong() {
+        return current;
+    }
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/impl/IntStatefulLongHash.java b/circe-checksum/src/main/java/com/scurrilous/circe/impl/IntStatefulLongHash.java
new file mode 100644
index 000000000..3b9a3acd5
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/impl/IntStatefulLongHash.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.impl;
+
+import java.nio.ByteBuffer;
+
+import com.scurrilous.circe.StatefulHash;
+import com.scurrilous.circe.StatefulIntHash;
+import com.scurrilous.circe.StatefulLongHash;
+import com.scurrilous.circe.StatelessLongHash;
+
+/**
+ * Promotes a {@link StatefulIntHash} to a {@link StatefulLongHash}.
+ */
+public final class IntStatefulLongHash implements StatefulLongHash {
+
+    private final StatefulIntHash intHash;
+
+    /**
+     * Constructs a new {@link IntStatefulLongHash} that delegates to the given
+     * {@link StatefulIntHash}.
+     * 
+     * @param intHash the underlying int-width hash
+     */
+    public IntStatefulLongHash(StatefulIntHash intHash) {
+        this.intHash = intHash;
+    }
+
+    public StatelessLongHash asStateless() {
+        return new IntStatelessLongHash(intHash.asStateless());
+    }
+
+    public String algorithm() {
+        return intHash.algorithm();
+    }
+
+    public int length() {
+        return intHash.length();
+    }
+
+    public StatefulHash createNew() {
+        return intHash.createNew();
+    }
+
+    public boolean supportsUnsafe() {
+        return intHash.supportsUnsafe();
+    }
+
+    public boolean supportsIncremental() {
+        return intHash.supportsIncremental();
+    }
+
+    public void reset() {
+        intHash.reset();
+    }
+
+    public void update(byte[] input) {
+        intHash.update(input);
+    }
+
+    public void update(byte[] input, int index, int length) {
+        intHash.update(input, index, length);
+    }
+
+    public void update(ByteBuffer input) {
+        intHash.update(input);
+    }
+
+    public void update(long address, long length) {
+        intHash.update(address, length);
+    }
+
+    public byte[] getBytes() {
+        return intHash.getBytes();
+    }
+
+    public int getBytes(byte[] output, int index, int maxLength) {
+        return intHash.getBytes(output, index, maxLength);
+    }
+
+    public byte getByte() {
+        return intHash.getByte();
+    }
+
+    public short getShort() {
+        return intHash.getShort();
+    }
+
+    public int getInt() {
+        return intHash.getInt();
+    }
+
+    public long getLong() {
+        return intHash.getLong();
+    }
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/impl/IntStatelessLongHash.java b/circe-checksum/src/main/java/com/scurrilous/circe/impl/IntStatelessLongHash.java
new file mode 100644
index 000000000..4111842bf
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/impl/IntStatelessLongHash.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.impl;
+
+import java.nio.ByteBuffer;
+
+import com.scurrilous.circe.StatefulLongHash;
+import com.scurrilous.circe.StatelessIntHash;
+import com.scurrilous.circe.StatelessLongHash;
+
+/**
+ * Promotes a {@link StatelessIntHash} to a {@link StatelessLongHash}.
+ */
+public final class IntStatelessLongHash implements StatelessLongHash {
+
+    private final StatelessIntHash intHash;
+
+    /**
+     * Constructs a new {@link IntStatelessLongHash} that delegates to the given
+     * {@link StatelessIntHash}.
+     * 
+     * @param intHash the underlying int-width hash
+     */
+    public IntStatelessLongHash(StatelessIntHash intHash) {
+        this.intHash = intHash;
+    }
+
+    @Override
+    public String algorithm() {
+        return intHash.algorithm();
+    }
+
+    @Override
+    public int length() {
+        return intHash.length();
+    }
+
+    @Override
+    public boolean supportsUnsafe() {
+        return intHash.supportsUnsafe();
+    }
+
+    @Override
+    public StatefulLongHash createStateful() {
+        return new IntStatefulLongHash(intHash.createStateful());
+    }
+
+    @Override
+    public long calculate(byte[] input) {
+        return intHash.calculate(input);
+    }
+
+    @Override
+    public long calculate(byte[] input, int index, int length) {
+        return intHash.calculate(input, index, length);
+    }
+
+    @Override
+    public long calculate(ByteBuffer input) {
+        return intHash.calculate(input);
+    }
+
+    @Override
+    public long calculate(long address, long length) {
+        return intHash.calculate(address, length);
+    }
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/impl/package-info.java b/circe-checksum/src/main/java/com/scurrilous/circe/impl/package-info.java
new file mode 100644
index 000000000..3c40ddaae
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/impl/package-info.java
@@ -0,0 +1,22 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+/**
+ * Provides support for implementing new {@linkplain
+ * com.scurrilous.circe.HashProvider hash providers} in the form of abstract
+ * base classes and utility classes.
+ */
+package com.scurrilous.circe.impl;
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/package-info.java b/circe-checksum/src/main/java/com/scurrilous/circe/package-info.java
new file mode 100644
index 000000000..fd710c51e
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/package-info.java
@@ -0,0 +1,22 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+/**
+ * Provides interfaces and minimal support classes for providing and consuming
+ * various forms of hash functions. The actual hash algorithms are implemented
+ * by {@linkplain com.scurrilous.circe.HashProvider hash providers}.
+ */
+package com.scurrilous.circe;
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/params/CrcParameters.java b/circe-checksum/src/main/java/com/scurrilous/circe/params/CrcParameters.java
new file mode 100644
index 000000000..10e9cc316
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/params/CrcParameters.java
@@ -0,0 +1,205 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.params;
+
+import com.scurrilous.circe.HashParameters;
+
+/**
+ * Hash parameters used to define a <a
+ * href="http://en.wikipedia.org/wiki/Cyclic_redundancy_check">cyclic redundancy
+ * check</a> (CRC). Includes some commonly used sets of parameters.
+ */
+public class CrcParameters implements HashParameters {
+
+    private final String name;
+    private final int bitWidth;
+    private final long polynomial;
+    private final long initial;
+    private final long xorOut;
+    private final boolean reflected;
+
+    /**
+     * Constructs a {@link CrcParameters} with the given parameters.
+     * 
+     * @param name the canonical name of the CRC function
+     * @param bitWidth the width of the CRC function
+     * @param polynomial the polynomial in binary form (non-reflected)
+     * @param initial the initial value of the CRC register
+     * @param xorOut a value XOR'ed with the CRC when it is read
+     * @param reflected indicates whether the CRC is reflected (LSB-first)
+     * @throws IllegalArgumentException if the width is less than 1 or greater
+     *             than 64
+     */
+    public CrcParameters(String name, int bitWidth, long polynomial, long initial, long xorOut,
+            boolean reflected) {
+        if (bitWidth < 1 || bitWidth > 64)
+            throw new IllegalArgumentException();
+        this.name = name;
+        this.bitWidth = bitWidth;
+        final long mask = bitWidth < 64 ? (1L << bitWidth) - 1 : ~0L;
+        this.polynomial = polynomial & mask;
+        this.initial = initial & mask;
+        this.xorOut = xorOut & mask;
+        this.reflected = reflected;
+    }
+
+    @Override
+    public String algorithm() {
+        return name;
+    }
+
+    /**
+     * Returns the width in bits of the CRC function. The width is also the
+     * position of the implicit set bit at the top of the polynomial.
+     * 
+     * @return the CRC width in bits
+     */
+    public int bitWidth() {
+        return bitWidth;
+    }
+
+    /**
+     * Returns the binary form of polynomial that defines the CRC function (with
+     * the implicit top bit omitted). For instance, the CRC-16 polynomial
+     * x<sup>16</sup> + x<sup>15</sup> + x<sup>2</sup> + 1 is represented as
+     * {@code 1000 0000 0000 0101} ({@code 0x8005}).
+     * 
+     * @return the CRC polynomial
+     */
+    public long polynomial() {
+        return polynomial;
+    }
+
+    /**
+     * Returns the initial value of the CRC register.
+     * 
+     * @return the CRC initial value
+     */
+    public long initial() {
+        return initial;
+    }
+
+    /**
+     * Returns the value XOR'ed with the CRC register when it is read to
+     * determine the output value.
+     * 
+     * @return the final XOR value
+     */
+    public long xorOut() {
+        return xorOut;
+    }
+
+    /**
+     * Returns whether the CRC function is "reflected". Reflected CRCs process
+     * data LSB-first, whereas "normal" CRCs are MSB-first.
+     * 
+     * @return whether the CRC function is reflected
+     */
+    public boolean reflected() {
+        return reflected;
+    }
+
+    /**
+     * Returns whether this object matches the given CRC parameters.
+     * 
+     * @param bitWidth the width of the CRC function
+     * @param polynomial the polynomial in binary form (non-reflected)
+     * @param initial the initial value of the CRC register
+     * @param xorOut a value XOR'ed with the CRC when it is read
+     * @param reflected indicates whether the CRC is reflected (LSB-first)
+     * @return true if all parameters match exactly, false otherwise
+     */
+    public boolean match(int bitWidth, long polynomial, long initial, long xorOut, boolean reflected) {
+        return bitWidth == this.bitWidth && polynomial == this.polynomial &&
+                initial == this.initial && xorOut == this.xorOut && reflected == this.reflected;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null || getClass() != obj.getClass())
+            return false;
+        final CrcParameters other = (CrcParameters) obj;
+        return bitWidth == other.bitWidth && polynomial == other.polynomial &&
+                initial == other.initial && xorOut == other.xorOut && reflected == other.reflected;
+    }
+
+    @Override
+    public int hashCode() {
+        return (int) (polynomial ^ (polynomial >>> 32) ^ initial ^ (initial >>> 32) ^ xorOut ^ (xorOut >>> 32)) ^
+                (reflected ? ~0 : 0);
+    }
+
+    /**
+     * Parameters for CRC-16, used in the ARC and LHA compression utilities.
+     */
+    public static final CrcParameters CRC16 = new CrcParameters("CRC-16", 16, 0x8005, 0, 0, true);
+
+    /**
+     * Parameters for CRC-16/CCITT, used in the Kermit protocol.
+     */
+    public static final CrcParameters CRC16_CCITT = new CrcParameters("CRC-16/CCITT", 16, 0x1021,
+            0, 0, true);
+
+    /**
+     * Parameters for CRC-16/XMODEM, used in the XMODEM protocol.
+     */
+    public static final CrcParameters CRC16_XMODEM = new CrcParameters("CRC-16/XMODEM", 16, 0x1021,
+            0, 0, false);
+
+    /**
+     * Parameters for CRC-32, used in Ethernet, SATA, PKZIP, ZMODEM, etc.
+     */
+    public static final CrcParameters CRC32 = new CrcParameters("CRC-32", 32, 0x04c11db7, ~0, ~0,
+            true);
+
+    /**
+     * Parameters for CRC-32/BZIP2, used in BZIP2.
+     */
+    public static final CrcParameters CRC32_BZIP2 = new CrcParameters("CRC-32/BZIP2", 32,
+            0x04c11db7, ~0, ~0, false);
+
+    /**
+     * Parameters for CRC-32C, used in iSCSI and SCTP.
+     */
+    public static final CrcParameters CRC32C = new CrcParameters("CRC-32C", 32, 0x1edc6f41, ~0, ~0,
+            true);
+
+    /**
+     * Parameters for CRC-32/MPEG-2, used in MPEG-2.
+     */
+    public static final CrcParameters CRC32_MPEG2 = new CrcParameters("CRC-32/MPEG-2", 32,
+            0x04c11db7, ~0, 0, false);
+
+    /**
+     * Parameters for CRC-32/POSIX, used in the {@code cksum} utility.
+     */
+    public static final CrcParameters CRC32_POSIX = new CrcParameters("CRC-32/POSIX", 32,
+            0x04c11db7, 0, ~0, false);
+
+    /**
+     * Parameters for CRC-64, used in the ECMA-182 standard for DLT-1 tapes.
+     */
+    public static final CrcParameters CRC64 = new CrcParameters("CRC-64", 64, 0x42f0e1eba9ea3693L,
+            0L, 0L, false);
+
+    /**
+     * Parameters for CRC-64/XZ, used in the {@code .xz} file format.
+     */
+    public static final CrcParameters CRC64_XZ = new CrcParameters("CRC-64/XZ", 64,
+            0x42f0e1eba9ea3693L, ~0L, ~0L, true);
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/params/MurmurHash3Parameters.java b/circe-checksum/src/main/java/com/scurrilous/circe/params/MurmurHash3Parameters.java
new file mode 100644
index 000000000..5eda097e5
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/params/MurmurHash3Parameters.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.params;
+
+import com.scurrilous.circe.HashParameters;
+
+/**
+ * Hash parameters for <a
+ * href="https://code.google.com/p/smhasher/wiki/MurmurHash3">MurmurHash3</a>.
+ */
+public final class MurmurHash3Parameters implements HashParameters {
+
+    private final MurmurHash3Variant variant;
+    private final int seed;
+
+    /**
+     * Constructs a {@link MurmurHash3Parameters} with the given variant and a
+     * seed value of zero.
+     * 
+     * @param variant the variant of the algorithm
+     */
+    public MurmurHash3Parameters(MurmurHash3Variant variant) {
+        this(variant, 0);
+    }
+
+    /**
+     * Constructs a {@link MurmurHash3Parameters} with the given variant and
+     * seed value.
+     * 
+     * @param variant the variant of the algorithm
+     * @param seed the seed value
+     */
+    public MurmurHash3Parameters(MurmurHash3Variant variant, int seed) {
+        this.variant = variant;
+        this.seed = seed;
+    }
+
+    /**
+     * Returns the variant of the hash algorithm.
+     * 
+     * @return the algorithm variant
+     */
+    public MurmurHash3Variant variant() {
+        return variant;
+    }
+
+    /**
+     * Returns the seed value for the hash function.
+     * 
+     * @return the seed value
+     */
+    public int seed() {
+        return seed;
+    }
+
+    @Override
+    public String algorithm() {
+        return variant.algorithm();
+    }
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/params/MurmurHash3Variant.java b/circe-checksum/src/main/java/com/scurrilous/circe/params/MurmurHash3Variant.java
new file mode 100644
index 000000000..12334448f
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/params/MurmurHash3Variant.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.params;
+
+/**
+ * Enumeration of MurmurHash3 variants.
+ */
+public enum MurmurHash3Variant {
+
+    /**
+     * 32-bit variant (optimized for x86).
+     */
+    X86_32("MurmurHash3_x86_32"),
+
+    /**
+     * 128-bit variant optimized for x86.
+     */
+    X86_128("MurmurHash3_x86_128"),
+
+    /**
+     * 128-bit variant optimized for x64.
+     */
+    X64_128("MurmurHash3_x64_128");
+
+    private final String algorithm;
+
+    private MurmurHash3Variant(String algorithm) {
+        this.algorithm = algorithm;
+    }
+
+    /**
+     * Returns the algorithm name corresponding to this variant.
+     * 
+     * @return this variant's algorithm name
+     */
+    public String algorithm() {
+        return algorithm;
+    }
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/params/SimpleHashParameters.java b/circe-checksum/src/main/java/com/scurrilous/circe/params/SimpleHashParameters.java
new file mode 100644
index 000000000..d69c020d1
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/params/SimpleHashParameters.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.params;
+
+import com.scurrilous.circe.HashParameters;
+
+/**
+ * Hash parameters consisting only of an algorithm name. Includes instances
+ * describing some commonly used algorithms.
+ */
+public class SimpleHashParameters implements HashParameters {
+
+    private final String algorithm;
+
+    /**
+     * Constructs a {@link SimpleHashParameters} with the given algorithm name.
+     * 
+     * @param algorithm the name of the hash algorithm
+     */
+    public SimpleHashParameters(String algorithm) {
+        this.algorithm = algorithm;
+    }
+
+    @Override
+    public String algorithm() {
+        return algorithm;
+    }
+
+    @Override
+    public int hashCode() {
+        return algorithm.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this)
+            return true;
+        if (obj == null || obj.getClass() != SimpleHashParameters.class)
+            return false;
+        return algorithm.equals(((SimpleHashParameters) obj).algorithm);
+    }
+
+    @Override
+    public String toString() {
+        return algorithm;
+    }
+
+    /**
+     * Represents the MD5 (128-bit) hash algorithm.
+     */
+    public static final SimpleHashParameters MD5 = new SimpleHashParameters("MD5");
+
+    /**
+     * Represents the SHA-1 (160-bit) hash algorithm.
+     */
+    public static final SimpleHashParameters SHA1 = new SimpleHashParameters("SHA-1");
+
+    /**
+     * Represents the SHA-256 (256-bit) hash algorithm.
+     */
+    public static final SimpleHashParameters SHA256 = new SimpleHashParameters("SHA-256");
+
+    /**
+     * Represents the SHA-384 (384-bit) hash algorithm.
+     */
+    public static final SimpleHashParameters SHA384 = new SimpleHashParameters("SHA-384");
+
+    /**
+     * Represents the SHA-512 (512-bit) hash algorithm.
+     */
+    public static final SimpleHashParameters SHA512 = new SimpleHashParameters("SHA-512");
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/params/SipHash24Parameters.java b/circe-checksum/src/main/java/com/scurrilous/circe/params/SipHash24Parameters.java
new file mode 100644
index 000000000..068f96df4
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/params/SipHash24Parameters.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.params;
+
+import com.scurrilous.circe.HashParameters;
+
+/**
+ * Hash parameters for <a href="https://131002.net/siphash/">SipHash-2-4</a>.
+ */
+public final class SipHash24Parameters implements HashParameters {
+
+    private final long seedLow;
+    private final long seedHigh;
+
+    /**
+     * Constructs a {@link SipHash24Parameters} with the default seed,
+     * {@code 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F}.
+     */
+    public SipHash24Parameters() {
+        this(0x0706050403020100L, 0x0f0e0d0c0b0a0908L);
+    }
+
+    /**
+     * Constructs a {@link SipHash24Parameters} with the given seed.
+     * 
+     * @param seedLow the low-order 64 bits of the seed
+     * @param seedHigh the high-order 64 bits of the seed
+     */
+    public SipHash24Parameters(long seedLow, long seedHigh) {
+        this.seedLow = seedLow;
+        this.seedHigh = seedHigh;
+    }
+
+    /**
+     * Returns the low-order 64 bits of the seed.
+     * 
+     * @return low-order bits of seed
+     */
+    public long seedLow() {
+        return seedLow;
+    }
+
+    /**
+     * Returns the high-order 64 bits of the seed.
+     * 
+     * @return high-order bits of seed
+     */
+    public long seedHigh() {
+        return seedHigh;
+    }
+
+    @Override
+    public String algorithm() {
+        return "SipHash-2-4";
+    }
+}
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/params/package-info.java b/circe-checksum/src/main/java/com/scurrilous/circe/params/package-info.java
new file mode 100644
index 000000000..87d9390eb
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/params/package-info.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+/**
+ * Defines {@linkplain com.scurrilous.circe.HashParameters hash parameters}
+ * classes that describe various commonly-used hash functions.
+ */
+package com.scurrilous.circe.params;
\ No newline at end of file
diff --git a/circe-checksum/src/main/java/com/scurrilous/circe/utils/NativeUtils.java b/circe-checksum/src/main/java/com/scurrilous/circe/utils/NativeUtils.java
new file mode 100644
index 000000000..8776092d5
--- /dev/null
+++ b/circe-checksum/src/main/java/com/scurrilous/circe/utils/NativeUtils.java
@@ -0,0 +1,104 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.scurrilous.circe.utils;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Locale;
+
+/**
+ * Utils for loading native checksum library.
+ */
+public class NativeUtils {
+
+    public static final String OS_NAME = System.getProperty("os.name").toLowerCase(Locale.US);
+
+    /**
+     * loads given library from the this jar. ie: this jar contains: /lib/pulsar-checksum.jnilib
+     * 
+     * @param path
+     *            : absolute path of the library in the jar <br/>
+     *            if this jar contains: /lib/pulsar-checksum.jnilib then provide the same absolute path as input
+     * @throws Exception
+     */
+    public static void loadLibraryFromJar(String path) throws Exception {
+
+        checkArgument(path.startsWith("/"), "absolute path must start with  /");
+
+        String[] parts = path.split("/");
+        String filename = (parts.length > 0) ? parts[parts.length - 1] : null;
+
+        File dir = File.createTempFile("native", "");
+        dir.delete();
+        if (!(dir.mkdir())) {
+            throw new IOException("Failed to create temp directory " + dir.getAbsolutePath());
+        }
+        dir.deleteOnExit();
+        File temp = new File(dir, filename);
+        temp.deleteOnExit();
+
+        byte[] buffer = new byte[1024];
+        int read;
+
+        InputStream input = NativeUtils.class.getResourceAsStream(path);
+        if (input == null) {
+            throw new FileNotFoundException("Couldn't find file into jar " + path);
+        }
+
+        OutputStream out = new FileOutputStream(temp);
+        try {
+            while ((read = input.read(buffer)) != -1) {
+                out.write(buffer, 0, read);
+            }
+        } finally {
+            out.close();
+            input.close();
+        }
+
+        if (!temp.exists()) {
+            throw new FileNotFoundException("Failed to copy file from jar at " + temp.getAbsolutePath());
+        }
+
+        System.load(temp.getAbsolutePath());
+    }
+
+    /**
+     * Returns jni library extension based on OS specification. Maven-nar generates jni library based on different OS :
+     * http://mark.donszelmann.org/maven-nar-plugin/aol.html (jni.extension)
+     * 
+     * @return
+     */
+    public static String libType() {
+
+        if (OS_NAME.indexOf("mac") >= 0) {
+            return "jnilib";
+        } else if (OS_NAME.indexOf("nix") >= 0 || OS_NAME.indexOf("nux") >= 0 || OS_NAME.indexOf("aix") > 0) {
+            return "so";
+        } else if (OS_NAME.indexOf("win") >= 0) {
+            return "dll";
+        }
+        throw new TypeNotPresentException(OS_NAME + " not supported", null);
+    }
+}
diff --git a/circe-checksum/src/main/resources/META-INF/services/com.scurrilous.circe.HashProvider b/circe-checksum/src/main/resources/META-INF/services/com.scurrilous.circe.HashProvider
new file mode 100644
index 000000000..7822e750f
--- /dev/null
+++ b/circe-checksum/src/main/resources/META-INF/services/com.scurrilous.circe.HashProvider
@@ -0,0 +1 @@
+com.scurrilous.circe.crc.StandardCrcProvider
\ No newline at end of file
diff --git a/circe-checksum/src/test/java/com/scurrilous/circe/CommonHashesTest.java b/circe-checksum/src/test/java/com/scurrilous/circe/CommonHashesTest.java
new file mode 100644
index 000000000..a42de857c
--- /dev/null
+++ b/circe-checksum/src/test/java/com/scurrilous/circe/CommonHashesTest.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe;
+
+import static org.testng.Assert.assertEquals;
+
+import java.nio.charset.Charset;
+
+import org.testng.annotations.Test;
+
+@SuppressWarnings("javadoc")
+public class CommonHashesTest {
+
+    private static final Charset ASCII = Charset.forName("ASCII");
+    private static final byte[] DIGITS = "123456789".getBytes(ASCII);
+
+
+    @Test
+    public void testCrc32() {
+        assertEquals(0xcbf43926, CommonHashes.crc32().calculate(DIGITS));
+    }
+
+    @Test
+    public void testCrc32c() {
+        assertEquals(0xe3069283, CommonHashes.crc32c().calculate(DIGITS));
+    }
+
+    @Test
+    public void testCrc64() {
+        assertEquals(0x6c40df5f0b497347L, CommonHashes.crc64().calculate(DIGITS));
+    }
+}
diff --git a/circe-checksum/src/test/java/com/scurrilous/circe/crc/CRCProvidersTest.java b/circe-checksum/src/test/java/com/scurrilous/circe/crc/CRCProvidersTest.java
new file mode 100644
index 000000000..b3ac059c6
--- /dev/null
+++ b/circe-checksum/src/test/java/com/scurrilous/circe/crc/CRCProvidersTest.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.crc;
+
+import static com.scurrilous.circe.HashSupport.HARDWARE;
+import static com.scurrilous.circe.HashSupport.INCREMENTAL;
+import static com.scurrilous.circe.HashSupport.INT_SIZED;
+import static com.scurrilous.circe.HashSupport.LONG_SIZED;
+import static com.scurrilous.circe.HashSupport.NATIVE;
+import static com.scurrilous.circe.HashSupport.STATEFUL;
+import static com.scurrilous.circe.HashSupport.STATELESS_INCREMENTAL;
+import static com.scurrilous.circe.params.CrcParameters.CRC32;
+import static com.scurrilous.circe.params.CrcParameters.CRC64;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.Map.Entry;
+import java.util.SortedMap;
+
+import org.testng.annotations.Test;
+
+import com.scurrilous.circe.HashProvider;
+import com.scurrilous.circe.HashProviders;
+import com.scurrilous.circe.HashSupport;
+import com.scurrilous.circe.IncrementalLongHash;
+
+@SuppressWarnings("javadoc")
+public class CRCProvidersTest {
+
+    @Test
+    public void testAll() {
+        final Iterator<HashProvider> i = HashProviders.iterator();
+        assertTrue(i.hasNext());
+        assertTrue(i.next() instanceof StandardCrcProvider);
+        assertFalse(i.hasNext());
+    }
+
+    @Test
+    public void testNonUnique() {
+        final HashProvider provider = HashProviders.best(CRC32);
+        final IncrementalLongHash i1 = provider.getIncrementalLong(CRC32);
+        final IncrementalLongHash i2 = provider.getIncrementalLong(CRC32);
+        assertTrue(i1 != i2);
+    }
+
+    @Test
+    public void testSearchCRCParametersCRC32() {
+        final SortedMap<EnumSet<HashSupport>, HashProvider> map = HashProviders.search(CRC32);
+        assertEquals(1, map.size());
+        final Entry<EnumSet<HashSupport>, HashProvider> entry = map.entrySet().iterator().next();
+        assertEquals(EnumSet.of(NATIVE, STATELESS_INCREMENTAL, INCREMENTAL, INT_SIZED, LONG_SIZED,
+                STATEFUL), entry.getKey());
+        assertTrue(entry.getValue() instanceof StandardCrcProvider);
+    }
+
+    @Test
+    public void testSearchCRCParametersCRC64() {
+        final SortedMap<EnumSet<HashSupport>, HashProvider> map = HashProviders.search(CRC64);
+        assertEquals(1, map.size());
+        final Entry<EnumSet<HashSupport>, HashProvider> entry = map.entrySet().iterator().next();
+        assertEquals(EnumSet.of(STATELESS_INCREMENTAL, INCREMENTAL, LONG_SIZED, STATEFUL),
+                entry.getKey());
+        assertTrue(entry.getValue() instanceof StandardCrcProvider);
+    }
+
+    @Test
+    public void testSearchCRCParametersEnumSet() {
+        assertEquals(1, HashProviders.search(CRC32, EnumSet.of(NATIVE)).size());
+        assertTrue(HashProviders.search(CRC64, EnumSet.of(NATIVE)).isEmpty());
+        assertTrue(HashProviders.search(CRC32, EnumSet.of(HARDWARE)).isEmpty());
+    }
+}
diff --git a/circe-checksum/src/test/java/com/scurrilous/circe/crc/CRCTest.java b/circe-checksum/src/test/java/com/scurrilous/circe/crc/CRCTest.java
new file mode 100644
index 000000000..c2803c995
--- /dev/null
+++ b/circe-checksum/src/test/java/com/scurrilous/circe/crc/CRCTest.java
@@ -0,0 +1,180 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.crc;
+
+import static com.scurrilous.circe.params.CrcParameters.CRC16;
+import static com.scurrilous.circe.params.CrcParameters.CRC16_CCITT;
+import static com.scurrilous.circe.params.CrcParameters.CRC16_XMODEM;
+import static com.scurrilous.circe.params.CrcParameters.CRC32;
+import static com.scurrilous.circe.params.CrcParameters.CRC32C;
+import static com.scurrilous.circe.params.CrcParameters.CRC32_BZIP2;
+import static com.scurrilous.circe.params.CrcParameters.CRC32_POSIX;
+import static com.scurrilous.circe.params.CrcParameters.CRC64;
+import static com.scurrilous.circe.params.CrcParameters.CRC64_XZ;
+import static org.testng.Assert.assertEquals;
+
+import java.nio.charset.Charset;
+
+import org.testng.annotations.Test;
+
+import com.scurrilous.circe.HashProvider;
+import com.scurrilous.circe.IncrementalIntHash;
+import com.scurrilous.circe.params.CrcParameters;
+
+/**
+ * Tests the {@link StandardCrcProvider} with various CRC algorithms. See the <a
+ * href="http://reveng.sourceforge.net/crc-catalogue/">Catalogue of parametrised
+ * CRC algorithms</a> for more information on these algorithms and others.
+ */
+@SuppressWarnings("javadoc")
+public class CRCTest {
+
+    private static final HashProvider PROVIDER = new StandardCrcProvider();
+    private static final Charset ASCII = Charset.forName("ASCII");
+    private static final byte[] DIGITS = "123456789".getBytes(ASCII);
+
+    @Test
+    public void testCRC3_ROHC() {
+        final CrcParameters CRC3_ROHC = new CrcParameters("CRC-3/ROHC", 3, 0x3, 0x7, 0, true);
+        assertEquals(0x6, PROVIDER.getIncrementalInt(CRC3_ROHC).calculate(DIGITS));
+    }
+
+    @Test
+    public void testCRC5_EPC() {
+        final CrcParameters CRC5_EPC = new CrcParameters("CRC-5/EPC", 5, 0x09, 0x09, 0, false);
+        assertEquals(0x00, PROVIDER.getIncrementalInt(CRC5_EPC).calculate(DIGITS));
+    }
+
+    @Test
+    public void testCRC5_USB() {
+        final CrcParameters CRC5_USB = new CrcParameters("CRC-5/USB", 5, 0x05, 0x1f, 0x1f, true);
+        assertEquals(0x19, PROVIDER.getIncrementalInt(CRC5_USB).calculate(DIGITS));
+    }
+
+    @Test
+    public void testCRC7() {
+        final CrcParameters CRC7 = new CrcParameters("CRC-7", 7, 0x09, 0, 0, false);
+        assertEquals(0x75, PROVIDER.getIncrementalInt(CRC7).calculate(DIGITS));
+    }
+
+    @Test
+    public void testCRC7ROHC() {
+        final CrcParameters CRC7_ROHC = new CrcParameters("CRC-7/ROHC", 7, 0x4f, 0x7f, 0, true);
+        assertEquals(0x53, PROVIDER.getIncrementalInt(CRC7_ROHC).calculate(DIGITS));
+    }
+
+    @Test
+    public void testCRC8() {
+        final CrcParameters CRC8 = new CrcParameters("CRC-8", 8, 0x07, 0, 0, false);
+        assertEquals(0xf4, PROVIDER.getIncrementalInt(CRC8).calculate(DIGITS));
+    }
+
+    @Test
+    public void testCRC10() {
+        final CrcParameters CRC10 = new CrcParameters("CRC-10", 10, 0x233, 0, 0, false);
+        assertEquals(0x199, PROVIDER.getIncrementalInt(CRC10).calculate(DIGITS));
+    }
+
+    @Test
+    public void testCRC15() {
+        final CrcParameters CRC15 = new CrcParameters("CRC-15", 15, 0x4599, 0, 0, false);
+        assertEquals(0x059e, PROVIDER.getIncrementalInt(CRC15).calculate(DIGITS));
+    }
+
+    @Test
+    public void testCRC16() {
+        assertEquals(0xbb3d, PROVIDER.getIncrementalInt(CRC16).calculate(DIGITS));
+    }
+
+    @Test
+    public void testCRC16_CCITT() {
+        assertEquals(0x2189, PROVIDER.getIncrementalInt(CRC16_CCITT).calculate(DIGITS));
+    }
+
+    @Test
+    public void testXMODEM() {
+        assertEquals(0x31c3, PROVIDER.getIncrementalInt(CRC16_XMODEM).calculate(DIGITS));
+    }
+
+    @Test
+    public void testCRC24() {
+        final CrcParameters CRC24 = new CrcParameters("CRC-24", 24, 0x864cfb, 0xb704ce, 0, false);
+        assertEquals(0x21cf02, PROVIDER.getIncrementalInt(CRC24).calculate(DIGITS));
+    }
+
+    @Test
+    public void testCRC32() {
+        assertEquals(0xcbf43926, PROVIDER.getIncrementalInt(CRC32).calculate(DIGITS));
+    }
+
+    @Test
+    public void testJavaCRC32() {
+        assertEquals(0xcbf43926, PROVIDER.getStatelessInt(CRC32).calculate(DIGITS));
+    }
+
+    @Test
+    public void testBZIP2() {
+        assertEquals(0xfc891918, PROVIDER.getIncrementalInt(CRC32_BZIP2).calculate(DIGITS));
+    }
+
+    @Test
+    public void testCRC32C() {
+        assertEquals(0xe3069283, PROVIDER.getIncrementalInt(CRC32C).calculate(DIGITS));
+    }
+
+    @Test
+    public void testCRC32D() {
+        final CrcParameters CRC32D = new CrcParameters("CRC-32D", 32, 0xa833982b, ~0, ~0, true);
+        assertEquals(0x87315576, PROVIDER.getIncrementalInt(CRC32D).calculate(DIGITS));
+    }
+
+    @Test
+    public void testPOSIX() {
+        assertEquals(0x765e7680, PROVIDER.getIncrementalInt(CRC32_POSIX).calculate(DIGITS));
+    }
+
+    @Test
+    public void testCRC32Q() {
+        final CrcParameters CRC32Q = new CrcParameters("CRC-32Q", 32, 0x814141ab, 0, 0, false);
+        assertEquals(0x3010bf7f, PROVIDER.getIncrementalInt(CRC32Q).calculate(DIGITS));
+    }
+
+    @Test
+    public void testCRC64() {
+        assertEquals(0x6c40df5f0b497347L, PROVIDER.getIncrementalLong(CRC64).calculate(DIGITS));
+    }
+
+    @Test
+    public void testCRC64_XZ() {
+        assertEquals(0x995dc9bbdf1939faL, PROVIDER.getIncrementalLong(CRC64_XZ).calculate(DIGITS));
+    }
+    
+    @Test
+    public void testCRC32CIncremental() {
+        // reflected
+        testIncremental(PROVIDER.getIncrementalInt(CRC32C));
+    }
+
+    private void testIncremental(IncrementalIntHash hash) {
+        final String data = "data";
+        final String combined = data + data;
+
+        final int dataChecksum = hash.calculate(data.getBytes(ASCII));
+        final int combinedChecksum = hash.calculate(combined.getBytes(ASCII));
+        final int incrementalChecksum = hash.resume(dataChecksum, data.getBytes(ASCII));
+        assertEquals(combinedChecksum, incrementalChecksum);
+    }
+}
\ No newline at end of file
diff --git a/circe-checksum/src/test/java/com/scurrilous/circe/impl/AbstractIncrementalIntHashTest.java b/circe-checksum/src/test/java/com/scurrilous/circe/impl/AbstractIncrementalIntHashTest.java
new file mode 100644
index 000000000..0f947c907
--- /dev/null
+++ b/circe-checksum/src/test/java/com/scurrilous/circe/impl/AbstractIncrementalIntHashTest.java
@@ -0,0 +1,142 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.impl;
+
+import static org.testng.Assert.*;
+
+import com.scurrilous.circe.StatefulHash;
+import java.nio.ByteBuffer;
+import mockit.Expectations;
+import mockit.Mocked;
+import org.testng.annotations.Test;
+
+@SuppressWarnings("javadoc")
+public class AbstractIncrementalIntHashTest {
+
+    @Mocked
+    private AbstractIncrementalIntHash hash;
+
+    @Test
+    public void testAsStateful() {
+        final byte[] input = new byte[10];
+        new Expectations(hash) {
+            {
+                hash.algorithm();
+                hash.length();
+                hash.initial();
+                result = 42;
+                hash.resumeUnchecked(42, input, 2, 4);
+                result = 99;
+            }
+        };
+        StatefulHash stateful = hash.createStateful();
+        stateful.algorithm();
+        stateful.length();
+        assertNotEquals(stateful, stateful.createNew());
+        stateful.reset();
+        stateful.update(input, 2, 4);
+        assertEquals(99, stateful.getInt());
+    }
+
+    @Test
+    public void testCalculateByteArray() {
+        final byte[] input = new byte[10];
+        new Expectations(hash) {
+            {
+                hash.initial();
+                result = 42;
+                hash.resume(42, input);
+            }
+        };
+        hash.calculate(input);
+    }
+
+    @Test
+    public void testCalculateByteArrayIntInt() {
+        final byte[] input = new byte[10];
+        new Expectations(hash) {
+            {
+                hash.initial();
+                result = 42;
+                hash.resume(42, input, 2, 4);
+            }
+        };
+        hash.calculate(input, 2, 4);
+    }
+
+    @Test
+    public void testCalculateByteBuffer() {
+        final ByteBuffer input = ByteBuffer.allocate(10);
+        new Expectations(hash) {
+            {
+                hash.initial();
+                result = 42;
+                hash.resume(42, input);
+            }
+        };
+        hash.calculate(input);
+    }
+
+    @Test
+    public void testResumeIntByteArray() {
+        final byte[] input = new byte[10];
+        new Expectations(hash) {
+            {
+                hash.resumeUnchecked(42, input, 0, input.length);
+            }
+        };
+        hash.resume(42, input);
+    }
+
+    @Test
+    public void testResumeIntByteArrayIntInt() {
+        final byte[] input = new byte[10];
+        new Expectations(hash) {
+            {
+                hash.resumeUnchecked(42, input, 2, 4);
+            }
+        };
+        hash.resume(42, input, 2, 4);
+    }
+
+    @Test
+    public void testResumeIntByteBuffer() {
+        final ByteBuffer input = ByteBuffer.allocate(20);
+        input.position(5);
+        input.limit(15);
+        new Expectations(hash) {
+            {
+                hash.resumeUnchecked(42, input.array(), input.arrayOffset() + 5, 10);
+            }
+        };
+        hash.resume(42, input);
+        assertEquals(input.limit(), input.position());
+    }
+
+    @Test
+    public void testResumeIntReadOnlyByteBuffer() {
+        final ByteBuffer input = ByteBuffer.allocate(20).asReadOnlyBuffer();
+        input.position(5);
+        input.limit(15);
+        new Expectations(hash) {
+            {
+                hash.resumeUnchecked(42, withInstanceOf(byte[].class), 0, 10);
+            }
+        };
+        hash.resume(42, input);
+        assertEquals(input.limit(), input.position());
+    }
+}
diff --git a/circe-checksum/src/test/java/com/scurrilous/circe/impl/AbstractIncrementalLongHashTest.java b/circe-checksum/src/test/java/com/scurrilous/circe/impl/AbstractIncrementalLongHashTest.java
new file mode 100644
index 000000000..0468da8b0
--- /dev/null
+++ b/circe-checksum/src/test/java/com/scurrilous/circe/impl/AbstractIncrementalLongHashTest.java
@@ -0,0 +1,147 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.impl;
+
+import static org.testng.Assert.*;
+
+import java.nio.ByteBuffer;
+
+import mockit.Expectations;
+import mockit.Mocked;
+
+import org.testng.annotations.Test;
+
+import com.scurrilous.circe.StatefulHash;
+import com.scurrilous.circe.impl.AbstractIncrementalLongHash;
+
+@SuppressWarnings("javadoc")
+public class AbstractIncrementalLongHashTest {
+
+    @Mocked
+    private AbstractIncrementalLongHash hash;
+
+    @Test
+    public void testAsStateful() {
+        final byte[] input = new byte[10];
+        new Expectations(hash) {
+            {
+                hash.algorithm();
+                hash.length();
+                hash.initial();
+                result = 0x4200000000L;
+                hash.resumeUnchecked(0x4200000000L, input, 2, 4);
+                result = 0x990000000000L;
+            }
+        };
+        StatefulHash stateful = hash.createStateful();
+        stateful.algorithm();
+        stateful.length();
+        assertNotSame(stateful, stateful.createNew());
+        stateful.reset();
+        stateful.update(input, 2, 4);
+        assertEquals(0, stateful.getInt());
+        assertEquals(0x990000000000L, stateful.getLong());
+    }
+
+    @Test
+    public void testCalculateByteArray() {
+        final byte[] input = new byte[10];
+        new Expectations(hash) {
+            {
+                hash.initial();
+                result = 0x4200000000L;
+                hash.resume(0x4200000000L, input);
+            }
+        };
+        hash.calculate(input);
+    }
+
+    @Test
+    public void testCalculateByteArrayIntInt() {
+        final byte[] input = new byte[10];
+        new Expectations(hash) {
+            {
+                hash.initial();
+                result = 0x4200000000L;
+                hash.resume(0x4200000000L, input, 2, 4);
+            }
+        };
+        hash.calculate(input, 2, 4);
+    }
+
+    @Test
+    public void testCalculateByteBuffer() {
+        final ByteBuffer input = ByteBuffer.allocate(10);
+        new Expectations(hash) {
+            {
+                hash.initial();
+                result = 0x4200000000L;
+                hash.resume(0x4200000000L, input);
+            }
+        };
+        hash.calculate(input);
+    }
+
+    @Test
+    public void testResumeLongByteArray() {
+        final byte[] input = new byte[10];
+        new Expectations(hash) {
+            {
+                hash.resumeUnchecked(0x4200000000L, input, 0, input.length);
+            }
+        };
+        hash.resume(0x4200000000L, input);
+    }
+
+    @Test
+    public void testResumeLongByteArrayIntInt() {
+        final byte[] input = new byte[10];
+        new Expectations(hash) {
+            {
+                hash.resumeUnchecked(0x4200000000L, input, 2, 4);
+            }
+        };
+        hash.resume(0x4200000000L, input, 2, 4);
+    }
+
+    @Test
+    public void testResumeLongByteBuffer() {
+        final ByteBuffer input = ByteBuffer.allocate(20);
+        input.position(5);
+        input.limit(15);
+        new Expectations(hash) {
+            {
+                hash.resumeUnchecked(0x4200000000L, input.array(), input.arrayOffset() + 5, 10);
+            }
+        };
+        hash.resume(0x4200000000L, input);
+        assertEquals(input.limit(), input.position());
+    }
+
+    @Test
+    public void testResumeLongReadOnlyByteBuffer() {
+        final ByteBuffer input = ByteBuffer.allocate(20).asReadOnlyBuffer();
+        input.position(5);
+        input.limit(15);
+        new Expectations(hash) {
+            {
+                hash.resumeUnchecked(0x4200000000L, withInstanceOf(byte[].class), 0, 10);
+            }
+        };
+        hash.resume(0x4200000000L, input);
+        assertEquals(input.limit(), input.position());
+    }
+}
diff --git a/circe-checksum/src/test/java/com/scurrilous/circe/impl/AbstractStatefulHashTest.java b/circe-checksum/src/test/java/com/scurrilous/circe/impl/AbstractStatefulHashTest.java
new file mode 100644
index 000000000..8cad8b1b1
--- /dev/null
+++ b/circe-checksum/src/test/java/com/scurrilous/circe/impl/AbstractStatefulHashTest.java
@@ -0,0 +1,193 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.impl;
+
+import static org.testng.Assert.assertEquals;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import mockit.Expectations;
+import mockit.Mocked;
+import mockit.NonStrictExpectations;
+
+import org.testng.annotations.Test;
+
+@SuppressWarnings("javadoc")
+public class AbstractStatefulHashTest {
+
+    @Mocked
+    private AbstractStatefulHash hash;
+
+    @Test
+    public void testUpdateByteArray() {
+        final byte[] input = new byte[42];
+        new Expectations(hash) {
+            {
+                hash.updateUnchecked(input, 0, input.length);
+            }
+        };
+        hash.update(input);
+    }
+
+    @Test
+    public void testUpdateByteArrayIntInt() {
+        final byte[] input = new byte[42];
+        new Expectations(hash) {
+            {
+                hash.updateUnchecked(input, 5, 10);
+            }
+        };
+        hash.update(input, 5, 10);
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void testUpdateByteArrayIntNegInt() {
+        final byte[] input = new byte[42];
+        new Expectations(hash) {
+        };
+        hash.update(input, 1, -1);
+    }
+
+    @Test(expectedExceptions = IndexOutOfBoundsException.class)
+    public void testUpdateByteArrayNegIntInt() {
+        final byte[] input = new byte[42];
+        new Expectations(hash) {
+        };
+        hash.update(input, -1, 10);
+    }
+
+    @Test(expectedExceptions = IndexOutOfBoundsException.class)
+    public void testUpdateByteArrayIntIntOverflow() {
+        final byte[] input = new byte[42];
+        new Expectations(hash) {
+        };
+        hash.update(input, 40, 3);
+    }
+
+    @Test
+    public void testUpdateByteBuffer() {
+        final ByteBuffer input = ByteBuffer.allocate(20);
+        input.position(5);
+        input.limit(15);
+        new Expectations(hash) {
+            {
+                hash.updateUnchecked(input.array(), input.arrayOffset() + 5, 10);
+            }
+        };
+        hash.update(input);
+        assertEquals(input.limit(), input.position());
+    }
+
+    @Test
+    public void testUpdateReadOnlyByteBuffer() {
+        final ByteBuffer input = ByteBuffer.allocate(20).asReadOnlyBuffer();
+        input.position(5);
+        input.limit(15);
+        new Expectations(hash) {
+            {
+                hash.updateUnchecked(withInstanceOf(byte[].class), 0, 10);
+            }
+        };
+        hash.update(input);
+        assertEquals(input.limit(), input.position());
+    }
+
+    @Test
+    public void testGetBytes() {
+        final List<byte[]> captures = new ArrayList<>();
+        new Expectations(hash) {
+            {
+                hash.length();
+                result = 5;
+                hash.writeBytes(withCapture(captures), 0, 5);
+            }
+        };
+        hash.getBytes();
+        assertEquals(5, captures.get(0).length);
+    }
+
+    @Test
+    public void testGetBytesByteArrayInt() {
+        final byte[] output = new byte[5];
+        new Expectations(hash) {
+            {
+                hash.length();
+                result = output.length;
+                hash.getLong();
+                result = 0x1234567890L;
+            }
+        };
+        hash.getBytes(output, 0, output.length);
+        assertEquals(new byte[] { (byte) 0x90, 0x78, 0x56, 0x34, 0x12 }, output);
+    }
+
+    @Test(expectedExceptions = IndexOutOfBoundsException.class)
+    public void testGetBytesByteArrayNegInt() {
+        final byte[] output = new byte[5];
+        new NonStrictExpectations(hash) {
+            {
+                hash.length();
+                result = output.length;
+            }
+        };
+        hash.getBytes(output, -1, output.length);
+    }
+
+    @Test(expectedExceptions = IndexOutOfBoundsException.class)
+    public void testGetBytesByteArrayIntOverflow() {
+        final byte[] output = new byte[5];
+        new Expectations(hash) {
+        };
+        hash.getBytes(output, 0, output.length + 1);
+    }
+
+    @Test
+    public void testGetBytesByteArrayIntPartial() {
+        final byte[] output = new byte[5];
+        new Expectations(hash) {
+            {
+                hash.length();
+                result = output.length + 1;
+                hash.writeBytes(output, 0, output.length);
+            }
+        };
+        hash.getBytes(output, 0, output.length);
+    }
+
+    @Test
+    public void testGetByte() {
+        new Expectations(hash) {
+            {
+                hash.getInt();
+                result = 0x12345678;
+            }
+        };
+        assertEquals(0x78, hash.getByte());
+    }
+
+    @Test
+    public void testGetShort() {
+        new Expectations(hash) {
+            {
+                hash.getInt();
+                result = 0x12345678;
+            }
+        };
+        assertEquals(0x5678, hash.getShort());
+    }
+}
diff --git a/circe-checksum/src/test/java/com/scurrilous/circe/impl/AbstractStatelessIntHashTest.java b/circe-checksum/src/test/java/com/scurrilous/circe/impl/AbstractStatelessIntHashTest.java
new file mode 100644
index 000000000..184b54640
--- /dev/null
+++ b/circe-checksum/src/test/java/com/scurrilous/circe/impl/AbstractStatelessIntHashTest.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.impl;
+
+import static org.testng.Assert.*;
+
+import java.nio.ByteBuffer;
+
+import mockit.Expectations;
+import mockit.Mocked;
+
+import org.testng.annotations.Test;
+
+import com.scurrilous.circe.impl.AbstractStatelessIntHash;
+
+@SuppressWarnings("javadoc")
+public class AbstractStatelessIntHashTest {
+
+    @Mocked
+    private AbstractStatelessIntHash hash;
+
+    @Test
+    public void testCalculateByteArray() {
+        final byte[] input = new byte[10];
+        new Expectations(hash) {
+            {
+                hash.calculateUnchecked(input, 0, input.length);
+            }
+        };
+        hash.calculate(input);
+    }
+
+    @Test
+    public void testCalculateByteArrayIntInt() {
+        final byte[] input = new byte[10];
+        new Expectations(hash) {
+            {
+                hash.calculateUnchecked(input, 2, 4);
+            }
+        };
+        hash.calculate(input, 2, 4);
+    }
+
+    @Test
+    public void testCalculateByteBuffer() {
+        final ByteBuffer input = ByteBuffer.allocate(20);
+        input.position(5);
+        input.limit(15);
+        new Expectations(hash) {
+            {
+                hash.calculateUnchecked(input.array(), input.arrayOffset() + 5, 10);
+            }
+        };
+        hash.calculate(input);
+        assertEquals(input.limit(), input.position());
+    }
+
+    @Test
+    public void testCalculateReadOnlyByteBuffer() {
+        final ByteBuffer input = ByteBuffer.allocate(20).asReadOnlyBuffer();
+        input.position(5);
+        input.limit(15);
+        new Expectations(hash) {
+            {
+                hash.calculateUnchecked(withInstanceOf(byte[].class), 0, 10);
+            }
+        };
+        hash.calculate(input);
+        assertEquals(input.limit(), input.position());
+    }
+}
diff --git a/circe-checksum/src/test/java/com/scurrilous/circe/impl/AbstractStatelessLongHashTest.java b/circe-checksum/src/test/java/com/scurrilous/circe/impl/AbstractStatelessLongHashTest.java
new file mode 100644
index 000000000..a4a607f3c
--- /dev/null
+++ b/circe-checksum/src/test/java/com/scurrilous/circe/impl/AbstractStatelessLongHashTest.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright 2014 Trevor Robinson
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+package com.scurrilous.circe.impl;
+
+import static org.testng.Assert.*;
+
+import java.nio.ByteBuffer;
+
+import mockit.Expectations;
+import mockit.Mocked;
+
+import org.testng.annotations.Test;
+
+import com.scurrilous.circe.impl.AbstractStatelessLongHash;
+
+@SuppressWarnings("javadoc")
+public class AbstractStatelessLongHashTest {
+
+    @Mocked
+    private AbstractStatelessLongHash hash;
+
+    @Test
+    public void testCalculateByteArray() {
+        final byte[] input = new byte[10];
+        new Expectations(hash) {
+            {
+                hash.calculateUnchecked(input, 0, input.length);
+            }
+        };
+        hash.calculate(input);
+    }
+
+    @Test
+    public void testCalculateByteArrayIntInt() {
+        final byte[] input = new byte[10];
+        new Expectations(hash) {
+            {
+                hash.calculateUnchecked(input, 2, 4);
+            }
+        };
+        hash.calculate(input, 2, 4);
+    }
+
+    @Test
+    public void testCalculateByteBuffer() {
+        final ByteBuffer input = ByteBuffer.allocate(20);
+        input.position(5);
+        input.limit(15);
+        new Expectations(hash) {
+            {
+                hash.calculateUnchecked(input.array(), input.arrayOffset() + 5, 10);
+            }
+        };
+        hash.calculate(input);
+        assertEquals(input.limit(), input.position());
+    }
+
+    @Test
+    public void testCalculateReadOnlyByteBuffer() {
+        final ByteBuffer input = ByteBuffer.allocate(20).asReadOnlyBuffer();
+        input.position(5);
+        input.limit(15);
+        new Expectations(hash) {
+            {
+                hash.calculateUnchecked(withInstanceOf(byte[].class), 0, 10);
+            }
+        };
+        hash.calculate(input);
+        assertEquals(input.limit(), input.position());
+    }
+}
diff --git a/circe-checksum/src/test/java/com/scurrilous/circe/utils/NativeUtilsTests.java b/circe-checksum/src/test/java/com/scurrilous/circe/utils/NativeUtilsTests.java
new file mode 100644
index 000000000..f1d49288e
--- /dev/null
+++ b/circe-checksum/src/test/java/com/scurrilous/circe/utils/NativeUtilsTests.java
@@ -0,0 +1,49 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.scurrilous.circe.utils;
+
+import java.io.FileNotFoundException;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/**
+ * Unit test of {@link NativeUtils}.
+ */
+public class NativeUtilsTests {
+
+    @Test
+    public void testLoadLibrary() throws Exception {
+
+        final String fileName = "test" + NativeUtils.libType();
+        try {
+            NativeUtils.loadLibraryFromJar(fileName);
+            Assert.fail("Should fail because of not having absolute path");
+        } catch (IllegalArgumentException e) {
+            // OK
+        }
+
+        try {
+            NativeUtils.loadLibraryFromJar("/" + fileName);
+            Assert.fail("Should fail because no file present into the jar");
+        } catch (FileNotFoundException e) {
+            // OK
+        }
+    }
+
+}
diff --git a/pom.xml b/pom.xml
index b3f6b75a8..d2847233b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -52,9 +52,10 @@
   </ciManagement>
   <modules>
     <module>buildtools</module>
-    <module>bookkeeper-proto</module>
+    <module>circe-checksum</module>
     <module>bookkeeper-common</module>
     <module>bookkeeper-stats</module>
+    <module>bookkeeper-proto</module>
     <module>bookkeeper-server</module>
     <module>bookkeeper-benchmark</module>
     <module>bookkeeper-stats-providers</module>


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services