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}.
+ *
+ * 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.
+ *
+ * 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/pulsar-checksum/src/main/java/com/scurrilous/circe/HashProviders.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/HashProviders.java
new file mode 100644
index 0000000000000000000000000000000000000000..7196af5c4ee25daafce044ec8981693c2b4b65a6
--- /dev/null
+++ b/pulsar-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 ALL_PROVIDERS = getAllProviders();
+
+ private static Collection getAllProviders() {
+ final ServiceLoader loader = ServiceLoader.load(HashProvider.class);
+ final LinkedList 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 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 required) {
+ HashProvider result = null;
+ EnumSet resultSupport = null;
+ for (final HashProvider provider : ALL_PROVIDERS) {
+ final EnumSet 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, 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, HashProvider> search(HashParameters params,
+ EnumSet required) {
+ final SortedMap, HashProvider> result = new TreeMap<>(
+ new HashSupport.SetComparator());
+ for (final HashProvider provider : ALL_PROVIDERS) {
+ final EnumSet support = provider.querySupport(params);
+ if (support.containsAll(required))
+ result.put(support, provider);
+ }
+ return result;
+ }
+}
diff --git a/pulsar-checksum/src/main/java/com/scurrilous/circe/HashSupport.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/HashSupport.java
new file mode 100644
index 0000000000000000000000000000000000000000..34a203c39832f006b07b855964134b0199120928
--- /dev/null
+++ b/pulsar-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 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 set1, EnumSet set2) {
+ // assumes iterators return flags in priority order
+ final Iterator i1 = set1.iterator();
+ final Iterator 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> {
+ @Override
+ public int compare(EnumSet o1, EnumSet o2) {
+ return HashSupport.compare(o1, o2);
+ }
+ }
+}
diff --git a/pulsar-checksum/src/main/java/com/scurrilous/circe/Hashes.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/Hashes.java
new file mode 100644
index 0000000000000000000000000000000000000000..786ead5f3d3dc0a8bca10567a64ae1e001b08b94
--- /dev/null
+++ b/pulsar-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}.
+ *
+ * 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.
+ *
+ * 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/pulsar-checksum/src/main/java/com/scurrilous/circe/IncrementalIntHash.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/IncrementalIntHash.java
new file mode 100644
index 0000000000000000000000000000000000000000..46320c77b82bf249809023e18ba20b10c86f200e
--- /dev/null
+++ b/pulsar-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 M1
+ * M2:
+ *
+ *
+ * h(M) = h'(h(M1), M2)
+ *
+ *
+ * where h corresponds to the {@link StatelessIntHash#calculate calculate}
+ * function and h' corresponds to the {@link #resume} function.
+ *
+ * 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/pulsar-checksum/src/main/java/com/scurrilous/circe/IncrementalLongHash.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/IncrementalLongHash.java
new file mode 100644
index 0000000000000000000000000000000000000000..382346df23ebf9e230371cb614679e615963beea
--- /dev/null
+++ b/pulsar-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
+ * M1 M2:
+ *
+ *
+ * h(M) = h'(h(M1), M2)
+ *
+ *
+ * where h corresponds to the {@link StatelessLongHash#calculate calculate}
+ * function and h' corresponds to the {@link #resume} function.
+ *
+ * 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/pulsar-checksum/src/main/java/com/scurrilous/circe/StatefulHash.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/StatefulHash.java
new file mode 100644
index 0000000000000000000000000000000000000000..94d355a119a4209b5d31f1d43db8908b579887be
--- /dev/null
+++ b/pulsar-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.
+ *
+ * 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 require 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/pulsar-checksum/src/main/java/com/scurrilous/circe/StatefulIntHash.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/StatefulIntHash.java
new file mode 100644
index 0000000000000000000000000000000000000000..ba6bcf17d609ffa6a1d0f556411a96154dcc1a55
--- /dev/null
+++ b/pulsar-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/pulsar-checksum/src/main/java/com/scurrilous/circe/StatefulLongHash.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/StatefulLongHash.java
new file mode 100644
index 0000000000000000000000000000000000000000..076401b5a268bdc83c16a60b26173f39ce21bcae
--- /dev/null
+++ b/pulsar-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/pulsar-checksum/src/main/java/com/scurrilous/circe/StatelessHash.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/StatelessHash.java
new file mode 100644
index 0000000000000000000000000000000000000000..8d8534702d1b557af38b1a3aba785ede233a4430
--- /dev/null
+++ b/pulsar-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/pulsar-checksum/src/main/java/com/scurrilous/circe/StatelessIntHash.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/StatelessIntHash.java
new file mode 100644
index 0000000000000000000000000000000000000000..3ddb7c47a0e7e7b817be7c21eafb3df3667e7284
--- /dev/null
+++ b/pulsar-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/pulsar-checksum/src/main/java/com/scurrilous/circe/StatelessLongHash.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/StatelessLongHash.java
new file mode 100644
index 0000000000000000000000000000000000000000..ddaef380c8e1131923b25f7dc0b390836ee3abd8
--- /dev/null
+++ b/pulsar-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/pulsar-checksum/src/main/java/com/scurrilous/circe/crc/AbstractIntCrc.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/crc/AbstractIntCrc.java
new file mode 100644
index 0000000000000000000000000000000000000000..f7fafbeee1f166ad086b1c4ab4de2ba8716f4c13
--- /dev/null
+++ b/pulsar-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/pulsar-checksum/src/main/java/com/scurrilous/circe/crc/AbstractLongCrc.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/crc/AbstractLongCrc.java
new file mode 100644
index 0000000000000000000000000000000000000000..e7c5e4bb250dfc66d0659865064a21470a090200
--- /dev/null
+++ b/pulsar-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/pulsar-checksum/src/main/java/com/scurrilous/circe/crc/JavaCrc32.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/crc/JavaCrc32.java
new file mode 100644
index 0000000000000000000000000000000000000000..afe22efc7bc1296f56f7640222adaa9c56a6b9ab
--- /dev/null
+++ b/pulsar-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/pulsar-checksum/src/main/java/com/scurrilous/circe/crc/NormalByteCrc.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/crc/NormalByteCrc.java
new file mode 100644
index 0000000000000000000000000000000000000000..88706d6dfb4633d80d79eff03a9e56b9525697c7
--- /dev/null
+++ b/pulsar-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/pulsar-checksum/src/main/java/com/scurrilous/circe/crc/NormalIntCrc.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/crc/NormalIntCrc.java
new file mode 100644
index 0000000000000000000000000000000000000000..56362207a03e49ba87096b5fc139331deca9fdad
--- /dev/null
+++ b/pulsar-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/pulsar-checksum/src/main/java/com/scurrilous/circe/crc/NormalLongCrc.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/crc/NormalLongCrc.java
new file mode 100644
index 0000000000000000000000000000000000000000..4a7284753260391e50966088877e7abfa02c984e
--- /dev/null
+++ b/pulsar-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/pulsar-checksum/src/main/java/com/scurrilous/circe/crc/ReflectedIntCrc.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/crc/ReflectedIntCrc.java
new file mode 100644
index 0000000000000000000000000000000000000000..aac5adf59d83254324612073423d73c8d9974519
--- /dev/null
+++ b/pulsar-checksum/src/main/java/com/scurrilous/circe/crc/ReflectedIntCrc.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 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 resumeRaw(int crc, byte[] input, int index, int length) {
+ crc = reflect(crc);
+ for (int i = 0; i < length; ++i)
+ crc = table[(crc ^ input[index + i]) & 0xff] ^ (crc >>> 8);
+ return crc;
+ }
+}
diff --git a/pulsar-checksum/src/main/java/com/scurrilous/circe/crc/ReflectedLongCrc.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/crc/ReflectedLongCrc.java
new file mode 100644
index 0000000000000000000000000000000000000000..951fbec6fba859fbbc313044efb53f970a9578cc
--- /dev/null
+++ b/pulsar-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/pulsar-checksum/src/main/java/com/scurrilous/circe/crc/StandardCrcProvider.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/crc/StandardCrcProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..b54fad6426ed1a7f9dbabfb35e03edabc58e2d25
--- /dev/null
+++ b/pulsar-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 {
+
+ /**
+ * Constructs a new {@link StandardCrcProvider}.
+ */
+ public StandardCrcProvider() {
+ super(CrcParameters.class);
+ }
+
+ @Override
+ protected EnumSet querySupportTyped(CrcParameters params) {
+ final EnumSet 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 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 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/pulsar-checksum/src/main/java/com/scurrilous/circe/crc/package-info.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/crc/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..c475546fce77dc181e8e16e0c64929d15fc3aba0
--- /dev/null
+++ b/pulsar-checksum/src/main/java/com/scurrilous/circe/crc/package-info.java
@@ -0,0 +1,9 @@
+/**
+ * Provides various implementations of cyclic redundancy
+ * check (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/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/AbstractHashProvider.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/AbstractHashProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..fb09624abf719d96f7faf6817885d3970eeb3fb4
--- /dev/null
+++ b/pulsar-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 base supported hash parameters type
+ */
+public abstract class AbstractHashProvider
implements HashProvider {
+
+ private final Class
parametersClass;
+
+ /**
+ * Constructs a new {@link AbstractHashProvider} with the given base
+ * parameters class.
+ *
+ * @param parametersClass the base hash parameters class supported
+ */
+ protected AbstractHashProvider(Class
parametersClass) {
+ this.parametersClass = parametersClass;
+ }
+
+ @Override
+ public final EnumSet 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 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}.
+ *
+ * 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 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 required) {
+ if (HashCacheLoader.hasCache()) {
+ final HashCache cache = HashCacheLoader.getCache();
+ try {
+ return cache.get(params, required, new Callable() {
+ @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 required) {
+ throw new UnsupportedOperationException();
+ }
+
+ private Hash castAndGet(HashParameters params, EnumSet 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/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/AbstractIncrementalIntHash.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/AbstractIncrementalIntHash.java
new file mode 100644
index 0000000000000000000000000000000000000000..094cf22889f6c80c079219344dd180cc07fa6e22
--- /dev/null
+++ b/pulsar-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/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/AbstractIncrementalLongHash.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/AbstractIncrementalLongHash.java
new file mode 100644
index 0000000000000000000000000000000000000000..9996f8eb4290889100e3839f38fec2063f73b298
--- /dev/null
+++ b/pulsar-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/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/AbstractStatefulHash.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/AbstractStatefulHash.java
new file mode 100644
index 0000000000000000000000000000000000000000..e3edcec6ecbf83eb151a6d086dbc3405afb8565b
--- /dev/null
+++ b/pulsar-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/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/AbstractStatelessIntHash.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/AbstractStatelessIntHash.java
new file mode 100644
index 0000000000000000000000000000000000000000..2228697445c4d6be136a0f0bb637f8cec7d439bc
--- /dev/null
+++ b/pulsar-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/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/AbstractStatelessLongHash.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/AbstractStatelessLongHash.java
new file mode 100644
index 0000000000000000000000000000000000000000..8688b99849eeb5686b8d326097d9170e99dfbf4f
--- /dev/null
+++ b/pulsar-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/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/DirectByteBufferAccess.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/DirectByteBufferAccess.java
new file mode 100644
index 0000000000000000000000000000000000000000..f3d6c1544699ead90339756ba3e9960cf8b47dff
--- /dev/null
+++ b/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/DirectByteBufferAccess.java
@@ -0,0 +1,18 @@
+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/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/DirectByteBufferAccessLoader.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/DirectByteBufferAccessLoader.java
new file mode 100644
index 0000000000000000000000000000000000000000..5f457d82d190a99b1246c11231077cfc1629dcd8
--- /dev/null
+++ b/pulsar-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 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/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/HashCache.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/HashCache.java
new file mode 100644
index 0000000000000000000000000000000000000000..e8d367e56b79097cc79104865584b309ed1a8de6
--- /dev/null
+++ b/pulsar-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 required, Callable loader)
+ throws ExecutionException;
+}
diff --git a/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/HashCacheLoader.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/HashCacheLoader.java
new file mode 100644
index 0000000000000000000000000000000000000000..55c7db43c74f5560eaa27015827ca64a5c83359d
--- /dev/null
+++ b/pulsar-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 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/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/IncrementalIntStatefulHash.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/IncrementalIntStatefulHash.java
new file mode 100644
index 0000000000000000000000000000000000000000..22f57b779e08fc7ac5d1f7b27e7afac55645a2fa
--- /dev/null
+++ b/pulsar-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/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/IncrementalLongStatefulHash.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/IncrementalLongStatefulHash.java
new file mode 100644
index 0000000000000000000000000000000000000000..9d7f7f89c44767bf64ac842e75ed4abc654b0f9f
--- /dev/null
+++ b/pulsar-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/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/IntStatefulLongHash.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/IntStatefulLongHash.java
new file mode 100644
index 0000000000000000000000000000000000000000..3b9a3acd5af17017c017049009a42f085deb4b03
--- /dev/null
+++ b/pulsar-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/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/IntStatelessLongHash.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/IntStatelessLongHash.java
new file mode 100644
index 0000000000000000000000000000000000000000..4111842bfa4820db4e190c298eca38387da254e8
--- /dev/null
+++ b/pulsar-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/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/package-info.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..558a867c0defd70b2bd2a68e8194cf97a428fdb2
--- /dev/null
+++ b/pulsar-checksum/src/main/java/com/scurrilous/circe/impl/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * 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/pulsar-checksum/src/main/java/com/scurrilous/circe/package-info.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..c2956bd72d23fca760e3533405576376806fe8a6
--- /dev/null
+++ b/pulsar-checksum/src/main/java/com/scurrilous/circe/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * 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/pulsar-checksum/src/main/java/com/scurrilous/circe/params/CrcParameters.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/params/CrcParameters.java
new file mode 100644
index 0000000000000000000000000000000000000000..10e9cc316662f87bedb5bf3cdb0b377dab5cd842
--- /dev/null
+++ b/pulsar-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 cyclic redundancy
+ * check (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
+ * x16 + x15 + x2 + 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/pulsar-checksum/src/main/java/com/scurrilous/circe/params/MurmurHash3Parameters.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/params/MurmurHash3Parameters.java
new file mode 100644
index 0000000000000000000000000000000000000000..5eda097e59ebea6caac578ac410e2da351b77b68
--- /dev/null
+++ b/pulsar-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 MurmurHash3.
+ */
+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/pulsar-checksum/src/main/java/com/scurrilous/circe/params/MurmurHash3Variant.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/params/MurmurHash3Variant.java
new file mode 100644
index 0000000000000000000000000000000000000000..12334448f816e7c0e1c89e8757d3a5b33fd02d0a
--- /dev/null
+++ b/pulsar-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/pulsar-checksum/src/main/java/com/scurrilous/circe/params/SimpleHashParameters.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/params/SimpleHashParameters.java
new file mode 100644
index 0000000000000000000000000000000000000000..d69c020d1730ce2d37c19e4b0275a6bcf280af50
--- /dev/null
+++ b/pulsar-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/pulsar-checksum/src/main/java/com/scurrilous/circe/params/SipHash24Parameters.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/params/SipHash24Parameters.java
new file mode 100644
index 0000000000000000000000000000000000000000..068f96df452cfbf5fdc5d70881993c8089436fb4
--- /dev/null
+++ b/pulsar-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 SipHash-2-4.
+ */
+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/pulsar-checksum/src/main/java/com/scurrilous/circe/params/package-info.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/params/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..80bd4e69fe12217b0c4e3aee6451ef6a2182483e
--- /dev/null
+++ b/pulsar-checksum/src/main/java/com/scurrilous/circe/params/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * Defines {@linkplain com.scurrilous.circe.HashParameters hash parameters}
+ * classes that describe various commonly-used hash functions.
+ */
+package com.scurrilous.circe.params;
diff --git a/pulsar-checksum/src/main/resources/META-INF/services/com.scurrilous.circe.HashProvider b/pulsar-checksum/src/main/resources/META-INF/services/com.scurrilous.circe.HashProvider
new file mode 100644
index 0000000000000000000000000000000000000000..7822e750f0b6a832d9436f0fe83b1bfa5cc39c42
--- /dev/null
+++ b/pulsar-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/pulsar-checksum/src/test/java/com/scurrilous/circe/CommonHashesTest.java b/pulsar-checksum/src/test/java/com/scurrilous/circe/CommonHashesTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a42de857c28c33c1fa88036900e8817446896767
--- /dev/null
+++ b/pulsar-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/pulsar-checksum/src/test/java/com/scurrilous/circe/crc/CRCProvidersTest.java b/pulsar-checksum/src/test/java/com/scurrilous/circe/crc/CRCProvidersTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b3ac059c6387f5043605e7c46cbde3e249894c75
--- /dev/null
+++ b/pulsar-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 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, HashProvider> map = HashProviders.search(CRC32);
+ assertEquals(1, map.size());
+ final Entry, 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, HashProvider> map = HashProviders.search(CRC64);
+ assertEquals(1, map.size());
+ final Entry, 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/pulsar-checksum/src/test/java/com/scurrilous/circe/crc/CRCTest.java b/pulsar-checksum/src/test/java/com/scurrilous/circe/crc/CRCTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..65ebe1a5a941976afd7f8a0bcb00ed0812f5330c
--- /dev/null
+++ b/pulsar-checksum/src/test/java/com/scurrilous/circe/crc/CRCTest.java
@@ -0,0 +1,163 @@
+/*******************************************************************************
+ * 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.params.CrcParameters;
+
+/**
+ * Tests the {@link StandardCrcProvider} with various CRC algorithms. See the Catalogue of parametrised
+ * CRC algorithms 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));
+ }
+}
diff --git a/pulsar-checksum/src/test/java/com/scurrilous/circe/impl/AbstractIncrementalIntHashTest.java b/pulsar-checksum/src/test/java/com/scurrilous/circe/impl/AbstractIncrementalIntHashTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..87596e82d324aaafa060fa9817ba09fd2eea3054
--- /dev/null
+++ b/pulsar-checksum/src/test/java/com/scurrilous/circe/impl/AbstractIncrementalIntHashTest.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.junit.Assert.*;
+
+import java.nio.ByteBuffer;
+
+import mockit.Expectations;
+import mockit.Mocked;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+import com.scurrilous.circe.StatefulHash;
+import com.scurrilous.circe.impl.AbstractIncrementalIntHash;
+
+@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/pulsar-checksum/src/test/java/com/scurrilous/circe/impl/AbstractIncrementalLongHashTest.java b/pulsar-checksum/src/test/java/com/scurrilous/circe/impl/AbstractIncrementalLongHashTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0468da8b0ed124459840111325606eda8bfafc6f
--- /dev/null
+++ b/pulsar-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/pulsar-checksum/src/test/java/com/scurrilous/circe/impl/AbstractStatefulHashTest.java b/pulsar-checksum/src/test/java/com/scurrilous/circe/impl/AbstractStatefulHashTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..8cad8b1b15327fb94f4014bcdc962791b2bb90ae
--- /dev/null
+++ b/pulsar-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 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/pulsar-checksum/src/test/java/com/scurrilous/circe/impl/AbstractStatelessIntHashTest.java b/pulsar-checksum/src/test/java/com/scurrilous/circe/impl/AbstractStatelessIntHashTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..184b54640500759b9f4f437a521072cebad7a884
--- /dev/null
+++ b/pulsar-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/pulsar-checksum/src/test/java/com/scurrilous/circe/impl/AbstractStatelessLongHashTest.java b/pulsar-checksum/src/test/java/com/scurrilous/circe/impl/AbstractStatelessLongHashTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a4a607f3c624e0e662007bb534c6a6bb847837e0
--- /dev/null
+++ b/pulsar-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());
+ }
+}