--- /dev/null
+package net.jpountz.xxhash;
+
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Field;
+import java.util.Random;
+
+import net.jpountz.util.Native;
+import net.jpountz.util.Utils;
+
+/**
+ * Entry point to get {@link XXHash32} and {@link StreamingXXHash32} instances.
+ * <p>
+ * This class has 3 instances<ul>
+ * <li>a {@link #nativeInstance() native} instance which is a JNI binding to
+ * <a href="http://code.google.com/p/xxhash/">the original LZ4 C implementation</a>.
+ * <li>a {@link #safeInstance() safe Java} instance which is a pure Java port
+ * of the original C library,</li>
+ * <li>an {@link #unsafeInstance() unsafe Java} instance which is a Java port
+ * using the unofficial {@link sun.misc.Unsafe} API.
+ * </ul>
+ * <p>
+ * Only the {@link #safeInstance() safe instance} is guaranteed to work on your
+ * JVM, as a consequence it is advised to use the {@link #fastestInstance()} or
+ * {@link #fastestJavaInstance()} to pull a {@link XXHashFactory} instance.
+ * <p>
+ * All methods from this class are very costly, so you should get an instance
+ * once, and then reuse it whenever possible. This is typically done by storing
+ * a {@link XXHashFactory} instance in a static field.
+ */
+public final class XXHashFactory {
+
+ private static XXHashFactory instance(String impl) {
+ try {
+ return new XXHashFactory(impl);
+ } catch (Exception e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private static XXHashFactory NATIVE_INSTANCE,
+ JAVA_UNSAFE_INSTANCE,
+ JAVA_SAFE_INSTANCE;
+
+ /** Return a {@link XXHashFactory} that returns {@link XXHash32} instances that
+ * are native bindings to the original C API.
+ * <p>
+ * Please note that this instance has some traps you should be aware of:<ol>
+ * <li>Upon loading this instance, files will be written to the temporary
+ * directory of the system. Although these files are supposed to be deleted
+ * when the JVM exits, they might remain on systems that don't support
+ * removal of files being used such as Windows.
+ * <li>The instance can only be loaded once per JVM. This can be a problem
+ * if your application uses multiple class loaders (such as most servlet
+ * containers): this instance will only be available to the children of the
+ * class loader which has loaded it. As a consequence, it is advised to
+ * either not use this instance in webapps or to put this library in the lib
+ * directory of your servlet container so that it is loaded by the system
+ * class loader.
+ * </ol>
+ */
+ public static synchronized XXHashFactory nativeInstance() {
+ if (NATIVE_INSTANCE == null) {
+ NATIVE_INSTANCE = instance("JNI");
+ }
+ return NATIVE_INSTANCE;
+ }
+
+ /** Return a {@link XXHashFactory} that returns {@link XXHash32} instances that
+ * are written with Java's official API. */
+ public static synchronized XXHashFactory safeInstance() {
+ if (JAVA_SAFE_INSTANCE == null) {
+ JAVA_SAFE_INSTANCE = instance("JavaSafe");
+ }
+ return JAVA_SAFE_INSTANCE;
+ }
+
+ /** Return a {@link XXHashFactory} that returns {@link XXHash32} instances that
+ * may use {@link sun.misc.Unsafe} to speed up hashing. */
+ public static synchronized XXHashFactory unsafeInstance() {
+ if (JAVA_UNSAFE_INSTANCE == null) {
+ JAVA_UNSAFE_INSTANCE = instance("JavaUnsafe");
+ }
+ return JAVA_UNSAFE_INSTANCE;
+ }
+
+ /**
+ * Return the fastest available {@link XXHashFactory} instance which does not
+ * rely on JNI bindings. It first tries to load the
+ * {@link #unsafeInstance() unsafe instance}, and then the
+ * {@link #safeInstance() safe Java instance} if the JVM doesn't have a
+ * working {@link sun.misc.Unsafe}.
+ */
+ public static XXHashFactory fastestJavaInstance() {
+ if (Utils.isUnalignedAccessAllowed()) {
+ try {
+ return unsafeInstance();
+ } catch (Throwable t) {
+ return safeInstance();
+ }
+ } else {
+ return safeInstance();
+ }
+ }
+
+ /**
+ * Return the fastest available {@link XXHashFactory} instance. If the class
+ * loader is the system class loader and if the
+ * {@link #nativeInstance() native instance} loads successfully, then the
+ * {@link #nativeInstance() native instance} is returned, otherwise the
+ * {@link #fastestJavaInstance() fastest Java instance} is returned.
+ * <p>
+ * Please read {@link #nativeInstance() javadocs of nativeInstance()} before
+ * using this method.
+ */
+ public static XXHashFactory fastestInstance() {
+ if (Native.isLoaded()
+ || Native.class.getClassLoader() == ClassLoader.getSystemClassLoader()) {
+ try {
+ return nativeInstance();
+ } catch (Throwable t) {
+ return fastestJavaInstance();
+ }
+ } else {
+ return fastestJavaInstance();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T> T classInstance(String cls) throws NoSuchFieldException, SecurityException, ClassNotFoundException, IllegalArgumentException, IllegalAccessException {
+ ClassLoader loader = XXHashFactory.class.getClassLoader();
+ loader = loader == null ? ClassLoader.getSystemClassLoader() : loader;
+ final Class<?> c = loader.loadClass(cls);
+ Field f = c.getField("INSTANCE");
+ return (T) f.get(null);
+ }
+
+ private final String impl;
+ private final XXHash32 hash32;
+ private final XXHash64 hash64;
+ private final StreamingXXHash32.Factory streamingHash32Factory;
+ private final StreamingXXHash64.Factory streamingHash64Factory;
+
+ private XXHashFactory(String impl) throws ClassNotFoundException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+ this.impl = impl;
+ hash32 = classInstance("net.jpountz.xxhash.XXHash32" + impl);
+ streamingHash32Factory = classInstance("net.jpountz.xxhash.StreamingXXHash32" + impl + "$Factory");
+ hash64 = classInstance("net.jpountz.xxhash.XXHash64" + impl);
+ streamingHash64Factory = classInstance("net.jpountz.xxhash.StreamingXXHash64" + impl + "$Factory");
+
+ // make sure it can run
+ final byte[] bytes = new byte[100];
+ final Random random = new Random();
+ random.nextBytes(bytes);
+ final int seed = random.nextInt();
+
+ final int h1 = hash32.hash(bytes, 0, bytes.length, seed);
+ final StreamingXXHash32 streamingHash32 = newStreamingHash32(seed);
+ streamingHash32.update(bytes, 0, bytes.length);
+ final int h2 = streamingHash32.getValue();
+ final long h3 = hash64.hash(bytes, 0, bytes.length, seed);
+ final StreamingXXHash64 streamingHash64 = newStreamingHash64(seed);
+ streamingHash64.update(bytes, 0, bytes.length);
+ final long h4 = streamingHash64.getValue();
+ if (h1 != h2) {
+ throw new AssertionError();
+ }
+ if (h3 != h4) {
+ throw new AssertionError();
+ }
+ }
+
+ /** Return a {@link XXHash32} instance. */
+ public XXHash32 hash32() {
+ return hash32;
+ }
+
+ /** Return a {@link XXHash64} instance. */
+ public XXHash64 hash64() {
+ return hash64;
+ }
+
+ /**
+ * Return a new {@link StreamingXXHash32} instance.
+ */
+ public StreamingXXHash32 newStreamingHash32(int seed) {
+ return streamingHash32Factory.newStreamingHash(seed);
+ }
+
+ /**
+ * Return a new {@link StreamingXXHash64} instance.
+ */
+ public StreamingXXHash64 newStreamingHash64(long seed) {
+ return streamingHash64Factory.newStreamingHash(seed);
+ }
+
+ /** Prints the fastest instance. */
+ public static void main(String[] args) {
+ System.out.println("Fastest instance is " + fastestInstance());
+ System.out.println("Fastest Java instance is " + fastestJavaInstance());
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + ":" + impl;
+ }
+
+}