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