]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/util/binary/BinaryFile.java
1e9dfa3fbe71ac5ac3789e4be6f3cbf94c9a0597
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / util / binary / BinaryFile.java
1 /*******************************************************************************\r
2  * Copyright (c) 2010, 2016 Association for Decentralized Information Management\r
3  * in 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.EOFException;\r
16 import java.io.File;\r
17 import java.io.IOException;\r
18 import java.io.RandomAccessFile;\r
19 import java.nio.ByteBuffer;\r
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. \r
34  * <p>\r
35  * Primitive number writes (int, short, char, double, float and long) and\r
36  * written in big endian (network) byte order. Use {@link Endian}\r
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\r
66         {\r
67                 this.raf = file;\r
68                 virtualLength = diskLength = raf.length();\r
69                 pointer = 0;\r
70                 buf = new byte[4096];\r
71         }\r
72 \r
73         public BinaryFile(File file) throws IOException\r
74         {\r
75                 this.raf = new RandomAccessFile(file, "rw");\r
76                 virtualLength = diskLength = raf.length();              \r
77                 this.file = file;\r
78                 pointer = 0;\r
79                 buf = new byte[4096];\r
80         }\r
81         \r
82         public BinaryFile(File file, String mode) throws IOException\r
83         {\r
84                 this.raf = new RandomAccessFile(file, mode);\r
85                 virtualLength = diskLength = raf.length();              \r
86                 this.file = file;\r
87                 pointer = 0;\r
88                 buf = new byte[4096];\r
89         }\r
90         \r
91         public BinaryFile(RandomAccessFile file, int bufSize) throws IOException\r
92         {\r
93                 this.raf = file;\r
94                 virtualLength = diskLength = raf.length();\r
95                 pointer = 0;\r
96                 buf = new byte[bufSize];\r
97         }\r
98 \r
99         public BinaryFile(File file, int bufSize) throws IOException\r
100         {\r
101                 this.raf = new RandomAccessFile(file, "rw");\r
102                 virtualLength = diskLength = raf.length();              \r
103                 this.file = file;\r
104                 pointer = 0;\r
105                 buf = new byte[bufSize];\r
106         }\r
107         \r
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.\r
119          * This method may be called several times.
120          *  
121          * @throws IOException
122          */
123         public synchronized void close() throws IOException {\r
124                 if (raf==null) return;
125                 flush();
126                 pointer = -1;
127                 raf.close();
128                 raf = null;
129                 buf = null;
130         }
131 \r
132         public synchronized boolean isOpen() {\r
133                 return buf!=null;\r
134         }\r
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         {\r
174                 assertReadable(1);
175                 
176                 int posInBuf = (int) (pointer - buf_pos);
177                 int result = buf[posInBuf] & 0xFF;
178                 pointer++;
179                 return result;
180         }       \r
181 \r
182         /**\r
183          * Get next 4 bytes as an int value. This is an optimization.\r
184          * @return long value\r
185          * @throws IOException\r
186          */\r
187         int _getInt() throws IOException\r
188         {\r
189                 assertReadable(4);              \r
190 \r
191                 int posInBuf = (int) (pointer - buf_pos);\r
192                 int result =\r
193                         (((int) (buf[posInBuf + 3] & 0xFF)) |\r
194                         (((int) (buf[posInBuf + 2] & 0xFF)) << 8) |\r
195                         (((int) (buf[posInBuf + 1] & 0xFF)) << 16) |\r
196                         (((int) (buf[posInBuf]     & 0xFF)) << 24));\r
197                 pointer+=4;\r
198                 return result;\r
199         }\r
200 \r
201         /**\r
202          * Get next 8 bytes as a long value. This is an optimization.\r
203          * @return long value\r
204          * @throws IOException\r
205          */\r
206         long _getLong() throws IOException\r
207         {\r
208                 assertReadable(8);\r
209 \r
210                 int posInBuf = (int) (pointer - buf_pos);\r
211                 long result =\r
212                         (((long) (buf[posInBuf + 7] & 0xFF)) |\r
213                         (((long) (buf[posInBuf + 6] & 0xFF)) << 8) |\r
214                         (((long) (buf[posInBuf + 5] & 0xFF)) << 16) |\r
215                         (((long) (buf[posInBuf + 4] & 0xFF)) << 24) |\r
216                         (((long) (buf[posInBuf + 3] & 0xFF)) << 32) |\r
217                         (((long) (buf[posInBuf + 2] & 0xFF)) << 40) |\r
218                         (((long) (buf[posInBuf + 1] & 0xFF)) << 48) |\r
219                         (((long) (buf[posInBuf]     & 0xFF)) << 56));\r
220                 pointer+=8;\r
221                 return result;\r
222         }\r
223 \r
224         /**\r
225          * Get next byte\r
226          * @return 0..255 or -1 on end of file\r
227          * @throws IOException\r
228          */\r
229         int _read() throws IOException\r
230         {\r
231                 if (readableBytesInBuffer()<1) {\r
232                         fill();\r
233                         if (readableBytesInBuffer()==0) return -1;\r
234                 }\r
235                 \r
236                 int posInBuf = (int) (pointer - buf_pos);\r
237                 int result = buf[posInBuf] & 0xFF;\r
238                 pointer++;\r
239                 return result;\r
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 \r
333         /**\r
334          * Assert there is a set number of bytes in read buffer.\r
335          * bytes should not be larger than read buffer size (buf.length, 4K) \r
336          * \r
337          * @param bytes\r
338          * @throws IOException\r
339          */\r
340         private void assertReadable(int bytes) throws IOException\r
341         {\r
342                 if (readableBytesInBuffer()<bytes) {\r
343                         fill();\r
344                         if (readableBytesInBuffer()<bytes) throw new EOFException(); \r
345                 }\r
346         }\r
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 \r
406         @Override
407         public byte readByte() throws IOException {
408                 return (byte) _get();
409         }\r
410         \r
411         @Override\r
412         public char readChar() throws IOException {\r
413                 return (char)((_get() << 8) | _get());\r
414         }\r
415         \r
416         @Override\r
417         public int readUnsignedByte() throws IOException {\r
418                 return _get() & 0x000000ff;\r
419         }       
420 \r
421         @Override\r
422         public boolean readBoolean() throws IOException {\r
423                 return _get()!=0;\r
424         }\r
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) {\r
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                         }\r
453                         if (length>0) {\r
454                                 fill();\r
455                                 if (readableBytesInBuffer()==0) {\r
456                                         throw new EOFException();\r
457                                 }\r
458                         }\r
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() );\r
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();\r
494         }
495
496         @Override
497         public short readShort() throws IOException {
498                 return (short) ( (_get() << 8) |  _get() ) ;
499         }\r
500         \r
501         @Override\r
502         public int readUnsignedShort() throws IOException {\r
503                 return (int) ( (_get() << 8) |  _get() ) ;\r
504         }       
505         \r
506     public final String readLine() throws IOException {\r
507         StringBuffer input = new StringBuffer();\r
508         int c = -1;\r
509         boolean eol = false;\r
510 \r
511         while (!eol) {\r
512             switch (c = _read()) {\r
513             case -1:\r
514             case '\n':\r
515                 eol = true;\r
516                 break;\r
517             case '\r':\r
518                 eol = true;\r
519                 long cur = position();\r
520                 if ((_read()) != '\n') {\r
521                     position(cur);\r
522                 }\r
523                 break;\r
524             default:\r
525                 input.append((char)c);\r
526                 break;\r
527             }\r
528         }\r
529 \r
530         if ((c == -1) && (input.length() == 0)) {\r
531             return null;\r
532         }\r
533         return input.toString();\r
534     }   \r
535     \r
536     public final String readUTF() throws IOException {\r
537         return DataInputStream.readUTF(this);\r
538     }    \r
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;\r
564                 virtualLength = diskLength = raf.length();              \r
565         }
566 \r
567         @Override\r
568         public long skipBytes(long bytes) throws IOException {          \r
569                 pointer += bytes;\r
570                 return bytes;\r
571         }\r
572         
573         @Override\r
574         public int skipBytes(int bytes) throws IOException {            \r
575                 pointer += bytes;\r
576                 return bytes;\r
577         }\r
578 \r
579 \r
580         // WRITE
581                 
582         void _put(int value) throws IOException
583         {
584                 prepareForWrite(1);
585                 int posInBuf = (int) (pointer - buf_pos);
586                 buf[posInBuf] = (byte) value;
587                 posInBuf++;
588                 pointer++;
589                 if (write_buf_count<posInBuf) write_buf_count = posInBuf;
590                 if (readable_bytes_count<write_buf_count) readable_bytes_count=write_buf_count;
591                 if (virtualLength<pointer) virtualLength=pointer;
592         }
593         
594         void _put(byte[] src, int offset, int length) throws IOException {
595                 while (length>0) {
596                         int n = Math.min(prepareForWrite(length), length);
597                         int posInBuf = (int) (pointer - buf_pos);
598                         System.arraycopy(src, offset, buf, posInBuf, n);                        
599                         pointer += n;
600                         posInBuf += n;
601                         offset += n;
602                         length -= n;
603                         if (write_buf_count<posInBuf) write_buf_count = posInBuf;
604                         if (readable_bytes_count<write_buf_count) readable_bytes_count=write_buf_count;
605                         if (virtualLength<pointer) virtualLength=pointer;
606                 }
607         }
608         
609         @Override
610         public void write(int b) throws IOException {
611                 _put(b);
612         }\r
613         \r
614         @Override\r
615         public void writeByte(int b) throws IOException {\r
616                 _put(b);\r
617         }       
618 \r
619         @Override\r
620         public void writeBoolean(boolean v) throws IOException {\r
621                 _put( v ? 1 : 0);\r
622         }\r
623         
624         @Override
625         public void writeFully(ByteBuffer src) throws IOException {
626                 if (src.hasArray()) {
627                         byte array[] = src.array();
628                         _put(array, src.position(), src.remaining());
629                         src.position(src.limit()); 
630                 } else 
631                         for (;src.hasRemaining();)
632                                 _put(src.get());
633         }
634
635         @Override
636         public void writeFully(ByteBuffer src, int length) throws IOException {
637                 if (src.hasArray()) {
638                         byte array[] = src.array();
639                         _put(array, src.position(), length);
640                         src.position(length); 
641                 } else {
642                         for (int i=0; i<length; i++)
643                                 _put(src.get());
644                 }
645         }
646
647         @Override
648         public void write(byte[] src, int offset, int length) throws IOException {
649                 _put(src, offset, length);
650         }
651
652         @Override
653         public void write(byte[] src) throws IOException {
654                 _put(src, 0, src.length);
655         }
656
657         @Override
658         public void writeDouble(double value) throws IOException {
659                 writeLong(Double.doubleToLongBits(value));
660         }
661
662         @Override
663         public void writeFloat(float value) throws IOException {
664                 writeInt(Float.floatToIntBits(value));
665         }
666
667         @Override
668         public void writeInt(int value) throws IOException {
669                 _put(value >>> 24);
670                 _put(value >>> 16);
671                 _put(value >>> 8);
672                 _put(value);
673         }
674
675         @Override
676         public void writeLong(long value) throws IOException {
677                 _put((int) (value >>> 56));
678                 _put((int) (value >>> 48));
679                 _put((int) (value >>> 40));
680                 _put((int) (value >>> 32));
681                 _put((int) (value >>> 24));
682                 _put((int) (value >>> 16));
683                 _put((int) (value >>> 8));
684                 _put((int) (value));
685         }
686
687         @Override
688         public void writeShort(int value) throws IOException {
689                 _put(value >> 8);
690                 _put(value);
691         }\r
692         \r
693         @Override\r
694         public void writeChar(int value) throws IOException {\r
695                 _put(value >> 8);\r
696                 _put(value);\r
697         }\r
698         \r
699         @Override\r
700         public void writeBytes(String s) throws IOException {\r
701                 int len = s.length();\r
702                 for (int i = 0 ; i < len ; i++) {\r
703                     _put((byte)s.charAt(i));\r
704                 }\r
705         }\r
706         \r
707         @Override\r
708         public void writeChars(String s) throws IOException {\r
709         int len = s.length();\r
710         for (int i = 0 ; i < len ; i++) {\r
711             int v = s.charAt(i);\r
712             _put((v >>> 8) & 0xFF); \r
713             _put((v >>> 0) & 0xFF); \r
714         }\r
715         }
716 \r
717         @Override\r
718         public void writeUTF(String s) throws IOException {\r
719                 int len = UTF8.getModifiedUTF8EncodingByteLength(s);\r
720                 writeShort(len);\r
721                 UTF8.writeModifiedUTF(this, s);\r
722         }\r
723         
724         @Override
725         public void insertBytes(long bytes, ByteSide side) throws IOException {         
726                 if (pointer>=virtualLength) {
727                         setLength(pointer + bytes);                     
728                         return;
729                 }
730
731                 // insertion to buffer window
732                 if (pointer>=buf_pos && (pointer<=buf_pos+readable_bytes_count))
733                 {
734                         // buffer window convers the end of the file & there is enough space in buffer to do the operation
735                         if (buf_pos+readable_bytes_count >= virtualLength && readable_bytes_count + bytes < buf.length) {
736                                 // Move right siade
737                                 int posInBuf = (int) (pointer - buf_pos);
738                                 System.arraycopy(buf, posInBuf, buf, (int) (posInBuf+bytes), (int) readable_bytes_count - posInBuf);
739                                 readable_bytes_count += bytes;
740                                 write_buf_count = readable_bytes_count;
741                                 virtualLength += bytes;
742                                 return;
743                         }                       
744                         
745                         writeFlush();
746                         reset();
747                 }
748                 
749                 writeFlush();
750                 reset();
751                 insertBytes(raf, pointer, bytes);               
752                 virtualLength += bytes;
753                 diskLength += bytes;
754                 
755                 // Move buffer
756                 if (buf_pos>pointer) {
757                         buf_pos += bytes;
758                 }
759         }
760
761         @Override
762         public void removeBytes(long bytes, ByteSide side) throws IOException {
763                 if (pointer+bytes>virtualLength || pointer<0) {
764                         throw new IOException("Pointer outside file");
765                 }
766                 
767                 if (pointer+bytes==virtualLength) {
768                         setLength(virtualLength - bytes);
769                         return;
770                 }
771
772                 // removal intersects buffer window
773                 if (pointer+bytes>=buf_pos && (pointer<=buf_pos+readable_bytes_count))
774                 {
775                         // buffer window covers the ending
776                         if (buf_pos+readable_bytes_count >= virtualLength) {
777                                 
778                                 // Scenario 1 : Pointer before buffer
779                                 if (pointer<buf_pos) {
780                                         int cut_end_InBuf = (int) (pointer+bytes - buf_pos);
781                                         System.arraycopy(buf, cut_end_InBuf, buf, 0, readable_bytes_count-cut_end_InBuf);
782                                         readable_bytes_count -= cut_end_InBuf;
783                                         write_buf_count = readable_bytes_count;
784                                         virtualLength -= bytes;
785                                         buf_pos = pointer;
786                                         return;
787                                 } else
788                                         
789                                 // Scenario 2 : Pointer within buffer
790                                 if (pointer>=buf_pos) {
791                                         int posInBuf = (int) (pointer - buf_pos);
792                                         System.arraycopy(buf, (int) (posInBuf+bytes), buf, posInBuf, (int) (readable_bytes_count - posInBuf - bytes) );
793                                         readable_bytes_count -= bytes;
794                                         write_buf_count = readable_bytes_count;
795                                         virtualLength -= bytes;
796                                         return;
797                                 }
798                         }
799                         
800                         writeFlush();
801                         reset();
802                 }
803                 
804                 writeFlush();
805                 reset();
806                 removeBytes(raf, pointer, bytes);
807                 virtualLength -= bytes;
808                 diskLength -= bytes;
809                 
810                 // Move buffer
811                 if (buf_pos>pointer) {
812                         buf_pos -= bytes;
813                 }
814         }
815
816         @Override
817         public long length() throws IOException {
818                 return virtualLength;
819         }
820         
821         @Override
822         public void setLength(long newLength) throws IOException {              
823                 virtualLength = newLength;
824                 if (buf_pos + readable_bytes_count > virtualLength) {
825                                 readable_bytes_count = (int) Math.max(virtualLength - buf_pos, 0L);
826                 }
827                 if (buf_pos + write_buf_count > virtualLength) {
828                                 write_buf_count = (int) Math.max(virtualLength - buf_pos, 0L);
829                 }               
830         }
831
832         private final static int bufferSize = 1024*32;
833         
834         /**
835          * Inserts bytes into a middle of a file.
836          * 
837          * @param file file
838          * @param position 
839          * @param bytes
840          * @throws IOException 
841          */
842         public static void insertBytes(RandomAccessFile file, long position, long bytes) 
843         throws IOException
844         {
845                 if (position<0) throw new IndexOutOfBoundsException("position cannot be below 0");
846                 if (bytes<0) throw new IndexOutOfBoundsException("bytes cannot be below 0");
847                 if (bytes==0) return;
848                 long length = file.length();
849                 if (position>=length) {
850                         file.setLength(position+bytes);
851                         return;
852                 }
853                 
854                 long bytesOnRight = length - position;
855                 
856                 // Create buffer - the buffer is cyclic window
857                 int bufLength = (int) Math.min(bytesOnRight, bufferSize);
858                 byte[] buf = new byte[bufLength];
859                 
860                 // Bytes transferred from right
861                 long n = 0;
862                 while (n<bytesOnRight) {
863                         // Bytes that have content
864                         int count = (int) Math.min(bufLength, bytesOnRight-n);
865                         file.seek( length-count-n );
866                         file.readFully(buf, 0, count);
867                         file.seek( length-count-n+bytes );
868                         file.write(buf, 0, count);
869                         n += count;
870                 }
871         }
872         
873         /**
874          * Remove bytes from a file 
875          * 
876          * @param file
877          * @param position 
878          * @param bytes
879          * @throws IOException 
880          */
881         public static void removeBytes(RandomAccessFile file, long position, long bytes) throws IOException
882         {
883                 if (position<0) throw new IndexOutOfBoundsException("position cannot be below 0");
884                 if (bytes<0) throw new IndexOutOfBoundsException("bytes cannot be below 0");
885                 if (bytes==0) return;
886                 long length = file.length();
887                 if (position>=length) return;
888                 // Truncate
889                 if (position+bytes>=length) {
890                         file.setLength(position);
891                         return;
892                 }
893                 long bytesOnRight = length - position - bytes;
894                 int bufLength = (int) Math.min(bytesOnRight, bufferSize);
895                 byte[] buf = new byte[bufLength];
896                 
897                 long n = 0;
898                 while (n<bytesOnRight) {
899                         int count = (int) Math.min(bufLength, bytesOnRight-n);
900                         file.seek( position + bytes + n );
901                         file.readFully( buf, 0, count );
902                         file.seek( position + n );
903                         file.write(buf, 0, count);
904                         n += count;
905                 }
906                 file.setLength(position + bytesOnRight);
907         }       
908
909         @Override
910         public String toString() {
911                 try {
912                         return "File(file="+file.getName()+", size="+length()+")";
913                 } catch (IOException e) {
914                         return "File()";
915                 }
916         }
917         
918 }
919