]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.procore/src/fi/vtt/simantics/procore/internal/ClusterChange.java
Merge commit '7684bae'
[simantics/platform.git] / bundles / org.simantics.db.procore / src / fi / vtt / simantics / procore / internal / ClusterChange.java
1 package fi.vtt.simantics.procore.internal;\r
2 \r
3 import java.util.ArrayList;\r
4 import java.util.Arrays;\r
5 import java.util.List;\r
6 \r
7 import org.simantics.db.exception.RuntimeDatabaseException;\r
8 import org.simantics.db.impl.ClusterTraitsBase;\r
9 import org.simantics.db.procore.cluster.ClusterImpl;\r
10 import org.simantics.db.procore.cluster.ClusterTraits;\r
11 import org.simantics.db.procore.cluster.ClusterTraitsSmall;\r
12 import org.simantics.db.service.Bytes;\r
13 import org.simantics.db.service.ClusterUID;\r
14 \r
15 import fi.vtt.simantics.procore.DebugPolicy;\r
16 import fi.vtt.simantics.procore.internal.ClusterStream.ClusterEnum;\r
17 import fi.vtt.simantics.procore.internal.ClusterStream.Data;\r
18 import fi.vtt.simantics.procore.internal.ClusterStream.DebugInfo;\r
19 import fi.vtt.simantics.procore.internal.ClusterStream.OpEnum;\r
20 import fi.vtt.simantics.procore.internal.ClusterStream.StmEnum;\r
21 import gnu.trove.map.hash.TIntByteHashMap;\r
22 import gnu.trove.map.hash.TLongIntHashMap;\r
23 \r
24 public final class ClusterChange {\r
25     public static final int VERSION = 1;\r
26     public static final byte ADD_OPERATION = 2;\r
27     public static final byte REMOVE_OPERATION = 3;\r
28     public static final byte DELETE_OPERATION = 5;\r
29 \r
30     public static final boolean DEBUG = false;\r
31     public static final boolean DEBUG_STAT = false;\r
32     public static final boolean DEBUG_CCS = false;\r
33 \r
34     private static DebugInfo sum = new DebugInfo();\r
35 \r
36     public final TIntByteHashMap foreignTable = new TIntByteHashMap();\r
37     private final DebugInfo info;\r
38     private final GraphSession graphSession;\r
39     public final ClusterUID clusterUID;\r
40     private final int SIZE_OFFSET;\r
41     private final int HEADER_SIZE;\r
42     // How much buffer is used before stream is flushed to server. The bigger the better.\r
43     public static final int MAX_FIXED_BYTES = (1<<15) + (1<<14);\r
44     private static final int MAX_FIXED_OPERATION_SIZE = 17 + 16;\r
45     private static final int MAX_FIXED_OPERATION_SIZE_AND_ROOM_FOR_ERROR = MAX_FIXED_OPERATION_SIZE + 36;\r
46     private int nextSize = MAX_FIXED_BYTES;\r
47     int byteIndex = 0;\r
48     private byte[] bytes = null; // Operation data.\r
49     private final byte[] header;\r
50     private boolean flushed = false;\r
51     private final ClusterStream clusterStream;\r
52     private final ClusterChange2 clusterChange2;\r
53 \r
54     public ClusterImpl clusterImpl;\r
55 \r
56     public ClusterChange(ClusterStream clusterStream, ClusterImpl clusterImpl) {\r
57         this.clusterImpl = clusterImpl;\r
58         clusterUID = clusterImpl.getClusterUID();\r
59         long[] longs = new long[ClusterUID.getLongLength()];\r
60         clusterUID.toLong(longs, 0);\r
61         this.graphSession = clusterStream.graphSession;\r
62         info = new DebugInfo();\r
63         HEADER_SIZE = 8 + longs.length * 8;\r
64         header = new byte[HEADER_SIZE];\r
65         SIZE_OFFSET = 0;\r
66         Bytes.writeLE(header, SIZE_OFFSET + 0, 0); // Correct byte vector size is set with setHeaderVectorSize() later.\r
67         Bytes.writeLE(header, SIZE_OFFSET + 4, VERSION);\r
68         for (int i=0, offset=8; i<longs.length; ++i, offset+=8)\r
69             Bytes.writeLE(header, offset, longs[i]);\r
70         //initBuffer();\r
71         this.clusterStream = clusterStream;\r
72         this.clusterChange2 = new ClusterChange2(clusterUID, clusterImpl);\r
73         clusterStream.changes.add(this);\r
74     }\r
75 \r
76     private void setHeaderVectorSize(int size) {\r
77         if (size < 0)\r
78             throw new RuntimeDatabaseException("Change set size can't be negative.");\r
79         int len = size + HEADER_SIZE - SIZE_OFFSET - 4;\r
80         Bytes.writeLE(header, SIZE_OFFSET, len);\r
81     }\r
82     @Override\r
83     public String toString() {\r
84         return super.toString() + " cluster=" + clusterImpl.getClusterUID() + " id=" + clusterImpl.getClusterId() + " off=" + byteIndex;\r
85     }\r
86     private final void initBuffer() {\r
87         flushed = false;\r
88         if (null == bytes || bytes.length < nextSize) {\r
89             bytes = new byte[nextSize];\r
90             nextSize = MAX_FIXED_BYTES;\r
91         }\r
92         byteIndex = 0;\r
93     }\r
94     private final void clear() {\r
95         if(clusterImpl != null && clusterImpl.change != null)\r
96                 clusterImpl.change.init();\r
97         foreignTable.clear();\r
98         //initBuffer();\r
99         bytes = null;\r
100         byteIndex = 0;\r
101         if (DEBUG_STAT)\r
102             info.clear();\r
103     }\r
104     private final void checkInitialization() {\r
105         if (0 == byteIndex)\r
106             clusterStream.changes.addChange(this);\r
107     }\r
108     private final void printlnd(String s) {\r
109         System.out.println("DEBUG: ClusterChange " + clusterUID + ": " + s);\r
110     }\r
111     public final void createResource(short index) {\r
112         checkInitialization();\r
113         if (DEBUG)\r
114             printlnd("New ri=" + index + " offset=" + byteIndex);\r
115         if (index > ClusterTraits.getMaxNumberOfResources())\r
116             throw new RuntimeDatabaseException("Illegal resource index=" + index + ".");\r
117         checkBufferSpace(null);\r
118         bytes[byteIndex++] = (byte)52;\r
119         bytes[byteIndex++] = (byte)index;\r
120         bytes[byteIndex++] = (byte)(index>>>8);\r
121     }\r
122     void flushCollect(Change c) {\r
123         flushInternal(graphSession, clusterUID);\r
124         if (DEBUG)\r
125             printlnd("Cluster change data was flushed.");\r
126         if (null != c) {\r
127             if (DEBUG)\r
128                 printlnd("Clearing lookup for " + c.toString());\r
129             c.lookup1 = null;\r
130             c.lookup2 = null;\r
131         }\r
132         if (null != clusterImpl) {\r
133             clusterImpl.foreignLookup = null;\r
134         }\r
135     }\r
136 \r
137     private final boolean checkBufferSpace(Change c) {\r
138         clusterStream.changes.checkFlush();\r
139         if(bytes == null) initBuffer();\r
140         if (MAX_FIXED_BYTES - byteIndex > MAX_FIXED_OPERATION_SIZE_AND_ROOM_FOR_ERROR) {\r
141             return false;\r
142         }\r
143         flushCollect(c);\r
144         initBuffer();\r
145         return true;\r
146     }\r
147 \r
148     private final void checkBufferSpace(int size) {\r
149         if(bytes == null) initBuffer();\r
150         if (bytes.length - byteIndex >= size)\r
151             return;\r
152         nextSize = Math.max(MAX_FIXED_BYTES, size);\r
153         flushInternal(graphSession, clusterUID);\r
154         initBuffer();\r
155     }\r
156 \r
157     final void addChange(Change c) {\r
158         checkInitialization();\r
159         checkBufferSpace(c);\r
160         byte operation = c.op0;\r
161         if(operation == ADD_OPERATION)\r
162             addStm(c, StmEnum.Add);\r
163         else if (operation == REMOVE_OPERATION)\r
164             addStm(c, StmEnum.Remove);\r
165         else if (operation == DELETE_OPERATION) {\r
166             if (DEBUG)\r
167                 printlnd("Delete value offset=" + byteIndex + " " + c);\r
168             addByte(OpEnum.Delete.getOrMask());\r
169             addShort(ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(c.key0));\r
170         }\r
171         c.lastArg = 0;\r
172     }\r
173 \r
174     private final void addForeignLong(short index, ClusterUID clusterUID) {\r
175         byteIndex = clusterUID.toByte(bytes, byteIndex);\r
176         bytes[byteIndex++] = (byte)(index & 0xFF);\r
177         bytes[byteIndex++] = (byte)(index >>> 8);\r
178     }\r
179 \r
180     private final ClusterEnum addIndexAndCluster(int key, ClusterUID clusterUID, byte lookIndex, byte[] lookup) {\r
181         assert(!clusterUID.equals(ClusterUID.Null));\r
182         short resourceIndex = ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(key);\r
183         if (clusterUID.equals(this.clusterUID)) {\r
184             bytes[byteIndex++] = (byte)(resourceIndex & 0xFF);\r
185             bytes[byteIndex++] = (byte)(resourceIndex >>> 8);\r
186             return ClusterEnum.Local;\r
187         }\r
188 \r
189         byte foreign = 0;\r
190         if(lookIndex > 0) {\r
191             if(lookup != null)\r
192                 foreign = lookup[lookIndex];\r
193         } else {\r
194             foreign = foreignTable.get(key);\r
195         }\r
196         if (0 != foreign) {\r
197             if (foreign > 256)\r
198                 throw new RuntimeDatabaseException("Internal error, contact application support." +\r
199                 "Too big foreing index=" + foreign + " max=256");\r
200             --foreign;\r
201             bytes[byteIndex++] = foreign;\r
202             return ClusterEnum.ForeignShort;\r
203         } else {\r
204             byte position = (byte) (foreignTable.size() + 1);\r
205             if(lookup != null)\r
206                 lookup[lookIndex] = position;\r
207             foreignTable.put(key, position);\r
208             if (DEBUG_STAT)\r
209                 info.sForeign = foreignTable.size();\r
210             if (clusterUID.equals(ClusterUID.Null))\r
211                 throw new RuntimeDatabaseException("Internal error, contact application support." +\r
212                 "Cluster unique id not defined for foreing cluster.");\r
213             addForeignLong(resourceIndex, clusterUID);\r
214             return ClusterEnum.ForeignLong;\r
215         }\r
216     }\r
217 \r
218     private final void addByte(byte b) {\r
219         bytes[byteIndex++] = b;\r
220     }\r
221 \r
222     private final void addShort(short s) {\r
223         bytes[byteIndex++] = (byte)(s & 0xFF);\r
224         bytes[byteIndex++] = (byte)(s >>> 8);\r
225     }\r
226 \r
227 //    private final void addShort(int s) {\r
228 //        bytes[byteIndex++] = (byte) (s & 0xFF);\r
229 //        bytes[byteIndex++] = (byte) ((s >>> 8) & 0xFF);\r
230 //    }\r
231 \r
232     private final void addInt(int i) {\r
233 //        System.err.println("addInt " + i + " " + i);\r
234         bytes[byteIndex++] = (byte) (i & 0xFF);\r
235         bytes[byteIndex++] = (byte) ((i >>> 8) & 0xFF);\r
236         bytes[byteIndex++] = (byte) ((i >>> 16) & 0xFF);\r
237         bytes[byteIndex++] = (byte) ((i >>> 24) & 0xFF);\r
238         // buffer.asIntBuffer().put(i);\r
239         // buffer.position(buffer.position()+4);\r
240     }\r
241 \r
242 //    private void addLong6(long l) {\r
243 ////        System.err.println("addLong " + l);\r
244 //        bytes[byteIndex++] = (byte) (l & 0xFF);\r
245 //        bytes[byteIndex++] = (byte) ((l >>> 8) & 0xFF);\r
246 //        bytes[byteIndex++] = (byte) ((l >>> 16) & 0xFF);\r
247 //        bytes[byteIndex++] = (byte) ((l >>> 24) & 0xFF);\r
248 //        bytes[byteIndex++] = (byte) ((l >>> 32) & 0xFF);\r
249 //        bytes[byteIndex++] = (byte) ((l >>> 40) & 0xFF);\r
250 //        // buffer.asLongBuffer().put(l);\r
251 //        // buffer.position(buffer.position() + 6);\r
252 //    }\r
253 \r
254     private void addLong7(long l) {\r
255         bytes[byteIndex++] = (byte) (l & 0xFF);\r
256         bytes[byteIndex++] = (byte) ((l >>> 8) & 0xFF);\r
257         bytes[byteIndex++] = (byte) ((l >>> 16) & 0xFF);\r
258         bytes[byteIndex++] = (byte) ((l >>> 24) & 0xFF);\r
259         bytes[byteIndex++] = (byte) ((l >>> 32) & 0xFF);\r
260         bytes[byteIndex++] = (byte) ((l >>> 40) & 0xFF);\r
261         bytes[byteIndex++] = (byte) ((l >>> 48) & 0xFF);\r
262         // buffer.asLongBuffer().put(l);\r
263         // buffer.position(buffer.position() + 7);\r
264     }\r
265 \r
266 //    private void addLong(long l) {\r
267 //        bytes[byteIndex++] = (byte) (l & 0xFF);\r
268 //        bytes[byteIndex++] = (byte) ((l >>> 8) & 0xFF);\r
269 //        bytes[byteIndex++] = (byte) ((l >>> 16) & 0xFF);\r
270 //        bytes[byteIndex++] = (byte) ((l >>> 24) & 0xFF);\r
271 //        bytes[byteIndex++] = (byte) ((l >>> 32) & 0xFF);\r
272 //        bytes[byteIndex++] = (byte) ((l >>> 40) & 0xFF);\r
273 //        bytes[byteIndex++] = (byte) ((l >>> 48) & 0xFF);\r
274 //        bytes[byteIndex++] = (byte) ((l >>> 56) & 0xFF);\r
275 //    }\r
276     private final byte bufferPop() {\r
277         return bytes[--byteIndex];\r
278     }\r
279 \r
280     final class DebugStm {\r
281         StmEnum e;\r
282         int r;\r
283         int p;\r
284         int o;\r
285         ClusterUID pc;\r
286         ClusterUID oc;\r
287 \r
288         DebugStm(StmEnum e, int r, int p, ClusterUID pc, int o, ClusterUID oc) {\r
289             this.e = e;\r
290             this.r = r;\r
291             this.p = p;\r
292             this.o = o;\r
293             this.pc = pc;\r
294             this.oc = oc;\r
295         }\r
296 \r
297         @Override\r
298         public String toString() {\r
299             short ri = ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(r);\r
300             short pi = ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(p);\r
301             short oi = ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(o);\r
302             return "" + e + " rk=" + r + " ri=" + ri + " rc=" + clusterUID\r
303             + " pk=" + p + " pi=" + pi + " pc=" + pc\r
304             + " ok=" + o + " oi=" + oi + " oc=" + oc;\r
305         }\r
306 \r
307         public String toString2() {\r
308             return "" + e + " r=" + r + " rc=" + clusterUID + " p=" + p\r
309                     + " pc=" + pc + " o=" + o + " oc=" + oc;\r
310         }\r
311 \r
312         public String toString3() {\r
313             short ri = ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(r);\r
314             short pi = ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(p);\r
315             short oi = ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(o);\r
316             return "" + e + " ri=" + ri\r
317             + " pi=" + pi + " pc=" + pc\r
318             + " oi=" + oi + " oc=" + oc;\r
319         }\r
320     }\r
321 \r
322     private List<DebugStm> debugStms = new ArrayList<DebugStm>();\r
323 \r
324     @SuppressWarnings("unused")\r
325     private final void addStm(Change c, StmEnum stmEnum) {\r
326 \r
327         if (DEBUG_STAT)\r
328             ++info.nStms;\r
329         if (DEBUG || DEBUG_CCS) {\r
330             DebugStm d = new DebugStm(stmEnum, c.key0, c.key1, c.clusterUID1, c.key2, c.clusterUID2);\r
331             if (DEBUG_CCS)\r
332                 debugStms.add(d);\r
333             if (DEBUG) {\r
334                 printlnd(d.toString3() + " offset=" + byteIndex);\r
335             }\r
336         }\r
337         // int opPos = buffer.position();\r
338         int opPos = byteIndex++;\r
339         // buffer.put((byte)0); // operation code\r
340         // addByte((byte)0);\r
341 \r
342         boolean done = true;\r
343 \r
344         ClusterEnum a = addIndexAndCluster(c.key1, c.clusterUID1, c.lookIndex1, c.lookup1);\r
345         byte ab = 0;\r
346 \r
347         // ForeignShort = byte\r
348         // Local = short\r
349         // ForeignLong = 8 byte\r
350         if (a != ClusterEnum.ForeignShort) {\r
351             ab = bufferPop();\r
352             done = false;\r
353         }\r
354 \r
355         ClusterEnum b = addIndexAndCluster(c.key2, c.clusterUID2, c.lookIndex2, c.lookup2);\r
356         byte bb = 0;\r
357         if (b != ClusterEnum.ForeignShort) {\r
358             bb = bufferPop();\r
359             done = false;\r
360         }\r
361 \r
362         int ri = ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(c.key0);\r
363         if (ClusterTraitsSmall.isIllegalResourceIndex(ri))\r
364             throw new RuntimeDatabaseException("Assertion error. Illegal resource index=" + ri);\r
365         bytes[byteIndex++] = (byte)ri; // index low byte\r
366         if(!done) {\r
367             Data data = ClusterEnum.getData(stmEnum, a, b);\r
368             int left = 6 - data.bits;\r
369             int op = ri >>> (8 + left);\r
370             ri >>>= 8;\r
371             ri &= (1 << left) - 1;\r
372             if (a != ClusterEnum.ForeignShort) {\r
373                 ri |= ab << left;\r
374                 left += 6;\r
375             }\r
376             if (b != ClusterEnum.ForeignShort) {\r
377                 ri |= bb << left;\r
378                 left += 6;\r
379             }\r
380             switch (data.bytes) {\r
381                 default:\r
382                     throw new RuntimeDatabaseException("Assertion error. Illegal number of bytes=" + data.bytes);\r
383                 case 2:\r
384                     bytes[byteIndex++] = (byte)(ri & 0xFF);\r
385                     bytes[byteIndex++] = (byte)((ri >>> 8) & 0xFF);\r
386                     break;\r
387                 case 1:\r
388                     bytes[byteIndex++] = (byte)(ri & 0xFF);\r
389                     break;\r
390                 case 0:\r
391                     break;\r
392             }\r
393             op |= data.mask;\r
394             this.bytes[opPos] = (byte)op;\r
395         } else {\r
396             if (stmEnum == StmEnum.Add)\r
397                 bytes[opPos] = (byte)((ri >>> 8) + 64);\r
398             else\r
399                 bytes[opPos] = (byte)((ri >>> 8) + 128);\r
400         }\r
401         if (DEBUG_STAT) {\r
402             if (a == ClusterEnum.Local && b == ClusterEnum.Local) {\r
403                 ++info.nLocal;\r
404             } else if (a == ClusterEnum.Local || b == ClusterEnum.Local) {\r
405                 ++info.nPartly;\r
406             } else {\r
407                 ++info.nForeign;\r
408             }\r
409         }\r
410         if (foreignTable.size() > 252)\r
411             flushInternal(graphSession, clusterUID);\r
412     }\r
413 \r
414     private final int modiValue(int ri, long value_offset, byte[] bytes, int offset, int size) {\r
415         if (DEBUG)\r
416             printlnd("Modify value ri=" + ri + " vo=" + value_offset + " size=" + size + " total=" + bytes.length);\r
417         if (ClusterTraitsBase.isIllegalResourceIndex(ri))\r
418             throw new RuntimeDatabaseException("Assertion error. Illegal resource index=" + ri);\r
419         if (value_offset > (1L << 58 - 1))\r
420             throw new RuntimeDatabaseException("Illegal value offset="\r
421                     + value_offset);\r
422         if (size < 0 || size > MAX_FIXED_BYTES - 1)\r
423             throw new RuntimeDatabaseException("Illegal value size=" + size);\r
424         if (offset + size > bytes.length)\r
425             throw new RuntimeDatabaseException("Illegal value size=" + size);\r
426         checkBufferSpace(12 + size);\r
427         addByte(OpEnum.Modify.getOrMask());\r
428         ri |= (value_offset >>> 56) << 14; // add top two bits\r
429         addShort((short) ri);\r
430         value_offset &= (1L << 56) - 1;\r
431         addLong7(value_offset);\r
432         addShort((short) size);\r
433         if (DEBUG)\r
434             System.out.println("Modify value fixed part end offset=" + byteIndex);\r
435         int copied = Math.min(size, this.bytes.length - byteIndex);\r
436         System.arraycopy(bytes, offset, this.bytes, byteIndex, copied);\r
437         byteIndex += size;\r
438         return copied;\r
439     }\r
440 \r
441     private final void modiValueBig(int ri, long voffset, int left, byte[] bytes, int offset) {\r
442         checkBufferSpace(0);\r
443         int current = Math.min(this.bytes.length - byteIndex - 12, left);\r
444         if(current >= 0) {\r
445             int written = modiValue(ri, voffset, bytes, offset, current);\r
446             voffset += written;\r
447             offset += written;\r
448             left -= written;\r
449         }\r
450         flushInternal(graphSession, clusterUID);\r
451         while (left > 0) {\r
452             int length = Math.min(left, (1 << 16) - 1);\r
453             if (DEBUG)\r
454                 printlnd("Modify big value ri=" + ri + " vo=" + voffset + " len=" + length);\r
455             int psize = length + 12;\r
456             setHeaderVectorSize(psize);\r
457             byte[] message = new byte[psize+HEADER_SIZE];\r
458             System.arraycopy(header, 0, message, 0, HEADER_SIZE);\r
459             int to = HEADER_SIZE;\r
460             Bytes.write(message, to++, OpEnum.Modify.getOrMask());\r
461             short index = (short)(ri | (voffset >>> 56)<<14); // add top two bits\r
462             Bytes.writeLE(message, to, index); to += 2;\r
463             Bytes.writeLE7(message, to, voffset & ((1L << 56) - 1)); to += 7;\r
464             Bytes.writeLE(message, to, (short)length); to += 2;\r
465             System.arraycopy(bytes, offset, message, to, length);\r
466             graphSession.updateCluster(new UpdateClusterFunction(message));\r
467             voffset += length;\r
468             offset += length;\r
469             left -= length;\r
470         }\r
471     }\r
472 \r
473     private final int setValueBig(int ri, byte[] bytes, int length_) {\r
474         checkBufferSpace(12);\r
475         int sum = 0;\r
476         int voffset = 0;\r
477         int offset = 0;\r
478         int left = length_;\r
479         while (left > 0) {\r
480             int length = Math.min(left, MAX_FIXED_BYTES - 12 - byteIndex);\r
481             if (DEBUG)\r
482                 printlnd("Set big value ri=" + ri + " vo=" + voffset + " len=" + length);\r
483             int written = modiValue(ri, voffset, bytes, offset, length);\r
484             sum += written;\r
485             voffset += written;\r
486             offset += written;\r
487             left -= written;\r
488             checkBufferSpace(12);\r
489         }\r
490         return sum;\r
491     }\r
492 \r
493     private final int setValueSmall(int ri, byte[] bytes, int length) {\r
494         checkBufferSpace(5 + length);\r
495         int pos = byteIndex;\r
496         int i = length << 14 | ri;\r
497         if (length < 32) {\r
498             byte op = (byte) (OpEnum.SetShort.getOrMask() | length >>> 2);\r
499             addByte(op);\r
500             short s = (short) i;\r
501             addShort(s);\r
502         } else {\r
503             addByte(OpEnum.Set.getOrMask());\r
504             addInt(i);\r
505         }\r
506         System.arraycopy(bytes, 0, this.bytes, byteIndex, length);\r
507         byteIndex += length;\r
508         int len = byteIndex - pos;\r
509         return len;\r
510     }\r
511 \r
512     final void setValue(short index, byte[] bytes) {\r
513         setValue(index, bytes, bytes.length);\r
514     }\r
515 \r
516     final void setValue(short index, byte[] bytes, int length) {\r
517         checkInitialization();\r
518         if (ClusterTraitsBase.isIllegalResourceIndex(index))\r
519             throw new RuntimeDatabaseException("Assertion error. Illegal resource index=" + index);\r
520         if (DEBUG)\r
521             printlnd("Set value ri=" + index\r
522                     + " len=" + length\r
523                     + " bytes=" + Arrays.toString(Arrays.copyOfRange(bytes, 0, Math.min(10, length))));\r
524         int len;\r
525         /*\r
526          * The limit for the cluster stream is (1<18)-1 but this avoids the\r
527          * conversion to big cluster.\r
528          */\r
529         if (length > ClusterTraitsSmall.VALUE_SIZE_MAX)\r
530             len = setValueBig(index, bytes, length);\r
531         else\r
532             len = setValueSmall(index, bytes, length);\r
533         if (DEBUG_STAT) {\r
534             ++info.nValues;\r
535             info.sValues += len + length;\r
536         }\r
537     }\r
538 \r
539     final void setValue(Change c, byte[] bytes, int length) {\r
540         short ri = ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(c.key0);\r
541         setValue(ri, bytes, length);\r
542         c.initValue();\r
543     }\r
544 \r
545     final void modiValue(Change c, long voffset, int length, byte[] bytes, int offset) {\r
546         checkInitialization();\r
547         int ri = ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(c.key0);\r
548         if (DEBUG)\r
549             printlnd("Modify value ri=" + ri\r
550                     + " voff=" + voffset\r
551                     + " vlen=" + length\r
552                     + " blen=" + bytes.length\r
553                     + " boff=" + offset\r
554                     + " bytes=" + Arrays.toString(Arrays.copyOfRange(bytes, 0, Math.min(10, bytes.length))));\r
555         modiValueBig(ri, voffset, length, bytes, offset);\r
556         c.init();\r
557         if (DEBUG_STAT) {\r
558             ++info.nValues;\r
559             info.sValues += length;\r
560         }\r
561     }\r
562     final void setImmutable(boolean immutable) {\r
563         checkInitialization();\r
564         clusterChange2.setImmutable(immutable);\r
565     }\r
566     final void undoValueEx(int resourceIndex) {\r
567         checkInitialization();\r
568         clusterChange2.undoValueEx(resourceIndex);\r
569     }\r
570     final void setDeleted(boolean deleted) {\r
571         checkInitialization();\r
572         clusterChange2.setDeleted(deleted);\r
573     }\r
574     final void corrupt() {\r
575         checkInitialization();\r
576         addByte((byte)0);\r
577     }\r
578     /**\r
579      * @param graphSession\r
580      * @param clusterId\r
581      * @return true if actually flushed something\r
582      */\r
583     final boolean flush(GraphSession graphSession, ClusterUID clusterUID) {\r
584         if (byteIndex > 0) {\r
585             if(DebugPolicy.REPORT_CLUSTER_STREAM)\r
586                 System.err.println("Flush cluster change set stream " + this);\r
587             setHeaderVectorSize(byteIndex);\r
588             byte[] copy = new byte[byteIndex + HEADER_SIZE];\r
589             System.arraycopy(header, 0, copy, 0, HEADER_SIZE);\r
590             System.arraycopy(bytes, 0, copy, HEADER_SIZE, byteIndex);\r
591             UpdateClusterFunction updateClusterFunction = new UpdateClusterFunction(copy);\r
592             if (DEBUG_CCS) {\r
593                 for (DebugStm stm : debugStms)\r
594                     printlnd(stm.toString2());\r
595                 debugStms.clear();\r
596             }\r
597             if (DEBUG_STAT) {\r
598                 info.tot = updateClusterFunction.operation.length;\r
599                 printlnd("ReallyFlush: " + info.toString());\r
600                 sum.add(info);\r
601                 printlnd("ReallyFlush sum: " + sum.toString());\r
602             }\r
603             // long start = System.nanoTime();\r
604             graphSession.updateCluster(updateClusterFunction);\r
605             // long duration = System.nanoTime() - start;\r
606             // duration2 += duration;\r
607             // System.err.println("updateCluster " + 1e-9*duration);\r
608             // System.err.println("updateCluster total " + 1e-9*duration2);\r
609             clear();\r
610             clusterChange2.flush(graphSession);\r
611             return true;\r
612         } else if (clusterChange2.isDirty()) {\r
613             clusterChange2.flush(graphSession);\r
614             clear();\r
615             return true;\r
616         } else if (flushed) {\r
617             flushed = false;\r
618             return true;\r
619         } else {\r
620             return true;\r
621         }\r
622     }\r
623 \r
624     final void flushInternal(GraphSession graphSession, ClusterUID clusterUID) {\r
625         flush(graphSession, clusterUID);\r
626         flushed = true;\r
627     }\r
628 \r
629     final class ForeignTable {\r
630         private final TLongIntHashMap table = new TLongIntHashMap();\r
631 \r
632         private long createKey(short index, long cluster) {\r
633             assert (cluster <= (1L << 48) - 1);\r
634             return (cluster << 14) | index;\r
635         }\r
636 \r
637         public int get(short index, long cluster) {\r
638             int value = table.get(createKey(index, cluster));\r
639             if (DEBUG)\r
640                 printlnd("ForeignTable get c=" + clusterUID + " i="\r
641                         + (value - 1) + " r=" + index + " rc=" + cluster);\r
642             return value;\r
643         }\r
644 \r
645         public int put(short index, long cluster, int value) {\r
646             if (DEBUG)\r
647                 printlnd("ForeignTable put c=" + clusterUID + " i="\r
648                         + (value - 1) + " r=" + index + " rc=" + cluster);\r
649             return table.put(createKey(index, cluster), value);\r
650         }\r
651 \r
652         public int size() {\r
653             return table.size();\r
654         }\r
655 \r
656         public void clear() {\r
657             table.clear();\r
658         }\r
659     }\r
660 \r
661     @Override\r
662     public int hashCode() {\r
663         return 31*clusterImpl.getClusterKey();\r
664     }\r
665 \r
666     @Override\r
667     public boolean equals(Object object) {\r
668         if (this == object)\r
669             return true;\r
670         else if (object == null)\r
671             return false;\r
672         else if (!(object instanceof ClusterChange))\r
673             return false;\r
674         ClusterChange r = (ClusterChange)object;\r
675         return r.clusterImpl.getClusterKey() == clusterImpl.getClusterKey();\r
676     }\r
677 \r
678 \r
679 }\r