All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.jpountz.lz4.LZ4Factory Maven / Gradle / Ivy

There is a newer version: 1.4.0
Show newest version
package net.jpountz.lz4;

/*
 * Copyright 2020 Adrien Grand and the lz4-java contributors.
 *
 * 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.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;

import net.jpountz.util.Native;
import net.jpountz.util.Utils;
import static net.jpountz.lz4.LZ4Constants.DEFAULT_COMPRESSION_LEVEL;
import static net.jpountz.lz4.LZ4Constants.MAX_COMPRESSION_LEVEL;

/**
 * Entry point for the LZ4 API.
 * 

* This class has 3 instances

    *
  • a {@link #nativeInstance() native} instance which is a JNI binding to * the original LZ4 C implementation. *
  • a {@link #safeInstance() safe Java} instance which is a pure Java port * of the original C library,
  • *
  • an {@link #unsafeInstance() unsafe Java} instance which is a Java port * using the unofficial {@link sun.misc.Unsafe} API. *
*

* 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 LZ4Factory} 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 LZ4Factory} instance in a static field. */ public final class LZ4Factory { private static LZ4Factory instance(String impl) { try { return new LZ4Factory(impl); } catch (Exception e) { throw new AssertionError(e); } } private static LZ4Factory NATIVE_INSTANCE, JAVA_UNSAFE_INSTANCE, JAVA_SAFE_INSTANCE; /** * Returns a {@link LZ4Factory} instance that returns compressors and * decompressors that are native bindings to the original C library. *

* 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. *
  3. From lz4-java version 1.6.0, a {@link LZ4FastDecompressor} instance * returned by {@link #fastDecompressor()} of this instance is SLOWER * than a {@link LZ4SafeDecompressor} instance returned by * {@link #safeDecompressor()}, due to a change in the original LZ4 * C implementation. The corresponding C API function is deprecated. * Hence use of {@link #fastDecompressor()} is deprecated * for this instance. *
* * @return a {@link LZ4Factory} instance that returns compressors and * decompressors that are native bindings to the original C library */ public static synchronized LZ4Factory nativeInstance() { if (NATIVE_INSTANCE == null) { NATIVE_INSTANCE = instance("JNI"); } return NATIVE_INSTANCE; } /** * Returns a {@link LZ4Factory} instance that returns compressors and * decompressors that are written with Java's official API. * * @return a {@link LZ4Factory} instance that returns compressors and * decompressors that are written with Java's official API. */ public static synchronized LZ4Factory safeInstance() { if (JAVA_SAFE_INSTANCE == null) { JAVA_SAFE_INSTANCE = instance("JavaSafe"); } return JAVA_SAFE_INSTANCE; } /** * Returns a {@link LZ4Factory} instance that returns compressors and * decompressors that may use {@link sun.misc.Unsafe} to speed up compression * and decompression. * * @return a {@link LZ4Factory} instance that returns compressors and * decompressors that may use {@link sun.misc.Unsafe} to speed up compression * and decompression. */ public static synchronized LZ4Factory unsafeInstance() { if (JAVA_UNSAFE_INSTANCE == null) { JAVA_UNSAFE_INSTANCE = instance("JavaUnsafe"); } return JAVA_UNSAFE_INSTANCE; } /** * Returns the fastest available {@link LZ4Factory} 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}. * * @return the fastest available {@link LZ4Factory} instance which does not * rely on JNI bindings. */ public static LZ4Factory fastestJavaInstance() { if (Utils.isUnalignedAccessAllowed()) { try { return unsafeInstance(); } catch (Throwable t) { return safeInstance(); } } else { return safeInstance(); } } /** * Returns the fastest available {@link LZ4Factory} 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. * * @return the fastest available {@link LZ4Factory} instance */ public static LZ4Factory 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 = LZ4Factory.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 LZ4Compressor fastCompressor; private final LZ4Compressor highCompressor; private final LZ4FastDecompressor fastDecompressor; private final LZ4SafeDecompressor safeDecompressor; private final LZ4Compressor[] highCompressors = new LZ4Compressor[MAX_COMPRESSION_LEVEL+1]; private LZ4Factory(String impl) throws ClassNotFoundException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InstantiationException, InvocationTargetException { this.impl = impl; fastCompressor = classInstance("net.jpountz.lz4.LZ4" + impl + "Compressor"); highCompressor = classInstance("net.jpountz.lz4.LZ4HC" + impl + "Compressor"); fastDecompressor = classInstance("net.jpountz.lz4.LZ4" + impl + "FastDecompressor"); safeDecompressor = classInstance("net.jpountz.lz4.LZ4" + impl + "SafeDecompressor"); Constructor highConstructor = highCompressor.getClass().getDeclaredConstructor(int.class); highCompressors[DEFAULT_COMPRESSION_LEVEL] = highCompressor; for(int level = 1; level <= MAX_COMPRESSION_LEVEL; level++) { if(level == DEFAULT_COMPRESSION_LEVEL) continue; highCompressors[level] = highConstructor.newInstance(level); } // quickly test that everything works as expected final byte[] original = new byte[] {'a','b','c','d',' ',' ',' ',' ',' ',' ','a','b','c','d','e','f','g','h','i','j'}; for (LZ4Compressor compressor : Arrays.asList(fastCompressor, highCompressor)) { final int maxCompressedLength = compressor.maxCompressedLength(original.length); final byte[] compressed = new byte[maxCompressedLength]; final int compressedLength = compressor.compress(original, 0, original.length, compressed, 0, maxCompressedLength); final byte[] restored = new byte[original.length]; fastDecompressor.decompress(compressed, 0, restored, 0, original.length); if (!Arrays.equals(original, restored)) { throw new AssertionError(); } Arrays.fill(restored, (byte) 0); final int decompressedLength = safeDecompressor.decompress(compressed, 0, compressedLength, restored, 0); if (decompressedLength != original.length || !Arrays.equals(original, restored)) { throw new AssertionError(); } } } /** * Returns a blazing fast {@link LZ4Compressor}. * * @return a blazing fast {@link LZ4Compressor} */ public LZ4Compressor fastCompressor() { return fastCompressor; } /** * Returns a {@link LZ4Compressor} which requires more memory than * {@link #fastCompressor()} and is slower but compresses more efficiently. * * @return a {@link LZ4Compressor} which requires more memory than * {@link #fastCompressor()} and is slower but compresses more efficiently. */ public LZ4Compressor highCompressor() { return highCompressor; } /** * Returns a {@link LZ4Compressor} which requires more memory than * {@link #fastCompressor()} and is slower but compresses more efficiently. * The compression level can be customized. *

For current implementations, the following is true about compression level:

    *
  1. It should be in range [1, 17]
  2. *
  3. A compression level higher than 17 would be treated as 17.
  4. *
  5. A compression level lower than 1 would be treated as 9.
  6. *
* Note that compression levels from different implementations * (native, unsafe Java, and safe Java) cannot be compared with one another. * Specifically, the native implementation of a high compression level * is not necessarily faster than the safe/unsafe Java implementation * of the same compression level. * * @param compressionLevel the compression level between [1, 17]; the higher the level, the higher the compression ratio * @return a {@link LZ4Compressor} which requires more memory than * {@link #fastCompressor()} and is slower but compresses more efficiently. */ public LZ4Compressor highCompressor(int compressionLevel) { if(compressionLevel > MAX_COMPRESSION_LEVEL) { compressionLevel = MAX_COMPRESSION_LEVEL; } else if (compressionLevel < 1) { compressionLevel = DEFAULT_COMPRESSION_LEVEL; } return highCompressors[compressionLevel]; } /** * Returns a {@link LZ4FastDecompressor} instance. * Use of this method is deprecated for the {@link #nativeInstance() native instance}. * * @return a {@link LZ4FastDecompressor} instance * * @see #nativeInstance() */ public LZ4FastDecompressor fastDecompressor() { return fastDecompressor; } /** * Returns a {@link LZ4SafeDecompressor} instance. * * @return a {@link LZ4SafeDecompressor} instance */ public LZ4SafeDecompressor safeDecompressor() { return safeDecompressor; } /** * Returns a {@link LZ4UnknownSizeDecompressor} instance. * @deprecated use {@link #safeDecompressor()} * * @return a {@link LZ4UnknownSizeDecompressor} instance */ public LZ4UnknownSizeDecompressor unknownSizeDecompressor() { return safeDecompressor(); } /** * Returns a {@link LZ4Decompressor} instance. * @deprecated use {@link #fastDecompressor()} * * @return a {@link LZ4Decompressor} instance */ public LZ4Decompressor decompressor() { return fastDecompressor(); } /** * Prints the fastest instance. * * @param args no argument required */ 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; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy