1 package net.jpountz.xxhash;
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 import java.lang.reflect.Field;
18 import java.util.Random;
20 import net.jpountz.util.Native;
21 import net.jpountz.util.Utils;
24 * Entry point to get {@link XXHash32} and {@link StreamingXXHash32} instances.
26 * This class has 3 instances<ul>
27 * <li>a {@link #nativeInstance() native} instance which is a JNI binding to
28 * <a href="http://code.google.com/p/xxhash/">the original LZ4 C implementation</a>.
29 * <li>a {@link #safeInstance() safe Java} instance which is a pure Java port
30 * of the original C library,</li>
31 * <li>an {@link #unsafeInstance() unsafe Java} instance which is a Java port
32 * using the unofficial {@link sun.misc.Unsafe} API.
35 * Only the {@link #safeInstance() safe instance} is guaranteed to work on your
36 * JVM, as a consequence it is advised to use the {@link #fastestInstance()} or
37 * {@link #fastestJavaInstance()} to pull a {@link XXHashFactory} instance.
39 * All methods from this class are very costly, so you should get an instance
40 * once, and then reuse it whenever possible. This is typically done by storing
41 * a {@link XXHashFactory} instance in a static field.
43 public final class XXHashFactory {
45 private static XXHashFactory instance(String impl) {
47 return new XXHashFactory(impl);
48 } catch (Exception e) {
49 throw new AssertionError(e);
53 private static XXHashFactory NATIVE_INSTANCE,
57 /** Return a {@link XXHashFactory} that returns {@link XXHash32} instances that
58 * are native bindings to the original C API.
60 * Please note that this instance has some traps you should be aware of:<ol>
61 * <li>Upon loading this instance, files will be written to the temporary
62 * directory of the system. Although these files are supposed to be deleted
63 * when the JVM exits, they might remain on systems that don't support
64 * removal of files being used such as Windows.
65 * <li>The instance can only be loaded once per JVM. This can be a problem
66 * if your application uses multiple class loaders (such as most servlet
67 * containers): this instance will only be available to the children of the
68 * class loader which has loaded it. As a consequence, it is advised to
69 * either not use this instance in webapps or to put this library in the lib
70 * directory of your servlet container so that it is loaded by the system
74 public static synchronized XXHashFactory nativeInstance() {
75 if (NATIVE_INSTANCE == null) {
76 NATIVE_INSTANCE = instance("JNI");
78 return NATIVE_INSTANCE;
81 /** Return a {@link XXHashFactory} that returns {@link XXHash32} instances that
82 * are written with Java's official API. */
83 public static synchronized XXHashFactory safeInstance() {
84 if (JAVA_SAFE_INSTANCE == null) {
85 JAVA_SAFE_INSTANCE = instance("JavaSafe");
87 return JAVA_SAFE_INSTANCE;
90 /** Return a {@link XXHashFactory} that returns {@link XXHash32} instances that
91 * may use {@link sun.misc.Unsafe} to speed up hashing. */
92 public static synchronized XXHashFactory unsafeInstance() {
93 if (JAVA_UNSAFE_INSTANCE == null) {
94 JAVA_UNSAFE_INSTANCE = instance("JavaUnsafe");
96 return JAVA_UNSAFE_INSTANCE;
100 * Return the fastest available {@link XXHashFactory} instance which does not
101 * rely on JNI bindings. It first tries to load the
102 * {@link #unsafeInstance() unsafe instance}, and then the
103 * {@link #safeInstance() safe Java instance} if the JVM doesn't have a
104 * working {@link sun.misc.Unsafe}.
106 public static XXHashFactory fastestJavaInstance() {
107 if (Utils.isUnalignedAccessAllowed()) {
109 return unsafeInstance();
110 } catch (Throwable t) {
111 return safeInstance();
114 return safeInstance();
119 * Return the fastest available {@link XXHashFactory} instance. If the class
120 * loader is the system class loader and if the
121 * {@link #nativeInstance() native instance} loads successfully, then the
122 * {@link #nativeInstance() native instance} is returned, otherwise the
123 * {@link #fastestJavaInstance() fastest Java instance} is returned.
125 * Please read {@link #nativeInstance() javadocs of nativeInstance()} before
128 public static XXHashFactory fastestInstance() {
129 if (!Native.failedToLoad()
130 && (Native.isLoaded()
131 || Native.class.getClassLoader() == ClassLoader.getSystemClassLoader())) {
133 return nativeInstance();
134 } catch (Throwable t) {
135 return fastestJavaInstance();
138 return fastestJavaInstance();
142 @SuppressWarnings("unchecked")
143 private static <T> T classInstance(String cls) throws NoSuchFieldException, SecurityException, ClassNotFoundException, IllegalArgumentException, IllegalAccessException {
144 ClassLoader loader = XXHashFactory.class.getClassLoader();
145 loader = loader == null ? ClassLoader.getSystemClassLoader() : loader;
146 final Class<?> c = loader.loadClass(cls);
147 Field f = c.getField("INSTANCE");
148 return (T) f.get(null);
151 private final String impl;
152 private final XXHash32 hash32;
153 private final XXHash64 hash64;
154 private final StreamingXXHash32.Factory streamingHash32Factory;
155 private final StreamingXXHash64.Factory streamingHash64Factory;
157 private XXHashFactory(String impl) throws ClassNotFoundException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
159 hash32 = classInstance("net.jpountz.xxhash.XXHash32" + impl);
160 streamingHash32Factory = classInstance("net.jpountz.xxhash.StreamingXXHash32" + impl + "$Factory");
161 hash64 = classInstance("net.jpountz.xxhash.XXHash64" + impl);
162 streamingHash64Factory = classInstance("net.jpountz.xxhash.StreamingXXHash64" + impl + "$Factory");
164 // make sure it can run
165 final byte[] bytes = new byte[100];
166 final Random random = new Random();
167 random.nextBytes(bytes);
168 final int seed = random.nextInt();
170 final int h1 = hash32.hash(bytes, 0, bytes.length, seed);
171 final StreamingXXHash32 streamingHash32 = newStreamingHash32(seed);
172 streamingHash32.update(bytes, 0, bytes.length);
173 final int h2 = streamingHash32.getValue();
174 final long h3 = hash64.hash(bytes, 0, bytes.length, seed);
175 final StreamingXXHash64 streamingHash64 = newStreamingHash64(seed);
176 streamingHash64.update(bytes, 0, bytes.length);
177 final long h4 = streamingHash64.getValue();
179 throw new AssertionError();
182 throw new AssertionError();
186 /** Return a {@link XXHash32} instance. */
187 public XXHash32 hash32() {
191 /** Return a {@link XXHash64} instance. */
192 public XXHash64 hash64() {
197 * Return a new {@link StreamingXXHash32} instance.
199 public StreamingXXHash32 newStreamingHash32(int seed) {
200 return streamingHash32Factory.newStreamingHash(seed);
204 * Return a new {@link StreamingXXHash64} instance.
206 public StreamingXXHash64 newStreamingHash64(long seed) {
207 return streamingHash64Factory.newStreamingHash(seed);
210 /** Prints the fastest instance. */
211 public static void main(String[] args) {
212 System.out.println("Fastest instance is " + fastestInstance());
213 System.out.println("Fastest Java instance is " + fastestJavaInstance());
217 public String toString() {
218 return getClass().getSimpleName() + ":" + impl;