You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bookkeeper.apache.org by si...@apache.org on 2018/01/07 05:45:59 UTC

[bookkeeper] branch master updated: ISSUE #915: Part 1 - Copy pulsar-checksum module as circe-checksum

This is an automated email from the ASF dual-hosted git repository.

sijie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/bookkeeper.git


The following commit(s) were added to refs/heads/master by this push:
     new a0b05c5  ISSUE #915: Part 1 - Copy pulsar-checksum module as circe-checksum
a0b05c5 is described below

commit a0b05c519b34dd1f4bcd1f440378aeb601c92c90
Author: Sijie Guo <si...@apache.org>
AuthorDate: Sat Jan 6 21:45:53 2018 -0800

    ISSUE #915: Part 1 - Copy pulsar-checksum module as circe-checksum
    
    Descriptions of the changes in this PR:
    
    - this change copies the files under `com.scurrilous.circe` in pulsar-checksum module into bookkeeper as `circe-checksum`.
    - this change doesn't copy files under `org.apache.pulsar` except NativeUtils. will have a subsequent change to add it.
    - Move NativeUtils to `com.scurrilous.circe.utils`.
    - Update notice files to respect to Trevor Robinson's change
    
    Author: Sijie Guo <si...@apache.org>
    
    Reviewers: Matteo Merli <mm...@apache.org>
    
    This closes #916 from sijie/copy_pulsar_checksum, closes #915
---
 NOTICE                                             |   5 +
 .../src/main/resources/NOTICE-all.bin.txt          |   6 +
 .../src/main/resources/NOTICE-server.bin.txt       |   6 +
 .../src/main/resources/bookkeeper/suppressions.xml |   3 +
 circe-checksum/pom.xml                             | 206 +++++++++++++++++++
 circe-checksum/src/main/assembly/assembly.xml      |  71 +++++++
 circe-checksum/src/main/circe/cpp/crc32c_sse42.cpp | 217 +++++++++++++++++++++
 .../src/main/circe/cpp/crc32c_sse42_jni.cpp        |  78 ++++++++
 circe-checksum/src/main/circe/cpp/gf2.hpp          | 174 +++++++++++++++++
 .../src/main/circe/include/crc32c_sse42.hpp        |  40 ++++
 circe-checksum/src/main/circe/include/int_types.h  |  43 ++++
 .../java/com/scurrilous/circe/CommonHashes.java    | 188 ++++++++++++++++++
 .../src/main/java/com/scurrilous/circe/Hash.java   |  53 +++++
 .../java/com/scurrilous/circe/HashParameters.java  |  30 +++
 .../java/com/scurrilous/circe/HashProvider.java    | 101 ++++++++++
 .../java/com/scurrilous/circe/HashProviders.java   | 125 ++++++++++++
 .../java/com/scurrilous/circe/HashSupport.java     | 179 +++++++++++++++++
 .../src/main/java/com/scurrilous/circe/Hashes.java | 123 ++++++++++++
 .../com/scurrilous/circe/IncrementalIntHash.java   |  88 +++++++++
 .../com/scurrilous/circe/IncrementalLongHash.java  |  88 +++++++++
 .../java/com/scurrilous/circe/StatefulHash.java    | 171 ++++++++++++++++
 .../java/com/scurrilous/circe/StatefulIntHash.java |  31 +++
 .../com/scurrilous/circe/StatefulLongHash.java     |  30 +++
 .../java/com/scurrilous/circe/StatelessHash.java   |  31 +++
 .../com/scurrilous/circe/StatelessIntHash.java     |  77 ++++++++
 .../com/scurrilous/circe/StatelessLongHash.java    |  77 ++++++++
 .../com/scurrilous/circe/crc/AbstractIntCrc.java   |  64 ++++++
 .../com/scurrilous/circe/crc/AbstractLongCrc.java  |  64 ++++++
 .../java/com/scurrilous/circe/crc/JavaCrc32.java   | 103 ++++++++++
 .../com/scurrilous/circe/crc/NormalByteCrc.java    |  46 +++++
 .../com/scurrilous/circe/crc/NormalIntCrc.java     |  48 +++++
 .../com/scurrilous/circe/crc/NormalLongCrc.java    |  48 +++++
 .../com/scurrilous/circe/crc/ReflectedIntCrc.java  |  49 +++++
 .../com/scurrilous/circe/crc/ReflectedLongCrc.java |  45 +++++
 .../java/com/scurrilous/circe/crc/Sse42Crc32C.java | 130 ++++++++++++
 .../scurrilous/circe/crc/StandardCrcProvider.java  |  82 ++++++++
 .../com/scurrilous/circe/crc/package-info.java     |  25 +++
 .../circe/impl/AbstractHashProvider.java           | 193 ++++++++++++++++++
 .../circe/impl/AbstractIncrementalIntHash.java     | 114 +++++++++++
 .../circe/impl/AbstractIncrementalLongHash.java    | 114 +++++++++++
 .../circe/impl/AbstractStatefulHash.java           | 132 +++++++++++++
 .../circe/impl/AbstractStatelessIntHash.java       |  79 ++++++++
 .../circe/impl/AbstractStatelessLongHash.java      |  79 ++++++++
 .../circe/impl/DirectByteBufferAccess.java         |  34 ++++
 .../circe/impl/DirectByteBufferAccessLoader.java   |  49 +++++
 .../java/com/scurrilous/circe/impl/HashCache.java  |  44 +++++
 .../com/scurrilous/circe/impl/HashCacheLoader.java |  58 ++++++
 .../circe/impl/IncrementalIntStatefulHash.java     |  92 +++++++++
 .../circe/impl/IncrementalLongStatefulHash.java    |  92 +++++++++
 .../scurrilous/circe/impl/IntStatefulLongHash.java | 109 +++++++++++
 .../circe/impl/IntStatelessLongHash.java           |  80 ++++++++
 .../com/scurrilous/circe/impl/package-info.java    |  22 +++
 .../java/com/scurrilous/circe/package-info.java    |  22 +++
 .../com/scurrilous/circe/params/CrcParameters.java | 205 +++++++++++++++++++
 .../circe/params/MurmurHash3Parameters.java        |  73 +++++++
 .../circe/params/MurmurHash3Variant.java           |  52 +++++
 .../circe/params/SimpleHashParameters.java         |  85 ++++++++
 .../circe/params/SipHash24Parameters.java          |  69 +++++++
 .../com/scurrilous/circe/params/package-info.java  |  21 ++
 .../com/scurrilous/circe/utils/NativeUtils.java    | 104 ++++++++++
 .../services/com.scurrilous.circe.HashProvider     |   1 +
 .../com/scurrilous/circe/CommonHashesTest.java     |  45 +++++
 .../com/scurrilous/circe/crc/CRCProvidersTest.java |  88 +++++++++
 .../java/com/scurrilous/circe/crc/CRCTest.java     | 180 +++++++++++++++++
 .../circe/impl/AbstractIncrementalIntHashTest.java | 142 ++++++++++++++
 .../impl/AbstractIncrementalLongHashTest.java      | 147 ++++++++++++++
 .../circe/impl/AbstractStatefulHashTest.java       | 193 ++++++++++++++++++
 .../circe/impl/AbstractStatelessIntHashTest.java   |  84 ++++++++
 .../circe/impl/AbstractStatelessLongHashTest.java  |  84 ++++++++
 .../scurrilous/circe/utils/NativeUtilsTests.java   |  49 +++++
 pom.xml                                            |   3 +-
 71 files changed, 5882 insertions(+), 1 deletion(-)

diff --git a/NOTICE b/NOTICE
index 1102436..52de65b 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 ba657cc..ac64c97 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 73e7fdb..bd0ec23 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 e28dd40..c45e2fa 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 0000000..001fd7c
--- /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 0000000..bded0ca
--- /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 0000000..904d437
--- /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 0000000..4a8b116
--- /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 0000000..ef963d3
--- /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 0000000..629f35e
--- /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 0000000..16c5c85
--- /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 0000000..cb6ef66
--- /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 0000000..1072e2d
--- /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 0000000..5e6a444
--- /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 0000000..219006e
--- /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 0000000..7196af5
--- /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 0000000..34a203c
--- /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 0000000..786ead5
--- /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 0000000..46320c7
--- /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 0000000..382346d
--- /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 0000000..94d355a
--- /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 0000000..ba6bcf1
--- /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 0000000..076401b
--- /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 0000000..8d85347
--- /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 0000000..3ddb7c4
--- /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 0000000..ddaef38
--- /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 0000000..f7fafbe
--- /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 0000000..e7c5e4b
--- /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 0000000..afe22ef
--- /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 0000000..88706d6
--- /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 0000000..5636220
--- /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 0000000..4a72847
--- /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 0000000..0b190fa
--- /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 0000000..951fbec
--- /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 0000000..28a989d
--- /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 0000000..b54fad6
--- /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 0000000..4e4f0fe
--- /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 0000000..fb09624
--- /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 0000000..094cf22
--- /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 0000000..9996f8e
--- /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 0000000..e3edcec
--- /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 0000000..2228697
--- /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 0000000..8688b99
--- /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 0000000..fdc5e5e
--- /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 0000000..5f457d8
--- /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 0000000..e8d367e
--- /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 0000000..55c7db4
--- /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 0000000..22f57b7
--- /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 0000000..9d7f7f8
--- /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 0000000..3b9a3ac
--- /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 0000000..4111842
--- /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 0000000..3c40dda
--- /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 0000000..fd710c5
--- /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 0000000..10e9cc3
--- /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 0000000..5eda097
--- /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 0000000..1233444
--- /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 0000000..d69c020
--- /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 0000000..068f96d
--- /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 0000000..87d9390
--- /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 0000000..8776092
--- /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 0000000..7822e75
--- /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 0000000..a42de85
--- /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 0000000..b3ac059
--- /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 0000000..c2803c9
--- /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 0000000..0f947c9
--- /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 0000000..0468da8
--- /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 0000000..8cad8b1
--- /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 0000000..184b546
--- /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 0000000..a4a607f
--- /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 0000000..f1d4928
--- /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 b3f6b75..d284723 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>

-- 
To stop receiving notification emails like this one, please contact
['"commits@bookkeeper.apache.org" <co...@bookkeeper.apache.org>'].