]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/util/binary/Blob.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / util / binary / Blob.java
1 /*******************************************************************************
2  *  Copyright (c) 2010 Association for Decentralized Information Management in
3  *  Industry THTH ry.
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
8  *
9  *  Contributors:
10  *      VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.databoard.util.binary;
13
14 import java.io.DataInputStream;
15 import java.io.IOException;
16 import java.nio.ByteBuffer;
17 import java.util.WeakHashMap;
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());
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) {
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         }
78         
79         public RandomAccessBinary getParent() {
80                 return parent;
81         }
82         
83         @Override
84         public void close() throws IOException {
85         }
86         
87         @Override
88         public boolean isOpen() {
89                 return parent.isOpen();
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;
140                 assert(bytes>=0);
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.
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) ) {
163                         start += bytes;
164                         return;
165                 }
166                 if (pos>=length) {
167                         return;
168                 }
169                 // Grow applies this blob
170                 if (
171                         ( pos==0 && side==ByteSide.Right ) ||
172                         ( pos==length && side==ByteSide.Left ) ||
173                         ( pos>0 && pos<length ) ) 
174                         length += bytes;
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;
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         {
232                 length -= bytes;
233                 assert(bytes>=0);
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.
249          * 
250          * @param parent
251          * @param pos position in this blob
252          * @param bytes
253          */
254         void parentShrunk(Blob parent, long pos, long bytes)
255         {
256                 if (pos<0) {
257                         start -= bytes;
258                         return;
259                 }
260                 if (pos>=length) {
261                         return;
262                 }
263                 // Change applies this blob
264                 length -= bytes;                
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         }
312         
313         @Override
314         public void reset() throws IOException {
315                 parent.reset();
316                 length = parent.length();
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         }
326         
327         @Override
328         public void writeByte(int b) throws IOException {
329                 assertHasWritableBytes(1);              
330                 parent.position(start + pointer);
331                 parent.write(b);
332                 pointer += 1;
333         }       
334         
335         @Override
336         public void writeBoolean(boolean v) throws IOException {
337                 assertHasWritableBytes(1);              
338                 parent.position(start + pointer);
339                 parent.write( (byte) (v ? 1 : 0));
340                 pointer += 1;
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         }
415         
416         @Override
417         public void writeChar(int value) throws IOException {
418                 assertHasWritableBytes(2);              
419                 parent.position(start + pointer);
420                 parent.writeChar(value);
421                 pointer += 2;
422         }       
423         
424         @Override
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);
430                 pointer += len;
431         }
432         
433         @Override
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);
439                 pointer += len*2;
440         }       
441         
442         @Override
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);
450                 pointer += len+2;       
451         }
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         }
461         
462         @Override
463         public int readUnsignedByte() throws IOException {
464                 assertHasReadableBytes(1);
465                 parent.position(start + pointer);
466                 int result = parent.readUnsignedByte();
467                 pointer += 1;
468                 return result;
469         }       
470         
471         @Override
472         public boolean readBoolean() throws IOException {
473                 assertHasReadableBytes(1);
474                 parent.position(start + pointer);
475                 boolean result = parent.readBoolean();
476                 pointer += 1;
477                 return result;
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         }
556         
557         @Override
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 );
563                 return result;
564         }       
565         
566     public final String readUTF() throws IOException {
567         return DataInputStream.readUTF(this);
568     }   
569         
570         @Override
571         public char readChar() throws IOException {
572                 assertHasReadableBytes(2);
573                 parent.position(start + pointer);
574                 char result = parent.readChar();
575                 pointer += 2;
576                 return result;
577         }       
578         
579         @Override
580         public int readUnsignedShort() throws IOException {
581                 assertHasReadableBytes(2);
582                 parent.position(start + pointer);
583                 int result = parent.readShort() & 0xffff;
584                 pointer += 2;
585                 return result;
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;
616                 return bytes;
617         }
618         
619         @Override
620         public int skipBytes(int bytes) throws IOException {
621                 pointer += bytes;
622                 return bytes;
623         }
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) {
642                 this.start = start;
643                 this.length = length;
644         }
645         
646 }
647