diff --git a/NOTICE b/NOTICE index 21bb0cebfae2eada629bed3969c0878c947af364..7425b031ac8ed7d220c7668e1635c9776a126aed 100644 --- a/NOTICE +++ b/NOTICE @@ -23,6 +23,14 @@ This product contains a modified portion of and depends on 'Protocol Buffers', Google's data interchange format, which can be obtained at: * LICENSE: - * license/LICENSE.protobuf.txt (New BSD License) + * protobuf/LICENSE.protobuf.txt * HOMEPAGE: * http://code.google.com/p/protobuf/ + +This product contains a modified portion of and depends on 'circe', +Hash Algorithm Framework & Library, which can be obtained at: + + * LICENSE : Apache License, Version 2.0 + * HOMEPAGE: + * https://github.com/trevorr/circe + \ No newline at end of file diff --git a/pom.xml b/pom.xml index b1546838c5077e986d2e28a95f69853a04314b2d..17d42fae2c4aaab1d2ab2f4dfa6feab3feeff7a7 100644 --- a/pom.xml +++ b/pom.xml @@ -132,6 +132,12 @@ flexible messaging model and an intuitive client API. powermock-module-testng 1.6.5 + + + com.googlecode.jmockit + jmockit + 1.7 + org.apache.zookeeper diff --git a/protobuf/LICENSE.protobuf.txt b/protobuf/LICENSE.protobuf.txt new file mode 100644 index 0000000000000000000000000000000000000000..327bafee7074f5330e0c61dee829f9902c5931f8 --- /dev/null +++ b/protobuf/LICENSE.protobuf.txt @@ -0,0 +1,42 @@ +This license applies to all parts of Protocol Buffers except the following: + + - Atomicops support for generic gcc, located in + src/google/protobuf/stubs/atomicops_internals_generic_gcc.h. + This file is copyrighted by Red Hat Inc. + + - Atomicops support for AIX/POWER, located in + src/google/protobuf/stubs/atomicops_internals_power.h. + This file is copyrighted by Bloomberg Finance LP. + +Copyright 2014, Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Code generated by the Protocol Buffer compiler is owned by the owner +of the input file used when generating it. This code is not +standalone and requires a support library to be linked with it. This +support library is itself covered by the above license. \ No newline at end of file diff --git a/pulsar-checksum/pom.xml b/pulsar-checksum/pom.xml index a75a94be56c520641056b4a867b3b42469cac85a..e17aa50808796815eb51f5a17b2ba025a15f84f7 100644 --- a/pulsar-checksum/pom.xml +++ b/pulsar-checksum/pom.xml @@ -53,29 +53,11 @@ com.google.guava guava - - - com.scurrilous - circe-api - 1.1-20140603.044855-1 - - + - com.scurrilous - circe-impl - 1.1-20140603.044907-1 - - - - com.scurrilous - circe-common - 1.1-20140603.044903-1 - - - - com.scurrilous - circe-crc - 1.1-20140603.044857-1 + com.googlecode.jmockit + jmockit + test @@ -183,11 +165,4 @@ - - - circe-crc-repo - https://oss.sonatype.org/content/repositories/staging - - - diff --git a/pulsar-checksum/src/main/java/com/scurrilous/circe/CommonHashes.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/CommonHashes.java new file mode 100644 index 0000000000000000000000000000000000000000..cb6ef6612078a069f52c57c414f278268c5d6c41 --- /dev/null +++ b/pulsar-checksum/src/main/java/com/scurrilous/circe/CommonHashes.java @@ -0,0 +1,188 @@ +/******************************************************************************* + * Copyright 2014 Trevor Robinson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +package com.scurrilous.circe; + +import com.scurrilous.circe.params.CrcParameters; +import com.scurrilous.circe.params.MurmurHash3Parameters; +import com.scurrilous.circe.params.MurmurHash3Variant; +import com.scurrilous.circe.params.SimpleHashParameters; +import com.scurrilous.circe.params.SipHash24Parameters; + +/** + * Static methods to obtain commonly-used hash functions. Note that a suitable + * provider JAR must be made available on the class path. This class does not + * have direct access to specific implementations; it simply constructs the hash + * parameters and uses the {@link Hashes} class to search for a provider. + */ +public final class CommonHashes { + + private CommonHashes() { + } + + /** + * Returns an incremental stateless hash function implementing the + * {@linkplain CrcParameters#CRC32 CRC-32} checksum algorithm. + * + * @return a CRC-32 stateless incremental integer hash + * @throws UnsupportedOperationException if no provider is available + */ + public static IncrementalIntHash crc32() { + return Hashes.getIncrementalInt(CrcParameters.CRC32); + } + + /** + * Returns an incremental stateless hash function implementing the + * {@linkplain CrcParameters#CRC32C CRC-32C} checksum algorithm. + * + * @return a CRC-32C stateless incremental integer hash + * @throws UnsupportedOperationException if no provider is available + */ + public static IncrementalIntHash crc32c() { + return Hashes.getIncrementalInt(CrcParameters.CRC32C); + } + + /** + * Returns an incremental stateless hash function implementing the + * {@linkplain CrcParameters#CRC64 CRC-64} checksum algorithm. + * + * @return a CRC-64 stateless incremental long integer hash + * @throws UnsupportedOperationException if no provider is available + */ + public static IncrementalLongHash crc64() { + return Hashes.getIncrementalLong(CrcParameters.CRC64); + } + + /** + * Returns a hash function implementing the MurmurHash3 algorithm, 32-bit + * x86 variant, with a seed of 0. + * + * @return a MurmurHash3 32-bit/x86 stateless integer hash + * @throws UnsupportedOperationException if no provider is available + */ + public static StatelessIntHash murmur3_32() { + return Hashes.getStatelessInt(new MurmurHash3Parameters(MurmurHash3Variant.X86_32)); + } + + /** + * Returns a hash function implementing the MurmurHash3 algorithm, 32-bit + * x86 variant, with the given seed value + * + * @param seed the 32-bit seed value + * @return a MurmurHash3 32-bit/x86 stateless integer hash + * @throws UnsupportedOperationException if no provider is available + */ + public static StatelessIntHash murmur3_32(int seed) { + return Hashes.getStatelessInt(new MurmurHash3Parameters(MurmurHash3Variant.X86_32, seed)); + } + + /** + * Returns a hash function implementing the MurmurHash3 algorithm, 128-bit + * x64 variant, with a seed of 0. + * + * @return a MurmurHash3 128-bit/x64 stateful hash + * @throws UnsupportedOperationException if no provider is available + */ + public static StatefulHash murmur3_128() { + return Hashes.createStateful(new MurmurHash3Parameters(MurmurHash3Variant.X64_128)); + } + + /** + * Returns a hash function implementing the MurmurHash3 algorithm, 128-bit + * x64 variant, with the given seed value. + * + * @param seed the 32-bit seed value + * @return a MurmurHash3 128-bit/x64 stateful hash + * @throws UnsupportedOperationException if no provider is available + */ + public static StatefulHash murmur3_128(int seed) { + return Hashes.createStateful(new MurmurHash3Parameters(MurmurHash3Variant.X64_128, seed)); + } + + /** + * Returns a hash function implementing the SipHash-2-4 algorithm (64 bits) + * with the default seed, + * {@code 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F}. + * + * @return a SipHash-2-4 stateless long integer hash + * @throws UnsupportedOperationException if no provider is available + */ + public static StatelessLongHash sipHash24() { + return Hashes.getStatelessLong(new SipHash24Parameters()); + } + + /** + * Returns a hash function implementing the SipHash-2-4 algorithm (64 bits) + * with the given seed value. + * + * @param seedLow the low-order 64 bits of the seed + * @param seedHigh the high-order 64 bits of the seed + * @return a SipHash-2-4 stateless long integer hash + * @throws UnsupportedOperationException if no provider is available + */ + public static StatelessLongHash sipHash24(long seedLow, long seedHigh) { + return Hashes.getStatelessLong(new SipHash24Parameters(seedLow, seedHigh)); + } + + /** + * Returns a hash function implementing the MD5 algorithm (128 bits). + * + * @return an MD5 stateful hash + * @throws UnsupportedOperationException if no provider is available + */ + public static StatefulHash md5() { + return Hashes.createStateful(SimpleHashParameters.MD5); + } + + /** + * Returns a hash function implementing the SHA-1 algorithm (160 bits). + * + * @return a SHA-1 stateful hash + * @throws UnsupportedOperationException if no provider is available + */ + public static StatefulHash sha1() { + return Hashes.createStateful(SimpleHashParameters.SHA1); + } + + /** + * Returns a hash function implementing the SHA-256 algorithm (256 bits). + * + * @return a SHA-256 stateful hash + * @throws UnsupportedOperationException if no provider is available + */ + public static StatefulHash sha256() { + return Hashes.createStateful(SimpleHashParameters.SHA256); + } + + /** + * Returns a hash function implementing the SHA-384 algorithm (384 bits). + * + * @return a SHA-384 stateful hash + * @throws UnsupportedOperationException if no provider is available + */ + public static StatefulHash sha384() { + return Hashes.createStateful(SimpleHashParameters.SHA384); + } + + /** + * Returns a hash function implementing the SHA-512 algorithm (512 bits). + * + * @return a SHA-512 stateful hash + * @throws UnsupportedOperationException if no provider is available + */ + public static StatefulHash sha512() { + return Hashes.createStateful(SimpleHashParameters.SHA512); + } +} diff --git a/pulsar-checksum/src/main/java/com/scurrilous/circe/Hash.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/Hash.java new file mode 100644 index 0000000000000000000000000000000000000000..1072e2d8151f10a9727c68faecaea6bfaec6f839 --- /dev/null +++ b/pulsar-checksum/src/main/java/com/scurrilous/circe/Hash.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright 2014 Trevor Robinson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +package com.scurrilous.circe; + +/** + * Abstract hash function. Each actual hash function is provided using a + * {@linkplain StatefulHash stateful} derived interface. Hash functions with an + * output length that fits within an {@code int} or a {@code long} are also + * generally provided using a {@linkplain StatelessHash stateless} derived + * interface. Given a stateless hash object, a method is provided for obtaining + * a new corresponding stateful object. + */ +public interface Hash { + + /** + * Returns the canonical name of this hash algorithm. + * + * @return the name of this hash algorithm + */ + String algorithm(); + + /** + * Returns the length in bytes of the output of this hash function. + * + * @return the hash length in bytes + */ + int length(); + + /** + * Returns whether this hash function supports unsafe access to arbitrary + * memory addresses using methods such as + * {@link StatefulHash#update(long, long)}, + * {@link StatelessIntHash#calculate(long, long)}, or + * {@link IncrementalIntHash#resume(int, long, long)}. Such functions are + * generally implemented in native code. + * + * @return true if unsafe access is supported, false if not + */ + boolean supportsUnsafe(); +} diff --git a/pulsar-checksum/src/main/java/com/scurrilous/circe/HashParameters.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/HashParameters.java new file mode 100644 index 0000000000000000000000000000000000000000..5e6a444f537a01c50b8fe18ddca40c279de1fa0a --- /dev/null +++ b/pulsar-checksum/src/main/java/com/scurrilous/circe/HashParameters.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright 2014 Trevor Robinson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +package com.scurrilous.circe; + +/** + * Base interface implemented by classes describing parameters for a particular + * family of hash algorithms. + */ +public interface HashParameters { + + /** + * Returns the canonical name of the hash algorithm. + * + * @return the name of the hash algorithm + */ + String algorithm(); +} diff --git a/pulsar-checksum/src/main/java/com/scurrilous/circe/HashProvider.java b/pulsar-checksum/src/main/java/com/scurrilous/circe/HashProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..219006e8cb5763a6e10868fb61cdf251fcaa6ce8 --- /dev/null +++ b/pulsar-checksum/src/main/java/com/scurrilous/circe/HashProvider.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright 2014 Trevor Robinson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +package com.scurrilous.circe; + +import java.util.EnumSet; + +/** + * Interface used to obtain instances of various kinds of hash algorithms. + */ +public interface HashProvider { + + /** + * Returns information about the available implementations corresponding to + * the given hash algorithm parameters. + * + * @param params the hash algorithm parameters + * @return a set of flags indicating the level of support + */ + EnumSet 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()); + } +}