1 /*******************************************************************************
2 * Copyright (c) 2010 Association for Decentralized Information Management in
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.databoard.util.binary;
14 import java.io.DataInputStream;
15 import java.io.IOException;
16 import java.nio.ByteBuffer;
17 import java.util.WeakHashMap;
20 * Blob is recursive random access binary. Blob is isolated
21 * random access binary, modifications, insertions and removals of bytes
22 * outside the represented bytes do not affect the blob. Insertions and removals
23 * of bytes to the parent do affect the blob, its start index, length and pointer
26 * A backend must not be wrapped in a blob more than once.
28 * Grow, Shrink, Insertion, and Removal affects child blobs if affected region
29 * intersects with a child.
31 * Grow, Shrink, Insertion, and Removal affects parent. It updates parent length,
32 * and start positions of the following (not preceding) siblings.
34 * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
36 public class Blob implements RandomAccessBinary {
39 RandomAccessBinary parent;
40 /** Position of index 0 at parent blob */
42 /** Size of this blob */
45 WeakHashMap<Blob, Object> children;
50 * Create a sub-blob to a random access binary.
55 public Blob(RandomAccessBinary parent) throws IOException {
56 this(parent, 0, parent.length());
60 * Create a sub-blob to a random access binary.
66 public Blob(RandomAccessBinary parent, long start, long length) {
72 public Blob createSubBlob(long start, long length) {
73 Blob result = new Blob(this, start, length);
74 if (children == null) children = new WeakHashMap<Blob, Object>(1);
75 children.put(result, Blob.class);
79 public RandomAccessBinary getParent() {
84 public void close() throws IOException {
88 public boolean isOpen() {
89 return parent.isOpen();
93 public void insertBytes(long bytes, ByteSide side) throws IOException {
95 if (bytes < 0) throw new IllegalArgumentException();
96 if (pointer < 0) throw new IndexOutOfBoundsException();
98 RandomAccessBinary backend = parent;
99 long startInBackend = start;
100 while ( (backend instanceof Blob) ) {
101 startInBackend += ((Blob) backend).start;
102 backend = ((Blob) backend).parent;
106 // Pointer outside blob, Add more bytes
107 if (pointer > length) {
108 bytes += pointer - length;
112 // Add bytes to the back-end
113 backend.position(startInBackend + pointer);
114 backend.insertBytes(bytes, side);
117 // Notify-chain towards parent
118 if (parent instanceof Blob) {
119 ((Blob)parent).childGrewBackend(this, start + pointer, bytes);
121 // Notify-chain towards children
122 if (children != null) {
123 for (Blob child : children.keySet()) {
124 // if (intersects(pointer, length, child.start, child.length))
125 child.parentGrew(this, pointer - child.start, bytes, side);
131 * A child has modified the back-end. Update the following siblings. The child
132 * has already updated its own length.
134 * @param pos position in this blob
137 void childGrewBackend(Blob child, long pos, long bytes)
142 if (children != null) {
143 for (Blob c : children.keySet()) {
144 if (c==child) continue;
145 if (pos <= c.start) {
150 if (parent instanceof Blob) ((Blob)parent).childGrewBackend(this, pos+start, bytes);
154 * Parent of this blob grew itself in the back-end.
157 * @param pos position in this blob
160 void parentGrew(Blob parent, long pos, long bytes, ByteSide side)
162 if (pos<0 || (pos==0 && side!=ByteSide.Right) ) {
169 // Grow applies this blob
171 ( pos==0 && side==ByteSide.Right ) ||
172 ( pos==length && side==ByteSide.Left ) ||
173 ( pos>0 && pos<length ) )
177 if (children!=null) {
178 for (Blob child : children.keySet()) {
179 // if (intersects(pointer, length, child.start, child.length))
180 child.parentGrew(this, pos - child.start, bytes, side);
187 * Remove bytes at pointer.
192 public void removeBytes(long bytes, ByteSide side) throws IOException {
193 if (pointer<0 || pointer+bytes>length) throw new IndexOutOfBoundsException();
194 if (bytes==0) return;
195 if (bytes<0) throw new IllegalArgumentException("bytes must be positive value");
198 RandomAccessBinary backend = parent;
199 long startInBackend = start;
200 while ( (backend instanceof Blob) ) {
201 startInBackend += ((Blob) backend).start;
202 backend = ((Blob) backend).parent;
205 // Remove bytes from the back-end
206 backend.position(startInBackend + pointer);
207 backend.removeBytes(bytes, side);
210 // Notify direct parent
211 if (parent instanceof Blob) {
212 ((Blob)parent).childShrankBackend(this, start + pointer, bytes);
214 // Notify direct children
215 if (children != null) {
216 for (Blob child : children.keySet()) {
217 // if (intersects(pointer, length, child.start, child.length))
218 child.parentShrunk(this, pointer - child.start, bytes);
224 * A child has modified the back-end. Update the following siblings. The child
225 * has already updated its own length.
227 * @param pos position in this blob
230 void childShrankBackend(Blob child, long pos, long bytes)
236 if (children != null) {
237 for (Blob c : children.keySet()) {
238 if (c==child) continue;
244 if (parent instanceof Blob) ((Blob) parent).childShrankBackend(this, pos+start, bytes);
248 * Parent of this blob shrank itself in the back-end.
251 * @param pos position in this blob
254 void parentShrunk(Blob parent, long pos, long bytes)
263 // Change applies this blob
267 if (children != null)
268 for (Blob child : children.keySet()) {
269 // if (intersects(pos, length, child.start, child.length))
270 child.parentShrunk(this, pos - child.start, bytes);
275 * Modify the size of the blob. The operation changes the size of the
276 * parent blob aswell.
278 * @param newLength new number of bytes
281 public void setLength(long newLength) throws IOException {
282 long oldLength = length;
283 if (oldLength==newLength) return;
285 if (oldLength < newLength) {
287 long oldPointer = pointer;
289 insertBytes( newLength - oldLength, ByteSide.Left );
290 pointer = oldPointer;
294 long oldPointer = pointer;
296 removeBytes( oldLength - newLength, ByteSide.Left );
297 pointer = oldPointer;
304 public RandomAccessBinary getSource() {
309 public void flush() throws IOException {
314 public void reset() throws IOException {
316 length = parent.length();
320 public void write(int b) throws IOException {
321 assertHasWritableBytes(1);
322 parent.position(start + pointer);
328 public void writeByte(int b) throws IOException {
329 assertHasWritableBytes(1);
330 parent.position(start + pointer);
336 public void writeBoolean(boolean v) throws IOException {
337 assertHasWritableBytes(1);
338 parent.position(start + pointer);
339 parent.write( (byte) (v ? 1 : 0));
344 public void writeFully(ByteBuffer src) throws IOException {
345 long bytes = src.remaining();
346 assertHasWritableBytes(bytes);
347 parent.position(start + pointer);
348 parent.writeFully(src);
353 public void writeFully(ByteBuffer src, int length) throws IOException {
354 assertHasWritableBytes(length);
355 parent.position(start + pointer);
356 parent.writeFully(src, length);
361 public void write(byte[] src, int offset, int length) throws IOException {
362 assertHasWritableBytes(length);
363 parent.position(start + pointer);
364 parent.write(src, offset, length);
369 public void write(byte[] src) throws IOException {
370 assertHasWritableBytes(src.length);
371 parent.position(start + pointer);
373 pointer += src.length;
377 public void writeDouble(double value) throws IOException {
378 assertHasWritableBytes(8);
379 parent.position(start + pointer);
380 parent.writeDouble(value);
385 public void writeFloat(float value) throws IOException {
386 assertHasWritableBytes(4);
387 parent.position(start + pointer);
388 parent.writeFloat(value);
393 public void writeInt(int value) throws IOException {
394 assertHasWritableBytes(4);
395 parent.position(start + pointer);
396 parent.writeInt(value);
401 public void writeLong(long value) throws IOException {
402 assertHasWritableBytes(8);
403 parent.position(start + pointer);
404 parent.writeLong(value);
409 public void writeShort(int value) throws IOException {
410 assertHasWritableBytes(2);
411 parent.position(start + pointer);
412 parent.writeShort(value);
417 public void writeChar(int value) throws IOException {
418 assertHasWritableBytes(2);
419 parent.position(start + pointer);
420 parent.writeChar(value);
425 public void writeBytes(String s) throws IOException {
426 int len = s.length();
427 assertHasWritableBytes(len);
428 parent.position(start + pointer);
429 parent.writeBytes(s);
434 public void writeChars(String s) throws IOException {
435 int len = s.length();
436 assertHasWritableBytes(len*2);
437 parent.position(start + pointer);
438 parent.writeChars(s);
443 public void writeUTF(String s) throws IOException {
444 int len = UTF8.getModifiedUTF8EncodingByteLength(s);
445 assertHasWritableBytes(len+2);
446 parent.position(start + pointer);
447 // parent.writeUTF(s);
448 parent.writeShort(len);
449 UTF8.writeUTF(this, s);
454 public byte readByte() throws IOException {
455 assertHasReadableBytes(1);
456 parent.position(start + pointer);
457 byte result = parent.readByte();
463 public int readUnsignedByte() throws IOException {
464 assertHasReadableBytes(1);
465 parent.position(start + pointer);
466 int result = parent.readUnsignedByte();
472 public boolean readBoolean() throws IOException {
473 assertHasReadableBytes(1);
474 parent.position(start + pointer);
475 boolean result = parent.readBoolean();
481 public void readFully(byte[] dst, int offset, int length) throws IOException {
482 assertHasReadableBytes(length);
483 parent.position(start + pointer);
484 parent.readFully(dst, offset, length);
489 public void readFully(byte[] dst) throws IOException {
490 assertHasReadableBytes(dst.length);
491 parent.position(start + pointer);
492 parent.readFully(dst);
493 pointer += dst.length;
497 public void readFully(ByteBuffer buf) throws IOException {
498 int bytes = (int) Math.min(buf.remaining(), length-pointer);
499 parent.position(start + pointer);
500 parent.readFully(buf, bytes);
505 public void readFully(ByteBuffer buf, int length) throws IOException {
506 assertHasReadableBytes(length);
507 parent.position(start + pointer);
508 parent.readFully(buf, length);
513 public double readDouble() throws IOException {
514 assertHasReadableBytes(8);
515 parent.position(start + pointer);
516 double result = parent.readDouble();
522 public float readFloat() throws IOException {
523 assertHasReadableBytes(4);
524 parent.position(start + pointer);
525 float result = parent.readFloat();
531 public int readInt() throws IOException {
532 assertHasReadableBytes(4);
533 parent.position(start + pointer);
534 int result = parent.readInt();
540 public long readLong() throws IOException {
541 assertHasReadableBytes(8);
542 parent.position(start + pointer);
543 long result = parent.readLong();
549 public short readShort() throws IOException {
550 assertHasReadableBytes(2);
551 parent.position(start + pointer);
552 short result = parent.readShort();
558 public String readLine() throws IOException {
559 assertHasReadableBytes(2);
560 parent.position(start + pointer);
561 String result = parent.readLine();
562 pointer += ( parent.position() - start - pointer );
566 public final String readUTF() throws IOException {
567 return DataInputStream.readUTF(this);
571 public char readChar() throws IOException {
572 assertHasReadableBytes(2);
573 parent.position(start + pointer);
574 char result = parent.readChar();
580 public int readUnsignedShort() throws IOException {
581 assertHasReadableBytes(2);
582 parent.position(start + pointer);
583 int result = parent.readShort() & 0xffff;
588 void assertHasReadableBytes(long count) {
589 if (pointer + count > length)
590 throw new IndexOutOfBoundsException();
593 void assertHasWritableBytes(long count) throws IOException {
594 if (pointer + count > length)
595 setLength(pointer + count);
599 public long length() throws IOException {
604 public long position() throws IOException {
609 public void position(long newPosition) throws IOException {
610 pointer = newPosition;
614 public long skipBytes(long bytes) throws IOException {
620 public int skipBytes(int bytes) throws IOException {
625 public long getStartPositionInSourceBinary() {
630 public String toString() {
631 return parent+"["+start+".."+(start+length)+"]";
634 static boolean intersects(long start1, long len1, long start2, long len2) {
635 if (start1 >= start2+len2) return false;
636 if (start1+len1 <= start2) return false;
641 public void setPositionInSource(long start, long length) {
643 this.length = length;