X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.databoard%2Fsrc%2Forg%2Fsimantics%2Fdataboard%2Futil%2Fbinary%2FBlob.java;fp=bundles%2Forg.simantics.databoard%2Fsrc%2Forg%2Fsimantics%2Fdataboard%2Futil%2Fbinary%2FBlob.java;h=db5e20e8bdba0cbc40df049bb31a3d010f847602;hp=0000000000000000000000000000000000000000;hb=969bd23cab98a79ca9101af33334000879fb60c5;hpb=866dba5cd5a3929bbeae85991796acb212338a08
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
index 000000000..db5e20e8b
--- /dev/null
+++ b/bundles/org.simantics.databoard/src/org/simantics/databoard/util/binary/Blob.java
@@ -0,0 +1,647 @@
+/*******************************************************************************
+ * Copyright (c) 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.databoard.util.binary;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.WeakHashMap;
+
+/**
+ * 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.
+ *
+ * A backend must not be wrapped in a blob more than once.
+ *
+ * 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
+ */
+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 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());
+ }
+
+ /**
+ * Create a sub-blob to a random access binary.
+ *
+ * @param parent
+ * @param start
+ * @param length
+ */
+ public Blob(RandomAccessBinary parent, long start, long length) {
+ 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(1);
+ children.put(result, Blob.class);
+ return result;
+ }
+
+ public RandomAccessBinary getParent() {
+ return parent;
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+
+ @Override
+ public boolean isOpen() {
+ return parent.isOpen();
+ }
+
+ @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;
+ assert(bytes>=0);
+
+ 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.
+ *
+ * @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) ) {
+ start += bytes;
+ return;
+ }
+ if (pos>=length) {
+ return;
+ }
+ // Grow applies this blob
+ if (
+ ( pos==0 && side==ByteSide.Right ) ||
+ ( pos==length && side==ByteSide.Left ) ||
+ ( pos>0 && poslength) throw new IndexOutOfBoundsException();
+ if (bytes==0) return;
+ 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)
+ {
+ length -= bytes;
+ assert(bytes>=0);
+
+ // 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.
+ *
+ * @param parent
+ * @param pos position in this blob
+ * @param bytes
+ */
+ void parentShrunk(Blob parent, long pos, long bytes)
+ {
+ if (pos<0) {
+ start -= bytes;
+ return;
+ }
+ if (pos>=length) {
+ return;
+ }
+ // Change applies this blob
+ length -= bytes;
+
+ // 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();
+ }
+
+ @Override
+ public void reset() throws IOException {
+ parent.reset();
+ length = parent.length();
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ assertHasWritableBytes(1);
+ parent.position(start + pointer);
+ parent.write(b);
+ pointer += 1;
+ }
+
+ @Override
+ public void writeByte(int b) throws IOException {
+ assertHasWritableBytes(1);
+ parent.position(start + pointer);
+ parent.write(b);
+ pointer += 1;
+ }
+
+ @Override
+ public void writeBoolean(boolean v) throws IOException {
+ assertHasWritableBytes(1);
+ parent.position(start + pointer);
+ parent.write( (byte) (v ? 1 : 0));
+ pointer += 1;
+ }
+
+ @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;
+ }
+
+ @Override
+ public void writeChar(int value) throws IOException {
+ assertHasWritableBytes(2);
+ parent.position(start + pointer);
+ parent.writeChar(value);
+ pointer += 2;
+ }
+
+ @Override
+ public void writeBytes(String s) throws IOException {
+ int len = s.length();
+ assertHasWritableBytes(len);
+ parent.position(start + pointer);
+ parent.writeBytes(s);
+ pointer += len;
+ }
+
+ @Override
+ public void writeChars(String s) throws IOException {
+ int len = s.length();
+ assertHasWritableBytes(len*2);
+ parent.position(start + pointer);
+ parent.writeChars(s);
+ pointer += len*2;
+ }
+
+ @Override
+ public void writeUTF(String s) throws IOException {
+ int len = UTF8.getModifiedUTF8EncodingByteLength(s);
+ assertHasWritableBytes(len+2);
+ parent.position(start + pointer);
+// parent.writeUTF(s);
+ parent.writeShort(len);
+ UTF8.writeUTF(this, s);
+ pointer += len+2;
+ }
+
+ @Override
+ public byte readByte() throws IOException {
+ assertHasReadableBytes(1);
+ parent.position(start + pointer);
+ byte result = parent.readByte();
+ pointer += 1;
+ return result;
+ }
+
+ @Override
+ public int readUnsignedByte() throws IOException {
+ assertHasReadableBytes(1);
+ parent.position(start + pointer);
+ int result = parent.readUnsignedByte();
+ pointer += 1;
+ return result;
+ }
+
+ @Override
+ public boolean readBoolean() throws IOException {
+ assertHasReadableBytes(1);
+ parent.position(start + pointer);
+ boolean result = parent.readBoolean();
+ pointer += 1;
+ return result;
+ }
+
+ @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;
+ }
+
+ @Override
+ public String readLine() throws IOException {
+ assertHasReadableBytes(2);
+ parent.position(start + pointer);
+ String result = parent.readLine();
+ pointer += ( parent.position() - start - pointer );
+ return result;
+ }
+
+ public final String readUTF() throws IOException {
+ return DataInputStream.readUTF(this);
+ }
+
+ @Override
+ public char readChar() throws IOException {
+ assertHasReadableBytes(2);
+ parent.position(start + pointer);
+ char result = parent.readChar();
+ pointer += 2;
+ return result;
+ }
+
+ @Override
+ public int readUnsignedShort() throws IOException {
+ assertHasReadableBytes(2);
+ parent.position(start + pointer);
+ int result = parent.readShort() & 0xffff;
+ pointer += 2;
+ return result;
+ }
+
+ 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;
+ return bytes;
+ }
+
+ @Override
+ public int skipBytes(int bytes) throws IOException {
+ pointer += bytes;
+ return bytes;
+ }
+
+ 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) {
+ this.start = start;
+ this.length = length;
+ }
+
+}
+