]> gerrit.simantics Code Review - simantics/platform.git/blob - 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
1 /*******************************************************************************\r
2  *  Copyright (c) 2010 Association for Decentralized Information Management in\r
3  *  Industry THTH ry.\r
4  *  All rights reserved. This program and the accompanying materials\r
5  *  are made available under the terms of the Eclipse Public License v1.0\r
6  *  which accompanies this distribution, and is available at\r
7  *  http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  *  Contributors:\r
10  *      VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.databoard.util.binary;
13
14 import java.io.DataInputStream;\r
15 import java.io.IOException;\r
16 import java.nio.ByteBuffer;\r
17 import java.util.WeakHashMap;\r
18
19 /**
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
24  * are changed. 
25  * <p>
26  * A backend must not be wrapped in a blob more than once.
27  * <p>
28  * Grow, Shrink, Insertion, and Removal affects child blobs if affected region 
29  * intersects with a child. 
30  * 
31  * Grow, Shrink, Insertion, and Removal affects parent. It updates parent length,
32  * and start positions of the following (not preceding) siblings.
33  *
34  * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
35  */
36 public class Blob implements RandomAccessBinary {
37
38         /** Parent Blob */
39         RandomAccessBinary parent;
40         /** Position of index 0 at parent blob */
41         long start; 
42         /** Size of this blob */
43         long length;
44         /** Children */
45         WeakHashMap<Blob, Object> children;
46         
47     long pointer = 0;    
48
49     /**
50      * Create a sub-blob to a random access binary.
51      * 
52      * @param parent
53      * @throws IOException 
54      */
55         public Blob(RandomAccessBinary parent) throws IOException {
56                 this(parent, 0, parent.length());\r
57         }
58     
59     /**
60      * Create a sub-blob to a random access binary.
61      * 
62      * @param parent
63      * @param start
64      * @param length
65      */
66         public Blob(RandomAccessBinary parent, long start, long length) {\r
67                 this.parent = parent;
68                 this.start = start;
69                 this.length = length;
70         }
71
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);
76                 return result;
77         }\r
78         \r
79         public RandomAccessBinary getParent() {\r
80                 return parent;\r
81         }
82         
83         @Override
84         public void close() throws IOException {\r
85         }\r
86         \r
87         @Override\r
88         public boolean isOpen() {\r
89                 return parent.isOpen();\r
90         }
91         
92         @Override
93         public void insertBytes(long bytes, ByteSide side) throws IOException {
94                 if (bytes==0) return;
95                 if (bytes < 0) throw new IllegalArgumentException();
96                 if (pointer < 0) throw new IndexOutOfBoundsException();
97                 
98                 RandomAccessBinary backend = parent;
99                 long startInBackend = start;
100                 while ( (backend instanceof Blob) ) {                   
101                         startInBackend += ((Blob) backend).start;
102                         backend = ((Blob) backend).parent;
103                 }
104                 
105                 
106                 // Pointer outside blob, Add more bytes 
107                 if (pointer > length) {
108                         bytes += pointer - length;
109                         pointer = length;
110                 }
111                 
112                 // Add bytes to the back-end
113                 backend.position(startInBackend + pointer);
114                 backend.insertBytes(bytes, side);
115                 length += bytes;
116                 
117                 // Notify-chain towards parent
118                 if (parent instanceof Blob) {
119                         ((Blob)parent).childGrewBackend(this, start + pointer, bytes);
120                 }
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);
126                         }
127                 }
128         }
129         
130         /**
131          * A child has modified the back-end. Update the following siblings. The child
132          * has already updated its own length. 
133          * @param child
134          * @param pos position in this blob
135          * @param bytes
136          */
137         void childGrewBackend(Blob child, long pos, long bytes)
138         {               
139                 length += bytes;\r
140                 assert(bytes>=0);\r
141                 
142                 if (children != null) {
143                         for (Blob c : children.keySet()) {
144                                 if (c==child) continue; 
145                                 if (pos <= c.start) {
146                                         c.start += bytes;
147                                 }
148                         }
149                 }
150                 if (parent instanceof Blob) ((Blob)parent).childGrewBackend(this, pos+start, bytes);
151         }
152         
153         /**
154          * Parent of this blob grew itself in the back-end.\r
155          *  
156          * @param parent
157          * @param pos position in this blob
158          * @param bytes
159          */
160         void parentGrew(Blob parent, long pos, long bytes, ByteSide side)
161         {
162                 if (pos<0 || (pos==0 && side!=ByteSide.Right) ) {\r
163                         start += bytes;\r
164                         return;\r
165                 }\r
166                 if (pos>=length) {\r
167                         return;\r
168                 }\r
169                 // Grow applies this blob\r
170                 if (\r
171                         ( pos==0 && side==ByteSide.Right ) ||\r
172                         ( pos==length && side==ByteSide.Left ) ||\r
173                         ( pos>0 && pos<length ) ) \r
174                         length += bytes;\r
175                 
176                 // Notify children
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);
181                         }
182                 }
183         }
184
185         
186         /**
187          * Remove bytes at pointer.
188          * 
189          * @param bytes
190          */
191         @Override
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;\r
195                 if (bytes<0) throw new IllegalArgumentException("bytes must be positive value");
196                 
197                 // Go to backend
198                 RandomAccessBinary backend = parent;
199                 long startInBackend = start;
200                 while ( (backend instanceof Blob) ) {                   
201                         startInBackend += ((Blob) backend).start;
202                         backend = ((Blob) backend).parent;
203                 }
204                                 
205                 // Remove bytes from the back-end
206                 backend.position(startInBackend + pointer);
207                 backend.removeBytes(bytes, side);
208                 length -= bytes;
209                 
210                 // Notify direct parent
211                 if (parent instanceof Blob) {
212                         ((Blob)parent).childShrankBackend(this, start + pointer, bytes);
213                 }
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);
219                         }
220                 }
221         }       
222         
223         /**
224          * A child has modified the back-end. Update the following siblings. The child
225          * has already updated its own length. 
226          * @param child
227          * @param pos position in this blob
228          * @param bytes
229          */
230         void childShrankBackend(Blob child, long pos, long bytes)
231         {\r
232                 length -= bytes;\r
233                 assert(bytes>=0);\r
234                 
235                 // update siblings
236                 if (children != null) {
237                         for (Blob c : children.keySet()) {
238                                 if (c==child) continue; 
239                                 if (pos < c.start) {
240                                         c.start -= bytes;
241                                 }
242                         }
243                 }
244                 if (parent instanceof Blob) ((Blob) parent).childShrankBackend(this, pos+start, bytes);
245         }
246         
247         /**
248          * Parent of this blob shrank itself in the back-end.\r
249          * \r
250          * @param parent
251          * @param pos position in this blob
252          * @param bytes
253          */
254         void parentShrunk(Blob parent, long pos, long bytes)
255         {\r
256                 if (pos<0) {\r
257                         start -= bytes;\r
258                         return;\r
259                 }\r
260                 if (pos>=length) {\r
261                         return;\r
262                 }\r
263                 // Change applies this blob\r
264                 length -= bytes;                \r
265                 
266                 // Notify children
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);
271                         }
272         }
273         
274         /**
275          * Modify the size of the blob. The operation changes the size of the 
276          * parent blob aswell.
277          * 
278          * @param newLength new number of bytes
279          */
280         @Override
281         public void setLength(long newLength) throws IOException {
282                 long oldLength = length;
283                 if (oldLength==newLength) return;
284                  
285                 if (oldLength < newLength) {
286                         // Grow
287                         long oldPointer = pointer;
288                         pointer = oldLength;
289                         insertBytes( newLength - oldLength, ByteSide.Left );
290                         pointer = oldPointer;
291                         return;
292                 } else {
293                         // Shrink
294                         long oldPointer = pointer;
295                         pointer = newLength;
296                         removeBytes( oldLength - newLength, ByteSide.Left );
297                         pointer = oldPointer;
298                         return;
299                 }
300
301         }       
302
303
304         public RandomAccessBinary getSource() {
305                 return parent;
306         }
307         
308         @Override
309         public void flush() throws IOException {
310                 parent.flush();
311         }\r
312         \r
313         @Override\r
314         public void reset() throws IOException {\r
315                 parent.reset();\r
316                 length = parent.length();\r
317         }
318
319         @Override
320         public void write(int b) throws IOException {
321                 assertHasWritableBytes(1);              
322                 parent.position(start + pointer);
323                 parent.write(b);
324                 pointer += 1;
325         }\r
326         \r
327         @Override\r
328         public void writeByte(int b) throws IOException {\r
329                 assertHasWritableBytes(1);              \r
330                 parent.position(start + pointer);\r
331                 parent.write(b);\r
332                 pointer += 1;\r
333         }       \r
334         \r
335         @Override\r
336         public void writeBoolean(boolean v) throws IOException {\r
337                 assertHasWritableBytes(1);              \r
338                 parent.position(start + pointer);\r
339                 parent.write( (byte) (v ? 1 : 0));\r
340                 pointer += 1;\r
341         }
342
343         @Override
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);
349                 pointer += bytes;
350         }
351
352         @Override
353         public void writeFully(ByteBuffer src, int length) throws IOException {
354                 assertHasWritableBytes(length);         
355                 parent.position(start + pointer);
356                 parent.writeFully(src, length);
357                 pointer += length;
358         }
359
360         @Override
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);
365                 pointer += length;
366         }
367
368         @Override
369         public void write(byte[] src) throws IOException {
370                 assertHasWritableBytes(src.length);             
371                 parent.position(start + pointer);
372                 parent.write(src);
373                 pointer += src.length;
374         }
375
376         @Override
377         public void writeDouble(double value) throws IOException {
378                 assertHasWritableBytes(8);              
379                 parent.position(start + pointer);
380                 parent.writeDouble(value);
381                 pointer += 8;
382         }
383
384         @Override
385         public void writeFloat(float value) throws IOException {
386                 assertHasWritableBytes(4);              
387                 parent.position(start + pointer);
388                 parent.writeFloat(value);
389                 pointer += 4;
390         }
391
392         @Override
393         public void writeInt(int value) throws IOException {
394                 assertHasWritableBytes(4);              
395                 parent.position(start + pointer);
396                 parent.writeInt(value);
397                 pointer += 4;
398         }
399
400         @Override
401         public void writeLong(long value) throws IOException {
402                 assertHasWritableBytes(8);              
403                 parent.position(start + pointer);
404                 parent.writeLong(value);
405                 pointer += 8;
406         }
407
408         @Override
409         public void writeShort(int value) throws IOException {
410                 assertHasWritableBytes(2);              
411                 parent.position(start + pointer);
412                 parent.writeShort(value);
413                 pointer += 2;
414         }\r
415         \r
416         @Override\r
417         public void writeChar(int value) throws IOException {\r
418                 assertHasWritableBytes(2);              \r
419                 parent.position(start + pointer);\r
420                 parent.writeChar(value);\r
421                 pointer += 2;\r
422         }       \r
423         \r
424         @Override\r
425         public void writeBytes(String s) throws IOException {\r
426                 int len = s.length();\r
427                 assertHasWritableBytes(len);            \r
428                 parent.position(start + pointer);\r
429                 parent.writeBytes(s);\r
430                 pointer += len;\r
431         }\r
432         \r
433         @Override\r
434         public void writeChars(String s) throws IOException {\r
435                 int len = s.length();\r
436                 assertHasWritableBytes(len*2);          \r
437                 parent.position(start + pointer);\r
438                 parent.writeChars(s);\r
439                 pointer += len*2;\r
440         }       \r
441         \r
442         @Override\r
443         public void writeUTF(String s) throws IOException {\r
444                 int len = UTF8.getModifiedUTF8EncodingByteLength(s);\r
445                 assertHasWritableBytes(len+2);          \r
446                 parent.position(start + pointer);\r
447 //              parent.writeUTF(s);\r
448                 parent.writeShort(len);\r
449                 UTF8.writeUTF(this, s);\r
450                 pointer += len+2;       \r
451         }\r
452
453         @Override
454         public byte readByte() throws IOException {
455                 assertHasReadableBytes(1);
456                 parent.position(start + pointer);
457                 byte result = parent.readByte();
458                 pointer += 1;
459                 return result;
460         }\r
461         \r
462         @Override\r
463         public int readUnsignedByte() throws IOException {\r
464                 assertHasReadableBytes(1);\r
465                 parent.position(start + pointer);\r
466                 int result = parent.readUnsignedByte();\r
467                 pointer += 1;\r
468                 return result;\r
469         }       \r
470         \r
471         @Override\r
472         public boolean readBoolean() throws IOException {\r
473                 assertHasReadableBytes(1);\r
474                 parent.position(start + pointer);\r
475                 boolean result = parent.readBoolean();\r
476                 pointer += 1;\r
477                 return result;\r
478         }       
479
480         @Override
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);
485                 pointer += length;
486         }
487
488         @Override
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;
494         }
495
496         @Override
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);
501                 pointer += bytes;
502         }
503
504         @Override
505         public void readFully(ByteBuffer buf, int length) throws IOException {
506                 assertHasReadableBytes(length);
507                 parent.position(start + pointer);
508                 parent.readFully(buf, length);
509                 pointer += length;
510         }
511
512         @Override
513         public double readDouble() throws IOException {
514                 assertHasReadableBytes(8);
515                 parent.position(start + pointer);
516                 double result = parent.readDouble();
517                 pointer += 8;
518                 return result;
519         }
520
521         @Override
522         public float readFloat() throws IOException {
523                 assertHasReadableBytes(4);
524                 parent.position(start + pointer);
525                 float result = parent.readFloat();
526                 pointer += 4;
527                 return result;
528         }
529
530         @Override
531         public int readInt() throws IOException {
532                 assertHasReadableBytes(4);
533                 parent.position(start + pointer);
534                 int result = parent.readInt();
535                 pointer += 4;
536                 return result;
537         }
538
539         @Override
540         public long readLong() throws IOException {
541                 assertHasReadableBytes(8);
542                 parent.position(start + pointer);
543                 long result = parent.readLong();
544                 pointer += 8;
545                 return result;
546         }
547
548         @Override
549         public short readShort() throws IOException {
550                 assertHasReadableBytes(2);
551                 parent.position(start + pointer);
552                 short result = parent.readShort();
553                 pointer += 2;
554                 return result;
555         }\r
556         \r
557         @Override\r
558         public String readLine() throws IOException {\r
559                 assertHasReadableBytes(2);\r
560                 parent.position(start + pointer);\r
561                 String result = parent.readLine();\r
562                 pointer += ( parent.position() - start - pointer );\r
563                 return result;\r
564         }       \r
565         \r
566     public final String readUTF() throws IOException {\r
567         return DataInputStream.readUTF(this);\r
568     }   \r
569         \r
570         @Override\r
571         public char readChar() throws IOException {\r
572                 assertHasReadableBytes(2);\r
573                 parent.position(start + pointer);\r
574                 char result = parent.readChar();\r
575                 pointer += 2;\r
576                 return result;\r
577         }       \r
578         \r
579         @Override\r
580         public int readUnsignedShort() throws IOException {\r
581                 assertHasReadableBytes(2);\r
582                 parent.position(start + pointer);\r
583                 int result = parent.readShort() & 0xffff;\r
584                 pointer += 2;\r
585                 return result;\r
586         }       
587         
588         void assertHasReadableBytes(long count) {
589                 if (pointer + count > length)
590                         throw new IndexOutOfBoundsException();
591         }
592         
593         void assertHasWritableBytes(long count) throws IOException {
594                 if (pointer + count > length)
595                         setLength(pointer + count);
596         }
597
598         @Override
599         public long length() throws IOException {
600                 return length;
601         }
602
603         @Override
604         public long position() throws IOException {
605                 return pointer;
606         }
607         
608         @Override
609         public void position(long newPosition) throws IOException {
610                 pointer = newPosition;
611         }
612
613         @Override
614         public long skipBytes(long bytes) throws IOException {
615                 pointer += bytes;\r
616                 return bytes;
617         }\r
618         
619         @Override\r
620         public int skipBytes(int bytes) throws IOException {\r
621                 pointer += bytes;\r
622                 return bytes;\r
623         }\r
624         
625         public long getStartPositionInSourceBinary() {
626                 return start;
627         }
628         
629         @Override
630         public String toString() {
631                 return parent+"["+start+".."+(start+length)+"]";
632         }
633
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;
637         return true;
638                 
639         }
640         
641         public void setPositionInSource(long start, long length) {\r
642                 this.start = start;
643                 this.length = length;
644         }
645         
646 }
647