]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.lz4/src/net/jpountz/xxhash/XXHashFactory.java
ab03dff1fcd81eb07b7a52f2ae92a499f600e63a
[simantics/platform.git] / bundles / org.simantics.lz4 / src / net / jpountz / xxhash / XXHashFactory.java
1 package net.jpountz.xxhash;
2
3 /*
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 import java.lang.reflect.Field;
18 import java.util.Random;
19
20 import net.jpountz.util.Native;
21 import net.jpountz.util.Utils;
22
23 /**
24  * Entry point to get {@link XXHash32} and {@link StreamingXXHash32} instances.
25  * <p>
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.
33  * </ul>
34  * <p>
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.
38  * <p>
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.
42  */
43 public final class XXHashFactory {
44
45   private static XXHashFactory instance(String impl) {
46     try {
47       return new XXHashFactory(impl);
48     } catch (Exception e) {
49       throw new AssertionError(e);
50     }
51   }
52
53   private static XXHashFactory NATIVE_INSTANCE,
54                                JAVA_UNSAFE_INSTANCE,
55                                JAVA_SAFE_INSTANCE;
56
57   /** Return a {@link XXHashFactory} that returns {@link XXHash32} instances that
58    *  are native bindings to the original C API.
59    * <p>
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
71    * class loader.
72    * </ol>
73    */
74   public static synchronized XXHashFactory nativeInstance() {
75     if (NATIVE_INSTANCE == null) {
76       NATIVE_INSTANCE = instance("JNI");
77     }
78     return NATIVE_INSTANCE;
79   }
80
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");
86     }
87     return JAVA_SAFE_INSTANCE;
88   }
89
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");
95     }
96     return JAVA_UNSAFE_INSTANCE;
97   }
98
99   /**
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}.
105    */
106   public static XXHashFactory fastestJavaInstance() {
107     if (Utils.isUnalignedAccessAllowed()) {
108       try {
109         return unsafeInstance();
110       } catch (Throwable t) {
111         return safeInstance();
112       }
113     } else {
114       return safeInstance();
115     }
116   }
117
118   /**
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.
124    * <p>
125    * Please read {@link #nativeInstance() javadocs of nativeInstance()} before
126    * using this method.
127    */
128   public static XXHashFactory fastestInstance() {
129     if (Native.isLoaded()
130         || Native.class.getClassLoader() == ClassLoader.getSystemClassLoader()) {
131       try {
132         return nativeInstance();
133       } catch (Throwable t) {
134         return fastestJavaInstance();
135       }
136     } else {
137       return fastestJavaInstance();
138     }
139   }
140
141   @SuppressWarnings("unchecked")
142   private static <T> T classInstance(String cls) throws NoSuchFieldException, SecurityException, ClassNotFoundException, IllegalArgumentException, IllegalAccessException {
143     ClassLoader loader = XXHashFactory.class.getClassLoader();
144     loader = loader == null ? ClassLoader.getSystemClassLoader() : loader;
145     final Class<?> c = loader.loadClass(cls);
146     Field f = c.getField("INSTANCE");
147     return (T) f.get(null);
148   }
149
150   private final String impl;
151   private final XXHash32 hash32;
152   private final XXHash64 hash64;
153   private final StreamingXXHash32.Factory streamingHash32Factory;
154   private final StreamingXXHash64.Factory streamingHash64Factory;
155
156   private XXHashFactory(String impl) throws ClassNotFoundException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
157     this.impl = impl;
158     hash32 = classInstance("net.jpountz.xxhash.XXHash32" + impl);
159     streamingHash32Factory = classInstance("net.jpountz.xxhash.StreamingXXHash32" + impl + "$Factory");
160     hash64 = classInstance("net.jpountz.xxhash.XXHash64" + impl);
161     streamingHash64Factory = classInstance("net.jpountz.xxhash.StreamingXXHash64" + impl + "$Factory");
162
163     // make sure it can run
164     final byte[] bytes = new byte[100];
165     final Random random = new Random();
166     random.nextBytes(bytes);
167     final int seed = random.nextInt();
168
169     final int h1 = hash32.hash(bytes, 0, bytes.length, seed);
170     final StreamingXXHash32 streamingHash32 = newStreamingHash32(seed);
171     streamingHash32.update(bytes, 0, bytes.length);
172     final int h2 = streamingHash32.getValue();
173     final long h3 = hash64.hash(bytes, 0, bytes.length, seed);
174     final StreamingXXHash64 streamingHash64 = newStreamingHash64(seed);
175     streamingHash64.update(bytes, 0, bytes.length);
176     final long h4 = streamingHash64.getValue();
177     if (h1 != h2) {
178       throw new AssertionError();
179     }
180     if (h3 != h4) {
181       throw new AssertionError();
182     }
183   }
184
185   /** Return a {@link XXHash32} instance. */
186   public XXHash32 hash32() {
187     return hash32;
188   }
189
190   /** Return a {@link XXHash64} instance. */
191   public XXHash64 hash64() {
192     return hash64;
193   }
194
195   /**
196    * Return a new {@link StreamingXXHash32} instance.
197    */
198   public StreamingXXHash32 newStreamingHash32(int seed) {
199     return streamingHash32Factory.newStreamingHash(seed);
200   }
201
202   /**
203    * Return a new {@link StreamingXXHash64} instance.
204    */
205   public StreamingXXHash64 newStreamingHash64(long seed) {
206     return streamingHash64Factory.newStreamingHash(seed);
207   }
208
209   /** Prints the fastest instance. */
210   public static void main(String[] args) {
211     System.out.println("Fastest instance is " + fastestInstance());
212     System.out.println("Fastest Java instance is " + fastestJavaInstance());
213   }
214
215   @Override
216   public String toString() {
217     return getClass().getSimpleName() + ":" + impl;
218   }
219
220 }