]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.acorn/src/org/simantics/acorn/internal/ClusterUpdateProcessorBase.java
NPE fix for Acorn cluster stream undo handling
[simantics/platform.git] / bundles / org.simantics.acorn / src / org / simantics / acorn / internal / ClusterUpdateProcessorBase.java
1 package org.simantics.acorn.internal;
2
3 import java.util.Arrays;
4 import java.util.HashMap;
5 import java.util.Map;
6
7 import org.simantics.acorn.ClusterManager;
8 import org.simantics.acorn.exception.IllegalAcornStateException;
9 import org.simantics.acorn.internal.ClusterStream.ClusterEnum;
10 import org.simantics.acorn.internal.ClusterStream.Data;
11 import org.simantics.acorn.internal.ClusterStream.StmEnum;
12 import org.simantics.db.exception.DatabaseException;
13 import org.simantics.db.service.Bytes;
14 import org.simantics.db.service.ClusterUID;
15 import org.slf4j.Logger;
16 import org.slf4j.LoggerFactory;
17
18 import fi.vtt.simantics.procore.internal.ClusterChange2;
19
20 abstract public class ClusterUpdateProcessorBase {
21
22         private static final Logger LOGGER = LoggerFactory.getLogger(ClusterUpdateProcessorBase.class);
23
24         public final static boolean DEBUG = false;
25
26         final protected ClusterManager manager;
27         final public byte[] bytes;
28         private int pos = 0;
29         final private int len;
30         final private ClusterUID uid;
31         final private int clusterKey;
32         final public int version;
33
34         final Map<ClusterUID, Integer> clusterKeyCache = new HashMap<>();
35
36         public int getResourceKey(ClusterUID uid, int index) throws IllegalAcornStateException {
37                 Integer match = clusterKeyCache.get(uid);
38                 if(match != null) return match+index;
39                 int key = manager.getResourceKeyWitoutMutex(uid, 0);
40                 clusterKeyCache.put(uid, key);
41                 return key+index;
42         }
43         
44         
45         public ClusterUpdateProcessorBase(ClusterManager client, byte[] operations) throws DatabaseException {
46                 this.manager = client;
47                 this.bytes = operations;
48                 this.len = Bytes.readLE4(bytes, 0)+4; // whatta?
49                 this.version = Bytes.readLE4(bytes, 4);
50                 long cuid1 = Bytes.readLE8(bytes, 8);
51                 long cuid2 = Bytes.readLE8(bytes, 16);
52                 uid = ClusterUID.make(cuid1, cuid2);
53                 pos = 24;
54                 client.clusterLRU.acquireMutex();
55                 try {
56                         clusterKey = client.clusterLRU.getClusterKeyByUID(cuid1, cuid2) << 12;
57                 } catch (Throwable t) {
58                         throw new IllegalStateException(t);
59                 } finally {
60                         client.clusterLRU.releaseMutex();
61                 }
62         }
63         
64         public ClusterUID getClusterUID() {
65                 return uid;
66         }
67         
68         private void processCreate() {
69                 int r = Bytes.readLE2(bytes, pos);
70                 pos+=2;
71                 if(DEBUG) System.err.println("DEBUG: New ri=" + r + " offset=" + (pos-3-24));
72                 try {
73                         create();
74                 } catch (DatabaseException e) {
75                         LOGGER.error("resource create failed", e);
76                 }
77         }
78
79         private void processDelete() {
80                 int ri = Bytes.readLE2(bytes, pos);
81                 pos += 2;
82
83                 if(DEBUG) System.err.println("DEBUG: Delete " + ri);
84
85                 try {
86                         delete(ri);
87                 } catch (DatabaseException e) {
88                         LOGGER.error("resource {} value delete failed", ri, e);
89                 }
90         }
91
92         private void processModify(int op) {
93                 
94                 int ri = Bytes.readLE2(bytes, pos);
95                 pos+=2;
96                 long offset = Bytes.readLE7(bytes, pos);
97                 pos+=7;
98                 int size = Bytes.readLE2(bytes, pos);
99                 pos+=2;
100                 
101                 offset += (ri>>14) << 56;
102                 ri = ri & 0x3FFF;
103                 
104                 if(size < 0)
105                         throw new IllegalStateException();
106                 if(ri < 1)
107                         throw new IllegalStateException();
108                 if(ri > 4095)
109                         throw new IllegalStateException();
110                 
111                 if(DEBUG) System.err.println("DEBUG: Modify " + ri + " " + offset + " " + size + " offset=" + (pos-1-24) + " " + Arrays.toString(Arrays.copyOf(valueBuffer,size)));
112
113                 try {
114                         modify(clusterKey + ri, offset, size, bytes, pos);
115                 } catch (DatabaseException e) {
116                         LOGGER.error("resource value modify(clusterKey: {}, ri: {}, offset: {}, size: {}, pos: {}) failed",
117                                         clusterKey, ri, offset, size, pos, e);
118                 }
119
120                 pos += size;
121                 
122         }
123
124         private void processSet(int op) {
125                 
126                 int s = Bytes.readLE4(bytes, pos);
127                 int length = (s >> 14);
128                 if(length < 1)
129                         throw new IllegalStateException();
130                 int r = s & 0x3FFF;
131                 
132                 pos += 4;
133                 System.arraycopy(bytes, pos, valueBuffer, 0, length);
134                 pos += length;
135
136                 if(DEBUG) System.err.println("DEBUG: Set " + r + " " + length + " offset=" + (pos-1-24) + " " + Arrays.toString(Arrays.copyOf(valueBuffer,length)));
137
138                 try {
139                         set(clusterKey+r, valueBuffer, length);
140                 } catch (DatabaseException e) {
141                         LOGGER.error("resource value set(clusterKey: {}, r: {}, length: {}) failed",
142                                         clusterKey, r, length, e);
143                 }
144                 
145         }
146
147         byte[] valueBuffer = new byte[65536];
148         
149         private void processSetShort(int op) {
150                 
151                 int s = Bytes.readLE2(bytes, pos);
152                 int length = ((op&7)<<2) + (s >> 14);
153                 if(length < 1)
154                         throw new IllegalStateException();
155                 if(length > 31)
156                         throw new IllegalStateException();
157                 int r = s & 0x3FFF;
158                 
159                 if(DEBUG) System.err.println("DEBUG: SetShort " + r + " " + length + " offset=" + (pos-1-24) + " " + Arrays.toString(Arrays.copyOf(valueBuffer,length)));
160                 pos += 2;
161
162                 System.arraycopy(bytes, pos, valueBuffer, 0, length);
163                 pos += length;
164                 
165                 try {
166                         set(clusterKey+r, valueBuffer, length);
167                 } catch (DatabaseException e) {
168                         LOGGER.error("resource value setShort(clusterKey: {}, r: {}, length: {}) failed",
169                                         clusterKey, r, length, e);
170                 }
171                 
172         }
173
174         private void processStatementResource(ClusterEnum enu, int pOrO) {
175                 if(ClusterEnum.ForeignShort == enu) {
176                         int fs = bytes[pos++]&0xff;
177                         foreignRefs[pOrO] = fs;
178                 } else if(ClusterEnum.Local == enu) {
179                         int lo = bytes[pos++]&0xff;
180                         lows[pOrO] = lo;
181                 } else {
182                         long l1 = Bytes.readLE8(bytes, pos);
183                         pos += 8;
184                         long l2 = Bytes.readLE8(bytes, pos);
185                         pos += 8;
186                         ClusterUID cuid = ClusterUID.make(l1, l2);
187                         foreignClusters[foreignPos] = cuid;
188                         int lo = bytes[pos++]&0xff;
189                         foreignIndices[foreignPos] = lo;
190                         foreignRefs[pOrO] = foreignPos;
191                         foreignPos++;
192                         lows[pOrO] = lo;
193                 }
194         }
195         
196         ClusterUID[] foreignClusters = new ClusterUID[256];
197         int[] foreignIndices = new int[256];
198         int foreignPos = 0;
199         int lows[] = new int[2];
200         int foreignRefs[] = new int[2];
201         
202         private void processStatement(int op, StmEnum stmEnum, ClusterEnum p, ClusterEnum o) throws IllegalAcornStateException {
203
204                 int curPos = pos-1-24;
205                 
206                 processStatementResource(p, 0);
207                 processStatementResource(o, 1);
208
209                 int ri = bytes[pos++]&0xff;
210                 int pi = 0;
211                 int oi = 0;
212                 
213                 ClusterUID puid = uid;
214                 ClusterUID ouid = puid;
215                 
216                 if(ClusterEnum.ForeignShort == p && ClusterEnum.ForeignShort == o) {
217                         ri |= (op&0x3F) << 8;
218                 } else {
219                         Data data = ClusterEnum.getData(stmEnum, p, o);
220                         // data.left is the amount of bytes in last two bytes
221                         if(data.bytes == 0) {
222                                 ri = ri | ((op&0x3F)<<8); 
223                         } else {
224                                 int extra = 0;
225                                 int opBits = data.bits;
226                                 int extraBits = 6-opBits;
227                                 if(data.bytes == 1) {
228                                         extra = bytes[pos++]&0xff;
229                                         int high = extra >> extraBits;
230                                         if(ClusterEnum.ForeignShort == p) {
231                                                 oi = lows[1] + (high<<8);
232                                         } else {
233                                                 pi = lows[0] + (high<<8);
234                                         }
235                                 } else {
236                                         extra = Bytes.readLE2(bytes, pos);
237                                         pos += 2;
238                                         int high1 = (extra >> extraBits)&((1<<6)-1);
239                                         int high2 = (extra >> (extraBits+6))&((1<<6)-1);
240                                         if(ClusterEnum.ForeignShort == p) {
241                                                 oi = lows[1] + (high1<<8);
242                                         } else {
243                                                 pi = lows[0] + (high1<<8);
244                                                 oi = lows[1] + (high2<<8);
245                                         }
246                                 }
247                                 ri = ri | ((extra&((1<<extraBits)-1))<<8) | ((op&((1<<opBits)-1))<<(8+extraBits)); 
248                         }
249                 }
250
251                 // Set foreigns
252                 if(ClusterEnum.ForeignLong == p) {
253                         int ref = foreignRefs[0];
254                         foreignIndices[ref] = pi;
255                         puid = foreignClusters[ref];
256                 }
257                 if(ClusterEnum.ForeignLong == o) {
258                         int ref = foreignRefs[1];
259                         foreignIndices[ref] = oi;
260                         ouid = foreignClusters[ref]; 
261                 }
262                 // Get foreigns
263                 if(ClusterEnum.ForeignShort == p) {
264                         int ref = foreignRefs[0];
265                         pi = foreignIndices[ref];
266                         puid = foreignClusters[ref]; 
267                 }
268                 if(ClusterEnum.ForeignShort == o) {
269                         int ref = foreignRefs[1];
270                         oi = foreignIndices[ref];
271                         ouid = foreignClusters[ref]; 
272                 }
273
274                 if(ri < 1)
275                         throw new IllegalStateException();
276                 if(pi < 1)
277                         throw new IllegalStateException();
278                 if(oi < 1)
279                         throw new IllegalStateException();
280                 if(ri > 4095)
281                         throw new IllegalStateException();
282                 if(pi > 4095)
283                         throw new IllegalStateException();
284                 if(oi > 4095)
285                         throw new IllegalStateException();
286
287                 if(StmEnum.Add == stmEnum) {
288                         
289                         if(DEBUG)
290                 System.err.println("DEBUG: ClusterChange " + uid + ": Add ri=" + ri + " pi=" + pi + " oi=" + oi + " pc=" + puid + " oc=" + ouid + " offset=" + curPos + " " + p.ordinal + " " + o.ordinal);
291             
292                         int predicateKey = getResourceKey(puid, pi);
293                         int objectKey = getResourceKey(ouid, oi);       
294                         try {
295                                 claim(clusterKey+ri, predicateKey, objectKey, puid, ouid);
296                         } catch (DatabaseException e) {
297                                 LOGGER.error("statement add(clusterKey: {}, ri: {}, predicateKey: {}, objectKey: {}, puid: {}, ouid: {}) failed",
298                                                 clusterKey, ri, predicateKey, objectKey, puid.toString(), ouid.toString(), e);
299                         }
300                         
301                 } else {
302                         
303                         if(DEBUG)
304                 System.err.println("DEBUG: ClusterChange " + uid + ": Rem ri=" + ri + " pi=" + pi + " oi=" + oi + " pc=" + puid + " oc=" + ouid + " offset=" + curPos + " " + p.ordinal + " " + o.ordinal);
305             
306                         int predicateKey = getResourceKey(puid, pi);
307                         int objectKey = getResourceKey(ouid, oi);
308                         try {
309                                 deny(clusterKey+ri, predicateKey, objectKey, puid, ouid);
310                         } catch (DatabaseException e) {
311                                 LOGGER.error("statement deny(clusterKey: {}, ri: {}, predicateKey: {}, objectKey: {}, puid: {}, ouid: {}) failed",
312                                                 clusterKey, ri, predicateKey, objectKey, puid.toString(), ouid.toString(), e);
313                         }
314                         
315                 }
316                 
317         }
318
319         public void process() throws IllegalAcornStateException {
320                 if (version == ClusterChange.VERSION) {
321                         process1();
322                 } else if (version == ClusterChange2.VERSION) {
323                         process2();
324                 }
325         }
326
327         private void process1() throws IllegalAcornStateException {
328                 
329                 foreignPos = 0;
330
331                 if(DEBUG) System.err.println("DEBUG: process " + uid + " " + len);
332                 
333                 // op resolution for statement operation:
334                 
335                 // 2 first bits
336                 // op: 01 | r8-13
337                 // op: 10 | r8-13
338
339                 // 3 first bits (000)
340                 // op: 000000 | r12-13
341                 // op: 000001 | r12-13
342                 // op: 000010 | r12-13 
343                 // op: 000011 | r12-13
344                 // op: 000100 | r12-13 
345                 // op: 000101 | r12-13 
346                 // op: 000110 | r12-13 
347                 // op: 000111 | r12-13 
348
349                 // 4 first bits
350                 // op: 1100 | r10-13
351                 // op: 1101 | r10-13 
352                 // op: 1110 | r10-13 
353                 // op: 1111 | r10-13
354                 // op: 0010 | r10-13
355                 
356                 // 6 bits
357                 // op: 00110001 = 49
358                 // op: 00110010 = 50
359                 // op: 00110011 = 51
360                 // other: 0011xxxx
361                 
362                 while(pos < len) {
363                 
364                         int op = bytes[pos++]&0xff;
365                         
366                         // common prefix: 0011
367                         switch(op) {
368                         
369                         case 49:
370                                 processStatement(op, StmEnum.Remove, ClusterEnum.Local, ClusterEnum.ForeignShort);
371                                 break;
372                         case 50:
373                                 processStatement(op, StmEnum.Remove, ClusterEnum.ForeignShort, ClusterEnum.ForeignLong);
374                                 break;
375                         case 51:
376                                 processStatement(op, StmEnum.Remove, ClusterEnum.ForeignLong, ClusterEnum.ForeignShort);
377                                 break;
378                         // 52 = 32+16+4 = 00110100
379                         case 52:
380                                 processCreate();
381                                 break;
382                         // 53 = 32+16+4+1 = 00110101
383                         case 53:
384                                 processSet(op);
385                                 break;
386                         // 54 = 32+16+4+2 = 00110110
387                         case 54:
388                                 processDelete();
389                                 break;
390                         // 55 = 32+16+4+2+1 = 00110111
391                         case 55:
392                                 processModify(op);
393                                 break;
394                         default:
395                                 
396                                 int bits6 = ((int)op)&0xC0;
397                                 switch(bits6) {
398                                 
399                                 case 0x40:
400                                         processStatement(op, StmEnum.Add, ClusterEnum.ForeignShort, ClusterEnum.ForeignShort);
401                                         break;
402                                 case 0x80:
403                                         processStatement(op, StmEnum.Remove, ClusterEnum.ForeignShort, ClusterEnum.ForeignShort);
404                                         break;
405                                 default:
406
407                                         int bits5 = ((int)op)&0xE0;
408                                         if(bits5 == 0) {
409
410                                                 int bits2 = (((int)op)&0xFC) >> 2;      
411                                                 
412                                                 // 3 top bits are 0
413                                                 // 6 bits of op
414                                                 
415                                                 switch(bits2) {
416
417                                                 case 0:
418                                                         processStatement(op, StmEnum.Add, ClusterEnum.Local, ClusterEnum.Local);
419                                                         break;
420                                                 case 1:
421                                                         processStatement(op, StmEnum.Remove, ClusterEnum.Local, ClusterEnum.Local);
422                                                         break;
423                                                 case 2:
424                                                         processStatement(op, StmEnum.Add, ClusterEnum.Local, ClusterEnum.ForeignLong);
425                                                         break;
426                                                 case 3:
427                                                         processStatement(op, StmEnum.Remove, ClusterEnum.Local, ClusterEnum.ForeignLong);
428                                                         break;
429                                                 case 4:
430                                                         processStatement(op, StmEnum.Add, ClusterEnum.ForeignLong, ClusterEnum.Local);
431                                                         break;
432                                                 case 5:
433                                                         processStatement(op, StmEnum.Remove, ClusterEnum.ForeignLong, ClusterEnum.Local);
434                                                         break;
435                                                 case 6:
436                                                         processStatement(op, StmEnum.Add, ClusterEnum.ForeignLong, ClusterEnum.ForeignLong);
437                                                         break;
438                                                 case 7:
439                                                         processStatement(op, StmEnum.Remove, ClusterEnum.ForeignLong, ClusterEnum.ForeignLong);
440                                                         break;
441                                                 
442                                                 }
443
444                                         } else {
445
446                                                 // 4 top bits of op
447                                                 // 4 low bits of payload
448
449                                                 int bits4 = (((int)op)&0xF0)>>4;
450                                                 switch(bits4) {
451                                                 case 0b1100:
452                                                         processStatement(op, StmEnum.Add, ClusterEnum.Local, ClusterEnum.ForeignShort);
453                                                         break;
454                                                 case 0b1101:
455                                                         processStatement(op, StmEnum.Add, ClusterEnum.ForeignShort, ClusterEnum.Local);
456                                                         break;
457                                                 case 0b1110:
458                                                         processStatement(op, StmEnum.Add, ClusterEnum.ForeignShort, ClusterEnum.ForeignLong);
459                                                         break;
460                                                 case 0b1111:
461                                                         processStatement(op, StmEnum.Add, ClusterEnum.ForeignLong, ClusterEnum.ForeignShort);
462                                                         break;
463                                                 case 0b0010:
464                                                         processStatement(op, StmEnum.Remove, ClusterEnum.ForeignShort, ClusterEnum.Local);
465                                                         break;
466                                                 case 0b0011:
467                                                         int bits3 = (((int)op)&0xF8)>>3;
468                                                         if(bits3 == 7)
469                                                                 processSetShort(op);
470                                                         break;
471                                                 }
472
473                                         }
474                                 
475                                 }
476                                 
477                         }
478                         
479                 }
480                 
481         }
482
483         private void process2() throws IllegalAcornStateException {
484
485                 while(pos < len) {
486
487                         int op = bytes[pos++]&0xff;
488
489                         switch(op) {
490
491                         case ClusterChange2.SET_IMMUTABLE_OPERATION:
492                                 processSetImmutable(op);
493                                 break;
494                         case ClusterChange2.UNDO_VALUE_OPERATION:
495                                 processUndoValue(op);
496                                 break;
497                         case ClusterChange2.SET_DELETED_OPERATION:
498                                 // TODO: implement?
499                                 break;
500                         default:
501                                 throw new IllegalAcornStateException("Can not process operation " + op + " for cluster " + uid);
502
503                         }
504                 }
505
506         }
507
508         private void processSetImmutable(int op) {
509                 int value = bytes[pos++]&0xff;
510                 setImmutable(value > 0);
511         }
512
513         private void processUndoValue(int op) {
514                 Bytes.readLE4(bytes, pos);
515                 pos+=4;
516         }
517
518         abstract void create() throws DatabaseException;
519         abstract void delete(int resourceIndex) throws DatabaseException;
520         abstract void modify(int resourceKey, long offset, int size, byte[] bytes, int pos) throws DatabaseException;
521         abstract void set(int resourceKey, byte[] bytes, int length) throws DatabaseException;
522
523         abstract void claim(int resourceKey, int predicateKey, int objectKey, ClusterUID puid, ClusterUID ouid) throws DatabaseException;
524         abstract void deny(int resourceKey, int predicateKey, int objectKey, ClusterUID puid, ClusterUID ouid) throws DatabaseException;
525
526         abstract void setImmutable(boolean value);
527
528 }