]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.databoard/src/org/simantics/databoard/util/binary/BinaryFile.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / util / binary / BinaryFile.java
diff --git a/bundles/org.simantics.databoard/src/org/simantics/databoard/util/binary/BinaryFile.java b/bundles/org.simantics.databoard/src/org/simantics/databoard/util/binary/BinaryFile.java
new file mode 100644 (file)
index 0000000..1e9dfa3
--- /dev/null
@@ -0,0 +1,919 @@
+/*******************************************************************************\r
+ * Copyright (c) 2010, 2016 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ *     VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.databoard.util.binary;
+
+import java.io.DataInputStream;\r
+import java.io.EOFException;\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.io.RandomAccessFile;\r
+import java.nio.ByteBuffer;\r
+
+/**
+ * This class is a Random Access File implementation to RandomAccessBinary.
+ * The implementation is buffered. The implementation is not multi-thread
+ * safe.
+ * <p>
+ * There is a buffer used for reading and writing. It has a buffer read and
+ * write position. When read, the buffer is filled. When written, the buffer 
+ * is written. When the file pointer is moved, the file size changed or flushed
+ * the buffers are cleared. If there were unwritten bytes, they are flushed 
+ * to disc.
+ * <p>
+ * There is internal pointer variable. The actual file pointer is moved on
+ * disc read and write operations. \r
+ * <p>\r
+ * Primitive number writes (int, short, char, double, float and long) and\r
+ * written in big endian (network) byte order. Use {@link Endian}\r
+ * to make little endian operations.
+ *
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
+ */
+public class BinaryFile implements RandomAccessBinary, BinaryReadable, BinaryWriteable {
+
+       RandomAccessFile raf;
+       File file;
+       
+       /** The internal read and write buffer */
+    byte buf[];        
+    
+    /** The number of valid bytes in the buffer */
+    int readable_bytes_count;
+       
+       /** The number written but unflushed bytes in buf starting from buf[0]*/
+       int write_buf_count;
+    
+    /** The pointer of buf[0] in the file. The value is valid only if value >=0 */
+    long buf_pos;
+    
+       /** File Pointer of current position */
+    long pointer;    
+
+    /** Transient size of the file */
+    long virtualLength;
+    long diskLength;
+    
+       public BinaryFile(RandomAccessFile file) throws IOException\r
+       {\r
+               this.raf = file;\r
+               virtualLength = diskLength = raf.length();\r
+               pointer = 0;\r
+               buf = new byte[4096];\r
+       }\r
+\r
+       public BinaryFile(File file) throws IOException\r
+       {\r
+               this.raf = new RandomAccessFile(file, "rw");\r
+               virtualLength = diskLength = raf.length();              \r
+               this.file = file;\r
+               pointer = 0;\r
+               buf = new byte[4096];\r
+       }\r
+       \r
+       public BinaryFile(File file, String mode) throws IOException\r
+       {\r
+               this.raf = new RandomAccessFile(file, mode);\r
+               virtualLength = diskLength = raf.length();              \r
+               this.file = file;\r
+               pointer = 0;\r
+               buf = new byte[4096];\r
+       }\r
+       \r
+       public BinaryFile(RandomAccessFile file, int bufSize) throws IOException\r
+       {\r
+               this.raf = file;\r
+               virtualLength = diskLength = raf.length();\r
+               pointer = 0;\r
+               buf = new byte[bufSize];\r
+       }\r
+\r
+       public BinaryFile(File file, int bufSize) throws IOException\r
+       {\r
+               this.raf = new RandomAccessFile(file, "rw");\r
+               virtualLength = diskLength = raf.length();              \r
+               this.file = file;\r
+               pointer = 0;\r
+               buf = new byte[bufSize];\r
+       }\r
+       \r
+       public static BinaryFile tempFile(long size) throws IOException
+       {
+               File tmpFile = File.createTempFile("Temp", ".file");
+               tmpFile.deleteOnExit();
+               BinaryFile file = new BinaryFile(tmpFile);
+               file.setLength( size );
+               return file;
+       }
+       
+       /**
+        * Closes the object. Note, this will close the input random access file.\r
+        * This method may be called several times.
+        *  
+        * @throws IOException
+        */
+       public synchronized void close() throws IOException {\r
+               if (raf==null) return;
+               flush();
+               pointer = -1;
+               raf.close();
+               raf = null;
+               buf = null;
+       }
+\r
+       public synchronized boolean isOpen() {\r
+               return buf!=null;\r
+       }\r
+       
+       public File file() {
+               return file;
+       }
+       
+       public RandomAccessFile getRandomAccessFile() {
+               return raf;
+       }
+
+       /**
+        * Get the number of readable bytes in buffer 
+        * 
+        * @return readable bytes or 0
+        */
+       private long readableBytesInBuffer() {
+               long posInBuf = pointer - buf_pos;
+               if (posInBuf<0) return 0;
+               long bytesLeft = readable_bytes_count - posInBuf;
+               return bytesLeft < 0 ? 0 : bytesLeft;
+       }
+       
+       /**
+        * Get the valid position of pointer in buf[]
+        * 
+        * @return pos or -1 
+        */
+       private long positionInReadBuffer() {
+               long posInBuf = pointer - buf_pos;
+               if (posInBuf<0 || posInBuf>readable_bytes_count) return -1;
+               return posInBuf;
+       }
+       
+       /**
+        * Get next byte
+        * @return 0..255
+        * @throws IOException
+        */
+       int _get() throws IOException
+       {\r
+               assertReadable(1);
+               
+               int posInBuf = (int) (pointer - buf_pos);
+               int result = buf[posInBuf] & 0xFF;
+               pointer++;
+               return result;
+       }       \r
+\r
+       /**\r
+        * Get next 4 bytes as an int value. This is an optimization.\r
+        * @return long value\r
+        * @throws IOException\r
+        */\r
+       int _getInt() throws IOException\r
+       {\r
+               assertReadable(4);              \r
+\r
+               int posInBuf = (int) (pointer - buf_pos);\r
+               int result =\r
+                       (((int) (buf[posInBuf + 3] & 0xFF)) |\r
+                       (((int) (buf[posInBuf + 2] & 0xFF)) << 8) |\r
+                       (((int) (buf[posInBuf + 1] & 0xFF)) << 16) |\r
+                       (((int) (buf[posInBuf]     & 0xFF)) << 24));\r
+               pointer+=4;\r
+               return result;\r
+       }\r
+\r
+       /**\r
+        * Get next 8 bytes as a long value. This is an optimization.\r
+        * @return long value\r
+        * @throws IOException\r
+        */\r
+       long _getLong() throws IOException\r
+       {\r
+               assertReadable(8);\r
+\r
+               int posInBuf = (int) (pointer - buf_pos);\r
+               long result =\r
+                       (((long) (buf[posInBuf + 7] & 0xFF)) |\r
+                       (((long) (buf[posInBuf + 6] & 0xFF)) << 8) |\r
+                       (((long) (buf[posInBuf + 5] & 0xFF)) << 16) |\r
+                       (((long) (buf[posInBuf + 4] & 0xFF)) << 24) |\r
+                       (((long) (buf[posInBuf + 3] & 0xFF)) << 32) |\r
+                       (((long) (buf[posInBuf + 2] & 0xFF)) << 40) |\r
+                       (((long) (buf[posInBuf + 1] & 0xFF)) << 48) |\r
+                       (((long) (buf[posInBuf]     & 0xFF)) << 56));\r
+               pointer+=8;\r
+               return result;\r
+       }\r
+\r
+       /**\r
+        * Get next byte\r
+        * @return 0..255 or -1 on end of file\r
+        * @throws IOException\r
+        */\r
+       int _read() throws IOException\r
+       {\r
+               if (readableBytesInBuffer()<1) {\r
+                       fill();\r
+                       if (readableBytesInBuffer()==0) return -1;\r
+               }\r
+               \r
+               int posInBuf = (int) (pointer - buf_pos);\r
+               int result = buf[posInBuf] & 0xFF;\r
+               pointer++;\r
+               return result;\r
+       }               
+       
+       void _get(byte[] dst, int offset, int length) throws IOException {
+               while (length>0) {
+                       long n = Math.min( readableBytesInBuffer(), length );
+                       int posInBuf = (int) (pointer - buf_pos);
+                       if (n>0) {
+                               System.arraycopy(buf, (int) posInBuf, dst, offset, (int) n);
+                               offset += n;
+                               length -= n;
+                               pointer += n;                           
+                       }
+                       
+                       if (length>0) {
+                               fill();
+                               if (readableBytesInBuffer()==0) {
+                                       throw new EOFException();
+                               }
+                       }
+               }               
+       }
+       
+       /**
+        * Flushes bytes 0..write_buf_count to file at buf_pos.
+        * Sets write_buf_count to 0.
+        * 
+        * @throws IOException
+        */
+       private void writeFlush() throws IOException {
+               if (write_buf_count>0 && buf_pos>=0) {
+                       raf.seek(buf_pos);
+                       raf.write(buf, 0, write_buf_count);
+                       if (buf_pos+write_buf_count>diskLength) diskLength = buf_pos+write_buf_count;
+                       write_buf_count = 0;
+               }
+               if (diskLength != virtualLength) {
+                       raf.setLength(virtualLength);
+                       diskLength = virtualLength;
+               }
+       }
+       
+       /**
+        * This method ensures that write buffer is ready for writing 
+        * at the pointer. Old buffer is flushed if necessary.
+        * 
+        * @param requested number of bytes, up to 4096 guaranteed
+        * @return the number of bytes that can be written to buf
+        * @throws IOException
+        */
+       private int prepareForWrite(int bytes) throws IOException {
+               // Ensure pointer is within buf[0..readable_bytes_count]
+               int posInBuf = (int) (pointer - buf_pos);
+               if (posInBuf<0) {
+                       writeFlush();
+                       readable_bytes_count = 0;
+                       buf_pos = pointer;
+                       posInBuf = 0;
+               } else
+               if (posInBuf>readable_bytes_count) {
+                       // Pointer is at wrong place, flush data
+                       writeFlush();
+                       
+                       // Move pointer
+                       int bytesToKeep = readable_bytes_count - posInBuf;
+                       if (bytesToKeep<0) bytesToKeep = 0;                     
+                       if (bytesToKeep>0) {
+                               System.arraycopy(buf, posInBuf, buf, 0, bytesToKeep);
+                               readable_bytes_count = bytesToKeep;
+                       } else {
+                               readable_bytes_count = 0;
+                       }
+                       buf_pos = pointer;
+                       posInBuf = 0;
+               }
+               // Ensure buf[] has room for bytes
+               if (buf.length - posInBuf < bytes) {
+                       writeFlush();
+                       int bytesToKeep = readable_bytes_count - posInBuf;
+                       if (bytesToKeep<0) bytesToKeep = 0;                     
+                       if (bytesToKeep>0) {
+                               System.arraycopy(buf, posInBuf, buf, 0, bytesToKeep);
+                               readable_bytes_count = bytesToKeep;
+                       } else {
+                               readable_bytes_count = 0;
+                       }
+                       buf_pos = pointer;
+                       posInBuf = 0;
+                       return buf.length;
+               }
+               
+               return (int) ( buf.length - posInBuf );
+       }
+\r
+       /**\r
+        * Assert there is a set number of bytes in read buffer.\r
+        * bytes should not be larger than read buffer size (buf.length, 4K) \r
+        * \r
+        * @param bytes\r
+        * @throws IOException\r
+        */\r
+       private void assertReadable(int bytes) throws IOException\r
+       {\r
+               if (readableBytesInBuffer()<bytes) {\r
+                       fill();\r
+                       if (readableBytesInBuffer()<bytes) throw new EOFException(); \r
+               }\r
+       }\r
+       
+       /**
+        * Read the buffer at file position pointer.  
+        * 
+        * @throws IOException
+        */
+       private void fill() throws IOException
+       {
+               writeFlush();
+               
+               // Reuse previous cache, if possible
+               long old_buf_start = buf_pos;   
+               long old_buf_end = buf_pos + readable_bytes_count;
+               int  old_buf_length = readable_bytes_count;
+               long new_buf_start = pointer;
+               long new_buf_end = Math.min(pointer + buf.length, virtualLength);
+               int  new_buf_length = (int) (new_buf_end - new_buf_start);
+               
+               boolean old_buf_start_in_new_buf = (old_buf_start >= new_buf_start) && (old_buf_start < new_buf_end);  
+               boolean old_buf_end_in_new_buf = (old_buf_end >= new_buf_start) && (old_buf_end < new_buf_end);  
+               
+               // Scenario 1, end of old buf in the new, but the whole old buffer doesnt fit in the new                
+               if (old_buf_end_in_new_buf && !old_buf_start_in_new_buf)
+               {
+                       int bytesToPreserve = (int) (old_buf_end - new_buf_start);
+                       int bytesToRead = (int) (new_buf_end - old_buf_end);
+                       System.arraycopy(buf, old_buf_length - bytesToPreserve, buf, 0, bytesToPreserve);
+                       // Read more
+                       raf.seek(new_buf_start + bytesToPreserve);
+                       raf.readFully(buf, bytesToPreserve, bytesToRead);
+                       buf_pos = pointer;
+                       readable_bytes_count = new_buf_length;
+                       return;
+               }
+               
+               // Scenario 2, start of old buf is in new, but not completely
+               if (old_buf_start_in_new_buf && !old_buf_end_in_new_buf) {
+                       int bytesToPreserve = (int) (new_buf_end-old_buf_start);
+                       int bytesToRead = (int) (old_buf_start-new_buf_start);
+                       System.arraycopy(buf, 0, buf, bytesToRead, bytesToPreserve);
+                       raf.seek(new_buf_start);
+                       raf.readFully(buf, 0, bytesToRead);
+                       buf_pos = pointer;
+                       readable_bytes_count = new_buf_length;
+                       return;
+               }
+               
+               // Scenario 3, old buf is partially in new buf (Omited)
+               
+               // Scenario 4, old and new buf do not intersect
+               {
+                       int bytesToRead = new_buf_length; 
+                       raf.seek(new_buf_start);
+                       raf.readFully(buf, 0, bytesToRead);
+                       buf_pos = pointer;
+                       readable_bytes_count = new_buf_length;
+               }
+       }
+\r
+       @Override
+       public byte readByte() throws IOException {
+               return (byte) _get();
+       }\r
+       \r
+       @Override\r
+       public char readChar() throws IOException {\r
+               return (char)((_get() << 8) | _get());\r
+       }\r
+       \r
+       @Override\r
+       public int readUnsignedByte() throws IOException {\r
+               return _get() & 0x000000ff;\r
+       }       
+\r
+       @Override\r
+       public boolean readBoolean() throws IOException {\r
+               return _get()!=0;\r
+       }\r
+       
+       @Override
+       public void readFully(byte[] dst, int offset, int length) throws IOException {
+               _get(dst, offset, length);
+       }
+
+       @Override
+       public void readFully(byte[] dst) throws IOException {
+               _get(dst, 0, dst.length);
+       }
+
+       @Override
+       public void readFully(ByteBuffer buf) throws IOException {
+               readFully(buf, buf.remaining());
+       }
+
+       @Override
+       public void readFully(ByteBuffer buf, int length) throws IOException {
+               while (length>0) {\r
+                       assertReadable( Math.min(this.buf.length, length) );
+                       long n = Math.min(readableBytesInBuffer(), length);
+                       if (n==0) throw new EOFException();
+                       long posInBuf = positionInReadBuffer();
+                       if (n>0 && posInBuf>=0) {
+                               buf.put(this.buf, (int)posInBuf, (int)n);
+                               length -= n;
+                               pointer += n;
+                       }\r
+                       if (length>0) {\r
+                               fill();\r
+                               if (readableBytesInBuffer()==0) {\r
+                                       throw new EOFException();\r
+                               }\r
+                       }\r
+               }
+       }
+
+       @Override
+       public double readDouble() throws IOException {
+               return Double.longBitsToDouble(readLong());
+       }
+
+       @Override
+       public float readFloat() throws IOException {
+               return Float.intBitsToFloat(readInt());
+       }
+
+       @Override
+       public int readInt() throws IOException {
+               return 
+//                     ( _get() << 24) |
+//                     ( _get() << 16) | 
+//                     ( _get() << 8) |
+//                     ( _get() );\r
+               _getInt();
+       }
+
+       @Override
+       public long readLong() throws IOException {
+               return
+//             ( ((long)_get()) << 56) |
+//             ( ((long)_get()) << 48) |
+//             ( ((long)_get()) << 40) |
+//             ( ((long)_get()) << 32) |
+//             ( ((long)_get()) << 24) |
+//             ( ((long)_get()) << 16) |
+//             ( ((long)_get()) << 8) |
+//             ( ((long)_get()) );
+               _getLong();\r
+       }
+
+       @Override
+       public short readShort() throws IOException {
+               return (short) ( (_get() << 8) |  _get() ) ;
+       }\r
+       \r
+       @Override\r
+       public int readUnsignedShort() throws IOException {\r
+               return (int) ( (_get() << 8) |  _get() ) ;\r
+       }       
+       \r
+    public final String readLine() throws IOException {\r
+       StringBuffer input = new StringBuffer();\r
+       int c = -1;\r
+       boolean eol = false;\r
+\r
+       while (!eol) {\r
+           switch (c = _read()) {\r
+           case -1:\r
+           case '\n':\r
+               eol = true;\r
+               break;\r
+           case '\r':\r
+               eol = true;\r
+               long cur = position();\r
+               if ((_read()) != '\n') {\r
+                   position(cur);\r
+               }\r
+               break;\r
+           default:\r
+               input.append((char)c);\r
+               break;\r
+           }\r
+       }\r
+\r
+       if ((c == -1) && (input.length() == 0)) {\r
+           return null;\r
+       }\r
+       return input.toString();\r
+    }  \r
+    \r
+    public final String readUTF() throws IOException {\r
+       return DataInputStream.readUTF(this);\r
+    }    \r
+       
+       @Override
+       public long position() {
+               return pointer;
+       }
+       
+       public void position(long newPosition) {
+               pointer = newPosition;
+       }       
+       
+       /**
+        * Flushes internal buffer
+        */
+       public void flush() throws IOException {
+               writeFlush();
+       }
+       
+       /**
+        * Clears read&write buffer. The file can be modified elsewere after this.
+        * 
+        * @throws IOException 
+        */
+       public void reset() throws IOException {
+               writeFlush();
+               readable_bytes_count = 0;\r
+               virtualLength = diskLength = raf.length();              \r
+       }
+\r
+       @Override\r
+       public long skipBytes(long bytes) throws IOException {          \r
+               pointer += bytes;\r
+               return bytes;\r
+       }\r
+       
+       @Override\r
+       public int skipBytes(int bytes) throws IOException {            \r
+               pointer += bytes;\r
+               return bytes;\r
+       }\r
+\r
+\r
+       // WRITE
+               
+       void _put(int value) throws IOException
+       {
+               prepareForWrite(1);
+               int posInBuf = (int) (pointer - buf_pos);
+               buf[posInBuf] = (byte) value;
+               posInBuf++;
+               pointer++;
+               if (write_buf_count<posInBuf) write_buf_count = posInBuf;
+               if (readable_bytes_count<write_buf_count) readable_bytes_count=write_buf_count;
+               if (virtualLength<pointer) virtualLength=pointer;
+       }
+       
+       void _put(byte[] src, int offset, int length) throws IOException {
+               while (length>0) {
+                       int n = Math.min(prepareForWrite(length), length);
+                       int posInBuf = (int) (pointer - buf_pos);
+                       System.arraycopy(src, offset, buf, posInBuf, n);                        
+                       pointer += n;
+                       posInBuf += n;
+                       offset += n;
+                       length -= n;
+                       if (write_buf_count<posInBuf) write_buf_count = posInBuf;
+                       if (readable_bytes_count<write_buf_count) readable_bytes_count=write_buf_count;
+                       if (virtualLength<pointer) virtualLength=pointer;
+               }
+       }
+       
+       @Override
+       public void write(int b) throws IOException {
+               _put(b);
+       }\r
+       \r
+       @Override\r
+       public void writeByte(int b) throws IOException {\r
+               _put(b);\r
+       }       
+\r
+       @Override\r
+       public void writeBoolean(boolean v) throws IOException {\r
+               _put( v ? 1 : 0);\r
+       }\r
+       
+       @Override
+       public void writeFully(ByteBuffer src) throws IOException {
+               if (src.hasArray()) {
+                       byte array[] = src.array();
+                       _put(array, src.position(), src.remaining());
+                       src.position(src.limit()); 
+               } else 
+                       for (;src.hasRemaining();)
+                               _put(src.get());
+       }
+
+       @Override
+       public void writeFully(ByteBuffer src, int length) throws IOException {
+               if (src.hasArray()) {
+                       byte array[] = src.array();
+                       _put(array, src.position(), length);
+                       src.position(length); 
+               } else {
+                       for (int i=0; i<length; i++)
+                               _put(src.get());
+               }
+       }
+
+       @Override
+       public void write(byte[] src, int offset, int length) throws IOException {
+               _put(src, offset, length);
+       }
+
+       @Override
+       public void write(byte[] src) throws IOException {
+               _put(src, 0, src.length);
+       }
+
+       @Override
+       public void writeDouble(double value) throws IOException {
+               writeLong(Double.doubleToLongBits(value));
+       }
+
+       @Override
+       public void writeFloat(float value) throws IOException {
+               writeInt(Float.floatToIntBits(value));
+       }
+
+       @Override
+       public void writeInt(int value) throws IOException {
+               _put(value >>> 24);
+               _put(value >>> 16);
+               _put(value >>> 8);
+               _put(value);
+       }
+
+       @Override
+       public void writeLong(long value) throws IOException {
+               _put((int) (value >>> 56));
+               _put((int) (value >>> 48));
+               _put((int) (value >>> 40));
+               _put((int) (value >>> 32));
+               _put((int) (value >>> 24));
+               _put((int) (value >>> 16));
+               _put((int) (value >>> 8));
+               _put((int) (value));
+       }
+
+       @Override
+       public void writeShort(int value) throws IOException {
+               _put(value >> 8);
+               _put(value);
+       }\r
+       \r
+       @Override\r
+       public void writeChar(int value) throws IOException {\r
+               _put(value >> 8);\r
+               _put(value);\r
+       }\r
+       \r
+       @Override\r
+       public void writeBytes(String s) throws IOException {\r
+               int len = s.length();\r
+               for (int i = 0 ; i < len ; i++) {\r
+                   _put((byte)s.charAt(i));\r
+               }\r
+       }\r
+       \r
+       @Override\r
+       public void writeChars(String s) throws IOException {\r
+        int len = s.length();\r
+        for (int i = 0 ; i < len ; i++) {\r
+            int v = s.charAt(i);\r
+            _put((v >>> 8) & 0xFF); \r
+            _put((v >>> 0) & 0xFF); \r
+        }\r
+       }
+\r
+       @Override\r
+       public void writeUTF(String s) throws IOException {\r
+               int len = UTF8.getModifiedUTF8EncodingByteLength(s);\r
+               writeShort(len);\r
+               UTF8.writeModifiedUTF(this, s);\r
+       }\r
+       
+       @Override
+       public void insertBytes(long bytes, ByteSide side) throws IOException {         
+               if (pointer>=virtualLength) {
+                       setLength(pointer + bytes);                     
+                       return;
+               }
+
+               // insertion to buffer window
+               if (pointer>=buf_pos && (pointer<=buf_pos+readable_bytes_count))
+               {
+                       // buffer window convers the end of the file & there is enough space in buffer to do the operation
+                       if (buf_pos+readable_bytes_count >= virtualLength && readable_bytes_count + bytes < buf.length) {
+                               // Move right siade
+                               int posInBuf = (int) (pointer - buf_pos);
+                               System.arraycopy(buf, posInBuf, buf, (int) (posInBuf+bytes), (int) readable_bytes_count - posInBuf);
+                               readable_bytes_count += bytes;
+                               write_buf_count = readable_bytes_count;
+                               virtualLength += bytes;
+                               return;
+                       }                       
+                       
+                       writeFlush();
+                       reset();
+               }
+               
+               writeFlush();
+               reset();
+               insertBytes(raf, pointer, bytes);               
+               virtualLength += bytes;
+               diskLength += bytes;
+               
+               // Move buffer
+               if (buf_pos>pointer) {
+                       buf_pos += bytes;
+               }
+       }
+
+       @Override
+       public void removeBytes(long bytes, ByteSide side) throws IOException {
+               if (pointer+bytes>virtualLength || pointer<0) {
+                       throw new IOException("Pointer outside file");
+               }
+               
+               if (pointer+bytes==virtualLength) {
+                       setLength(virtualLength - bytes);
+                       return;
+               }
+
+               // removal intersects buffer window
+               if (pointer+bytes>=buf_pos && (pointer<=buf_pos+readable_bytes_count))
+               {
+                       // buffer window covers the ending
+                       if (buf_pos+readable_bytes_count >= virtualLength) {
+                               
+                               // Scenario 1 : Pointer before buffer
+                               if (pointer<buf_pos) {
+                                       int cut_end_InBuf = (int) (pointer+bytes - buf_pos);
+                                       System.arraycopy(buf, cut_end_InBuf, buf, 0, readable_bytes_count-cut_end_InBuf);
+                                       readable_bytes_count -= cut_end_InBuf;
+                                       write_buf_count = readable_bytes_count;
+                                       virtualLength -= bytes;
+                                       buf_pos = pointer;
+                                       return;
+                               } else
+                                       
+                               // Scenario 2 : Pointer within buffer
+                               if (pointer>=buf_pos) {
+                                       int posInBuf = (int) (pointer - buf_pos);
+                                       System.arraycopy(buf, (int) (posInBuf+bytes), buf, posInBuf, (int) (readable_bytes_count - posInBuf - bytes) );
+                                       readable_bytes_count -= bytes;
+                                       write_buf_count = readable_bytes_count;
+                                       virtualLength -= bytes;
+                                       return;
+                               }
+                       }
+                       
+                       writeFlush();
+                       reset();
+               }
+               
+               writeFlush();
+               reset();
+               removeBytes(raf, pointer, bytes);
+               virtualLength -= bytes;
+               diskLength -= bytes;
+               
+               // Move buffer
+               if (buf_pos>pointer) {
+                       buf_pos -= bytes;
+               }
+       }
+
+       @Override
+       public long length() throws IOException {
+               return virtualLength;
+       }
+       
+       @Override
+       public void setLength(long newLength) throws IOException {              
+               virtualLength = newLength;
+               if (buf_pos + readable_bytes_count > virtualLength) {
+                               readable_bytes_count = (int) Math.max(virtualLength - buf_pos, 0L);
+               }
+               if (buf_pos + write_buf_count > virtualLength) {
+                               write_buf_count = (int) Math.max(virtualLength - buf_pos, 0L);
+               }               
+       }
+
+       private final static int bufferSize = 1024*32;
+       
+       /**
+        * Inserts bytes into a middle of a file.
+        * 
+        * @param file file
+        * @param position 
+        * @param bytes
+        * @throws IOException 
+        */
+       public static void insertBytes(RandomAccessFile file, long position, long bytes) 
+       throws IOException
+       {
+               if (position<0) throw new IndexOutOfBoundsException("position cannot be below 0");
+               if (bytes<0) throw new IndexOutOfBoundsException("bytes cannot be below 0");
+               if (bytes==0) return;
+               long length = file.length();
+               if (position>=length) {
+                       file.setLength(position+bytes);
+                       return;
+               }
+               
+               long bytesOnRight = length - position;
+               
+               // Create buffer - the buffer is cyclic window
+               int bufLength = (int) Math.min(bytesOnRight, bufferSize);
+               byte[] buf = new byte[bufLength];
+               
+               // Bytes transferred from right
+               long n = 0;
+               while (n<bytesOnRight) {
+                       // Bytes that have content
+                       int count = (int) Math.min(bufLength, bytesOnRight-n);
+                       file.seek( length-count-n );
+                       file.readFully(buf, 0, count);
+                       file.seek( length-count-n+bytes );
+                       file.write(buf, 0, count);
+                       n += count;
+               }
+       }
+       
+       /**
+        * Remove bytes from a file 
+        * 
+        * @param file
+        * @param position 
+        * @param bytes
+        * @throws IOException 
+        */
+       public static void removeBytes(RandomAccessFile file, long position, long bytes) throws IOException
+       {
+               if (position<0) throw new IndexOutOfBoundsException("position cannot be below 0");
+               if (bytes<0) throw new IndexOutOfBoundsException("bytes cannot be below 0");
+               if (bytes==0) return;
+               long length = file.length();
+               if (position>=length) return;
+               // Truncate
+               if (position+bytes>=length) {
+                       file.setLength(position);
+                       return;
+               }
+               long bytesOnRight = length - position - bytes;
+               int bufLength = (int) Math.min(bytesOnRight, bufferSize);
+               byte[] buf = new byte[bufLength];
+               
+               long n = 0;
+               while (n<bytesOnRight) {
+                       int count = (int) Math.min(bufLength, bytesOnRight-n);
+                       file.seek( position + bytes + n );
+                       file.readFully( buf, 0, count );
+                       file.seek( position + n );
+                       file.write(buf, 0, count);
+                       n += count;
+               }
+               file.setLength(position + bytesOnRight);
+       }       
+
+       @Override
+       public String toString() {
+               try {
+                       return "File(file="+file.getName()+", size="+length()+")";
+               } catch (IOException e) {
+                       return "File()";
+               }
+       }
+       
+}
+