]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/util/binary/BinaryFile.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / util / binary / BinaryFile.java
1 /*******************************************************************************
2  * Copyright (c) 2010, 2016 Association for Decentralized Information Management
3  * in 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.EOFException;
16 import java.io.File;
17 import java.io.IOException;
18 import java.io.RandomAccessFile;
19 import java.nio.ByteBuffer;
20
21 /**
22  * This class is a Random Access File implementation to RandomAccessBinary.
23  * The implementation is buffered. The implementation is not multi-thread
24  * safe.
25  * <p>
26  * There is a buffer used for reading and writing. It has a buffer read and
27  * write position. When read, the buffer is filled. When written, the buffer 
28  * is written. When the file pointer is moved, the file size changed or flushed
29  * the buffers are cleared. If there were unwritten bytes, they are flushed 
30  * to disc.
31  * <p>
32  * There is internal pointer variable. The actual file pointer is moved on
33  * disc read and write operations. 
34  * <p>
35  * Primitive number writes (int, short, char, double, float and long) and
36  * written in big endian (network) byte order. Use {@link Endian}
37  * to make little endian operations.
38  *
39  * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
40  */
41 public class BinaryFile implements RandomAccessBinary, BinaryReadable, BinaryWriteable {
42
43         RandomAccessFile raf;
44         File file;
45         
46         /** The internal read and write buffer */
47     byte buf[]; 
48     
49     /** The number of valid bytes in the buffer */
50     int readable_bytes_count;
51         
52         /** The number written but unflushed bytes in buf starting from buf[0]*/
53         int write_buf_count;
54     
55     /** The pointer of buf[0] in the file. The value is valid only if value >=0 */
56     long buf_pos;
57     
58         /** File Pointer of current position */
59     long pointer;    
60
61     /** Transient size of the file */
62     long virtualLength;
63     long diskLength;
64     
65         public BinaryFile(RandomAccessFile file) throws IOException
66         {
67                 this.raf = file;
68                 virtualLength = diskLength = raf.length();
69                 pointer = 0;
70                 buf = new byte[4096];
71         }
72
73         public BinaryFile(File file) throws IOException
74         {
75                 this.raf = new RandomAccessFile(file, "rw");
76                 virtualLength = diskLength = raf.length();              
77                 this.file = file;
78                 pointer = 0;
79                 buf = new byte[4096];
80         }
81         
82         public BinaryFile(File file, String mode) throws IOException
83         {
84                 this.raf = new RandomAccessFile(file, mode);
85                 virtualLength = diskLength = raf.length();              
86                 this.file = file;
87                 pointer = 0;
88                 buf = new byte[4096];
89         }
90         
91         public BinaryFile(RandomAccessFile file, int bufSize) throws IOException
92         {
93                 this.raf = file;
94                 virtualLength = diskLength = raf.length();
95                 pointer = 0;
96                 buf = new byte[bufSize];
97         }
98
99         public BinaryFile(File file, int bufSize) throws IOException
100         {
101                 this.raf = new RandomAccessFile(file, "rw");
102                 virtualLength = diskLength = raf.length();              
103                 this.file = file;
104                 pointer = 0;
105                 buf = new byte[bufSize];
106         }
107         
108         public static BinaryFile tempFile(long size) throws IOException
109         {
110                 File tmpFile = File.createTempFile("Temp", ".file");
111                 tmpFile.deleteOnExit();
112                 BinaryFile file = new BinaryFile(tmpFile);
113                 file.setLength( size );
114                 return file;
115         }
116         
117         /**
118          * Closes the object. Note, this will close the input random access file.
119          * This method may be called several times.
120          *  
121          * @throws IOException
122          */
123         public synchronized void close() throws IOException {
124                 if (raf==null) return;
125                 flush();
126                 pointer = -1;
127                 raf.close();
128                 raf = null;
129                 buf = null;
130         }
131
132         public synchronized boolean isOpen() {
133                 return buf!=null;
134         }
135         
136         public File file() {
137                 return file;
138         }
139         
140         public RandomAccessFile getRandomAccessFile() {
141                 return raf;
142         }
143
144         /**
145          * Get the number of readable bytes in buffer 
146          * 
147          * @return readable bytes or 0
148          */
149         private long readableBytesInBuffer() {
150                 long posInBuf = pointer - buf_pos;
151                 if (posInBuf<0) return 0;
152                 long bytesLeft = readable_bytes_count - posInBuf;
153                 return bytesLeft < 0 ? 0 : bytesLeft;
154         }
155         
156         /**
157          * Get the valid position of pointer in buf[]
158          * 
159          * @return pos or -1 
160          */
161         private long positionInReadBuffer() {
162                 long posInBuf = pointer - buf_pos;
163                 if (posInBuf<0 || posInBuf>readable_bytes_count) return -1;
164                 return posInBuf;
165         }
166         
167         /**
168          * Get next byte
169          * @return 0..255
170          * @throws IOException
171          */
172         int _get() throws IOException
173         {
174                 assertReadable(1);
175                 
176                 int posInBuf = (int) (pointer - buf_pos);
177                 int result = buf[posInBuf] & 0xFF;
178                 pointer++;
179                 return result;
180         }       
181
182         /**
183          * Get next 4 bytes as an int value. This is an optimization.
184          * @return long value
185          * @throws IOException
186          */
187         int _getInt() throws IOException
188         {
189                 assertReadable(4);              
190
191                 int posInBuf = (int) (pointer - buf_pos);
192                 int result =
193                         (((int) (buf[posInBuf + 3] & 0xFF)) |
194                         (((int) (buf[posInBuf + 2] & 0xFF)) << 8) |
195                         (((int) (buf[posInBuf + 1] & 0xFF)) << 16) |
196                         (((int) (buf[posInBuf]     & 0xFF)) << 24));
197                 pointer+=4;
198                 return result;
199         }
200
201         /**
202          * Get next 8 bytes as a long value. This is an optimization.
203          * @return long value
204          * @throws IOException
205          */
206         long _getLong() throws IOException
207         {
208                 assertReadable(8);
209
210                 int posInBuf = (int) (pointer - buf_pos);
211                 long result =
212                         (((long) (buf[posInBuf + 7] & 0xFF)) |
213                         (((long) (buf[posInBuf + 6] & 0xFF)) << 8) |
214                         (((long) (buf[posInBuf + 5] & 0xFF)) << 16) |
215                         (((long) (buf[posInBuf + 4] & 0xFF)) << 24) |
216                         (((long) (buf[posInBuf + 3] & 0xFF)) << 32) |
217                         (((long) (buf[posInBuf + 2] & 0xFF)) << 40) |
218                         (((long) (buf[posInBuf + 1] & 0xFF)) << 48) |
219                         (((long) (buf[posInBuf]     & 0xFF)) << 56));
220                 pointer+=8;
221                 return result;
222         }
223
224         /**
225          * Get next byte
226          * @return 0..255 or -1 on end of file
227          * @throws IOException
228          */
229         int _read() throws IOException
230         {
231                 if (readableBytesInBuffer()<1) {
232                         fill();
233                         if (readableBytesInBuffer()==0) return -1;
234                 }
235                 
236                 int posInBuf = (int) (pointer - buf_pos);
237                 int result = buf[posInBuf] & 0xFF;
238                 pointer++;
239                 return result;
240         }               
241         
242         void _get(byte[] dst, int offset, int length) throws IOException {
243                 while (length>0) {
244                         long n = Math.min( readableBytesInBuffer(), length );
245                         int posInBuf = (int) (pointer - buf_pos);
246                         if (n>0) {
247                                 System.arraycopy(buf, (int) posInBuf, dst, offset, (int) n);
248                                 offset += n;
249                                 length -= n;
250                                 pointer += n;                           
251                         }
252                         
253                         if (length>0) {
254                                 fill();
255                                 if (readableBytesInBuffer()==0) {
256                                         throw new EOFException();
257                                 }
258                         }
259                 }               
260         }
261         
262         /**
263          * Flushes bytes 0..write_buf_count to file at buf_pos.
264          * Sets write_buf_count to 0.
265          * 
266          * @throws IOException
267          */
268         private void writeFlush() throws IOException {
269                 if (write_buf_count>0 && buf_pos>=0) {
270                         raf.seek(buf_pos);
271                         raf.write(buf, 0, write_buf_count);
272                         if (buf_pos+write_buf_count>diskLength) diskLength = buf_pos+write_buf_count;
273                         write_buf_count = 0;
274                 }
275                 if (diskLength != virtualLength) {
276                         raf.setLength(virtualLength);
277                         diskLength = virtualLength;
278                 }
279         }
280         
281         /**
282          * This method ensures that write buffer is ready for writing 
283          * at the pointer. Old buffer is flushed if necessary.
284          * 
285          * @param requested number of bytes, up to 4096 guaranteed
286          * @return the number of bytes that can be written to buf
287          * @throws IOException
288          */
289         private int prepareForWrite(int bytes) throws IOException {
290                 // Ensure pointer is within buf[0..readable_bytes_count]
291                 int posInBuf = (int) (pointer - buf_pos);
292                 if (posInBuf<0) {
293                         writeFlush();
294                         readable_bytes_count = 0;
295                         buf_pos = pointer;
296                         posInBuf = 0;
297                 } else
298                 if (posInBuf>readable_bytes_count) {
299                         // Pointer is at wrong place, flush data
300                         writeFlush();
301                         
302                         // Move pointer
303                         int bytesToKeep = readable_bytes_count - posInBuf;
304                         if (bytesToKeep<0) bytesToKeep = 0;                     
305                         if (bytesToKeep>0) {
306                                 System.arraycopy(buf, posInBuf, buf, 0, bytesToKeep);
307                                 readable_bytes_count = bytesToKeep;
308                         } else {
309                                 readable_bytes_count = 0;
310                         }
311                         buf_pos = pointer;
312                         posInBuf = 0;
313                 }
314                 // Ensure buf[] has room for bytes
315                 if (buf.length - posInBuf < bytes) {
316                         writeFlush();
317                         int bytesToKeep = readable_bytes_count - posInBuf;
318                         if (bytesToKeep<0) bytesToKeep = 0;                     
319                         if (bytesToKeep>0) {
320                                 System.arraycopy(buf, posInBuf, buf, 0, bytesToKeep);
321                                 readable_bytes_count = bytesToKeep;
322                         } else {
323                                 readable_bytes_count = 0;
324                         }
325                         buf_pos = pointer;
326                         posInBuf = 0;
327                         return buf.length;
328                 }
329                 
330                 return (int) ( buf.length - posInBuf );
331         }
332
333         /**
334          * Assert there is a set number of bytes in read buffer.
335          * bytes should not be larger than read buffer size (buf.length, 4K) 
336          * 
337          * @param bytes
338          * @throws IOException
339          */
340         private void assertReadable(int bytes) throws IOException
341         {
342                 if (readableBytesInBuffer()<bytes) {
343                         fill();
344                         if (readableBytesInBuffer()<bytes) throw new EOFException(); 
345                 }
346         }
347         
348         /**
349          * Read the buffer at file position pointer.  
350          * 
351          * @throws IOException
352          */
353         private void fill() throws IOException
354         {
355                 writeFlush();
356                 
357                 // Reuse previous cache, if possible
358                 long old_buf_start = buf_pos;   
359                 long old_buf_end = buf_pos + readable_bytes_count;
360                 int  old_buf_length = readable_bytes_count;
361                 long new_buf_start = pointer;
362                 long new_buf_end = Math.min(pointer + buf.length, virtualLength);
363                 int  new_buf_length = (int) (new_buf_end - new_buf_start);
364                 
365                 boolean old_buf_start_in_new_buf = (old_buf_start >= new_buf_start) && (old_buf_start < new_buf_end);  
366                 boolean old_buf_end_in_new_buf = (old_buf_end >= new_buf_start) && (old_buf_end < new_buf_end);  
367                 
368                 // Scenario 1, end of old buf in the new, but the whole old buffer doesnt fit in the new                
369                 if (old_buf_end_in_new_buf && !old_buf_start_in_new_buf)
370                 {
371                         int bytesToPreserve = (int) (old_buf_end - new_buf_start);
372                         int bytesToRead = (int) (new_buf_end - old_buf_end);
373                         System.arraycopy(buf, old_buf_length - bytesToPreserve, buf, 0, bytesToPreserve);
374                         // Read more
375                         raf.seek(new_buf_start + bytesToPreserve);
376                         raf.readFully(buf, bytesToPreserve, bytesToRead);
377                         buf_pos = pointer;
378                         readable_bytes_count = new_buf_length;
379                         return;
380                 }
381                 
382                 // Scenario 2, start of old buf is in new, but not completely
383                 if (old_buf_start_in_new_buf && !old_buf_end_in_new_buf) {
384                         int bytesToPreserve = (int) (new_buf_end-old_buf_start);
385                         int bytesToRead = (int) (old_buf_start-new_buf_start);
386                         System.arraycopy(buf, 0, buf, bytesToRead, bytesToPreserve);
387                         raf.seek(new_buf_start);
388                         raf.readFully(buf, 0, bytesToRead);
389                         buf_pos = pointer;
390                         readable_bytes_count = new_buf_length;
391                         return;
392                 }
393                 
394                 // Scenario 3, old buf is partially in new buf (Omited)
395                 
396                 // Scenario 4, old and new buf do not intersect
397                 {
398                         int bytesToRead = new_buf_length; 
399                         raf.seek(new_buf_start);
400                         raf.readFully(buf, 0, bytesToRead);
401                         buf_pos = pointer;
402                         readable_bytes_count = new_buf_length;
403                 }
404         }
405
406         @Override
407         public byte readByte() throws IOException {
408                 return (byte) _get();
409         }
410         
411         @Override
412         public char readChar() throws IOException {
413                 return (char)((_get() << 8) | _get());
414         }
415         
416         @Override
417         public int readUnsignedByte() throws IOException {
418                 return _get() & 0x000000ff;
419         }       
420
421         @Override
422         public boolean readBoolean() throws IOException {
423                 return _get()!=0;
424         }
425         
426         @Override
427         public void readFully(byte[] dst, int offset, int length) throws IOException {
428                 _get(dst, offset, length);
429         }
430
431         @Override
432         public void readFully(byte[] dst) throws IOException {
433                 _get(dst, 0, dst.length);
434         }
435
436         @Override
437         public void readFully(ByteBuffer buf) throws IOException {
438                 readFully(buf, buf.remaining());
439         }
440
441         @Override
442         public void readFully(ByteBuffer buf, int length) throws IOException {
443                 while (length>0) {
444                         assertReadable( Math.min(this.buf.length, length) );
445                         long n = Math.min(readableBytesInBuffer(), length);
446                         if (n==0) throw new EOFException();
447                         long posInBuf = positionInReadBuffer();
448                         if (n>0 && posInBuf>=0) {
449                                 buf.put(this.buf, (int)posInBuf, (int)n);
450                                 length -= n;
451                                 pointer += n;
452                         }
453                         if (length>0) {
454                                 fill();
455                                 if (readableBytesInBuffer()==0) {
456                                         throw new EOFException();
457                                 }
458                         }
459                 }
460         }
461
462         @Override
463         public double readDouble() throws IOException {
464                 return Double.longBitsToDouble(readLong());
465         }
466
467         @Override
468         public float readFloat() throws IOException {
469                 return Float.intBitsToFloat(readInt());
470         }
471
472         @Override
473         public int readInt() throws IOException {
474                 return 
475 //                      ( _get() << 24) |
476 //                      ( _get() << 16) | 
477 //                      ( _get() << 8) |
478 //                      ( _get() );
479                 _getInt();
480         }
481
482         @Override
483         public long readLong() throws IOException {
484                 return
485 //              ( ((long)_get()) << 56) |
486 //              ( ((long)_get()) << 48) |
487 //              ( ((long)_get()) << 40) |
488 //              ( ((long)_get()) << 32) |
489 //              ( ((long)_get()) << 24) |
490 //              ( ((long)_get()) << 16) |
491 //              ( ((long)_get()) << 8) |
492 //              ( ((long)_get()) );
493                 _getLong();
494         }
495
496         @Override
497         public short readShort() throws IOException {
498                 return (short) ( (_get() << 8) |  _get() ) ;
499         }
500         
501         @Override
502         public int readUnsignedShort() throws IOException {
503                 return (int) ( (_get() << 8) |  _get() ) ;
504         }       
505         
506     public final String readLine() throws IOException {
507         StringBuffer input = new StringBuffer();
508         int c = -1;
509         boolean eol = false;
510
511         while (!eol) {
512             switch (c = _read()) {
513             case -1:
514             case '\n':
515                 eol = true;
516                 break;
517             case '\r':
518                 eol = true;
519                 long cur = position();
520                 if ((_read()) != '\n') {
521                     position(cur);
522                 }
523                 break;
524             default:
525                 input.append((char)c);
526                 break;
527             }
528         }
529
530         if ((c == -1) && (input.length() == 0)) {
531             return null;
532         }
533         return input.toString();
534     }   
535     
536     public final String readUTF() throws IOException {
537         return DataInputStream.readUTF(this);
538     }    
539         
540         @Override
541         public long position() {
542                 return pointer;
543         }
544         
545         public void position(long newPosition) {
546                 pointer = newPosition;
547         }       
548         
549         /**
550          * Flushes internal buffer
551          */
552         public void flush() throws IOException {
553                 writeFlush();
554         }
555         
556         /**
557          * Clears read&write buffer. The file can be modified elsewere after this.
558          * 
559          * @throws IOException 
560          */
561         public void reset() throws IOException {
562                 writeFlush();
563                 readable_bytes_count = 0;
564                 virtualLength = diskLength = raf.length();              
565         }
566
567         @Override
568         public long skipBytes(long bytes) throws IOException {          
569                 pointer += bytes;
570                 return bytes;
571         }
572         
573         @Override
574         public int skipBytes(int bytes) throws IOException {            
575                 pointer += bytes;
576                 return bytes;
577         }
578
579
580         // WRITE
581
582         void _putLong(long value) throws IOException
583         {
584                 prepareForWrite(8);
585                 int posInBuf = (int) (pointer - buf_pos);
586                 buf[posInBuf] = (byte) (value >>> 56);
587                 buf[posInBuf+1] = (byte) (value >>> 48);
588                 buf[posInBuf+2] = (byte) (value >>> 40);
589                 buf[posInBuf+3] = (byte) (value >>> 32);
590                 buf[posInBuf+4] = (byte) (value >>> 24);
591                 buf[posInBuf+5] = (byte) (value >>> 16);
592                 buf[posInBuf+6] = (byte) (value >>> 8);
593                 buf[posInBuf+7] = (byte) value;
594                 posInBuf += 8;
595                 pointer += 8;
596                 if (write_buf_count<posInBuf) write_buf_count = posInBuf;
597                 if (readable_bytes_count<write_buf_count) readable_bytes_count=write_buf_count;
598                 if (virtualLength<pointer) virtualLength=pointer;
599         }
600
601         void _putInt(int value) throws IOException
602         {
603                 prepareForWrite(4);
604                 int posInBuf = (int) (pointer - buf_pos);
605                 buf[posInBuf] = (byte) (value >>> 24);
606                 buf[posInBuf+1] = (byte) (value >>> 16);
607                 buf[posInBuf+2] = (byte) (value >>> 8);
608                 buf[posInBuf+3] = (byte) value;
609                 posInBuf += 4;
610                 pointer += 4;
611                 if (write_buf_count<posInBuf) write_buf_count = posInBuf;
612                 if (readable_bytes_count<write_buf_count) readable_bytes_count=write_buf_count;
613                 if (virtualLength<pointer) virtualLength=pointer;
614         }
615
616         void _put(int value) throws IOException
617         {
618                 prepareForWrite(1);
619                 int posInBuf = (int) (pointer - buf_pos);
620                 buf[posInBuf] = (byte) value;
621                 posInBuf++;
622                 pointer++;
623                 if (write_buf_count<posInBuf) write_buf_count = posInBuf;
624                 if (readable_bytes_count<write_buf_count) readable_bytes_count=write_buf_count;
625                 if (virtualLength<pointer) virtualLength=pointer;
626         }
627         
628         void _put(byte[] src, int offset, int length) throws IOException {
629                 while (length>0) {
630                         int n = Math.min(prepareForWrite(length), length);
631                         int posInBuf = (int) (pointer - buf_pos);
632                         System.arraycopy(src, offset, buf, posInBuf, n);                        
633                         pointer += n;
634                         posInBuf += n;
635                         offset += n;
636                         length -= n;
637                         if (write_buf_count<posInBuf) write_buf_count = posInBuf;
638                         if (readable_bytes_count<write_buf_count) readable_bytes_count=write_buf_count;
639                         if (virtualLength<pointer) virtualLength=pointer;
640                 }
641         }
642         
643         @Override
644         public void write(int b) throws IOException {
645                 _put(b);
646         }
647         
648         @Override
649         public void writeByte(int b) throws IOException {
650                 _put(b);
651         }       
652
653         @Override
654         public void writeBoolean(boolean v) throws IOException {
655                 _put( v ? 1 : 0);
656         }
657         
658         @Override
659         public void writeFully(ByteBuffer src) throws IOException {
660                 if (src.hasArray()) {
661                         byte array[] = src.array();
662                         _put(array, src.position(), src.remaining());
663                         src.position(src.limit()); 
664                 } else 
665                         for (;src.hasRemaining();)
666                                 _put(src.get());
667         }
668
669         @Override
670         public void writeFully(ByteBuffer src, int length) throws IOException {
671                 if (src.hasArray()) {
672                         byte array[] = src.array();
673                         _put(array, src.position(), length);
674                         src.position(length); 
675                 } else {
676                         for (int i=0; i<length; i++)
677                                 _put(src.get());
678                 }
679         }
680
681         @Override
682         public void write(byte[] src, int offset, int length) throws IOException {
683                 _put(src, offset, length);
684         }
685
686         @Override
687         public void write(byte[] src) throws IOException {
688                 _put(src, 0, src.length);
689         }
690
691         @Override
692         public void writeDouble(double value) throws IOException {
693                 writeLong(Double.doubleToLongBits(value));
694         }
695
696         @Override
697         public void writeFloat(float value) throws IOException {
698                 writeInt(Float.floatToIntBits(value));
699         }
700
701         @Override
702         public void writeInt(int value) throws IOException {
703                 _putInt(value);
704         }
705
706         @Override
707         public void writeLong(long value) throws IOException {
708                 _putLong(value);
709         }
710
711         @Override
712         public void writeShort(int value) throws IOException {
713                 _put(value >> 8);
714                 _put(value);
715         }
716         
717         @Override
718         public void writeChar(int value) throws IOException {
719                 _put(value >> 8);
720                 _put(value);
721         }
722         
723         @Override
724         public void writeBytes(String s) throws IOException {
725                 int len = s.length();
726                 for (int i = 0 ; i < len ; i++) {
727                     _put((byte)s.charAt(i));
728                 }
729         }
730         
731         @Override
732         public void writeChars(String s) throws IOException {
733         int len = s.length();
734         for (int i = 0 ; i < len ; i++) {
735             int v = s.charAt(i);
736             _put((v >>> 8) & 0xFF); 
737             _put((v >>> 0) & 0xFF); 
738         }
739         }
740
741         @Override
742         public void writeUTF(String s) throws IOException {
743                 int len = UTF8.getModifiedUTF8EncodingByteLength(s);
744                 writeShort(len);
745                 UTF8.writeModifiedUTF(this, s);
746         }
747         
748         @Override
749         public void insertBytes(long bytes, ByteSide side) throws IOException {         
750                 if (pointer>=virtualLength) {
751                         setLength(pointer + bytes);                     
752                         return;
753                 }
754
755                 // insertion to buffer window
756                 if (pointer>=buf_pos && (pointer<=buf_pos+readable_bytes_count))
757                 {
758                         // buffer window convers the end of the file & there is enough space in buffer to do the operation
759                         if (buf_pos+readable_bytes_count >= virtualLength && readable_bytes_count + bytes < buf.length) {
760                                 // Move right siade
761                                 int posInBuf = (int) (pointer - buf_pos);
762                                 System.arraycopy(buf, posInBuf, buf, (int) (posInBuf+bytes), (int) readable_bytes_count - posInBuf);
763                                 readable_bytes_count += bytes;
764                                 write_buf_count = readable_bytes_count;
765                                 virtualLength += bytes;
766                                 return;
767                         }                       
768                         
769                         writeFlush();
770                         reset();
771                 }
772                 
773                 writeFlush();
774                 reset();
775                 insertBytes(raf, pointer, bytes);               
776                 virtualLength += bytes;
777                 diskLength += bytes;
778                 
779                 // Move buffer
780                 if (buf_pos>pointer) {
781                         buf_pos += bytes;
782                 }
783         }
784
785         @Override
786         public void removeBytes(long bytes, ByteSide side) throws IOException {
787                 if (pointer+bytes>virtualLength || pointer<0) {
788                         throw new IOException("Pointer outside file");
789                 }
790                 
791                 if (pointer+bytes==virtualLength) {
792                         setLength(virtualLength - bytes);
793                         return;
794                 }
795
796                 // removal intersects buffer window
797                 if (pointer+bytes>=buf_pos && (pointer<=buf_pos+readable_bytes_count))
798                 {
799                         // buffer window covers the ending
800                         if (buf_pos+readable_bytes_count >= virtualLength) {
801                                 
802                                 // Scenario 1 : Pointer before buffer
803                                 if (pointer<buf_pos) {
804                                         int cut_end_InBuf = (int) (pointer+bytes - buf_pos);
805                                         System.arraycopy(buf, cut_end_InBuf, buf, 0, readable_bytes_count-cut_end_InBuf);
806                                         readable_bytes_count -= cut_end_InBuf;
807                                         write_buf_count = readable_bytes_count;
808                                         virtualLength -= bytes;
809                                         buf_pos = pointer;
810                                         return;
811                                 } else
812                                         
813                                 // Scenario 2 : Pointer within buffer
814                                 if (pointer>=buf_pos) {
815                                         int posInBuf = (int) (pointer - buf_pos);
816                                         System.arraycopy(buf, (int) (posInBuf+bytes), buf, posInBuf, (int) (readable_bytes_count - posInBuf - bytes) );
817                                         readable_bytes_count -= bytes;
818                                         write_buf_count = readable_bytes_count;
819                                         virtualLength -= bytes;
820                                         return;
821                                 }
822                         }
823                         
824                         writeFlush();
825                         reset();
826                 }
827                 
828                 writeFlush();
829                 reset();
830                 removeBytes(raf, pointer, bytes);
831                 virtualLength -= bytes;
832                 diskLength -= bytes;
833                 
834                 // Move buffer
835                 if (buf_pos>pointer) {
836                         buf_pos -= bytes;
837                 }
838         }
839
840         @Override
841         public long length() throws IOException {
842                 return virtualLength;
843         }
844         
845         @Override
846         public void setLength(long newLength) throws IOException {              
847                 virtualLength = newLength;
848                 if (buf_pos + readable_bytes_count > virtualLength) {
849                                 readable_bytes_count = (int) Math.max(virtualLength - buf_pos, 0L);
850                 }
851                 if (buf_pos + write_buf_count > virtualLength) {
852                                 write_buf_count = (int) Math.max(virtualLength - buf_pos, 0L);
853                 }               
854         }
855
856         private final static int bufferSize = 1024*32;
857         
858         /**
859          * Inserts bytes into a middle of a file.
860          * 
861          * @param file file
862          * @param position 
863          * @param bytes
864          * @throws IOException 
865          */
866         public static void insertBytes(RandomAccessFile file, long position, long bytes) 
867         throws IOException
868         {
869                 if (position<0) throw new IndexOutOfBoundsException("position cannot be below 0");
870                 if (bytes<0) throw new IndexOutOfBoundsException("bytes cannot be below 0");
871                 if (bytes==0) return;
872                 long length = file.length();
873                 if (position>=length) {
874                         file.setLength(position+bytes);
875                         return;
876                 }
877                 
878                 long bytesOnRight = length - position;
879                 
880                 // Create buffer - the buffer is cyclic window
881                 int bufLength = (int) Math.min(bytesOnRight, bufferSize);
882                 byte[] buf = new byte[bufLength];
883                 
884                 // Bytes transferred from right
885                 long n = 0;
886                 while (n<bytesOnRight) {
887                         // Bytes that have content
888                         int count = (int) Math.min(bufLength, bytesOnRight-n);
889                         file.seek( length-count-n );
890                         file.readFully(buf, 0, count);
891                         file.seek( length-count-n+bytes );
892                         file.write(buf, 0, count);
893                         n += count;
894                 }
895         }
896         
897         /**
898          * Remove bytes from a file 
899          * 
900          * @param file
901          * @param position 
902          * @param bytes
903          * @throws IOException 
904          */
905         public static void removeBytes(RandomAccessFile file, long position, long bytes) throws IOException
906         {
907                 if (position<0) throw new IndexOutOfBoundsException("position cannot be below 0");
908                 if (bytes<0) throw new IndexOutOfBoundsException("bytes cannot be below 0");
909                 if (bytes==0) return;
910                 long length = file.length();
911                 if (position>=length) return;
912                 // Truncate
913                 if (position+bytes>=length) {
914                         file.setLength(position);
915                         return;
916                 }
917                 long bytesOnRight = length - position - bytes;
918                 int bufLength = (int) Math.min(bytesOnRight, bufferSize);
919                 byte[] buf = new byte[bufLength];
920                 
921                 long n = 0;
922                 while (n<bytesOnRight) {
923                         int count = (int) Math.min(bufLength, bytesOnRight-n);
924                         file.seek( position + bytes + n );
925                         file.readFully( buf, 0, count );
926                         file.seek( position + n );
927                         file.write(buf, 0, count);
928                         n += count;
929                 }
930                 file.setLength(position + bytesOnRight);
931         }       
932
933         @Override
934         public String toString() {
935                 try {
936                         return "File(file="+file.getName()+", size="+length()+")";
937                 } catch (IOException e) {
938                         return "File()";
939                 }
940         }
941         
942 }
943