1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.fastlz;
15 import java.io.FileNotFoundException;
16 import java.io.FileOutputStream;
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.io.OutputStream;
20 import java.io.UnsupportedEncodingException;
22 import java.net.URLDecoder;
23 import java.nio.ByteBuffer;
25 import org.simantics.compressions.impl.Buffers;
26 import org.simantics.fastlz.impl.FastLZConfig;
27 import org.simantics.fastlz.impl.FastLZInputStream;
28 import org.simantics.fastlz.impl.FastLZOutputStream;
29 import org.simantics.fastlz.impl.OS;
30 import org.simantics.fastlz.java.FastLZJavaInputStream;
31 import org.simantics.fastlz.java.FastLZJavaOutputStream;
34 * @author Tuukka Lehtonen
36 @SuppressWarnings("resource")
39 private static boolean nativeInitialized = false;
41 public static boolean isNativeInitialized() {
42 return nativeInitialized;
45 private static boolean isNativeDisabled() {
46 return "false".equalsIgnoreCase(System.getProperty("fastlz.native", "true"));
49 public synchronized static void initialize(File path) {
50 if (nativeInitialized || isNativeDisabled())
53 // System.out.println("INITIALIZE FASTLZ: " + path);
55 String osName = System.getProperty("os.name");
56 String osArch = System.getProperty("os.arch");
57 String lib = "fastlz";
61 lib += OS.formOsArchSuffix();
62 System.loadLibrary(lib);
64 if (path.isDirectory()) {
65 lib = System.mapLibraryName(lib + OS.formOsArchSuffix());
66 lib = new File(path, lib).toString();
67 } else if (path.isFile()) {
68 lib = path.toString();
70 throw new IllegalArgumentException("Invalid path for FastLZ library: " + path);
74 } catch (UnsatisfiedLinkError e) {
75 String fallback = "fastlz";
76 fallback += OS.formOsArchSuffix();
77 System.loadLibrary(fallback);
80 nativeInitialized = true;
81 } catch (UnsatisfiedLinkError e) {
82 System.err.println("Couldn't load library '" + lib + "' for os '" + osName + "' and architecture '" + osArch + "'");
88 if (!isNativeDisabled() && FastLZConfig.attemptStaticInitialization) {
89 String osName = System.getProperty("os.name");
90 String osArch = System.getProperty("os.arch");
91 String libName = OS.resolveLibName();
92 URL libURL = FastLZ.class.getResource("/" + libName);
93 // System.out.println("FastLZ libURL: " + libURL);
94 // System.out.println(".: " + FastLZ.class.getResource("."));
95 // System.out.println("..: " + FastLZ.class.getResource("../../../" + resolveLibName()));
96 // System.out.println("libname: " + FastLZ.class.getResource(resolveLibName()));
97 // System.out.println("/libname: " + FastLZ.class.getResource("/" + resolveLibName()));
98 // System.out.println("libname stream: " + FastLZ.class.getResourceAsStream(resolveLibName()));
99 // System.out.println("/libname stream: " + FastLZ.class.getResourceAsStream("/" + resolveLibName()));
100 if (libURL != null) {
101 if ("file".equals(libURL.getProtocol())) {
103 File path = new File(URLDecoder.decode(libURL.getPath(), "UTF-8"));
105 } catch (UnsupportedEncodingException e) {
108 } else if ("jar".equals(libURL.getProtocol())) {
110 File libFile = OS.extractLib(libURL, libName);
112 } catch (FileNotFoundException e) {
114 } catch (IOException e) {
118 System.err.println("Unsupported URL protocol '" + libURL + "' for FastLZ native library file '" + libName + "' for os '" + osName + "' and architecture '" + osArch + "'");
121 initialize(null); // Final fallback. (Needed when running DB tests by ant script).
127 * The output buffer must be at least 5% larger than the input buffer and
128 * can not be smaller than 66 bytes.
130 * @param inputSize size of uncompressed input data in bytes
131 * @return maximum amount of bytes needed for the compressed data
133 public static int compressBound(int inputSize) {
134 return Math.max(66, inputSize + inputSize/20+1);
138 * Compress a block of data in the input buffer and returns the size of
139 * compressed block. The size of input buffer is specified by length. The
140 * minimum input buffer size is 16.
143 * The output buffer must be at least 5% larger than the input buffer and
144 * can not be smaller than 66 bytes.
147 * If the input is not compressible, the return value might be larger than
148 * length (input buffer size).
151 * The input buffer and the output buffer can not overlap.
154 * The input and output buffers are assumed to be direct byte buffers.
156 static native int compress(ByteBuffer input, int inputOffset, int length,
157 ByteBuffer output, int outputOffset);
160 * Decompress a block of compressed data and returns the size of the
161 * decompressed block. If error occurs, e.g. the compressed data is
162 * corrupted or the output buffer is not large enough, then 0 (zero) will be
166 * The input buffer and the output buffer can not overlap.
169 * Decompression is memory safe and guaranteed not to write the output
170 * buffer more than what is specified in maxout.
173 * The input and output buffers are assumed to be direct byte buffers.
175 static native int decompress(ByteBuffer input, int inputOffset, int length,
176 ByteBuffer output, int outputOffset, int maxout);
179 * Compress a block of data in the input buffer and returns the size of
180 * compressed block. The size of input buffer is specified by length. The
181 * minimum input buffer size is 16.
184 * The output buffer must be at least 5% larger than the input buffer and
185 * can not be smaller than 66 bytes.
188 * If the input is not compressible, the return value might be larger than
189 * length (input buffer size).
192 * The input buffer and the output buffer can not overlap.
195 * It is recommended to have both input buffers as direct or heap buffers,
196 * not mixed. Mixing different types of buffers will hurt performance a lot.
197 * If both buffers are direct byte buffers and native decompression is
198 * available, it will be employed.
200 public static int compressBuffer(ByteBuffer input, int inputOffset, int length,
201 ByteBuffer output, int outputOffset) {
202 if (output.isReadOnly())
203 throw new IllegalArgumentException("read-only output buffer");
205 if (isNativeInitialized()) {
206 if (input.isDirect() && output.isDirect())
207 return compress(input, inputOffset, length, output, outputOffset);
210 byte[] inarr = Buffers.getInputArray(input);
211 byte[] outarr = Buffers.getOutputArray(output);
212 int result = FastLZJava.compress(inarr, inputOffset, length, outarr, outputOffset);
213 Buffers.writeOutput(output, outarr);
218 * Decompress a block of compressed data and returns the size of the
219 * decompressed block. If error occurs, e.g. the compressed data is
220 * corrupted or the output buffer is not large enough, then 0 (zero) will be
224 * The input buffer and the output buffer can not overlap.
227 * Decompression is memory safe and guaranteed not to write the output
228 * buffer more than what is specified in maxout.
231 * It is recommended to have both input buffers as direct or heap buffers,
232 * not mixed. Mixing different types of buffers will hurt performance a lot.
233 * If both buffers are direct byte buffers and native decompression is
234 * available, it will be employed.
236 public static int decompressBuffer(ByteBuffer input, int inputOffset, int length,
237 ByteBuffer output, int outputOffset, int maxout) {
238 if (output.isReadOnly())
239 throw new IllegalArgumentException("read-only output buffer");
241 if (isNativeInitialized()) {
242 if (input.isDirect() && output.isDirect())
243 return decompress(input, inputOffset, length, output, outputOffset, maxout);
246 byte[] inarr = Buffers.getInputArray(input);
247 byte[] outarr = Buffers.getOutputArray(output);
248 int result = FastLZJava.decompress(inarr, inputOffset, length, outarr, outputOffset, maxout);
249 Buffers.writeOutput(output, outarr);
254 * @param file the FastLZ-compressed file to read
255 * @return input stream that decompresses its output using the FastLZ
256 * algorithm. Caller is responsible of closing the returned stream.
257 * @throws FileNotFoundException see
258 * {@link FileOutputStream#FileOutputStream(File)} for when this is
261 public static InputStream read(File file) throws FileNotFoundException {
262 return nativeInitialized ? new FastLZInputStream(file)
263 : new FastLZJavaInputStream(file);
270 * the input stream to decompress
271 * @return a stream that decompresses the specified FastLZ compressed input
274 public static InputStream read(InputStream input) {
275 return nativeInitialized ? new FastLZInputStream(input)
276 : new FastLZJavaInputStream(input);
280 * @param file the FastLZ-compressed file to write
281 * @return output stream that compresses its input using the FastLZ
282 * algorithm. Caller is responsible of closing the returned stream.
283 * @throws FileNotFoundException see
284 * {@link FileOutputStream#FileOutputStream(File)} for when this is
287 public static OutputStream write(File file) throws FileNotFoundException {
288 return nativeInitialized ? new FastLZOutputStream(file)
289 : new FastLZJavaOutputStream(file);
294 * the stream to write compressed output into
295 * @return a stream that compresses into the specified output stream with
298 public static OutputStream write(OutputStream output) {
299 return nativeInitialized ? new FastLZOutputStream(output)
300 : new FastLZJavaOutputStream(output);
304 * Warning: custom utility for performance reasons. Only used by
305 * org.simantics.db.procore. Do not use elsewhere.
308 * @param deflatedSize
309 * @param inflatedSize
313 public static synchronized native int decompressCluster(ByteBuffer deflated, int deflatedSize, int inflatedSize, Object[] arrays);