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