/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.fastlz; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLDecoder; import java.nio.ByteBuffer; import org.simantics.compressions.impl.Buffers; import org.simantics.fastlz.impl.FastLZConfig; import org.simantics.fastlz.impl.FastLZInputStream; import org.simantics.fastlz.impl.FastLZOutputStream; import org.simantics.fastlz.impl.OS; import org.simantics.fastlz.java.FastLZJavaInputStream; import org.simantics.fastlz.java.FastLZJavaOutputStream; /** * @author Tuukka Lehtonen */ @SuppressWarnings("resource") public class FastLZ { private static boolean nativeInitialized = false; public static boolean isNativeInitialized() { return nativeInitialized; } private static boolean isNativeDisabled() { return "false".equalsIgnoreCase(System.getProperty("fastlz.native", "true")); } public synchronized static void initialize(File path) { if (nativeInitialized || isNativeDisabled()) return; // System.out.println("INITIALIZE FASTLZ: " + path); String osName = System.getProperty("os.name"); String osArch = System.getProperty("os.arch"); String lib = "fastlz"; try { if (path == null) { lib += OS.formOsArchSuffix(); System.loadLibrary(lib); } else { if (path.isDirectory()) { lib = System.mapLibraryName(lib + OS.formOsArchSuffix()); lib = new File(path, lib).toString(); } else if (path.isFile()) { lib = path.toString(); } else { throw new IllegalArgumentException("Invalid path for FastLZ library: " + path); } try { System.load(lib); } catch (UnsatisfiedLinkError e) { String fallback = "fastlz"; fallback += OS.formOsArchSuffix(); System.loadLibrary(fallback); } } nativeInitialized = true; } catch (UnsatisfiedLinkError e) { System.err.println("Couldn't load library '" + lib + "' for os '" + osName + "' and architecture '" + osArch + "'"); e.printStackTrace(); } } static { if (!isNativeDisabled() && FastLZConfig.attemptStaticInitialization) { String osName = System.getProperty("os.name"); String osArch = System.getProperty("os.arch"); String libName = OS.resolveLibName(); URL libURL = FastLZ.class.getResource("/" + libName); // System.out.println("FastLZ libURL: " + libURL); // System.out.println(".: " + FastLZ.class.getResource(".")); // System.out.println("..: " + FastLZ.class.getResource("../../../" + resolveLibName())); // System.out.println("libname: " + FastLZ.class.getResource(resolveLibName())); // System.out.println("/libname: " + FastLZ.class.getResource("/" + resolveLibName())); // System.out.println("libname stream: " + FastLZ.class.getResourceAsStream(resolveLibName())); // System.out.println("/libname stream: " + FastLZ.class.getResourceAsStream("/" + resolveLibName())); if (libURL != null) { if ("file".equals(libURL.getProtocol())) { try { File path = new File(URLDecoder.decode(libURL.getPath(), "UTF-8")); initialize(path); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } else if ("jar".equals(libURL.getProtocol())) { try { File libFile = OS.extractLib(libURL, libName); initialize(libFile); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } else { System.err.println("Unsupported URL protocol '" + libURL + "' for FastLZ native library file '" + libName + "' for os '" + osName + "' and architecture '" + osArch + "'"); } } else { initialize(null); // Final fallback. (Needed when running DB tests by ant script). } } } /** * The output buffer must be at least 5% larger than the input buffer and * can not be smaller than 66 bytes. * * @param inputSize size of uncompressed input data in bytes * @return maximum amount of bytes needed for the compressed data */ public static int compressBound(int inputSize) { return Math.max(66, inputSize + inputSize/20+1); } /** * Compress a block of data in the input buffer and returns the size of * compressed block. The size of input buffer is specified by length. The * minimum input buffer size is 16. * *

* The output buffer must be at least 5% larger than the input buffer and * can not be smaller than 66 bytes. * *

* If the input is not compressible, the return value might be larger than * length (input buffer size). * *

* The input buffer and the output buffer can not overlap. * *

* The input and output buffers are assumed to be direct byte buffers. */ static native int compress(ByteBuffer input, int inputOffset, int length, ByteBuffer output, int outputOffset); /** * Decompress a block of compressed data and returns the size of the * decompressed block. If error occurs, e.g. the compressed data is * corrupted or the output buffer is not large enough, then 0 (zero) will be * returned instead. * *

* The input buffer and the output buffer can not overlap. * *

* Decompression is memory safe and guaranteed not to write the output * buffer more than what is specified in maxout. * *

* The input and output buffers are assumed to be direct byte buffers. */ static native int decompress(ByteBuffer input, int inputOffset, int length, ByteBuffer output, int outputOffset, int maxout); /** * Compress a block of data in the input buffer and returns the size of * compressed block. The size of input buffer is specified by length. The * minimum input buffer size is 16. * *

* The output buffer must be at least 5% larger than the input buffer and * can not be smaller than 66 bytes. * *

* If the input is not compressible, the return value might be larger than * length (input buffer size). * *

* The input buffer and the output buffer can not overlap. * *

* It is recommended to have both input buffers as direct or heap buffers, * not mixed. Mixing different types of buffers will hurt performance a lot. * If both buffers are direct byte buffers and native decompression is * available, it will be employed. */ public static int compressBuffer(ByteBuffer input, int inputOffset, int length, ByteBuffer output, int outputOffset) { if (output.isReadOnly()) throw new IllegalArgumentException("read-only output buffer"); if (isNativeInitialized()) { if (input.isDirect() && output.isDirect()) return compress(input, inputOffset, length, output, outputOffset); } byte[] inarr = Buffers.getInputArray(input); byte[] outarr = Buffers.getOutputArray(output); int result = FastLZJava.compress(inarr, inputOffset, length, outarr, outputOffset); Buffers.writeOutput(output, outarr); return result; } /** * Decompress a block of compressed data and returns the size of the * decompressed block. If error occurs, e.g. the compressed data is * corrupted or the output buffer is not large enough, then 0 (zero) will be * returned instead. * *

* The input buffer and the output buffer can not overlap. * *

* Decompression is memory safe and guaranteed not to write the output * buffer more than what is specified in maxout. * *

* It is recommended to have both input buffers as direct or heap buffers, * not mixed. Mixing different types of buffers will hurt performance a lot. * If both buffers are direct byte buffers and native decompression is * available, it will be employed. */ public static int decompressBuffer(ByteBuffer input, int inputOffset, int length, ByteBuffer output, int outputOffset, int maxout) { if (output.isReadOnly()) throw new IllegalArgumentException("read-only output buffer"); if (isNativeInitialized()) { if (input.isDirect() && output.isDirect()) return decompress(input, inputOffset, length, output, outputOffset, maxout); } byte[] inarr = Buffers.getInputArray(input); byte[] outarr = Buffers.getOutputArray(output); int result = FastLZJava.decompress(inarr, inputOffset, length, outarr, outputOffset, maxout); Buffers.writeOutput(output, outarr); return result; } /** * @param file the FastLZ-compressed file to read * @return input stream that decompresses its output using the FastLZ * algorithm. Caller is responsible of closing the returned stream. * @throws FileNotFoundException see * {@link FileOutputStream#FileOutputStream(File)} for when this is * thrown */ public static InputStream read(File file) throws FileNotFoundException { return nativeInitialized ? new FastLZInputStream(file) : new FastLZJavaInputStream(file); } /** * @param input * @return * @param input * the input stream to decompress * @return a stream that decompresses the specified FastLZ compressed input * stream */ public static InputStream read(InputStream input) { return nativeInitialized ? new FastLZInputStream(input) : new FastLZJavaInputStream(input); } /** * @param file the FastLZ-compressed file to write * @return output stream that compresses its input using the FastLZ * algorithm. Caller is responsible of closing the returned stream. * @throws FileNotFoundException see * {@link FileOutputStream#FileOutputStream(File)} for when this is * thrown */ public static OutputStream write(File file) throws FileNotFoundException { return nativeInitialized ? new FastLZOutputStream(file) : new FastLZJavaOutputStream(file); } /** * @param output * the stream to write compressed output into * @return a stream that compresses into the specified output stream with * FastLZ */ public static OutputStream write(OutputStream output) { return nativeInitialized ? new FastLZOutputStream(output) : new FastLZJavaOutputStream(output); } /** * Warning: custom utility for performance reasons. Only used by * org.simantics.db.procore. Do not use elsewhere. * * @param deflated * @param deflatedSize * @param inflatedSize * @param arrays * @return */ public static synchronized native int decompressCluster(ByteBuffer deflated, int deflatedSize, int inflatedSize, Object[] arrays); }