package org.simantics.db.server.internal; import java.nio.ByteBuffer; import java.util.Arrays; import org.simantics.fastlz.FastLZ; import org.simantics.fastlz.FastLZJava; /** * @author Tuukka Lehtonen */ public class ClusterDecompressor { private byte[] inflateBuffer; public Object[] inflateCluster(int inflateSize, ByteBuffer deflatedCluster) throws CompressionException { int deflatedSize = deflatedCluster.limit(); Object[] arrays = new Object[3]; int inflated = decompressCluster(deflatedCluster, deflatedSize, inflateSize, arrays); if (inflated != inflateSize) throw new CompressionException("decompression error, inflated " + inflated + " bytes when " + inflateSize + " expected."); return arrays; } /** * Wrapper for the old native direct byte buffer version of cluster * decompression. * * @param deflated * @param deflatedSize * @param inflatedSize * @param arrays * expects an array of 3 elements. The decompression result will * be stored in here. [0] = cluster long array (long[]), [1] = * cluster int array (int[]), [2] = cluster byte array (byte[]). * @return the amount of bytes inflated from the deflated cluster. Matches * inflatedSize when successful. * * @see FastLZ#decompressCluster(ByteBuffer, int, int, Object[]) */ public int decompressCluster(ByteBuffer deflated, int deflatedSize, int inflatedSize, Object[] arrays) { if (deflated.isDirect()) { if (FastLZ.isNativeInitialized()) { FastLZ.decompressCluster(deflated, deflatedSize, inflatedSize, arrays); return inflatedSize; } // REALLY SLOW FALLBACK: java code for direct byte buffers } byte[] array = null; if (deflated.hasArray()) { array = deflated.array(); } else { // SLOW FALLBACK: read-only heap byte buffer array = new byte[deflatedSize]; int pos = deflated.position(); deflated.position(0); deflated.get(array, 0, deflatedSize); deflated.position(pos); } return decompressCluster(array, deflatedSize, inflatedSize, arrays); } /** * @param deflated * @param deflatedSize * @param inflatedSize * @param arrays * @return */ public synchronized int decompressCluster(byte[] deflated, int deflatedSize, int inflatedSize, Object[] arrays) { if (inflateBuffer == null) { inflateBuffer = new byte[inflatedSize]; } else if (inflateBuffer.length < inflatedSize) { inflateBuffer = new byte[inflatedSize]; } return decompressCluster(deflated, deflatedSize, inflateBuffer, inflatedSize, arrays); } /** * @param deflated * @param deflatedSize * @param inflated * @param inflatedSize * @param arrays * @return amount of bytes inflated from the original deflated buffer. * Should match inflatedSize if everything went ok, otherwise will * not. */ public static int decompressCluster(byte[] deflated, int deflatedSize, byte[] inflated, int inflatedSize, Object[] arrays) { if (inflated.length < inflatedSize) throw new IllegalArgumentException("inflate buffer size (" + inflated.length + ") is smaller than inflated size (" + inflatedSize + ")"); int decompressedBytes = FastLZJava.decompress(deflated, 0, deflatedSize, inflated, 0, inflatedSize); if (decompressedBytes != inflatedSize) return decompressedBytes; int offset = 0; int longCount = readInt(inflated, offset); long[] longs = new long[longCount]; copyLongs(inflated, offset + 4, longs); offset += 4 + 8*longCount; int intCount = readInt(inflated, offset); int[] ints = new int[intCount]; copyInts(inflated, offset + 4, ints); offset += 4 + 4*intCount; int byteCount = readInt(inflated, offset); byte[] bytes = Arrays.copyOfRange(inflated, offset + 4, offset + 4 + byteCount); arrays[0] = longs; arrays[1] = ints; arrays[2] = bytes; return decompressedBytes; } private static void copyInts(byte[] bytes, int i, int[] ints) { int offset = i; int count = ints.length; for (int a = 0; a < count; ++a, offset += 4) { int value = (((int) bytes[offset] & 0xff)) | (((int) bytes[offset+1] & 0xff) << 8) | (((int) bytes[offset+2] & 0xff) << 16) | (((int) bytes[offset+3] & 0xff) << 24); ints[a] = value; } } private static void copyLongs(byte[] bytes, int i, long[] longs) { int offset = i; int count = longs.length; for (int a = 0; a < count; ++a, offset += 8) { long value = (((long) bytes[offset] & 0xff)) | (((long) bytes[offset+1] & 0xff) << 8) | (((long) bytes[offset+2] & 0xff) << 16) | (((long) bytes[offset+3] & 0xff) << 24) | (((long) bytes[offset+4] & 0xff) << 32) | (((long) bytes[offset+5] & 0xff) << 40) | (((long) bytes[offset+6] & 0xff) << 48) | (((long) bytes[offset+7] & 0xff) << 56); longs[a] = value; } } private static int readInt(byte[] array, int offset) { return (((int) array[offset] & 0xff)) | (((int) array[offset+1] & 0xff) << 8) | (((int) array[offset+2] & 0xff) << 16) | (((int) array[offset+3] & 0xff) << 24); } }