]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.lz4/src/net/jpountz/lz4/LZ4Factory.java
Fixed org.simantics.lz4 to use bundle data area when running in OSGi
[simantics/platform.git] / bundles / org.simantics.lz4 / src / net / jpountz / lz4 / LZ4Factory.java
1 package net.jpountz.lz4;
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.Constructor;
18 import java.lang.reflect.Field;
19 import java.lang.reflect.InvocationTargetException;
20 import java.util.Arrays;
21
22 import net.jpountz.util.Native;
23 import net.jpountz.util.Utils;
24 import static net.jpountz.lz4.LZ4Constants.DEFAULT_COMPRESSION_LEVEL;
25 import static net.jpountz.lz4.LZ4Constants.MAX_COMPRESSION_LEVEL;
26
27 /**
28  * Entry point for the LZ4 API.
29  * <p>
30  * This class has 3 instances<ul>
31  * <li>a {@link #nativeInstance() native} instance which is a JNI binding to
32  * <a href="http://code.google.com/p/lz4/">the original LZ4 C implementation</a>.
33  * <li>a {@link #safeInstance() safe Java} instance which is a pure Java port
34  * of the original C library,</li>
35  * <li>an {@link #unsafeInstance() unsafe Java} instance which is a Java port
36  * using the unofficial {@link sun.misc.Unsafe} API.
37  * </ul>
38  * <p>
39  * Only the {@link #safeInstance() safe instance} is guaranteed to work on your
40  * JVM, as a consequence it is advised to use the {@link #fastestInstance()} or
41  * {@link #fastestJavaInstance()} to pull a {@link LZ4Factory} instance.
42  * <p>
43  * All methods from this class are very costly, so you should get an instance
44  * once, and then reuse it whenever possible. This is typically done by storing
45  * a {@link LZ4Factory} instance in a static field.
46  */
47 public final class LZ4Factory {
48
49   private static LZ4Factory instance(String impl) {
50     try {
51       return new LZ4Factory(impl);
52     } catch (Exception e) {
53       throw new AssertionError(e);
54     }
55   }
56
57   private static LZ4Factory NATIVE_INSTANCE,
58                             JAVA_UNSAFE_INSTANCE,
59                             JAVA_SAFE_INSTANCE;
60
61   /**
62    * Return a {@link LZ4Factory} instance that returns compressors and
63    * decompressors that are native bindings to the original C library.
64    * <p>
65    * Please note that this instance has some traps you should be aware of:<ol>
66    * <li>Upon loading this instance, files will be written to the temporary
67    * directory of the system. Although these files are supposed to be deleted
68    * when the JVM exits, they might remain on systems that don't support
69    * removal of files being used such as Windows.
70    * <li>The instance can only be loaded once per JVM. This can be a problem
71    * if your application uses multiple class loaders (such as most servlet
72    * containers): this instance will only be available to the children of the
73    * class loader which has loaded it. As a consequence, it is advised to
74    * either not use this instance in webapps or to put this library in the lib
75    * directory of your servlet container so that it is loaded by the system
76    * class loader.
77    * </ol>
78    */
79   public static synchronized LZ4Factory nativeInstance() {
80     if (NATIVE_INSTANCE == null) {
81       NATIVE_INSTANCE = instance("JNI");
82     }
83     return NATIVE_INSTANCE;
84   }
85
86   /** Return a {@link LZ4Factory} instance that returns compressors and
87    *  decompressors that are written with Java's official API. */
88   public static synchronized LZ4Factory safeInstance() {
89     if (JAVA_SAFE_INSTANCE == null) {
90       JAVA_SAFE_INSTANCE = instance("JavaSafe");
91     }
92     return JAVA_SAFE_INSTANCE;
93   }
94
95   /** Return a {@link LZ4Factory} instance that returns compressors and
96    *  decompressors that may use {@link sun.misc.Unsafe} to speed up compression
97    *  and decompression. */
98   public static synchronized LZ4Factory unsafeInstance() {
99     if (JAVA_UNSAFE_INSTANCE == null) {
100       JAVA_UNSAFE_INSTANCE = instance("JavaUnsafe");
101     }
102     return JAVA_UNSAFE_INSTANCE;
103   }
104
105   /**
106    * Return the fastest available {@link LZ4Factory} instance which does not
107    * rely on JNI bindings. It first tries to load the
108    * {@link #unsafeInstance() unsafe instance}, and then the
109    * {@link #safeInstance() safe Java instance} if the JVM doesn't have a
110    * working {@link sun.misc.Unsafe}.
111    */
112   public static LZ4Factory fastestJavaInstance() {
113     if (Utils.isUnalignedAccessAllowed()) {
114       try {
115         return unsafeInstance();
116       } catch (Throwable t) {
117         return safeInstance();
118       }
119     } else {
120       return safeInstance();
121     }
122   }
123
124   /**
125    * Return the fastest available {@link LZ4Factory} instance. If the class
126    * loader is the system class loader and if the
127    * {@link #nativeInstance() native instance} loads successfully, then the
128    * {@link #nativeInstance() native instance} is returned, otherwise the
129    * {@link #fastestJavaInstance() fastest Java instance} is returned.
130    * <p>
131    * Please read {@link #nativeInstance() javadocs of nativeInstance()} before
132    * using this method.
133    */
134   public static LZ4Factory fastestInstance() {
135     if (!Native.failedToLoad()
136         && (Native.isLoaded()
137             || Native.class.getClassLoader() == ClassLoader.getSystemClassLoader())) {
138       try {
139         return nativeInstance();
140       } catch (Throwable t) {
141         return fastestJavaInstance();
142       }
143     } else {
144       return fastestJavaInstance();
145     }
146   }
147
148   @SuppressWarnings("unchecked")
149   private static <T> T classInstance(String cls) throws NoSuchFieldException, SecurityException, ClassNotFoundException, IllegalArgumentException, IllegalAccessException {
150     ClassLoader loader = LZ4Factory.class.getClassLoader();
151     loader = loader == null ? ClassLoader.getSystemClassLoader() : loader;
152     final Class<?> c = loader.loadClass(cls);
153     Field f = c.getField("INSTANCE");
154     return (T) f.get(null);
155   }
156
157   private final String impl;
158   private final LZ4Compressor fastCompressor;
159   private final LZ4Compressor highCompressor;
160   private final LZ4FastDecompressor fastDecompressor;
161   private final LZ4SafeDecompressor safeDecompressor;
162   private final LZ4Compressor[] highCompressors = new LZ4Compressor[MAX_COMPRESSION_LEVEL+1];
163
164   private LZ4Factory(String impl) throws ClassNotFoundException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InstantiationException, InvocationTargetException {
165     this.impl = impl;
166     fastCompressor = classInstance("net.jpountz.lz4.LZ4" + impl + "Compressor");
167     highCompressor = classInstance("net.jpountz.lz4.LZ4HC" + impl + "Compressor");
168     fastDecompressor = classInstance("net.jpountz.lz4.LZ4" + impl + "FastDecompressor");
169     safeDecompressor = classInstance("net.jpountz.lz4.LZ4" + impl + "SafeDecompressor");
170     Constructor<? extends LZ4Compressor> highConstructor = highCompressor.getClass().getDeclaredConstructor(int.class);
171     highCompressors[DEFAULT_COMPRESSION_LEVEL] = highCompressor;
172     for(int level = 1; level <= MAX_COMPRESSION_LEVEL; level++) {
173       if(level == DEFAULT_COMPRESSION_LEVEL) continue;
174       highCompressors[level] = highConstructor.newInstance(level);
175     }
176
177     // quickly test that everything works as expected
178     final byte[] original = new byte[] {'a','b','c','d',' ',' ',' ',' ',' ',' ','a','b','c','d','e','f','g','h','i','j'};
179     for (LZ4Compressor compressor : Arrays.asList(fastCompressor, highCompressor)) {
180       final int maxCompressedLength = compressor.maxCompressedLength(original.length);
181       final byte[] compressed = new byte[maxCompressedLength];
182       final int compressedLength = compressor.compress(original, 0, original.length, compressed, 0, maxCompressedLength);
183       final byte[] restored = new byte[original.length];
184       fastDecompressor.decompress(compressed, 0, restored, 0, original.length);
185       if (!Arrays.equals(original, restored)) {
186         throw new AssertionError();
187       }
188       Arrays.fill(restored, (byte) 0);
189       final int decompressedLength = safeDecompressor.decompress(compressed, 0, compressedLength, restored, 0);
190       if (decompressedLength != original.length || !Arrays.equals(original, restored)) {
191         throw new AssertionError();
192       }
193     }
194
195   }
196
197   /** Return a blazing fast {@link LZ4Compressor}. */
198   public LZ4Compressor fastCompressor() {
199     return fastCompressor;
200   }
201
202   /** Return a {@link LZ4Compressor} which requires more memory than
203    * {@link #fastCompressor()} and is slower but compresses more efficiently. */
204   public LZ4Compressor highCompressor() {
205     return highCompressor;
206   }
207
208   /** Return a {@link LZ4Compressor} which requires more memory than
209    * {@link #fastCompressor()} and is slower but compresses more efficiently.
210    * The compression level can be customized.
211    * <p>For current implementations, the following is true about compression level:<ol>
212    *   <li>It should be in range [1, 17]</li>
213    *   <li>A compression level higher than 17 would be treated as 17.</li>
214    *   <li>A compression level lower than 1 would be treated as 9.</li>
215    * </ol></p>
216    */
217   public LZ4Compressor highCompressor(int compressionLevel) {
218     if(compressionLevel > MAX_COMPRESSION_LEVEL) {
219       compressionLevel = MAX_COMPRESSION_LEVEL;
220     } else if(compressionLevel < 1) {
221       compressionLevel = DEFAULT_COMPRESSION_LEVEL;
222     }
223     return highCompressors[compressionLevel];
224   }
225
226   /** Return a {@link LZ4FastDecompressor} instance. */
227   public LZ4FastDecompressor fastDecompressor() {
228     return fastDecompressor;
229   }
230
231   /** Return a {@link LZ4SafeDecompressor} instance. */
232   public LZ4SafeDecompressor safeDecompressor() {
233     return safeDecompressor;
234   }
235
236   /** Return a {@link LZ4UnknownSizeDecompressor} instance.
237    * @deprecated use {@link #safeDecompressor()} */
238   public LZ4UnknownSizeDecompressor unknownSizeDecompressor() {
239     return safeDecompressor();
240   }
241
242   /** Return a {@link LZ4Decompressor} instance.
243    * @deprecated use {@link #fastDecompressor()} */
244   public LZ4Decompressor decompressor() {
245     return fastDecompressor();
246   }
247
248   /** Prints the fastest instance. */
249   public static void main(String[] args) {
250     System.out.println("Fastest instance is " + fastestInstance());
251     System.out.println("Fastest Java instance is " + fastestJavaInstance());
252   }
253
254   @Override
255   public String toString() {
256     return getClass().getSimpleName() + ":" + impl;
257   }
258
259 }