]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.databoard/src/org/simantics/databoard/util/binary/Blob.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / util / binary / Blob.java
diff --git a/bundles/org.simantics.databoard/src/org/simantics/databoard/util/binary/Blob.java b/bundles/org.simantics.databoard/src/org/simantics/databoard/util/binary/Blob.java
new file mode 100644 (file)
index 0000000..db5e20e
--- /dev/null
@@ -0,0 +1,647 @@
+/*******************************************************************************\r
+ *  Copyright (c) 2010 Association for Decentralized Information Management in\r
+ *  Industry THTH ry.\r
+ *  All rights reserved. This program and the accompanying materials\r
+ *  are made available under the terms of the Eclipse Public License v1.0\r
+ *  which accompanies this distribution, and is available at\r
+ *  http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ *  Contributors:\r
+ *      VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.databoard.util.binary;
+
+import java.io.DataInputStream;\r
+import java.io.IOException;\r
+import java.nio.ByteBuffer;\r
+import java.util.WeakHashMap;\r
+
+/**
+ * Blob is recursive random access binary. Blob is isolated 
+ * random access binary, modifications, insertions and removals of bytes
+ * outside the represented bytes do not affect the blob. Insertions and removals
+ * of bytes to the parent do affect the blob, its start index, length and pointer
+ * are changed. 
+ * <p>
+ * A backend must not be wrapped in a blob more than once.
+ * <p>
+ * Grow, Shrink, Insertion, and Removal affects child blobs if affected region 
+ * intersects with a child. 
+ * 
+ * Grow, Shrink, Insertion, and Removal affects parent. It updates parent length,
+ * and start positions of the following (not preceding) siblings.
+ *
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
+ */
+public class Blob implements RandomAccessBinary {
+
+       /** Parent Blob */
+       RandomAccessBinary parent;
+       /** Position of index 0 at parent blob */
+       long start; 
+       /** Size of this blob */
+       long length;
+       /** Children */
+       WeakHashMap<Blob, Object> children;
+       
+    long pointer = 0;    
+
+    /**
+     * Create a sub-blob to a random access binary.
+     * 
+     * @param parent
+     * @throws IOException 
+     */
+       public Blob(RandomAccessBinary parent) throws IOException {
+               this(parent, 0, parent.length());\r
+       }
+    
+    /**
+     * Create a sub-blob to a random access binary.
+     * 
+     * @param parent
+     * @param start
+     * @param length
+     */
+       public Blob(RandomAccessBinary parent, long start, long length) {\r
+               this.parent = parent;
+               this.start = start;
+               this.length = length;
+       }
+
+       public Blob createSubBlob(long start, long length) {
+               Blob result = new Blob(this, start, length);
+               if (children == null) children = new WeakHashMap<Blob, Object>(1); 
+               children.put(result, Blob.class);
+               return result;
+       }\r
+       \r
+       public RandomAccessBinary getParent() {\r
+               return parent;\r
+       }
+       
+       @Override
+       public void close() throws IOException {\r
+       }\r
+       \r
+       @Override\r
+       public boolean isOpen() {\r
+               return parent.isOpen();\r
+       }
+       
+       @Override
+       public void insertBytes(long bytes, ByteSide side) throws IOException {
+               if (bytes==0) return;
+               if (bytes < 0) throw new IllegalArgumentException();
+               if (pointer < 0) throw new IndexOutOfBoundsException();
+               
+               RandomAccessBinary backend = parent;
+               long startInBackend = start;
+               while ( (backend instanceof Blob) ) {                   
+                       startInBackend += ((Blob) backend).start;
+                       backend = ((Blob) backend).parent;
+               }
+               
+               
+               // Pointer outside blob, Add more bytes 
+               if (pointer > length) {
+                       bytes += pointer - length;
+                       pointer = length;
+               }
+               
+               // Add bytes to the back-end
+               backend.position(startInBackend + pointer);
+               backend.insertBytes(bytes, side);
+               length += bytes;
+               
+               // Notify-chain towards parent
+               if (parent instanceof Blob) {
+                       ((Blob)parent).childGrewBackend(this, start + pointer, bytes);
+               }
+               // Notify-chain towards children 
+               if (children != null) {
+                       for (Blob child : children.keySet()) {
+//                             if (intersects(pointer, length, child.start, child.length)) 
+                                       child.parentGrew(this, pointer - child.start, bytes, side);
+                       }
+               }
+       }
+       
+       /**
+        * A child has modified the back-end. Update the following siblings. The child
+        * has already updated its own length. 
+        * @param child
+        * @param pos position in this blob
+        * @param bytes
+        */
+       void childGrewBackend(Blob child, long pos, long bytes)
+       {               
+               length += bytes;\r
+               assert(bytes>=0);\r
+               
+               if (children != null) {
+                       for (Blob c : children.keySet()) {
+                               if (c==child) continue; 
+                               if (pos <= c.start) {
+                                       c.start += bytes;
+                               }
+                       }
+               }
+               if (parent instanceof Blob) ((Blob)parent).childGrewBackend(this, pos+start, bytes);
+       }
+       
+       /**
+        * Parent of this blob grew itself in the back-end.\r
+        *  
+        * @param parent
+        * @param pos position in this blob
+        * @param bytes
+        */
+       void parentGrew(Blob parent, long pos, long bytes, ByteSide side)
+       {
+               if (pos<0 || (pos==0 && side!=ByteSide.Right) ) {\r
+                       start += bytes;\r
+                       return;\r
+               }\r
+               if (pos>=length) {\r
+                       return;\r
+               }\r
+               // Grow applies this blob\r
+               if (\r
+                       ( pos==0 && side==ByteSide.Right ) ||\r
+                       ( pos==length && side==ByteSide.Left ) ||\r
+                       ( pos>0 && pos<length ) ) \r
+                       length += bytes;\r
+               
+               // Notify children
+               if (children!=null) {
+                       for (Blob child : children.keySet()) {
+//                             if (intersects(pointer, length, child.start, child.length)) 
+                                       child.parentGrew(this, pos - child.start, bytes, side);
+                       }
+               }
+       }
+
+       
+       /**
+        * Remove bytes at pointer.
+        * 
+        * @param bytes
+        */
+       @Override
+       public void removeBytes(long bytes, ByteSide side) throws IOException {
+               if (pointer<0 || pointer+bytes>length) throw new IndexOutOfBoundsException();
+               if (bytes==0) return;\r
+               if (bytes<0) throw new IllegalArgumentException("bytes must be positive value");
+               
+               // Go to backend
+               RandomAccessBinary backend = parent;
+               long startInBackend = start;
+               while ( (backend instanceof Blob) ) {                   
+                       startInBackend += ((Blob) backend).start;
+                       backend = ((Blob) backend).parent;
+               }
+                               
+               // Remove bytes from the back-end
+               backend.position(startInBackend + pointer);
+               backend.removeBytes(bytes, side);
+               length -= bytes;
+               
+               // Notify direct parent
+               if (parent instanceof Blob) {
+                       ((Blob)parent).childShrankBackend(this, start + pointer, bytes);
+               }
+               // Notify direct children 
+               if (children != null) {
+                       for (Blob child : children.keySet()) {
+//                             if (intersects(pointer, length, child.start, child.length)) 
+                                       child.parentShrunk(this, pointer - child.start, bytes);
+                       }
+               }
+       }       
+       
+       /**
+        * A child has modified the back-end. Update the following siblings. The child
+        * has already updated its own length. 
+        * @param child
+        * @param pos position in this blob
+        * @param bytes
+        */
+       void childShrankBackend(Blob child, long pos, long bytes)
+       {\r
+               length -= bytes;\r
+               assert(bytes>=0);\r
+               
+               // update siblings
+               if (children != null) {
+                       for (Blob c : children.keySet()) {
+                               if (c==child) continue; 
+                               if (pos < c.start) {
+                                       c.start -= bytes;
+                               }
+                       }
+               }
+               if (parent instanceof Blob) ((Blob) parent).childShrankBackend(this, pos+start, bytes);
+       }
+       
+       /**
+        * Parent of this blob shrank itself in the back-end.\r
+        * \r
+        * @param parent
+        * @param pos position in this blob
+        * @param bytes
+        */
+       void parentShrunk(Blob parent, long pos, long bytes)
+       {\r
+               if (pos<0) {\r
+                       start -= bytes;\r
+                       return;\r
+               }\r
+               if (pos>=length) {\r
+                       return;\r
+               }\r
+               // Change applies this blob\r
+               length -= bytes;                \r
+               
+               // Notify children
+               if (children != null)
+                       for (Blob child : children.keySet()) {
+//                             if (intersects(pos, length, child.start, child.length)) 
+                                       child.parentShrunk(this, pos - child.start, bytes);
+                       }
+       }
+       
+       /**
+        * Modify the size of the blob. The operation changes the size of the 
+        * parent blob aswell.
+        * 
+        * @param newLength new number of bytes
+        */
+       @Override
+       public void setLength(long newLength) throws IOException {
+               long oldLength = length;
+               if (oldLength==newLength) return;
+                
+               if (oldLength < newLength) {
+                       // Grow
+                       long oldPointer = pointer;
+                       pointer = oldLength;
+                       insertBytes( newLength - oldLength, ByteSide.Left );
+                       pointer = oldPointer;
+                       return;
+               } else {
+                       // Shrink
+                       long oldPointer = pointer;
+                       pointer = newLength;
+                       removeBytes( oldLength - newLength, ByteSide.Left );
+                       pointer = oldPointer;
+                       return;
+               }
+
+       }       
+
+
+       public RandomAccessBinary getSource() {
+               return parent;
+       }
+       
+       @Override
+       public void flush() throws IOException {
+               parent.flush();
+       }\r
+       \r
+       @Override\r
+       public void reset() throws IOException {\r
+               parent.reset();\r
+               length = parent.length();\r
+       }
+
+       @Override
+       public void write(int b) throws IOException {
+               assertHasWritableBytes(1);              
+               parent.position(start + pointer);
+               parent.write(b);
+               pointer += 1;
+       }\r
+       \r
+       @Override\r
+       public void writeByte(int b) throws IOException {\r
+               assertHasWritableBytes(1);              \r
+               parent.position(start + pointer);\r
+               parent.write(b);\r
+               pointer += 1;\r
+       }       \r
+       \r
+       @Override\r
+       public void writeBoolean(boolean v) throws IOException {\r
+               assertHasWritableBytes(1);              \r
+               parent.position(start + pointer);\r
+               parent.write( (byte) (v ? 1 : 0));\r
+               pointer += 1;\r
+       }
+
+       @Override
+       public void writeFully(ByteBuffer src) throws IOException {
+               long bytes = src.remaining();
+               assertHasWritableBytes(bytes);          
+               parent.position(start + pointer);
+               parent.writeFully(src);
+               pointer += bytes;
+       }
+
+       @Override
+       public void writeFully(ByteBuffer src, int length) throws IOException {
+               assertHasWritableBytes(length);         
+               parent.position(start + pointer);
+               parent.writeFully(src, length);
+               pointer += length;
+       }
+
+       @Override
+       public void write(byte[] src, int offset, int length) throws IOException {
+               assertHasWritableBytes(length);         
+               parent.position(start + pointer);
+               parent.write(src, offset, length);
+               pointer += length;
+       }
+
+       @Override
+       public void write(byte[] src) throws IOException {
+               assertHasWritableBytes(src.length);             
+               parent.position(start + pointer);
+               parent.write(src);
+               pointer += src.length;
+       }
+
+       @Override
+       public void writeDouble(double value) throws IOException {
+               assertHasWritableBytes(8);              
+               parent.position(start + pointer);
+               parent.writeDouble(value);
+               pointer += 8;
+       }
+
+       @Override
+       public void writeFloat(float value) throws IOException {
+               assertHasWritableBytes(4);              
+               parent.position(start + pointer);
+               parent.writeFloat(value);
+               pointer += 4;
+       }
+
+       @Override
+       public void writeInt(int value) throws IOException {
+               assertHasWritableBytes(4);              
+               parent.position(start + pointer);
+               parent.writeInt(value);
+               pointer += 4;
+       }
+
+       @Override
+       public void writeLong(long value) throws IOException {
+               assertHasWritableBytes(8);              
+               parent.position(start + pointer);
+               parent.writeLong(value);
+               pointer += 8;
+       }
+
+       @Override
+       public void writeShort(int value) throws IOException {
+               assertHasWritableBytes(2);              
+               parent.position(start + pointer);
+               parent.writeShort(value);
+               pointer += 2;
+       }\r
+       \r
+       @Override\r
+       public void writeChar(int value) throws IOException {\r
+               assertHasWritableBytes(2);              \r
+               parent.position(start + pointer);\r
+               parent.writeChar(value);\r
+               pointer += 2;\r
+       }       \r
+       \r
+       @Override\r
+       public void writeBytes(String s) throws IOException {\r
+               int len = s.length();\r
+               assertHasWritableBytes(len);            \r
+               parent.position(start + pointer);\r
+               parent.writeBytes(s);\r
+               pointer += len;\r
+       }\r
+       \r
+       @Override\r
+       public void writeChars(String s) throws IOException {\r
+               int len = s.length();\r
+               assertHasWritableBytes(len*2);          \r
+               parent.position(start + pointer);\r
+               parent.writeChars(s);\r
+               pointer += len*2;\r
+       }       \r
+       \r
+       @Override\r
+       public void writeUTF(String s) throws IOException {\r
+               int len = UTF8.getModifiedUTF8EncodingByteLength(s);\r
+               assertHasWritableBytes(len+2);          \r
+               parent.position(start + pointer);\r
+//             parent.writeUTF(s);\r
+               parent.writeShort(len);\r
+               UTF8.writeUTF(this, s);\r
+               pointer += len+2;       \r
+       }\r
+
+       @Override
+       public byte readByte() throws IOException {
+               assertHasReadableBytes(1);
+               parent.position(start + pointer);
+               byte result = parent.readByte();
+               pointer += 1;
+               return result;
+       }\r
+       \r
+       @Override\r
+       public int readUnsignedByte() throws IOException {\r
+               assertHasReadableBytes(1);\r
+               parent.position(start + pointer);\r
+               int result = parent.readUnsignedByte();\r
+               pointer += 1;\r
+               return result;\r
+       }       \r
+       \r
+       @Override\r
+       public boolean readBoolean() throws IOException {\r
+               assertHasReadableBytes(1);\r
+               parent.position(start + pointer);\r
+               boolean result = parent.readBoolean();\r
+               pointer += 1;\r
+               return result;\r
+       }       
+
+       @Override
+       public void readFully(byte[] dst, int offset, int length) throws IOException {
+               assertHasReadableBytes(length);
+               parent.position(start + pointer);
+               parent.readFully(dst, offset, length);
+               pointer += length;
+       }
+
+       @Override
+       public void readFully(byte[] dst) throws IOException {
+               assertHasReadableBytes(dst.length);
+               parent.position(start + pointer);
+               parent.readFully(dst);          
+               pointer += dst.length;
+       }
+
+       @Override
+       public void readFully(ByteBuffer buf) throws IOException {
+               int bytes = (int) Math.min(buf.remaining(), length-pointer);
+               parent.position(start + pointer);
+               parent.readFully(buf, bytes);
+               pointer += bytes;
+       }
+
+       @Override
+       public void readFully(ByteBuffer buf, int length) throws IOException {
+               assertHasReadableBytes(length);
+               parent.position(start + pointer);
+               parent.readFully(buf, length);
+               pointer += length;
+       }
+
+       @Override
+       public double readDouble() throws IOException {
+               assertHasReadableBytes(8);
+               parent.position(start + pointer);
+               double result = parent.readDouble();
+               pointer += 8;
+               return result;
+       }
+
+       @Override
+       public float readFloat() throws IOException {
+               assertHasReadableBytes(4);
+               parent.position(start + pointer);
+               float result = parent.readFloat();
+               pointer += 4;
+               return result;
+       }
+
+       @Override
+       public int readInt() throws IOException {
+               assertHasReadableBytes(4);
+               parent.position(start + pointer);
+               int result = parent.readInt();
+               pointer += 4;
+               return result;
+       }
+
+       @Override
+       public long readLong() throws IOException {
+               assertHasReadableBytes(8);
+               parent.position(start + pointer);
+               long result = parent.readLong();
+               pointer += 8;
+               return result;
+       }
+
+       @Override
+       public short readShort() throws IOException {
+               assertHasReadableBytes(2);
+               parent.position(start + pointer);
+               short result = parent.readShort();
+               pointer += 2;
+               return result;
+       }\r
+       \r
+       @Override\r
+       public String readLine() throws IOException {\r
+               assertHasReadableBytes(2);\r
+               parent.position(start + pointer);\r
+               String result = parent.readLine();\r
+               pointer += ( parent.position() - start - pointer );\r
+               return result;\r
+       }       \r
+       \r
+    public final String readUTF() throws IOException {\r
+       return DataInputStream.readUTF(this);\r
+    }  \r
+       \r
+       @Override\r
+       public char readChar() throws IOException {\r
+               assertHasReadableBytes(2);\r
+               parent.position(start + pointer);\r
+               char result = parent.readChar();\r
+               pointer += 2;\r
+               return result;\r
+       }       \r
+       \r
+       @Override\r
+       public int readUnsignedShort() throws IOException {\r
+               assertHasReadableBytes(2);\r
+               parent.position(start + pointer);\r
+               int result = parent.readShort() & 0xffff;\r
+               pointer += 2;\r
+               return result;\r
+       }       
+       
+       void assertHasReadableBytes(long count) {
+               if (pointer + count > length)
+                       throw new IndexOutOfBoundsException();
+       }
+       
+       void assertHasWritableBytes(long count) throws IOException {
+               if (pointer + count > length)
+                       setLength(pointer + count);
+       }
+
+       @Override
+       public long length() throws IOException {
+               return length;
+       }
+
+       @Override
+       public long position() throws IOException {
+               return pointer;
+       }
+       
+       @Override
+       public void position(long newPosition) throws IOException {
+               pointer = newPosition;
+       }
+
+       @Override
+       public long skipBytes(long bytes) throws IOException {
+               pointer += bytes;\r
+               return bytes;
+       }\r
+       
+       @Override\r
+       public int skipBytes(int bytes) throws IOException {\r
+               pointer += bytes;\r
+               return bytes;\r
+       }\r
+       
+       public long getStartPositionInSourceBinary() {
+               return start;
+       }
+       
+       @Override
+       public String toString() {
+               return parent+"["+start+".."+(start+length)+"]";
+       }
+
+       static boolean intersects(long start1, long len1, long start2, long len2) {
+        if (start1 >= start2+len2) return false;
+        if (start1+len1 <= start2) return false;
+        return true;
+               
+       }
+       
+       public void setPositionInSource(long start, long length) {\r
+               this.start = start;
+               this.length = length;
+       }
+       
+}
+