--- /dev/null
+package org.simantics.acorn.internal;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.simantics.acorn.ClusterManager;
+import org.simantics.acorn.internal.ClusterStream.ClusterEnum;
+import org.simantics.acorn.internal.ClusterStream.Data;
+import org.simantics.acorn.internal.ClusterStream.StmEnum;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.service.Bytes;
+import org.simantics.db.service.ClusterUID;
+
+abstract public class ClusterUpdateProcessorBase {
+
+ public final static boolean DEBUG = false;
+
+ final protected ClusterManager manager;
+ final public byte[] bytes;
+ private int pos = 0;
+ final private int len;
+ final private ClusterUID uid;
+ final private int clusterKey;
+ final public int version;
+
+ final Map<ClusterUID, Integer> clusterKeyCache = new HashMap<ClusterUID, Integer>();
+
+ public int getResourceKey(ClusterUID uid, int index) {
+ Integer match = clusterKeyCache.get(uid);
+ if(match != null) return match+index;
+ int key = manager.getResourceKeyWitoutMutex(uid, 0);
+ clusterKeyCache.put(uid, key);
+ return key+index;
+ }
+
+
+ public ClusterUpdateProcessorBase(ClusterManager client, byte[] operations) throws DatabaseException {
+ this.manager = client;
+ this.bytes = operations;
+ this.len = Bytes.readLE4(bytes, 0)+4; // whatta?
+ version = Bytes.readLE4(bytes, 4);
+ long cuid1 = Bytes.readLE8(bytes, 8);
+ long cuid2 = Bytes.readLE8(bytes, 16);
+ uid = ClusterUID.make(cuid1, cuid2);
+ pos = 24;
+ client.clusterLRU.acquireMutex();
+ try {
+ clusterKey = client.clusterLRU.getClusterKeyByUID(cuid1, cuid2) << 12;
+ } catch (Throwable t) {
+ throw new IllegalStateException(t);
+ } finally {
+ client.clusterLRU.releaseMutex();
+ }
+ }
+
+ public ClusterUID getClusterUID() {
+ return uid;
+ }
+
+ private void processCreate() {
+ int r = Bytes.readLE2(bytes, pos);
+ pos+=2;
+ if(DEBUG) System.err.println("DEBUG: New ri=" + r + " offset=" + (pos-3-24));
+ try {
+ create();
+ } catch (DatabaseException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ private void processDelete() {
+
+ int ri = Bytes.readLE2(bytes, pos);
+ pos += 2;
+
+ if(DEBUG) System.err.println("DEBUG: Delete " + ri);
+
+ try {
+ delete(ri);
+ } catch (DatabaseException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ private void processModify(int op) {
+
+ int ri = Bytes.readLE2(bytes, pos);
+ pos+=2;
+ long offset = Bytes.readLE7(bytes, pos);
+ pos+=7;
+ int size = Bytes.readLE2(bytes, pos);
+ pos+=2;
+
+ offset += (ri>>14) << 56;
+ ri = ri & 0x3FFF;
+
+ if(size < 0)
+ throw new IllegalStateException();
+ if(ri < 1)
+ throw new IllegalStateException();
+ if(ri > 4095)
+ throw new IllegalStateException();
+
+ if(DEBUG) System.err.println("DEBUG: Modify " + ri + " " + offset + " " + size + " offset=" + (pos-1-24) + " " + Arrays.toString(Arrays.copyOf(valueBuffer,size)));
+
+ try {
+ modify(clusterKey + ri, offset, size, bytes, pos);
+ } catch (DatabaseException e) {
+ e.printStackTrace();
+ }
+
+ pos += size;
+
+ }
+
+ private void processSet(int op) {
+
+ int s = Bytes.readLE4(bytes, pos);
+ int length = (s >> 14);
+ if(length < 1)
+ throw new IllegalStateException();
+ int r = s & 0x3FFF;
+
+ pos += 4;
+ System.arraycopy(bytes, pos, valueBuffer, 0, length);
+ pos += length;
+
+ if(DEBUG) System.err.println("DEBUG: Set " + r + " " + length + " offset=" + (pos-1-24) + " " + Arrays.toString(Arrays.copyOf(valueBuffer,length)));
+
+ try {
+ set(clusterKey+r, valueBuffer, length);
+ } catch (DatabaseException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ byte[] valueBuffer = new byte[65536];
+
+ private void processSetShort(int op) {
+
+ int s = Bytes.readLE2(bytes, pos);
+ int length = ((op&7)<<2) + (s >> 14);
+ if(length < 1)
+ throw new IllegalStateException();
+ if(length > 31)
+ throw new IllegalStateException();
+ int r = s & 0x3FFF;
+
+ if(DEBUG) System.err.println("DEBUG: SetShort " + r + " " + length + " offset=" + (pos-1-24) + " " + Arrays.toString(Arrays.copyOf(valueBuffer,length)));
+ pos += 2;
+
+ System.arraycopy(bytes, pos, valueBuffer, 0, length);
+ pos += length;
+
+ try {
+ set(clusterKey+r, valueBuffer, length);
+ } catch (DatabaseException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ private void processStatementResource(ClusterEnum enu, int pOrO) {
+ if(ClusterEnum.ForeignShort == enu) {
+ int fs = bytes[pos++]&0xff;
+ foreignRefs[pOrO] = fs;
+ } else if(ClusterEnum.Local == enu) {
+ int lo = bytes[pos++]&0xff;
+ lows[pOrO] = lo;
+ } else {
+ long l1 = Bytes.readLE8(bytes, pos);
+ pos += 8;
+ long l2 = Bytes.readLE8(bytes, pos);
+ pos += 8;
+ ClusterUID cuid = ClusterUID.make(l1, l2);
+ foreignClusters[foreignPos] = cuid;
+ int lo = bytes[pos++]&0xff;
+ foreignIndices[foreignPos] = lo;
+ foreignRefs[pOrO] = foreignPos;
+ foreignPos++;
+ lows[pOrO] = lo;
+ }
+ }
+
+ ClusterUID[] foreignClusters = new ClusterUID[256];
+ int[] foreignIndices = new int[256];
+ int foreignPos = 0;
+ int lows[] = new int[2];
+ int foreignRefs[] = new int[2];
+
+ private void processStatement(int op, StmEnum stmEnum, ClusterEnum p, ClusterEnum o) {
+
+ int curPos = pos-1-24;
+
+ processStatementResource(p, 0);
+ processStatementResource(o, 1);
+
+ int ri = bytes[pos++]&0xff;
+ int pi = 0;
+ int oi = 0;
+
+ ClusterUID puid = uid;
+ ClusterUID ouid = puid;
+
+ if(ClusterEnum.ForeignShort == p && ClusterEnum.ForeignShort == o) {
+ ri |= (op&0x3F) << 8;
+ } else {
+ Data data = ClusterEnum.getData(stmEnum, p, o);
+ // data.left is the amount of bytes in last two bytes
+ if(data.bytes == 0) {
+ ri = ri | ((op&0x3F)<<8);
+ } else {
+ int extra = 0;
+ int opBits = data.bits;
+ int extraBits = 6-opBits;
+ if(data.bytes == 1) {
+ extra = bytes[pos++]&0xff;
+ int high = extra >> extraBits;
+ if(ClusterEnum.ForeignShort == p) {
+ oi = lows[1] + (high<<8);
+ } else {
+ pi = lows[0] + (high<<8);
+ }
+ } else {
+ extra = Bytes.readLE2(bytes, pos);
+ pos += 2;
+ int high1 = (extra >> extraBits)&((1<<6)-1);
+ int high2 = (extra >> (extraBits+6))&((1<<6)-1);
+ if(ClusterEnum.ForeignShort == p) {
+ oi = lows[1] + (high1<<8);
+ } else {
+ pi = lows[0] + (high1<<8);
+ oi = lows[1] + (high2<<8);
+ }
+ }
+ ri = ri | ((extra&((1<<extraBits)-1))<<8) | ((op&((1<<opBits)-1))<<(8+extraBits));
+ }
+ }
+
+ // Set foreigns
+ if(ClusterEnum.ForeignLong == p) {
+ int ref = foreignRefs[0];
+ foreignIndices[ref] = pi;
+ puid = foreignClusters[ref];
+ }
+ if(ClusterEnum.ForeignLong == o) {
+ int ref = foreignRefs[1];
+ foreignIndices[ref] = oi;
+ ouid = foreignClusters[ref];
+ }
+ // Get foreigns
+ if(ClusterEnum.ForeignShort == p) {
+ int ref = foreignRefs[0];
+ pi = foreignIndices[ref];
+ puid = foreignClusters[ref];
+ }
+ if(ClusterEnum.ForeignShort == o) {
+ int ref = foreignRefs[1];
+ oi = foreignIndices[ref];
+ ouid = foreignClusters[ref];
+ }
+
+ if(ri < 1)
+ throw new IllegalStateException();
+ if(pi < 1)
+ throw new IllegalStateException();
+ if(oi < 1)
+ throw new IllegalStateException();
+ if(ri > 4095)
+ throw new IllegalStateException();
+ if(pi > 4095)
+ throw new IllegalStateException();
+ if(oi > 4095)
+ throw new IllegalStateException();
+
+ if(StmEnum.Add == stmEnum) {
+
+ if(DEBUG)
+ System.err.println("DEBUG: ClusterChange " + uid + ": Add ri=" + ri + " pi=" + pi + " oi=" + oi + " pc=" + puid + " oc=" + ouid + " offset=" + curPos + " " + p.ordinal + " " + o.ordinal);
+
+ int predicateKey = getResourceKey(puid, pi);
+ int objectKey = getResourceKey(ouid, oi);
+ try {
+ claim(clusterKey+ri, predicateKey, objectKey, puid, ouid);
+ } catch (DatabaseException e) {
+ e.printStackTrace();
+ }
+
+ } else {
+
+ if(DEBUG)
+ System.err.println("DEBUG: ClusterChange " + uid + ": Rem ri=" + ri + " pi=" + pi + " oi=" + oi + " pc=" + puid + " oc=" + ouid + " offset=" + curPos + " " + p.ordinal + " " + o.ordinal);
+
+ int predicateKey = getResourceKey(puid, pi);
+ int objectKey = getResourceKey(ouid, oi);
+ try {
+ deny(clusterKey+ri, predicateKey, objectKey, puid, ouid);
+ } catch (DatabaseException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ }
+
+ public void process() {
+
+ foreignPos = 0;
+
+ if(DEBUG) System.err.println("DEBUG: process " + uid + " " + len);
+
+ // op resolution for statement operation:
+
+ // 2 first bits
+ // op: 01 | r8-13
+ // op: 10 | r8-13
+
+ // 3 first bits (000)
+ // op: 000000 | r12-13
+ // op: 000001 | r12-13
+ // op: 000010 | r12-13
+ // op: 000011 | r12-13
+ // op: 000100 | r12-13
+ // op: 000101 | r12-13
+ // op: 000110 | r12-13
+ // op: 000111 | r12-13
+
+ // 4 first bits
+ // op: 1100 | r10-13
+ // op: 1101 | r10-13
+ // op: 1110 | r10-13
+ // op: 1111 | r10-13
+ // op: 0010 | r10-13
+
+ // 6 bits
+ // op: 00110001 = 49
+ // op: 00110010 = 50
+ // op: 00110011 = 51
+ // other: 0011xxxx
+
+ while(pos < len) {
+
+ int op = bytes[pos++]&0xff;
+
+ // common prefix: 0011
+ switch(op) {
+
+ case 49:
+ processStatement(op, StmEnum.Remove, ClusterEnum.Local, ClusterEnum.ForeignShort);
+ break;
+ case 50:
+ processStatement(op, StmEnum.Remove, ClusterEnum.ForeignShort, ClusterEnum.ForeignLong);
+ break;
+ case 51:
+ processStatement(op, StmEnum.Remove, ClusterEnum.ForeignLong, ClusterEnum.ForeignShort);
+ break;
+ // 52 = 32+16+4 = 00110100
+ case 52:
+ processCreate();
+ break;
+ // 53 = 32+16+4+1 = 00110101
+ case 53:
+ processSet(op);
+ break;
+ // 54 = 32+16+4+2 = 00110110
+ case 54:
+ processDelete();
+ break;
+ // 55 = 32+16+4+2+1 = 00110111
+ case 55:
+ processModify(op);
+ break;
+ default:
+
+ int bits6 = ((int)op)&0xC0;
+ switch(bits6) {
+
+ case 0x40:
+ processStatement(op, StmEnum.Add, ClusterEnum.ForeignShort, ClusterEnum.ForeignShort);
+ break;
+ case 0x80:
+ processStatement(op, StmEnum.Remove, ClusterEnum.ForeignShort, ClusterEnum.ForeignShort);
+ break;
+ default:
+
+ int bits5 = ((int)op)&0xE0;
+ if(bits5 == 0) {
+
+ int bits2 = (((int)op)&0xFC) >> 2;
+
+ // 3 top bits are 0
+ // 6 bits of op
+
+ switch(bits2) {
+
+ case 0:
+ processStatement(op, StmEnum.Add, ClusterEnum.Local, ClusterEnum.Local);
+ break;
+ case 1:
+ processStatement(op, StmEnum.Remove, ClusterEnum.Local, ClusterEnum.Local);
+ break;
+ case 2:
+ processStatement(op, StmEnum.Add, ClusterEnum.Local, ClusterEnum.ForeignLong);
+ break;
+ case 3:
+ processStatement(op, StmEnum.Remove, ClusterEnum.Local, ClusterEnum.ForeignLong);
+ break;
+ case 4:
+ processStatement(op, StmEnum.Add, ClusterEnum.ForeignLong, ClusterEnum.Local);
+ break;
+ case 5:
+ processStatement(op, StmEnum.Remove, ClusterEnum.ForeignLong, ClusterEnum.Local);
+ break;
+ case 6:
+ processStatement(op, StmEnum.Add, ClusterEnum.ForeignLong, ClusterEnum.ForeignLong);
+ break;
+ case 7:
+ processStatement(op, StmEnum.Remove, ClusterEnum.ForeignLong, ClusterEnum.ForeignLong);
+ break;
+
+ }
+
+ } else {
+
+ // 4 top bits of op
+ // 4 low bits of payload
+
+ int bits4 = (((int)op)&0xF0)>>4;
+ switch(bits4) {
+ case 0b1100:
+ processStatement(op, StmEnum.Add, ClusterEnum.Local, ClusterEnum.ForeignShort);
+ break;
+ case 0b1101:
+ processStatement(op, StmEnum.Add, ClusterEnum.ForeignShort, ClusterEnum.Local);
+ break;
+ case 0b1110:
+ processStatement(op, StmEnum.Add, ClusterEnum.ForeignShort, ClusterEnum.ForeignLong);
+ break;
+ case 0b1111:
+ processStatement(op, StmEnum.Add, ClusterEnum.ForeignLong, ClusterEnum.ForeignShort);
+ break;
+ case 0b0010:
+ processStatement(op, StmEnum.Remove, ClusterEnum.ForeignShort, ClusterEnum.Local);
+ break;
+ case 0b0011:
+ int bits3 = (((int)op)&0xF8)>>3;
+ if(bits3 == 7)
+ processSetShort(op);
+ break;
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+
+ abstract void create() throws DatabaseException;
+ abstract void delete(int resourceIndex) throws DatabaseException;
+ abstract void modify(int resourceKey, long offset, int size, byte[] bytes, int pos) throws DatabaseException;
+ abstract void set(int resourceKey, byte[] bytes, int length) throws DatabaseException;
+
+ abstract void claim(int resourceKey, int predicateKey, int objectKey, ClusterUID puid, ClusterUID ouid) throws DatabaseException;
+ abstract void deny(int resourceKey, int predicateKey, int objectKey, ClusterUID puid, ClusterUID ouid) throws DatabaseException;
+
+}