package org.simantics.datatypes.utils; import gnu.trove.map.hash.TObjectIntHashMap; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.util.Collection; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.simantics.databoard.Bindings; import org.simantics.databoard.accessor.reference.ChildReference; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.binding.error.BindingException; import org.simantics.databoard.binding.error.RuntimeBindingConstructionException; import org.simantics.databoard.binding.impl.BindingPrintContext; import org.simantics.databoard.serialization.RuntimeSerializerConstructionException; import org.simantics.databoard.serialization.Serializer; import org.simantics.databoard.util.IdentityPair; import org.simantics.datatypes.DatatypeResource; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.WriteGraph; import org.simantics.db.common.primitiverequest.RelatedValue; import org.simantics.db.common.procedure.adapter.TransientCacheListener; import org.simantics.db.common.utils.Logger; import org.simantics.db.exception.DatabaseException; import org.simantics.db.service.Bytes; import org.simantics.db.service.SerialisationSupport; import org.simantics.layer0.Layer0; import org.simantics.utils.datastructures.Pair; interface LogContentManager { LogContentBean getContentBean(ReadGraph graph, Resource node) throws DatabaseException; void setContentBean(WriteGraph graph, Resource node, LogContentBean bean) throws DatabaseException; } class LogContentBinding extends Binding { private final SerialisationSupport ss; public LogContentBinding(SerialisationSupport ss) { this.ss = ss; } private final Serializer serializer = new Serializer() { @Override public void serialize(DataOutput out, TObjectIntHashMap identities, Object obj) throws IOException { throw new UnsupportedOperationException(); } @Override public void serialize(DataOutput out, Object obj) throws IOException { throw new UnsupportedOperationException(); } @Override public Object deserialize(DataInput in, List identities) throws IOException { throw new UnsupportedOperationException(); } @Override public Object deserialize(DataInput in) throws IOException { throw new UnsupportedOperationException(); } @Override public void deserializeTo(DataInput in, List identities, Object dst) throws IOException { throw new UnsupportedOperationException(); } @Override public void deserializeTo(DataInput in, Object dst) throws IOException { throw new UnsupportedOperationException(); } @Override public void skip(DataInput in, List identities) throws IOException { throw new UnsupportedOperationException(); } @Override public void skip(DataInput in) throws IOException { throw new UnsupportedOperationException(); } @Override public Integer getConstantSize() { throw new UnsupportedOperationException(); } @Override public int getSize(Object obj, TObjectIntHashMap identities) throws IOException { throw new UnsupportedOperationException(); } @Override public int getSize(Object obj) throws IOException { throw new UnsupportedOperationException(); } @Override public int getMinSize() { throw new UnsupportedOperationException(); } public byte[] serialize(Object obj) throws IOException { LogContentBean bean = (LogContentBean)obj; int bytes = 1 + 4 + 4 + 8 * bean.stamps.length + 8 * bean.resources.length; byte[] result = new byte[bytes]; Bytes.write(result, 0, bean.leaf ? (byte)1 : (byte)0); int byteIndex = 1; Bytes.writeLE(result, byteIndex, bean.n); byteIndex += 4; Bytes.writeLE(result, byteIndex, bean.stamps.length); byteIndex += 4; for(long l : bean.stamps) { Bytes.writeLE(result, byteIndex, l); byteIndex += 8; } for(PossibleResource pr : bean.resources) { Bytes.writeLE(result, byteIndex, pr.longValue()); byteIndex += 8; } return result; } public Object deserialize(byte[] data) throws IOException { LogContentBean result = new LogContentBean(); try { result.leaf = Bytes.read(data, 0) == 1 ? true : false; int byteIndex = 1; result.n = Bytes.readLE4(data, byteIndex); byteIndex += 4; int t = Bytes.readLE4(data, byteIndex); byteIndex += 4; result.stamps = new long[t]; result.resources = new PossibleResource[t]; for(int i=0;i T accept(Visitor v) { throw new UnsupportedOperationException(); } @Override public boolean isInstance(Object obj) { throw new UnsupportedOperationException(); } @Override public void readFrom(Binding srcBinding, Object src, Object dst) throws BindingException { throw new UnsupportedOperationException(); } @Override public void assertInstaceIsValid(Object obj, Set validInstances) throws BindingException { throw new UnsupportedOperationException(); } @Override public int deepHashValue(Object value, IdentityHashMap hashedObjects) throws BindingException { throw new UnsupportedOperationException(); } @Override public int deepCompare(Object o1, Object o2, Set> compareHistory) throws BindingException { throw new UnsupportedOperationException(); } @Override protected void toString(Object value, BindingPrintContext ctx) throws BindingException { throw new UnsupportedOperationException(); } @Override public int getComponentCount() { throw new UnsupportedOperationException(); } @Override public Binding getComponentBinding(int index) { throw new UnsupportedOperationException(); } @Override public Binding getComponentBinding(ChildReference path) { throw new UnsupportedOperationException(); } @Override public Serializer serializer() throws RuntimeSerializerConstructionException { return serializer; } } final public class LogUtils implements LogContentManager { final public static boolean DEBUG = false; final public Binding CONTENT_BEAN_BINDING; final public DatatypeResource DATA; public LogUtils(ReadGraph graph) throws DatabaseException { try { CONTENT_BEAN_BINDING = new LogContentBinding(graph.getService(SerialisationSupport.class)); DATA = DatatypeResource.getInstance(graph); } catch (RuntimeBindingConstructionException e) { Logger.defaultLogError(e); throw new DatabaseException(e); } } public Resource create(WriteGraph graph, int t, int stamp) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); Resource tree = graph.newResource(); graph.claim(tree, L0.InstanceOf, null, DATA.Log); Resource index = createIndexNode(graph, this, stamp, t); graph.claim(tree, DATA.Log_root, DATA.Log_root_Inverse, index); graph.claimLiteral(tree, DATA.Log_t, t, Bindings.INTEGER); Resource leaf = createLeafNode(graph, this, stamp, t); graph.claim(index, DATA.BTreeNode_Content, null, leaf); LogContentBean rContent = getContentBean(graph, index); rContent.n = 1; rContent.stamps[0] = stamp; rContent.resources[0].r = leaf; setContentBean(graph, index, rContent); return tree; } public void insert(WriteGraph graph, Resource T, int stamp, Resource v) throws DatabaseException { Resource r = getRoot(graph, T); int t = getDegree(graph, T); insertImpl(graph, this, T, r, t, stamp, v); } static class BatchContentManager implements LogContentManager { final private LogUtils bu; final Map beans = new HashMap(); public BatchContentManager(LogUtils bu) { this.bu = bu; } @Override public LogContentBean getContentBean(ReadGraph graph, Resource node) throws DatabaseException { LogContentBean bean = beans.get(node); if(bean != null) return bean; return bu.getContentBean(graph, node); } @Override public void setContentBean(WriteGraph graph, Resource node, LogContentBean bean) throws DatabaseException { beans.put(node, bean); } public void apply(WriteGraph graph) throws DatabaseException { for(Map.Entry entry : beans.entrySet()) { bu.setContentBean(graph, entry.getKey(), entry.getValue()); } } } public void insertAll(WriteGraph graph, Resource T, Collection> values) throws DatabaseException { Resource r = getRoot(graph, T); int t = getDegree(graph, T); BatchContentManager cm = new BatchContentManager(this); for(Pair entry : values) { insertImpl(graph, cm, T, r, t, entry.first, entry.second); } cm.apply(graph); } // Implementation private void insertImpl(WriteGraph graph, LogContentManager manager, Resource T, Resource r, int t, int k, Resource v) throws DatabaseException { int code = insertImpl2(graph, manager, 0, T, r, t, k, v); if(code > 0) { LogContentBean rContent = manager.getContentBean(graph, r); if(DEBUG) System.err.println("[insert index code=" + code + "]"); Resource newRoot = createIndexNode(graph, manager, k, t); graph.claim(newRoot, DATA.Log_Node_Contains, null, r); setRoot(graph, T, newRoot); LogContentBean nContent = manager.getContentBean(graph, newRoot); nContent.stamps[0] = rContent.stamps[0]; nContent.resources[0].r = r; nContent.n = 1; manager.setContentBean(graph, newRoot, nContent); Resource leaf = createLeaf(graph, manager, newRoot, code, k, t); LogContentBean lContent = manager.getContentBean(graph, leaf); lContent.n = 1; lContent.stamps[0] = k; lContent.resources[0].r = v; if(DEBUG) System.err.println("[insert " + k + "]: started a new branch"); manager.setContentBean(graph, leaf, lContent); } } private int insertImpl2(WriteGraph graph, LogContentManager manager, int level, Resource T, Resource r, int t, int k, Resource v) throws DatabaseException { LogContentBean rContent = manager.getContentBean(graph, r); // Index if(!rContent.leaf) { Resource child = rContent.resources[rContent.n-1].r; int code = insertImpl2(graph, manager, level+1, T, child, t, k, v); if(code == 0) { // Value was inserted successfully return 0; } else { // The child was full if(rContent.n < t) { // We can create a new child Resource leaf = createLeaf(graph, manager, r, code-level-1, k, t); LogContentBean lContent = manager.getContentBean(graph, leaf); lContent.stamps[0] = k; lContent.resources[0].r = v; lContent.n = 1; manager.setContentBean(graph, leaf, lContent); if(DEBUG) System.err.println("[insert " + k + "]: created a fresh leaf"); return 0; } else { // We are full, let the parent handle this return code; } } } // Leaf else { if(rContent.n < t) { if(DEBUG) System.err.println("[insert " + k + "]: fit into leaf at level " + level); // Append rContent.stamps[rContent.n] = k; rContent.resources[rContent.n].r = v; rContent.n++; manager.setContentBean(graph, r, rContent); return 0; } else { // This leaf is full return level; } } } private Resource createLeaf(WriteGraph graph, LogContentManager manager, Resource r, int code, int stamp, int t) throws DatabaseException { LogContentBean rContent = manager.getContentBean(graph, r); if(code == 0) { if(DEBUG) System.err.println("[insert leaf code=" + code + "]"); Resource result = createLeafNode(graph, manager, stamp, t); graph.claim(r, DATA.Log_Node_Contains, null, result); rContent.stamps[rContent.n] = stamp; rContent.resources[rContent.n].r = result; rContent.n++; manager.setContentBean(graph, r, rContent); return result; } else { if(DEBUG) System.err.println("[insert index code=" + code + "]"); Resource index = createIndexNode(graph, manager, stamp, t); graph.claim(r, DATA.Log_Node_Contains, null, index); rContent.stamps[rContent.n] = stamp; rContent.resources[rContent.n].r = index; rContent.n++; manager.setContentBean(graph, r, rContent); return createLeaf(graph, manager, index, code-1, stamp, t); } } private Resource createIndexNode(WriteGraph graph, LogContentManager manager, int stamp, int t) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); Resource result = graph.newResource(); graph.claim(result, L0.InstanceOf, null, DATA.Log_IndexNode); manager.setContentBean(graph, result, LogContentBean.create(t, stamp, false)); return result; } private Resource createLeafNode(WriteGraph graph, LogContentManager manager, int stamp, int t) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); Resource result = graph.newResource(); graph.claim(result, L0.InstanceOf, null, DATA.Log_LeafNode); manager.setContentBean(graph, result, LogContentBean.create(t, stamp, true)); return result; } private Resource getRoot(ReadGraph graph, Resource T) throws DatabaseException { return graph.getPossibleObject(T, DATA.Log_root); } private void setRoot(WriteGraph graph, Resource T, Resource r) throws DatabaseException { graph.deny(T, DATA.Log_root); graph.claim(T, DATA.Log_root, r); } public void setContentBean(WriteGraph graph, Resource node, LogContentBean bean) throws DatabaseException { graph.claimLiteral(node, DATA.Log_Node_content, DATA.Log_Content, bean, CONTENT_BEAN_BINDING ); } private int getDegree(ReadGraph graph, Resource tree) throws DatabaseException { return graph.syncRequest(new RelatedValue(tree, DATA.Log_t, Bindings.INTEGER), TransientCacheListener.instance()); } public LogContentBean getContentBean(ReadGraph graph, Resource node) throws DatabaseException { return graph.syncRequest(new RelatedValue(node, DATA.Log_Node_content, CONTENT_BEAN_BINDING), TransientCacheListener.instance()); } }