/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.db.procore.cluster; import java.util.Vector; import org.simantics.db.exception.InternalException; import org.simantics.db.exception.RuntimeDatabaseException; import org.simantics.db.procore.cluster.CCSParser.ReferenceType; import org.simantics.db.procore.cluster.CCSParser.StmEnum; import org.simantics.db.service.ClusterUID; public class ClusterChangeSet implements ClusterChangeSetI { static interface IteratorI { } static private final boolean DEBUG = false; static private final int HEADER_SIZE = 20; private final int version; private final ClusterUID clusterUID; private final byte[] bytes; public static ClusterChangeSetI create(byte[] rawData, String compressionCodec) throws InternalException { // TODO: Find maybe a better way to do this byte[] bytes; if (compressionCodec.equals("LZ4")) { LZ4.DecompressStruct data = LZ4.decompress(rawData); bytes = data.bytes; } else if (compressionCodec.equals("FLZ")) { FastLZ.DecompressStruct data = FastLZ.decompress(rawData); bytes = data.bytes; } else { throw new RuntimeDatabaseException("No compressionCodec given!"); } return new ClusterChangeSet(bytes); } @Override public String toString() { return "cid=" + clusterUID + " len=" + bytes.length; } private ClusterChangeSet(byte[] abytes) { this.bytes = abytes; if (bytes.length < HEADER_SIZE + 4) throw new RuntimeDatabaseException("Cluster change must contain header and size. length=" + bytes.length); version = CCSParser.getInt(bytes, 0); if (version <1 || version > 2) throw new RuntimeDatabaseException("Unsupported cluster change set version=" + version); clusterUID = ClusterUID.make(abytes, 4); if (ClusterUID.Null.equals(clusterUID)) throw new RuntimeDatabaseException("Cluster uid can not be null."); if (DEBUG) System.out.println("DEBUG: Cluster uid=" + clusterUID); int p = HEADER_SIZE; int size = CCSParser.getInt(bytes, p); if (2 == version) { System.out.println("Cluster change set version=2 is not supported. skipping..."); return; } for (; size > 0; size = CCSParser.getInt(bytes, p)) { mBlocks.push_back(new ClusterChangeSetBlock(p+=4, size, bytes)); p += size; // skip block data } } @Override public ClusterUID getClusterUID() { return clusterUID; } private boolean updateRequired() { return false; } private void update() { } @Override public void getNextOperation(Operation ar) { Iterator pIterator = null; if (null == ar.iterator) { if (updateRequired()) this.update(); int N = mBlocks.size(); if (0 == N) { ar.type = OperationEnum.EndOf; return; } pIterator = new Iterator(N-1); ar.iterator = pIterator; } else pIterator = (Iterator)ar.iterator; if (pIterator.block > pIterator.lastBlock) { ar.type = OperationEnum.EndOf; ar.iterator = null; return; } else if (pIterator.offset >= mBlocks.get(pIterator.block).size) { ++pIterator.block; pIterator.foreignTable.init(); pIterator.offset = 0; if (pIterator.block > pIterator.lastBlock) { ar.type = OperationEnum.EndOf; ar.iterator = null; return; } } if (pIterator.offset >= mBlocks.get(pIterator.block).size) { ++pIterator.block; pIterator.foreignTable.init(); pIterator.offset = 0; if (pIterator.block > pIterator.lastBlock) { ar.type = OperationEnum.EndOf; ar.iterator = null; return; } } ClusterChangeSetBlock r = mBlocks.get(pIterator.block); int inc = next(ar, pIterator, r.bytes, r.offset + pIterator.offset, r.offset + r.size); if (inc <= 0) { if (inc < 0) System.out.println("Iterarto offset increment is negative!"); ar.type = OperationEnum.EndOf; ar.iterator = null; } else pIterator.offset += inc; } private int next(Operation ar, Iterator pIterator, byte[] bytes, int ab, int ae) { CCSParser.Args args = new CCSParser.Args(clusterUID, pIterator.foreignTable, ar); final int pe = ae; final int pb = ab; int p = pb; if (p < pe) { if (DEBUG) System.out.println("CCS.next offset=" + p); short ri = 0; // resource index short kraa = (short)(bytes[p] & 0xFF); switch (kraa) { case 0: case 1: case 2: case 3: //0x000000?? add - ri12 + pi14 + oi14 = 40 / 8 = 5 p = CCSParser.parseStm(StmEnum.Add, args, bytes, p, 2, ReferenceType.Local, ReferenceType.Local); break; case 4: case 5: case 6: case 7: //0x000001?? rem - ri12 + pi14 + oi14 = 40 / 8 = 5 p = CCSParser.parseStm(StmEnum.Remove, args, bytes, p, 2, ReferenceType.Local, ReferenceType.Local); break; case 8: case 9: case 10: case 11: //0x000010?? add - ri12 + pi14 + oc62 = 88 / 8 = 11 p = CCSParser.parseStm(StmEnum.Add, args, bytes, p, 2, ReferenceType.Local, ReferenceType.ForeignLong); break; case 12: case 13: case 14: case 15: //0x000011?? rem - ri12 + pi14 + oc62 = 88 / 8 = 11 p = CCSParser.parseStm(StmEnum.Remove, args, bytes, p, 2, ReferenceType.Local, ReferenceType.ForeignLong); break; case 16: case 17: case 18: case 19: //0x000100?? add - ri12 + pc62 + oi14 = 88 / 8 = 11 p = CCSParser.parseStm(StmEnum.Add, args, bytes, p, 2, ReferenceType.ForeignLong, ReferenceType.Local); break; case 20: case 21: case 22: case 23: //0x000101?? rem - ri12 + pc62 + oi14 = 88 / 8 = 11 p = CCSParser.parseStm(StmEnum.Remove, args, bytes, p, 2, ReferenceType.ForeignLong, ReferenceType.Local); break; case 24: case 25: case 26: case 27: //0x000110?? add - ri12 + pc62 + oc62 = 136 / 8 = 17 p = CCSParser.parseStm(StmEnum.Add, args, bytes, p, 2, ReferenceType.ForeignLong, ReferenceType.ForeignLong); break; case 28: case 29: case 30: case 31: //0x000111?? rem - ri12 + pc62 + oc62 = 136 / 8 = 17 p = CCSParser.parseStm(StmEnum.Remove, args, bytes, p, 2, ReferenceType.ForeignLong, ReferenceType.ForeignLong); break; case 32: case 33: case 34: case 35: case 36: case 37: case 38: case 39: case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47: case 48: //0x0010???? rem - ri10 + pr08 + oi14 = 32 / 8 = 4 p = CCSParser.parseStm(StmEnum.Remove, args, bytes, p, 4, ReferenceType.ForeignShort, ReferenceType.Local); break; case 49: //0x00110001 rem - ri14 + pi14 + pr08 + pad4 = 40 / 8 = 5 p = CCSParser.parseStm(StmEnum.Remove, args, bytes, p, 0, ReferenceType.Local, ReferenceType.ForeignShort); break; case 50: //0x00110010 rem - ri14 + pr08 + oc62 + pad4 = 88 / 8 = 11 p = CCSParser.parseStm(StmEnum.Remove, args, bytes, p, 0, ReferenceType.ForeignShort, ReferenceType.ForeignLong); break; case 51: //0x00110011 rem - ri14 + pc62 + or08 + pad4 = 88 / 8 = 11 p = CCSParser.parseStm(StmEnum.Remove, args, bytes, p, 0, ReferenceType.ForeignLong, ReferenceType.ForeignShort); break; case 52: //0x00110100 cre - ri14 + pad2 = 16 / 8 = 2 ri = CCSParser.getShort(bytes, p+1); if (DEBUG) System.out.println("Creating resource r=" + ri + " rc=" + clusterUID); args.createResource(ri); p += 3; break; case 53: { //0x00110101 set - ri14 + sz18 = 32 / 8 = 4 + sz * bytes int t = CCSParser.getInt(bytes, p + 1); ri = (short)(t & 0x3FFF); int s = t >>> 14; args.setValue(ri, bytes, p + 5, s); p += 5 + s; break; } case 54: //0x00110110 del - ri14 + pad2 = 16 / 8 = 2 ri = CCSParser.getShort(bytes, p+1); args.deleteValue(ri); p += 3; break; case 55: { //0x00110111 mod - ri14 + of58 + sz16 = 88 / 8 = 11 + sz * bytes ri = CCSParser.getShort(bytes, p+1); ri &= (1<<14)-1; // mask top 2 bits long vo = CCSParser.getLongN(bytes, p+3, 7); // get low 7 bytes vo |= (long)(bytes[p+2] & 0xC0) << (56-6); // add the top 2 bits p += 10; int vsize = CCSParser.getShort(bytes, p); if (vsize < 0) vsize += 65536; p += 2; if (DEBUG) System.out.println("Modifying value r=" + ri + " rc=" + args.clusterUID + " value offset=" + vo + " size=" + vsize); if (pe - p < vsize) throw new RuntimeException("Illegal size=" + vsize + " limit=" + (pe - p)); args.modifyValue(ri, vo, vsize, bytes, p); p += vsize; break; } case 56: case 57: case 58: case 59: case 60: case 61: case 62: case 63: { //0x00111??? set - ri14 + sz2 = 16 / 8 = 2 + sz * bytes; byte s = (byte)(bytes[p] & 0x7); s <<= 2; ri = CCSParser.getShort(bytes, p+1); ri &= 0x3FFF; byte t = bytes[p + 2]; s |= (t &0xFF) >> 6; args.setValue(ri, bytes, p + 3, s); p += 3 + s; break; } case 64: case 65: case 66: case 67: case 68: case 69: case 70: case 71: case 72: case 73: case 74: case 75: case 76: case 77: case 78: case 79: case 80: case 81: case 82: case 83: case 84: case 85: case 86: case 87: case 88: case 89: case 90: case 91: case 92: case 93: case 94: case 95: case 96: case 97: case 98: case 99: case 100: case 101: case 102: case 103: case 104: case 105: case 106: case 107: case 108: case 109: case 110: case 111: case 112: case 113: case 114: case 115: case 116: case 117: case 118: case 119: case 120: case 121: case 122: case 123: case 124: case 125: case 126: case 127: //0x01?????? add - ri08 + pr08 + or08 = 24 / 8 = 3 p = CCSParser.parseStm(StmEnum.Add, args, bytes, p, 6, ReferenceType.ForeignShort, ReferenceType.ForeignShort); break; case 128: case 129: case 130: case 131: case 132: case 133: case 134: case 135: case 136: case 137: case 138: case 139: case 140: case 141: case 142: case 143: case 144: case 145: case 146: case 147: case 148: case 149: case 150: case 151: case 152: case 153: case 154: case 155: case 156: case 157: case 158: case 159: case 160: case 161: case 162: case 163: case 164: case 165: case 166: case 167: case 168: case 169: case 170: case 171: case 172: case 173: case 174: case 175: case 176: case 177: case 178: case 179: case 180: case 181: case 182: case 183: case 184: case 185: case 186: case 187: case 188: case 189: case 190:case 191: //0x10?????? rem - ri08 + pr08 + or08 = 24 / 8 = 3 p = CCSParser.parseStm(StmEnum.Remove, args, bytes, p, 6, ReferenceType.ForeignShort, ReferenceType.ForeignShort); break; case 192: case 193: case 194: case 195: case 196: case 197: case 198: case 199: case 200: case 201: case 202: case 203: case 204: case 205: case 206: case 207: //0x1100???? add - ri10 + pi14 + pr08 = 32 / 8 = 4 p = CCSParser.parseStm(StmEnum.Add, args, bytes, p, 4, ReferenceType.Local, ReferenceType.ForeignShort); break; case 208: case 209: case 210: case 211: case 212: case 213: case 214: case 215: case 216: case 217: case 218: case 219: case 220: case 221: case 222: case 223: //0x1101???? add - ri10 + pr08 + oi14 = 32 / 8 = 4 p = CCSParser.parseStm(StmEnum.Add, args, bytes, p, 4, ReferenceType.ForeignShort, ReferenceType.Local); break; case 224: case 225: case 226: case 227: case 228: case 229: case 230: case 231: case 232: case 233: case 234: case 235: case 236: case 237: case 238: case 239: //0x1110???? add - ri10 + pr08 + oc62 = 80 / 8 = 10 p = CCSParser.parseStm(StmEnum.Add, args, bytes, p, 4, ReferenceType.ForeignShort, ReferenceType.ForeignLong); break; case 240: case 241: case 242: case 243: case 244: case 245: case 246: case 247: case 248: case 249: case 250: case 251: case 252: case 253: case 254: case 255: //0x1111???? add - ri10 + pc62 + or08 = 80 / 8 = 10 p = CCSParser.parseStm(StmEnum.Add, args, bytes, p, 4, ReferenceType.ForeignLong, ReferenceType.ForeignShort); break; default: throw new RuntimeException("Internal error, contact application support."); } } return p - pb; } private static class ClusterChangeSetBlock { ClusterChangeSetBlock(int offset, int size, byte[] bytes) { this.bytes = bytes; this.offset = offset; this.size = size; } byte[] bytes; int offset; int size; } private static class Blocks { Blocks() { mBlocks = new Vector(); mBlocks.ensureCapacity(BLOCK_INCREMENT); } int size() { return mBlocks.size(); } void push_back(ClusterChangeSetBlock ar) { if (mBlocks.size() == mBlocks.capacity()) mBlocks.ensureCapacity(mBlocks.size() + BLOCK_INCREMENT); mBlocks.add(ar); } ClusterChangeSetBlock get(int i) { return mBlocks.get(i); } // How many block elements are allocated when out of space. private static final int BLOCK_INCREMENT = 10; private Vector mBlocks; } private static class Iterator implements IteratorI { Iterator(int lastBlock) { this.lastBlock = lastBlock; this.foreignTable = new CCSParser.ForeignTable(); } int lastBlock; int block; int offset; CCSParser.ForeignTable foreignTable; } private Blocks mBlocks = new Blocks(); }