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. *

* This class has 3 instances

*

* 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. *

* 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. *

* Please note that this instance has some traps you should be aware of:

    *
  1. 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. *
  2. 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. *
*/ 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. *

* 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 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; } }