--- /dev/null
+package fi.vtt.simantics.procore.internal;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.List;\r
+\r
+import org.simantics.db.exception.RuntimeDatabaseException;\r
+import org.simantics.db.impl.ClusterTraitsBase;\r
+import org.simantics.db.procore.cluster.ClusterImpl;\r
+import org.simantics.db.procore.cluster.ClusterTraits;\r
+import org.simantics.db.procore.cluster.ClusterTraitsSmall;\r
+import org.simantics.db.service.Bytes;\r
+import org.simantics.db.service.ClusterUID;\r
+\r
+import fi.vtt.simantics.procore.DebugPolicy;\r
+import fi.vtt.simantics.procore.internal.ClusterStream.ClusterEnum;\r
+import fi.vtt.simantics.procore.internal.ClusterStream.Data;\r
+import fi.vtt.simantics.procore.internal.ClusterStream.DebugInfo;\r
+import fi.vtt.simantics.procore.internal.ClusterStream.OpEnum;\r
+import fi.vtt.simantics.procore.internal.ClusterStream.StmEnum;\r
+import gnu.trove.map.hash.TIntByteHashMap;\r
+import gnu.trove.map.hash.TLongIntHashMap;\r
+\r
+public final class ClusterChange {\r
+ public static final int VERSION = 1;\r
+ public static final byte ADD_OPERATION = 2;\r
+ public static final byte REMOVE_OPERATION = 3;\r
+ public static final byte DELETE_OPERATION = 5;\r
+\r
+ public static final boolean DEBUG = false;\r
+ public static final boolean DEBUG_STAT = false;\r
+ public static final boolean DEBUG_CCS = false;\r
+\r
+ private static DebugInfo sum = new DebugInfo();\r
+\r
+ public final TIntByteHashMap foreignTable = new TIntByteHashMap();\r
+ private final DebugInfo info;\r
+ private final GraphSession graphSession;\r
+ public final ClusterUID clusterUID;\r
+ private final int SIZE_OFFSET;\r
+ private final int HEADER_SIZE;\r
+ // How much buffer is used before stream is flushed to server. The bigger the better.\r
+ public static final int MAX_FIXED_BYTES = (1<<15) + (1<<14);\r
+ private static final int MAX_FIXED_OPERATION_SIZE = 17 + 16;\r
+ private static final int MAX_FIXED_OPERATION_SIZE_AND_ROOM_FOR_ERROR = MAX_FIXED_OPERATION_SIZE + 36;\r
+ private int nextSize = MAX_FIXED_BYTES;\r
+ int byteIndex = 0;\r
+ private byte[] bytes = null; // Operation data.\r
+ private final byte[] header;\r
+ private boolean flushed = false;\r
+ private final ClusterStream clusterStream;\r
+ private final ClusterChange2 clusterChange2;\r
+\r
+ public ClusterImpl clusterImpl;\r
+\r
+ public ClusterChange(ClusterStream clusterStream, ClusterImpl clusterImpl) {\r
+ this.clusterImpl = clusterImpl;\r
+ clusterUID = clusterImpl.getClusterUID();\r
+ long[] longs = new long[ClusterUID.getLongLength()];\r
+ clusterUID.toLong(longs, 0);\r
+ this.graphSession = clusterStream.graphSession;\r
+ info = new DebugInfo();\r
+ HEADER_SIZE = 8 + longs.length * 8;\r
+ header = new byte[HEADER_SIZE];\r
+ SIZE_OFFSET = 0;\r
+ Bytes.writeLE(header, SIZE_OFFSET + 0, 0); // Correct byte vector size is set with setHeaderVectorSize() later.\r
+ Bytes.writeLE(header, SIZE_OFFSET + 4, VERSION);\r
+ for (int i=0, offset=8; i<longs.length; ++i, offset+=8)\r
+ Bytes.writeLE(header, offset, longs[i]);\r
+ //initBuffer();\r
+ this.clusterStream = clusterStream;\r
+ this.clusterChange2 = new ClusterChange2(clusterUID, clusterImpl);\r
+ clusterStream.changes.add(this);\r
+ }\r
+\r
+ private void setHeaderVectorSize(int size) {\r
+ if (size < 0)\r
+ throw new RuntimeDatabaseException("Change set size can't be negative.");\r
+ int len = size + HEADER_SIZE - SIZE_OFFSET - 4;\r
+ Bytes.writeLE(header, SIZE_OFFSET, len);\r
+ }\r
+ @Override\r
+ public String toString() {\r
+ return super.toString() + " cluster=" + clusterImpl.getClusterUID() + " id=" + clusterImpl.getClusterId() + " off=" + byteIndex;\r
+ }\r
+ private final void initBuffer() {\r
+ flushed = false;\r
+ if (null == bytes || bytes.length < nextSize) {\r
+ bytes = new byte[nextSize];\r
+ nextSize = MAX_FIXED_BYTES;\r
+ }\r
+ byteIndex = 0;\r
+ }\r
+ private final void clear() {\r
+ if(clusterImpl != null && clusterImpl.change != null)\r
+ clusterImpl.change.init();\r
+ foreignTable.clear();\r
+ //initBuffer();\r
+ bytes = null;\r
+ byteIndex = 0;\r
+ if (DEBUG_STAT)\r
+ info.clear();\r
+ }\r
+ private final void checkInitialization() {\r
+ if (0 == byteIndex)\r
+ clusterStream.changes.addChange(this);\r
+ }\r
+ private final void printlnd(String s) {\r
+ System.out.println("DEBUG: ClusterChange " + clusterUID + ": " + s);\r
+ }\r
+ public final void createResource(short index) {\r
+ checkInitialization();\r
+ if (DEBUG)\r
+ printlnd("New ri=" + index + " offset=" + byteIndex);\r
+ if (index > ClusterTraits.getMaxNumberOfResources())\r
+ throw new RuntimeDatabaseException("Illegal resource index=" + index + ".");\r
+ checkBufferSpace(null);\r
+ bytes[byteIndex++] = (byte)52;\r
+ bytes[byteIndex++] = (byte)index;\r
+ bytes[byteIndex++] = (byte)(index>>>8);\r
+ }\r
+ void flushCollect(Change c) {\r
+ flushInternal(graphSession, clusterUID);\r
+ if (DEBUG)\r
+ printlnd("Cluster change data was flushed.");\r
+ if (null != c) {\r
+ if (DEBUG)\r
+ printlnd("Clearing lookup for " + c.toString());\r
+ c.lookup1 = null;\r
+ c.lookup2 = null;\r
+ }\r
+ if (null != clusterImpl) {\r
+ clusterImpl.foreignLookup = null;\r
+ }\r
+ }\r
+\r
+ private final boolean checkBufferSpace(Change c) {\r
+ clusterStream.changes.checkFlush();\r
+ if(bytes == null) initBuffer();\r
+ if (MAX_FIXED_BYTES - byteIndex > MAX_FIXED_OPERATION_SIZE_AND_ROOM_FOR_ERROR) {\r
+ return false;\r
+ }\r
+ flushCollect(c);\r
+ initBuffer();\r
+ return true;\r
+ }\r
+\r
+ private final void checkBufferSpace(int size) {\r
+ if(bytes == null) initBuffer();\r
+ if (bytes.length - byteIndex >= size)\r
+ return;\r
+ nextSize = Math.max(MAX_FIXED_BYTES, size);\r
+ flushInternal(graphSession, clusterUID);\r
+ initBuffer();\r
+ }\r
+\r
+ final void addChange(Change c) {\r
+ checkInitialization();\r
+ checkBufferSpace(c);\r
+ byte operation = c.op0;\r
+ if(operation == ADD_OPERATION)\r
+ addStm(c, StmEnum.Add);\r
+ else if (operation == REMOVE_OPERATION)\r
+ addStm(c, StmEnum.Remove);\r
+ else if (operation == DELETE_OPERATION) {\r
+ if (DEBUG)\r
+ printlnd("Delete value offset=" + byteIndex + " " + c);\r
+ addByte(OpEnum.Delete.getOrMask());\r
+ addShort(ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(c.key0));\r
+ }\r
+ c.lastArg = 0;\r
+ }\r
+\r
+ private final void addForeignLong(short index, ClusterUID clusterUID) {\r
+ byteIndex = clusterUID.toByte(bytes, byteIndex);\r
+ bytes[byteIndex++] = (byte)(index & 0xFF);\r
+ bytes[byteIndex++] = (byte)(index >>> 8);\r
+ }\r
+\r
+ private final ClusterEnum addIndexAndCluster(int key, ClusterUID clusterUID, byte lookIndex, byte[] lookup) {\r
+ assert(!clusterUID.equals(ClusterUID.Null));\r
+ short resourceIndex = ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(key);\r
+ if (clusterUID.equals(this.clusterUID)) {\r
+ bytes[byteIndex++] = (byte)(resourceIndex & 0xFF);\r
+ bytes[byteIndex++] = (byte)(resourceIndex >>> 8);\r
+ return ClusterEnum.Local;\r
+ }\r
+\r
+ byte foreign = 0;\r
+ if(lookIndex > 0) {\r
+ if(lookup != null)\r
+ foreign = lookup[lookIndex];\r
+ } else {\r
+ foreign = foreignTable.get(key);\r
+ }\r
+ if (0 != foreign) {\r
+ if (foreign > 256)\r
+ throw new RuntimeDatabaseException("Internal error, contact application support." +\r
+ "Too big foreing index=" + foreign + " max=256");\r
+ --foreign;\r
+ bytes[byteIndex++] = foreign;\r
+ return ClusterEnum.ForeignShort;\r
+ } else {\r
+ byte position = (byte) (foreignTable.size() + 1);\r
+ if(lookup != null)\r
+ lookup[lookIndex] = position;\r
+ foreignTable.put(key, position);\r
+ if (DEBUG_STAT)\r
+ info.sForeign = foreignTable.size();\r
+ if (clusterUID.equals(ClusterUID.Null))\r
+ throw new RuntimeDatabaseException("Internal error, contact application support." +\r
+ "Cluster unique id not defined for foreing cluster.");\r
+ addForeignLong(resourceIndex, clusterUID);\r
+ return ClusterEnum.ForeignLong;\r
+ }\r
+ }\r
+\r
+ private final void addByte(byte b) {\r
+ bytes[byteIndex++] = b;\r
+ }\r
+\r
+ private final void addShort(short s) {\r
+ bytes[byteIndex++] = (byte)(s & 0xFF);\r
+ bytes[byteIndex++] = (byte)(s >>> 8);\r
+ }\r
+\r
+// private final void addShort(int s) {\r
+// bytes[byteIndex++] = (byte) (s & 0xFF);\r
+// bytes[byteIndex++] = (byte) ((s >>> 8) & 0xFF);\r
+// }\r
+\r
+ private final void addInt(int i) {\r
+// System.err.println("addInt " + i + " " + i);\r
+ bytes[byteIndex++] = (byte) (i & 0xFF);\r
+ bytes[byteIndex++] = (byte) ((i >>> 8) & 0xFF);\r
+ bytes[byteIndex++] = (byte) ((i >>> 16) & 0xFF);\r
+ bytes[byteIndex++] = (byte) ((i >>> 24) & 0xFF);\r
+ // buffer.asIntBuffer().put(i);\r
+ // buffer.position(buffer.position()+4);\r
+ }\r
+\r
+// private void addLong6(long l) {\r
+//// System.err.println("addLong " + l);\r
+// bytes[byteIndex++] = (byte) (l & 0xFF);\r
+// bytes[byteIndex++] = (byte) ((l >>> 8) & 0xFF);\r
+// bytes[byteIndex++] = (byte) ((l >>> 16) & 0xFF);\r
+// bytes[byteIndex++] = (byte) ((l >>> 24) & 0xFF);\r
+// bytes[byteIndex++] = (byte) ((l >>> 32) & 0xFF);\r
+// bytes[byteIndex++] = (byte) ((l >>> 40) & 0xFF);\r
+// // buffer.asLongBuffer().put(l);\r
+// // buffer.position(buffer.position() + 6);\r
+// }\r
+\r
+ private void addLong7(long l) {\r
+ bytes[byteIndex++] = (byte) (l & 0xFF);\r
+ bytes[byteIndex++] = (byte) ((l >>> 8) & 0xFF);\r
+ bytes[byteIndex++] = (byte) ((l >>> 16) & 0xFF);\r
+ bytes[byteIndex++] = (byte) ((l >>> 24) & 0xFF);\r
+ bytes[byteIndex++] = (byte) ((l >>> 32) & 0xFF);\r
+ bytes[byteIndex++] = (byte) ((l >>> 40) & 0xFF);\r
+ bytes[byteIndex++] = (byte) ((l >>> 48) & 0xFF);\r
+ // buffer.asLongBuffer().put(l);\r
+ // buffer.position(buffer.position() + 7);\r
+ }\r
+\r
+// private void addLong(long l) {\r
+// bytes[byteIndex++] = (byte) (l & 0xFF);\r
+// bytes[byteIndex++] = (byte) ((l >>> 8) & 0xFF);\r
+// bytes[byteIndex++] = (byte) ((l >>> 16) & 0xFF);\r
+// bytes[byteIndex++] = (byte) ((l >>> 24) & 0xFF);\r
+// bytes[byteIndex++] = (byte) ((l >>> 32) & 0xFF);\r
+// bytes[byteIndex++] = (byte) ((l >>> 40) & 0xFF);\r
+// bytes[byteIndex++] = (byte) ((l >>> 48) & 0xFF);\r
+// bytes[byteIndex++] = (byte) ((l >>> 56) & 0xFF);\r
+// }\r
+ private final byte bufferPop() {\r
+ return bytes[--byteIndex];\r
+ }\r
+\r
+ final class DebugStm {\r
+ StmEnum e;\r
+ int r;\r
+ int p;\r
+ int o;\r
+ ClusterUID pc;\r
+ ClusterUID oc;\r
+\r
+ DebugStm(StmEnum e, int r, int p, ClusterUID pc, int o, ClusterUID oc) {\r
+ this.e = e;\r
+ this.r = r;\r
+ this.p = p;\r
+ this.o = o;\r
+ this.pc = pc;\r
+ this.oc = oc;\r
+ }\r
+\r
+ @Override\r
+ public String toString() {\r
+ short ri = ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(r);\r
+ short pi = ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(p);\r
+ short oi = ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(o);\r
+ return "" + e + " rk=" + r + " ri=" + ri + " rc=" + clusterUID\r
+ + " pk=" + p + " pi=" + pi + " pc=" + pc\r
+ + " ok=" + o + " oi=" + oi + " oc=" + oc;\r
+ }\r
+\r
+ public String toString2() {\r
+ return "" + e + " r=" + r + " rc=" + clusterUID + " p=" + p\r
+ + " pc=" + pc + " o=" + o + " oc=" + oc;\r
+ }\r
+\r
+ public String toString3() {\r
+ short ri = ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(r);\r
+ short pi = ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(p);\r
+ short oi = ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(o);\r
+ return "" + e + " ri=" + ri\r
+ + " pi=" + pi + " pc=" + pc\r
+ + " oi=" + oi + " oc=" + oc;\r
+ }\r
+ }\r
+\r
+ private List<DebugStm> debugStms = new ArrayList<DebugStm>();\r
+\r
+ @SuppressWarnings("unused")\r
+ private final void addStm(Change c, StmEnum stmEnum) {\r
+\r
+ if (DEBUG_STAT)\r
+ ++info.nStms;\r
+ if (DEBUG || DEBUG_CCS) {\r
+ DebugStm d = new DebugStm(stmEnum, c.key0, c.key1, c.clusterUID1, c.key2, c.clusterUID2);\r
+ if (DEBUG_CCS)\r
+ debugStms.add(d);\r
+ if (DEBUG) {\r
+ printlnd(d.toString3() + " offset=" + byteIndex);\r
+ }\r
+ }\r
+ // int opPos = buffer.position();\r
+ int opPos = byteIndex++;\r
+ // buffer.put((byte)0); // operation code\r
+ // addByte((byte)0);\r
+\r
+ boolean done = true;\r
+\r
+ ClusterEnum a = addIndexAndCluster(c.key1, c.clusterUID1, c.lookIndex1, c.lookup1);\r
+ byte ab = 0;\r
+\r
+ // ForeignShort = byte\r
+ // Local = short\r
+ // ForeignLong = 8 byte\r
+ if (a != ClusterEnum.ForeignShort) {\r
+ ab = bufferPop();\r
+ done = false;\r
+ }\r
+\r
+ ClusterEnum b = addIndexAndCluster(c.key2, c.clusterUID2, c.lookIndex2, c.lookup2);\r
+ byte bb = 0;\r
+ if (b != ClusterEnum.ForeignShort) {\r
+ bb = bufferPop();\r
+ done = false;\r
+ }\r
+\r
+ int ri = ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(c.key0);\r
+ if (ClusterTraitsSmall.isIllegalResourceIndex(ri))\r
+ throw new RuntimeDatabaseException("Assertion error. Illegal resource index=" + ri);\r
+ bytes[byteIndex++] = (byte)ri; // index low byte\r
+ if(!done) {\r
+ Data data = ClusterEnum.getData(stmEnum, a, b);\r
+ int left = 6 - data.bits;\r
+ int op = ri >>> (8 + left);\r
+ ri >>>= 8;\r
+ ri &= (1 << left) - 1;\r
+ if (a != ClusterEnum.ForeignShort) {\r
+ ri |= ab << left;\r
+ left += 6;\r
+ }\r
+ if (b != ClusterEnum.ForeignShort) {\r
+ ri |= bb << left;\r
+ left += 6;\r
+ }\r
+ switch (data.bytes) {\r
+ default:\r
+ throw new RuntimeDatabaseException("Assertion error. Illegal number of bytes=" + data.bytes);\r
+ case 2:\r
+ bytes[byteIndex++] = (byte)(ri & 0xFF);\r
+ bytes[byteIndex++] = (byte)((ri >>> 8) & 0xFF);\r
+ break;\r
+ case 1:\r
+ bytes[byteIndex++] = (byte)(ri & 0xFF);\r
+ break;\r
+ case 0:\r
+ break;\r
+ }\r
+ op |= data.mask;\r
+ this.bytes[opPos] = (byte)op;\r
+ } else {\r
+ if (stmEnum == StmEnum.Add)\r
+ bytes[opPos] = (byte)((ri >>> 8) + 64);\r
+ else\r
+ bytes[opPos] = (byte)((ri >>> 8) + 128);\r
+ }\r
+ if (DEBUG_STAT) {\r
+ if (a == ClusterEnum.Local && b == ClusterEnum.Local) {\r
+ ++info.nLocal;\r
+ } else if (a == ClusterEnum.Local || b == ClusterEnum.Local) {\r
+ ++info.nPartly;\r
+ } else {\r
+ ++info.nForeign;\r
+ }\r
+ }\r
+ if (foreignTable.size() > 252)\r
+ flushInternal(graphSession, clusterUID);\r
+ }\r
+\r
+ private final int modiValue(int ri, long value_offset, byte[] bytes, int offset, int size) {\r
+ if (DEBUG)\r
+ printlnd("Modify value ri=" + ri + " vo=" + value_offset + " size=" + size + " total=" + bytes.length);\r
+ if (ClusterTraitsBase.isIllegalResourceIndex(ri))\r
+ throw new RuntimeDatabaseException("Assertion error. Illegal resource index=" + ri);\r
+ if (value_offset > (1L << 58 - 1))\r
+ throw new RuntimeDatabaseException("Illegal value offset="\r
+ + value_offset);\r
+ if (size < 0 || size > MAX_FIXED_BYTES - 1)\r
+ throw new RuntimeDatabaseException("Illegal value size=" + size);\r
+ if (offset + size > bytes.length)\r
+ throw new RuntimeDatabaseException("Illegal value size=" + size);\r
+ checkBufferSpace(12 + size);\r
+ addByte(OpEnum.Modify.getOrMask());\r
+ ri |= (value_offset >>> 56) << 14; // add top two bits\r
+ addShort((short) ri);\r
+ value_offset &= (1L << 56) - 1;\r
+ addLong7(value_offset);\r
+ addShort((short) size);\r
+ if (DEBUG)\r
+ System.out.println("Modify value fixed part end offset=" + byteIndex);\r
+ int copied = Math.min(size, this.bytes.length - byteIndex);\r
+ System.arraycopy(bytes, offset, this.bytes, byteIndex, copied);\r
+ byteIndex += size;\r
+ return copied;\r
+ }\r
+\r
+ private final void modiValueBig(int ri, long voffset, int left, byte[] bytes, int offset) {\r
+ checkBufferSpace(0);\r
+ int current = Math.min(this.bytes.length - byteIndex - 12, left);\r
+ if(current >= 0) {\r
+ int written = modiValue(ri, voffset, bytes, offset, current);\r
+ voffset += written;\r
+ offset += written;\r
+ left -= written;\r
+ }\r
+ flushInternal(graphSession, clusterUID);\r
+ while (left > 0) {\r
+ int length = Math.min(left, (1 << 16) - 1);\r
+ if (DEBUG)\r
+ printlnd("Modify big value ri=" + ri + " vo=" + voffset + " len=" + length);\r
+ int psize = length + 12;\r
+ setHeaderVectorSize(psize);\r
+ byte[] message = new byte[psize+HEADER_SIZE];\r
+ System.arraycopy(header, 0, message, 0, HEADER_SIZE);\r
+ int to = HEADER_SIZE;\r
+ Bytes.write(message, to++, OpEnum.Modify.getOrMask());\r
+ short index = (short)(ri | (voffset >>> 56)<<14); // add top two bits\r
+ Bytes.writeLE(message, to, index); to += 2;\r
+ Bytes.writeLE7(message, to, voffset & ((1L << 56) - 1)); to += 7;\r
+ Bytes.writeLE(message, to, (short)length); to += 2;\r
+ System.arraycopy(bytes, offset, message, to, length);\r
+ graphSession.updateCluster(new UpdateClusterFunction(message));\r
+ voffset += length;\r
+ offset += length;\r
+ left -= length;\r
+ }\r
+ }\r
+\r
+ private final int setValueBig(int ri, byte[] bytes, int length_) {\r
+ checkBufferSpace(12);\r
+ int sum = 0;\r
+ int voffset = 0;\r
+ int offset = 0;\r
+ int left = length_;\r
+ while (left > 0) {\r
+ int length = Math.min(left, MAX_FIXED_BYTES - 12 - byteIndex);\r
+ if (DEBUG)\r
+ printlnd("Set big value ri=" + ri + " vo=" + voffset + " len=" + length);\r
+ int written = modiValue(ri, voffset, bytes, offset, length);\r
+ sum += written;\r
+ voffset += written;\r
+ offset += written;\r
+ left -= written;\r
+ checkBufferSpace(12);\r
+ }\r
+ return sum;\r
+ }\r
+\r
+ private final int setValueSmall(int ri, byte[] bytes, int length) {\r
+ checkBufferSpace(5 + length);\r
+ int pos = byteIndex;\r
+ int i = length << 14 | ri;\r
+ if (length < 32) {\r
+ byte op = (byte) (OpEnum.SetShort.getOrMask() | length >>> 2);\r
+ addByte(op);\r
+ short s = (short) i;\r
+ addShort(s);\r
+ } else {\r
+ addByte(OpEnum.Set.getOrMask());\r
+ addInt(i);\r
+ }\r
+ System.arraycopy(bytes, 0, this.bytes, byteIndex, length);\r
+ byteIndex += length;\r
+ int len = byteIndex - pos;\r
+ return len;\r
+ }\r
+\r
+ final void setValue(short index, byte[] bytes) {\r
+ setValue(index, bytes, bytes.length);\r
+ }\r
+\r
+ final void setValue(short index, byte[] bytes, int length) {\r
+ checkInitialization();\r
+ if (ClusterTraitsBase.isIllegalResourceIndex(index))\r
+ throw new RuntimeDatabaseException("Assertion error. Illegal resource index=" + index);\r
+ if (DEBUG)\r
+ printlnd("Set value ri=" + index\r
+ + " len=" + length\r
+ + " bytes=" + Arrays.toString(Arrays.copyOfRange(bytes, 0, Math.min(10, length))));\r
+ int len;\r
+ /*\r
+ * The limit for the cluster stream is (1<18)-1 but this avoids the\r
+ * conversion to big cluster.\r
+ */\r
+ if (length > ClusterTraitsSmall.VALUE_SIZE_MAX)\r
+ len = setValueBig(index, bytes, length);\r
+ else\r
+ len = setValueSmall(index, bytes, length);\r
+ if (DEBUG_STAT) {\r
+ ++info.nValues;\r
+ info.sValues += len + length;\r
+ }\r
+ }\r
+\r
+ final void setValue(Change c, byte[] bytes, int length) {\r
+ short ri = ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(c.key0);\r
+ setValue(ri, bytes, length);\r
+ c.initValue();\r
+ }\r
+\r
+ final void modiValue(Change c, long voffset, int length, byte[] bytes, int offset) {\r
+ checkInitialization();\r
+ int ri = ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(c.key0);\r
+ if (DEBUG)\r
+ printlnd("Modify value ri=" + ri\r
+ + " voff=" + voffset\r
+ + " vlen=" + length\r
+ + " blen=" + bytes.length\r
+ + " boff=" + offset\r
+ + " bytes=" + Arrays.toString(Arrays.copyOfRange(bytes, 0, Math.min(10, bytes.length))));\r
+ modiValueBig(ri, voffset, length, bytes, offset);\r
+ c.init();\r
+ if (DEBUG_STAT) {\r
+ ++info.nValues;\r
+ info.sValues += length;\r
+ }\r
+ }\r
+ final void setImmutable(boolean immutable) {\r
+ checkInitialization();\r
+ clusterChange2.setImmutable(immutable);\r
+ }\r
+ final void undoValueEx(int resourceIndex) {\r
+ checkInitialization();\r
+ clusterChange2.undoValueEx(resourceIndex);\r
+ }\r
+ final void setDeleted(boolean deleted) {\r
+ checkInitialization();\r
+ clusterChange2.setDeleted(deleted);\r
+ }\r
+ final void corrupt() {\r
+ checkInitialization();\r
+ addByte((byte)0);\r
+ }\r
+ /**\r
+ * @param graphSession\r
+ * @param clusterId\r
+ * @return true if actually flushed something\r
+ */\r
+ final boolean flush(GraphSession graphSession, ClusterUID clusterUID) {\r
+ if (byteIndex > 0) {\r
+ if(DebugPolicy.REPORT_CLUSTER_STREAM)\r
+ System.err.println("Flush cluster change set stream " + this);\r
+ setHeaderVectorSize(byteIndex);\r
+ byte[] copy = new byte[byteIndex + HEADER_SIZE];\r
+ System.arraycopy(header, 0, copy, 0, HEADER_SIZE);\r
+ System.arraycopy(bytes, 0, copy, HEADER_SIZE, byteIndex);\r
+ UpdateClusterFunction updateClusterFunction = new UpdateClusterFunction(copy);\r
+ if (DEBUG_CCS) {\r
+ for (DebugStm stm : debugStms)\r
+ printlnd(stm.toString2());\r
+ debugStms.clear();\r
+ }\r
+ if (DEBUG_STAT) {\r
+ info.tot = updateClusterFunction.operation.length;\r
+ printlnd("ReallyFlush: " + info.toString());\r
+ sum.add(info);\r
+ printlnd("ReallyFlush sum: " + sum.toString());\r
+ }\r
+ // long start = System.nanoTime();\r
+ graphSession.updateCluster(updateClusterFunction);\r
+ // long duration = System.nanoTime() - start;\r
+ // duration2 += duration;\r
+ // System.err.println("updateCluster " + 1e-9*duration);\r
+ // System.err.println("updateCluster total " + 1e-9*duration2);\r
+ clear();\r
+ clusterChange2.flush(graphSession);\r
+ return true;\r
+ } else if (clusterChange2.isDirty()) {\r
+ clusterChange2.flush(graphSession);\r
+ clear();\r
+ return true;\r
+ } else if (flushed) {\r
+ flushed = false;\r
+ return true;\r
+ } else {\r
+ return true;\r
+ }\r
+ }\r
+\r
+ final void flushInternal(GraphSession graphSession, ClusterUID clusterUID) {\r
+ flush(graphSession, clusterUID);\r
+ flushed = true;\r
+ }\r
+\r
+ final class ForeignTable {\r
+ private final TLongIntHashMap table = new TLongIntHashMap();\r
+\r
+ private long createKey(short index, long cluster) {\r
+ assert (cluster <= (1L << 48) - 1);\r
+ return (cluster << 14) | index;\r
+ }\r
+\r
+ public int get(short index, long cluster) {\r
+ int value = table.get(createKey(index, cluster));\r
+ if (DEBUG)\r
+ printlnd("ForeignTable get c=" + clusterUID + " i="\r
+ + (value - 1) + " r=" + index + " rc=" + cluster);\r
+ return value;\r
+ }\r
+\r
+ public int put(short index, long cluster, int value) {\r
+ if (DEBUG)\r
+ printlnd("ForeignTable put c=" + clusterUID + " i="\r
+ + (value - 1) + " r=" + index + " rc=" + cluster);\r
+ return table.put(createKey(index, cluster), value);\r
+ }\r
+\r
+ public int size() {\r
+ return table.size();\r
+ }\r
+\r
+ public void clear() {\r
+ table.clear();\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public int hashCode() {\r
+ return 31*clusterImpl.getClusterKey();\r
+ }\r
+\r
+ @Override\r
+ public boolean equals(Object object) {\r
+ if (this == object)\r
+ return true;\r
+ else if (object == null)\r
+ return false;\r
+ else if (!(object instanceof ClusterChange))\r
+ return false;\r
+ ClusterChange r = (ClusterChange)object;\r
+ return r.clusterImpl.getClusterKey() == clusterImpl.getClusterKey();\r
+ }\r
+\r
+\r
+}\r