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; + } + +} +