Index: ocean/lib/jdom.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: ocean\lib\jdom.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: ocean/lib/commons-lang-2.3.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: ocean\lib\commons-lang-2.3.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: ocean/lib/slf4j-api-1.5.2.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: ocean\lib\slf4j-api-1.5.2.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: ocean/lib/slf4j-simple-1.5.2.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: ocean\lib\slf4j-simple-1.5.2.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: ocean/lib/commons-io-1.3.2.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: ocean\lib\commons-io-1.3.2.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: ocean/lib/commons-io-1.3.2.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: ocean\lib\commons-io-1.3.2.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: ocean/lib/commons-lang-2.3.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: ocean\lib\commons-lang-2.3.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: ocean/lib/jdom.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: ocean\lib\jdom.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: ocean/lib/slf4j-api-1.5.2.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: ocean\lib\slf4j-api-1.5.2.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: ocean/lib/slf4j-simple-1.5.2.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: ocean\lib\slf4j-simple-1.5.2.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: ocean/src/com/imagero/uio/Input.java
===================================================================
--- ocean/src/com/imagero/uio/Input.java	(revision 0)
+++ ocean/src/com/imagero/uio/Input.java	(revision 0)
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.imagero.uio;
+
+import java.io.IOException;
+
+/**
+ * Unfortunately InputStream is not interface.
+ *
+ * @author Andrei Kouznetsov
+ * Date: 08.11.2003
+ * Time: 19:25:33
+ */
+public interface Input {
+	int read() throws IOException;
+	long skip(long n) throws IOException;
+	int read(byte [] b) throws IOException;
+	int read(byte [] b, int off, int len) throws IOException;
+}
Index: ocean/src/com/imagero/uio/FilterDataOutput.java
===================================================================
--- ocean/src/com/imagero/uio/FilterDataOutput.java	(revision 0)
+++ ocean/src/com/imagero/uio/FilterDataOutput.java	(revision 0)
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+ package com.imagero.uio;
+
+import java.io.DataOutput;
+import java.io.IOException;
+
+public class FilterDataOutput implements DataOutput {
+    /**
+     * The underlying output to be filtered.
+     */
+    protected DataOutput out;
+
+    public FilterDataOutput(DataOutput out) {
+        this.out = out;
+    }
+
+    public void write(int b) throws IOException {
+        out.write(b);
+    }
+
+    public void write(byte b[]) throws IOException {
+        write(b, 0, b.length);
+    }
+
+    public void write(byte b[], int off, int len) throws IOException {
+        if ((off | len | (b.length - (len + off)) | (off + len)) < 0)
+            throw new IndexOutOfBoundsException();
+
+        for (int i = 0; i < len; i++) {
+            write(b[off + i]);
+        }
+    }
+
+    public void writeBoolean(boolean v) throws IOException {
+        out.writeBoolean(v);
+    }
+
+    public void writeByte(int v) throws IOException {
+        out.writeByte(v);
+    }
+
+    public void writeShort(int v) throws IOException {
+        out.writeShort(v);
+    }
+
+    public void writeChar(int v) throws IOException {
+        out.writeChar(v);
+    }
+
+    public void writeInt(int v) throws IOException {
+        out.writeInt(v);
+    }
+
+    public void writeLong(long v) throws IOException {
+        out.writeLong(v);
+    }
+
+    public void writeFloat(float v) throws IOException {
+        out.writeFloat(v);
+    }
+
+    public void writeDouble(double v) throws IOException {
+        out.writeDouble(v);
+    }
+
+    public void writeBytes(String s) throws IOException {
+        out.writeBytes(s);
+    }
+
+    public void writeChars(String s) throws IOException {
+        out.writeChars(s);
+    }
+
+    public void writeUTF(String str) throws IOException {
+        out.writeUTF(str);
+    }
+}
Index: ocean/src/com/imagero/uio/ByteArray.java
===================================================================
--- ocean/src/com/imagero/uio/ByteArray.java	(revision 0)
+++ ocean/src/com/imagero/uio/ByteArray.java	(revision 0)
@@ -0,0 +1,22 @@
+package com.imagero.uio;
+
+/**
+ * Date: 09.04.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class ByteArray {
+
+    byte[] buffer;
+
+    int position;
+    int bitOffset;
+
+    public static final int[] N_MASK = {0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+    public static final int[] K_MASK = {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF};
+    public static final int[] I_MASK = {0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF};
+
+    public ByteArray(byte[] buffer) {
+        this.buffer = buffer;
+    }
+}
Index: ocean/src/com/imagero/uio/Transformer.java
===================================================================
--- ocean/src/com/imagero/uio/Transformer.java	(revision 0)
+++ ocean/src/com/imagero/uio/Transformer.java	(revision 0)
@@ -0,0 +1,495 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio;
+
+import com.imagero.uio.io.BitInputStream;
+import com.imagero.uio.io.BitOutputStream;
+import com.imagero.uio.io.ByteArrayOutputStreamExt;
+import com.imagero.uio.RandomAccessInput;
+import com.imagero.uio.xform.XTransformer;
+import com.imagero.uio.xform.ByteToXBE;
+import com.imagero.uio.xform.ByteToXLE;
+import com.imagero.uio.xform.XtoByteBE;
+import com.imagero.uio.xform.XtoByteLE;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+/**
+ * Primitive type conversion, array copying, etc.
+ *
+ * @author Andrey Kuznetsov
+ */
+public class Transformer extends XTransformer {
+
+
+
+    public static final int byteToInt(byte[] source, int sourceOffset, int[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            return ByteToXBE.byteToInt(source, sourceOffset, dest, destOffset);
+        } else {
+            return ByteToXLE.byteToInt(source, sourceOffset, dest, destOffset);
+        }
+    }
+
+    public static final void byteToInt(byte[] source, int sourceOffset, int count, int[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            ByteToXBE.byteToInt(source, sourceOffset, count, dest, destOffset);
+        } else {
+            ByteToXLE.byteToInt(source, sourceOffset, count, dest, destOffset);
+        }
+    }
+
+    public static final int intToByte(int[] source, int sourceOffset, byte[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            return XtoByteBE.intToByte(source, sourceOffset, dest, destOffset);
+        } else {
+            return XtoByteLE.intToByte(source, sourceOffset, dest, destOffset);
+        }
+    }
+
+    public static final void intToByte(int[] source, int srcOffset, int count, byte[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            XtoByteBE.intToByte(source, srcOffset, count, dest, destOffset);
+        } else {
+            XtoByteLE.intToByte(source, srcOffset, count, dest, destOffset);
+        }
+    }
+
+    public static int intToByte(int v, byte[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            return XtoByteBE.intToByte(v, dest, destOffset);
+        } else {
+            return XtoByteLE.intToByte(v, dest, destOffset);
+        }
+    }
+
+    public static final int byteToInt(byte[] source, int sourceOffset, boolean bigEndian) {
+        if (bigEndian) {
+            return ByteToXBE.byteToInt(source, sourceOffset);
+        } else {
+            return ByteToXLE.byteToInt(source, sourceOffset);
+        }
+    }
+
+
+/* **********************************************************************/
+
+    public static int byteToChar(byte[] source, int sourceOffset, char[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            return ByteToXBE.byteToChar(source, sourceOffset, dest, destOffset);
+        } else {
+            return ByteToXLE.byteToChar(source, sourceOffset, dest, destOffset);
+        }
+    }
+
+    public static void byteToChar(byte[] source, int sourceOffset, int count, char[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            ByteToXBE.byteToChar(source, sourceOffset, count, dest, destOffset);
+        } else {
+            ByteToXLE.byteToChar(source, sourceOffset, count, dest, destOffset);
+        }
+    }
+
+    public static int byteToChar(byte[] source, int sourceOffset, boolean bigEndian) {
+        if (bigEndian) {
+            return ByteToXBE.byteToChar(source, sourceOffset);
+        } else {
+            return ByteToXLE.byteToChar(source, sourceOffset);
+        }
+    }
+
+/* **********************************************************************/
+    public static int charToByte(char[] source, int srcOffset, byte[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            return XtoByteBE.charToByte(source, srcOffset, dest, destOffset);
+        } else {
+            return XtoByteLE.charToByte(source, srcOffset, dest, destOffset);
+        }
+    }
+
+    public static int charToByte(char v, byte[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            return XtoByteBE.charToByte(v, dest, destOffset);
+        } else {
+            return XtoByteLE.charToByte(v, dest, destOffset);
+        }
+    }
+
+    public static void charToByte(char[] source, int srcOffset, int count, byte[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            XtoByteBE.charToByte(source, srcOffset, count, dest, destOffset);
+        } else {
+            XtoByteLE.charToByte(source, srcOffset, count, dest, destOffset);
+        }
+    }
+
+/* **********************************************************************/
+
+    public static int byteToDouble(byte[] source, int sourceOffset, double[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            return ByteToXBE.byteToDoubleBE(source, sourceOffset, dest, destOffset);
+        } else {
+            return ByteToXLE.byteToDoubleLE(source, sourceOffset, dest, destOffset);
+        }
+    }
+
+    public static void byteToDouble(byte[] source, int sourceOffset, int count, double[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            ByteToXBE.byteToDoubleBE(source, sourceOffset, count, dest, destOffset);
+        } else {
+            ByteToXLE.byteToDoubleLE(source, sourceOffset, count, dest, destOffset);
+        }
+    }
+
+    public static double byteToDouble(byte[] source, int sourceOffset, boolean bigEndian) {
+        if (bigEndian) {
+            return ByteToXBE.byteToDoubleBE(source, sourceOffset);
+        } else {
+            return ByteToXLE.byteToDoubleLE(source, sourceOffset);
+        }
+    }
+
+/* **********************************************************************/
+
+    public static int doubleToByte(double[] source, int srcOffset, byte[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            return XtoByteBE.doubleToByteBE(source, srcOffset, dest, destOffset);
+        } else {
+            return XtoByteLE.doubleToByteLE(source, srcOffset, dest, destOffset);
+        }
+    }
+
+    public static void doubleToByte(double[] source, int srcOffset, int count, byte[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            XtoByteBE.doubleToByteBE(source, srcOffset, count, dest, destOffset);
+        } else {
+            XtoByteLE.doubleToByteLE(source, srcOffset, count, dest, destOffset);
+        }
+    }
+
+    public static int doubleToByte(double d, byte[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            return XtoByteBE.doubleToByteBE(d, dest, destOffset);
+        } else {
+            return XtoByteLE.doubleToByteLE(d, dest, destOffset);
+        }
+    }
+
+/* **********************************************************************/
+
+    public static int byteToFloat(byte[] source, int sourceOffset, float[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            return ByteToXBE.byteToFloatBE(source, sourceOffset, dest, destOffset);
+        } else {
+            return ByteToXLE.byteToFloatLE(source, sourceOffset, dest, destOffset);
+        }
+    }
+
+    public static void byteToFloat(byte[] source, int sourceOffset, int count, float[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            ByteToXBE.byteToFloatBE(source, sourceOffset, count, dest, destOffset);
+        } else {
+            ByteToXLE.byteToFloatLE(source, sourceOffset, count, dest, destOffset);
+        }
+    }
+
+    public static float byteToFloat(byte[] source, int sourceOffset, boolean bigEndian) {
+        if (bigEndian) {
+            return ByteToXBE.byteToFloatBE(source, sourceOffset);
+        } else {
+            return ByteToXLE.byteToFloatLE(source, sourceOffset);
+        }
+    }
+
+/* **********************************************************************/
+
+    public static int floatToByte(float[] source, int offset, byte[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            return XtoByteBE.floatToByteBE(source, offset, dest, destOffset);
+        } else {
+            return XtoByteLE.floatToByteLE(source, offset, dest, destOffset);
+        }
+    }
+
+    public static void floatToByte(float[] source, int offset, int count, byte[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            XtoByteBE.floatToByteBE(source, offset, count, dest, destOffset);
+        } else {
+            XtoByteLE.floatToByteLE(source, offset, count, dest, destOffset);
+        }
+    }
+
+    public static int floatToByte(float f, byte[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            return XtoByteBE.floatToByteBE(f, dest, destOffset);
+        } else {
+            return XtoByteLE.floatToByteLE(f, dest, destOffset);
+        }
+    }
+
+/* **********************************************************************/
+
+    public static int byteToLong(byte[] source, int sourceOffset, long[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            return ByteToXBE.byteToLongBE(source, sourceOffset, dest, destOffset);
+        } else {
+            return ByteToXLE.byteToLongLE(source, sourceOffset, dest, destOffset);
+        }
+    }
+
+    public static void byteToLong(byte[] source, int sourceOffset, int count, long[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            ByteToXBE.byteToLongBE(source, sourceOffset, count, dest, destOffset);
+        } else {
+            ByteToXLE.byteToLongLE(source, sourceOffset, count, dest, destOffset);
+        }
+    }
+
+    public static long byteToLong(byte[] source, int sourceOffset, boolean bigEndian) {
+        if (bigEndian) {
+            return ByteToXBE.byteToLongBE(source, sourceOffset);
+        } else {
+            return ByteToXLE.byteToLongLE(source, sourceOffset);
+        }
+    }
+
+/* **********************************************************************/
+
+    public static int longToByte(long[] source, int offset, byte[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            return XtoByteBE.longToByteBE(source, offset, dest, destOffset);
+        } else {
+            return XtoByteLE.longToByteLE(source, offset, dest, destOffset);
+        }
+    }
+
+    public static void longToByte(long[] source, int offset, int count, byte[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            XtoByteBE.longToByteBE(source, offset, count, dest, destOffset);
+        } else {
+            XtoByteLE.longToByteLE(source, offset, count, dest, destOffset);
+        }
+    }
+
+    public static int longToByte(long v, byte[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            return XtoByteBE.longToByteBE(v, dest, destOffset);
+        } else {
+            return XtoByteLE.longToByteLE(v, dest, destOffset);
+        }
+    }
+
+/* **********************************************************************/
+
+
+    public static int byteToShort(byte[] source, int sourceOffset, short[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            return ByteToXBE.byteToShortBE(source, sourceOffset, dest, destOffset);
+        } else {
+            return ByteToXLE.byteToShortLE(source, sourceOffset, dest, destOffset);
+        }
+    }
+
+    public static void byteToShort(byte[] source, int sourceOffset, int count, short[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            ByteToXBE.byteToShortBE(source, sourceOffset, count, dest, destOffset);
+        } else {
+            ByteToXLE.byteToShortLE(source, sourceOffset, count, dest, destOffset);
+        }
+    }
+
+    public static int byteToShort(byte[] source, int sourceOffset, boolean bigEndian) {
+        if (bigEndian) {
+            return ByteToXBE.byteToShortBE(source, sourceOffset);
+        } else {
+            return ByteToXLE.byteToShortLE(source, sourceOffset);
+        }
+    }
+
+/* **********************************************************************/
+
+    public static int shortToByte(short[] source, int offset, byte[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            return XtoByteBE.shortToByteBE(source, offset, dest, destOffset);
+        } else {
+            return XtoByteBE.shortToByteBE(source, offset, dest, destOffset);
+        }
+    }
+
+    public static void shortToByte(short[] source, int offset, int count, byte[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            XtoByteBE.shortToByteBE(source, offset, count, dest, destOffset);
+        } else {
+            XtoByteLE.shortToByteLE(source, offset, count, dest, destOffset);
+        }
+    }
+
+    public static int shortToByte(short v, byte[] dest, int destOffset, boolean bigEndian) {
+        if (bigEndian) {
+            return XtoByteBE.shortToByteBE(v, dest, destOffset);
+        } else {
+            return XtoByteLE.shortToByteLE(v, dest, destOffset);
+        }
+    }
+
+    public static final byte[] readByteLine(RandomAccessInput in) throws IOException {
+        long start = in.getFilePointer();
+        long end = start;
+        boolean finished = false;
+        boolean eof = false;
+        int length = 0;
+        while (!finished) {
+            int k = in.read();
+            switch (k) {
+                case -1:
+                    eof = true;
+                    finished = true;
+                    break;
+                case '\n':
+                    finished = true;
+                    end = in.getFilePointer();
+                    length = (int) (end - start);
+                    break;
+                case '\r':
+                    finished = true;
+                    end = in.getFilePointer();
+                    length = (int) (end - start);
+                    if ((in.read()) == '\n') {
+                        end = in.getFilePointer();
+                    }
+                    break;
+                default:
+                    k = 0;
+                    break;
+            }
+        }
+        if (eof && length == 0) {
+            return null;
+        }
+        byte[] b = new byte[length];
+        in.seek(start);
+        in.readFully(b);
+        in.seek(end);
+        return b;
+    }
+
+    public static final int readByteLine(RandomAccessInput in, byte[] dest) throws IOException {
+        long start = in.getFilePointer();
+        long end = start;
+        boolean finished = false;
+        boolean eof = false;
+        int length = 0;
+        int cnt = 0;
+        while (!finished) {
+            if (cnt++ >= dest.length) {
+                finished = true;
+                break;
+            }
+            switch (in.read()) {
+                case -1:
+                    eof = true;
+                    finished = true;
+                    break;
+                case '\n':
+                    finished = true;
+                    end = in.getFilePointer();
+                    length = (int) (end - start);
+                    break;
+                case '\r':
+                    finished = true;
+                    end = in.getFilePointer();
+                    length = (int) (end - start);
+                    if ((in.read()) == '\n') {
+                        end = in.getFilePointer();
+                    }
+                    break;
+            }
+        }
+
+        if (eof && length == 0) {
+            return 0;
+        }
+        if (length == 0) {
+            end = in.getFilePointer();
+            length = Math.min(dest.length, (int) (end - start));
+        }
+        in.seek(start);
+        in.readFully(dest, 0, length);
+        in.seek(end);
+        return length;
+    }
+
+    /**
+     * Make right shift for all bytes of given array
+     * @param src byte array
+     * @param first value used to fill empty bits of first byte in array
+     * @param shift shift amount (from 1 to 7)
+     * @throws java.io.IOException
+     */
+    public static void shiftRight(byte[] src, int first, int shift) throws IOException {
+        ByteArrayOutputStreamExt out = new ByteArrayOutputStreamExt(src.length + 1);
+        BitOutputStream bos = new BitOutputStream(out);
+
+        bos.write(first, shift);
+        for (int i = 0; i < src.length; i++) {
+            bos.write(src[i] & 0xFF);
+        }
+        bos.close();
+
+        byte[] dst = out.drain();
+        System.arraycopy(dst, 0, src, 0, src.length);
+    }
+
+    /**
+     * Make left shift for all bytes in given array
+     * @param src byte array
+     * @param shift shift amount (from 1 to 7)
+     * @throws java.io.IOException
+     */
+    public static void shiftLeft(byte[] src, int shift) throws IOException {
+        ByteArrayInputStream in = new ByteArrayInputStream(src);
+        BitInputStream bis = new BitInputStream(in);
+        bis.read(shift);
+        bis.setBitsToRead(8);
+        int a = bis.read();
+        int p = 0;
+        while (a != -1 && p < src.length) {
+            int b = bis.read();
+            src[p++] = (byte) a;
+            if (b == -1) {
+                break;
+            }
+            a = b;
+        }
+    }
+}
Index: ocean/src/com/imagero/uio/ReadUtil.java
===================================================================
--- ocean/src/com/imagero/uio/ReadUtil.java	(revision 0)
+++ ocean/src/com/imagero/uio/ReadUtil.java	(revision 0)
@@ -0,0 +1,350 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+ package com.imagero.uio;
+
+import com.imagero.uio.io.UnexpectedEOFException;
+
+import java.io.IOException;
+
+/**
+ * Methods to fill data in primitive arrays
+ *
+ * Date: 01.03.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class ReadUtil {
+    public static int read(RandomAccessInput in, short[] dest) throws IOException {
+        return read(in, dest, 0, dest.length, in.getByteOrder());
+    }
+
+    public static int read(RandomAccessInput in, short[] dest, int byteOrder) throws IOException {
+        return read(in, dest, 0, dest.length, byteOrder);
+    }
+
+    public static int read(RandomAccessInput in, short[] dest, int offset, int length) throws IOException {
+        return read(in, dest, offset, length, in.getByteOrder());
+    }
+
+    public static int read(RandomAccessInput in, char[] dest) throws IOException {
+        return read(in, dest, 0, dest.length, in.getByteOrder());
+    }
+
+    public static int read(RandomAccessInput in, char[] dest, int byteOrder) throws IOException {
+        return read(in, dest, 0, dest.length, byteOrder);
+    }
+
+    public static int read(RandomAccessInput in, char[] dest, int offset, int length) throws IOException {
+        return read(in, dest, offset, length, in.getByteOrder());
+    }
+
+    public static int read(RandomAccessInput in, int[] dest) throws IOException {
+        return read(in, dest, 0, dest.length, in.getByteOrder());
+    }
+
+    public static int read(RandomAccessInput in, int[] dest, int byteOrder) throws IOException {
+        return read(in, dest, 0, dest.length, byteOrder);
+    }
+
+    public static int read(RandomAccessInput in, int[] dest, int offset, int length) throws IOException {
+        return read(in, dest, offset, length, in.getByteOrder());
+    }
+
+    public static int read(RandomAccessInput in, long[] dest) throws IOException {
+        return read(in, dest, 0, dest.length, in.getByteOrder());
+    }
+
+    public static int read(RandomAccessInput in, long[] dest, int byteOrder) throws IOException {
+        return read(in, dest, 0, dest.length, byteOrder);
+    }
+
+    public static int read(RandomAccessInput in, long[] dest, int offset, int length) throws IOException {
+        return read(in, dest, offset, length, in.getByteOrder());
+    }
+
+    public static int read(RandomAccessInput in, float[] dest) throws IOException {
+        return read(in, dest, 0, dest.length, in.getByteOrder());
+    }
+
+    public static int read(RandomAccessInput in, float[] dest, int byteOrder) throws IOException {
+        return read(in, dest, 0, dest.length, byteOrder);
+    }
+
+    public static int read(RandomAccessInput in, float[] dest, int offset, int length) throws IOException {
+        return read(in, dest, offset, length, in.getByteOrder());
+    }
+
+    public static int read(RandomAccessInput in, double[] dest) throws IOException {
+        return read(in, dest, 0, dest.length, in.getByteOrder());
+    }
+
+    public static int read(RandomAccessInput in, double[] dest, int byteOrder) throws IOException {
+        return read(in, dest, 0, dest.length, byteOrder);
+    }
+
+    public static int read(RandomAccessInput in, double[] dest, int offset, int length) throws IOException {
+        return read(in, dest, offset, length, in.getByteOrder());
+    }
+
+    public static void readFully(RandomAccessInput in, short[] dest) throws IOException {
+        readFully(in, dest, 0, dest.length, in.getByteOrder());
+    }
+
+    public static void readFully(RandomAccessInput in, short[] dest, int byteOrder) throws IOException {
+        readFully(in, dest, 0, dest.length, byteOrder);
+    }
+
+    public static void readFully(RandomAccessInput in, short[] dest, int destOffset, int len) throws IOException {
+        readFully(in, dest, destOffset, len, in.getByteOrder());
+    }
+
+    public static void readFully(RandomAccessInput in, char[] dest) throws IOException {
+        readFully(in, dest, 0, dest.length, in.getByteOrder());
+    }
+
+    public static void readFully(RandomAccessInput in, char[] dest, int byteOrder) throws IOException {
+        readFully(in, dest, 0, dest.length, byteOrder);
+    }
+
+    public static void readFully(RandomAccessInput in, char[] dest, int destOffset, int len) throws IOException {
+        readFully(in, dest, destOffset, len, in.getByteOrder());
+    }
+
+    public static void readFully(RandomAccessInput in, int[] dest) throws IOException {
+        readFully(in, dest, 0, dest.length, in.getByteOrder());
+    }
+
+    public static void readFully(RandomAccessInput in, int[] dest, int byteOrder) throws IOException {
+        readFully(in, dest, 0, dest.length, byteOrder);
+    }
+
+    public static void readFully(RandomAccessInput in, int[] dest, int destOffset, int len) throws IOException {
+        readFully(in, dest, destOffset, len, in.getByteOrder());
+    }
+
+    public static void readFully(RandomAccessInput in, long[] dest) throws IOException {
+        readFully(in, dest, 0, dest.length, in.getByteOrder());
+    }
+
+    public static void readFully(RandomAccessInput in, long[] dest, int byteOrder) throws IOException {
+        readFully(in, dest, 0, dest.length, byteOrder);
+    }
+
+    public static void readFully(RandomAccessInput in, long[] dest, int destOffset, int len) throws IOException {
+        readFully(in, dest, destOffset, len, in.getByteOrder());
+    }
+
+    public static void readFully(RandomAccessInput in, float[] dest) throws IOException {
+        readFully(in, dest, 0, dest.length, in.getByteOrder());
+    }
+
+    public static void readFully(RandomAccessInput in, float[] dest, int byteOrder) throws IOException {
+        readFully(in, dest, 0, dest.length, byteOrder);
+    }
+
+    public static void readFully(RandomAccessInput in, float[] dest, int destOffset, int len) throws IOException {
+        readFully(in, dest, destOffset, len, in.getByteOrder());
+    }
+
+    public static void readFully(RandomAccessInput in, double[] dest) throws IOException {
+        readFully(in, dest, 0, dest.length, in.getByteOrder());
+    }
+
+    public static void readFully(RandomAccessInput in, double[] dest, int byteOrder) throws IOException {
+        readFully(in, dest, 0, dest.length, byteOrder);
+    }
+
+    public static void readFully(RandomAccessInput in, double[] dest, int destOffset, int len) throws IOException {
+        readFully(in, dest, destOffset, len, in.getByteOrder());
+    }
+
+    public static void readFully(RandomAccessInput in, short[] dest, int offset, int length, int byteOrder) throws IOException {
+        int sum = 0;
+        while (length > 0) {
+            int read = read(in, dest, offset, length, byteOrder);
+            if (read <= 0) {
+                throw new UnexpectedEOFException(sum);
+            }
+            sum += read;
+            length -= read;
+            offset += read;
+        }
+    }
+
+    public static void readFully(RandomAccessInput in, char[] dest, int offset, int length, int byteOrder) throws IOException {
+        int sum = 0;
+        while (length > 0) {
+            int read = read(in, dest, offset, length, byteOrder);
+            if (read <= 0) {
+                throw new UnexpectedEOFException(sum);
+            }
+            sum += read;
+            length -= read;
+            offset += read;
+        }
+    }
+
+    public static void readFully(RandomAccessInput in, int[] dest, int offset, int length, int byteOrder) throws IOException {
+        int sum = 0;
+        while (length > 0) {
+            int read = read(in, dest, offset, length, byteOrder);
+            if (read <= 0) {
+                throw new UnexpectedEOFException(sum);
+            }
+            sum += read;
+            length -= read;
+            offset += read;
+        }
+    }
+
+    public static void readFully(RandomAccessInput in, long[] dest, int offset, int length, int byteOrder) throws IOException {
+        int sum = 0;
+        while (length > 0) {
+            int read = read(in, dest, offset, length, byteOrder);
+            if (read <= 0) {
+                throw new UnexpectedEOFException(sum);
+            }
+            sum += read;
+            length -= read;
+            offset += read;
+        }
+    }
+
+    public static void readFully(RandomAccessInput in, float[] dest, int offset, int length, int byteOrder) throws IOException {
+        int sum = 0;
+        while (length > 0) {
+            int read = read(in, dest, offset, length, byteOrder);
+            if (read <= 0) {
+                throw new UnexpectedEOFException(sum);
+            }
+            sum += read;
+            length -= read;
+            offset += read;
+        }
+    }
+
+    public static void readFully(RandomAccessInput in, double[] dest, int offset, int length, int byteOrder) throws IOException {
+        int sum = 0;
+        while (length > 0) {
+            int read = read(in, dest, offset, length, byteOrder);
+            if (read <= 0) {
+                throw new UnexpectedEOFException(sum);
+            }
+            sum += read;
+            length -= read;
+            offset += read;
+        }
+    }
+
+    public static int read(RandomAccessInput in, short[] dest, int destOffset, int len, int byteOrder) throws IOException {
+        byte[] b = new byte[len * 2];
+        int cnt = in.read(b);
+
+        if ((cnt & 1) != 0) {
+            in.seek(in.getFilePointer() - 1);
+        }
+        final int count = cnt >> 1;
+        Transformer.byteToShort(b, 0, count, dest, destOffset, byteOrder == RandomAccessInput.BIG_ENDIAN);
+        return count;
+    }
+
+    public static int read(RandomAccessInput in, char[] dest, int destOffset, int len, int byteOrder) throws IOException {
+        byte[] b = new byte[len * 2];
+
+        int cnt = in.read(b);
+
+        if ((cnt & 1) != 0) {
+            in.seek(in.getFilePointer() - 1);
+        }
+
+        final int count = cnt >> 1;
+        Transformer.byteToChar(b, 0, count, dest, destOffset, byteOrder == RandomAccessInput.BIG_ENDIAN);
+        return count;
+    }
+
+    public static int read(RandomAccessInput in, int[] dest, int destOffset, int len, int byteOrder) throws IOException {
+        byte[] b = new byte[len * 4];
+
+        int cnt = in.read(b);
+
+        int r3 = cnt & 3;
+        if (r3 != 0) {
+            in.seek(in.getFilePointer() - r3);
+        }
+
+        final int count = cnt >> 2;
+        Transformer.byteToInt(b, 0, count, dest, destOffset, byteOrder == RandomAccessInput.BIG_ENDIAN);
+        return count;
+    }
+
+    public static int read(RandomAccessInput in, float[] dest, int destOffset, int len, int byteOrder) throws IOException {
+        byte[] b = new byte[len * 4];
+        int cnt = in.read(b);
+
+        int r3 = cnt & 3;
+        if (r3 != 0) {
+            in.seek(in.getFilePointer() - r3);
+        }
+
+        final int count = cnt >> 2;
+        Transformer.byteToFloat(b, 0, count, dest, destOffset, byteOrder == RandomAccessInput.BIG_ENDIAN);
+        return count;
+    }
+
+    public static int read(RandomAccessInput in, long[] dest, int destOffset, int len, int byteOrder) throws IOException {
+        byte[] b = new byte[len * 8];
+        int cnt = in.read(b);
+
+        int r7 = cnt & 7;
+        if (r7 != 0) {
+            in.seek(in.getFilePointer() - r7);
+        }
+
+        final int count = cnt >> 3;
+        Transformer.byteToLong(b, 0, count, dest, destOffset, byteOrder == RandomAccessInput.BIG_ENDIAN);
+        return count;
+    }
+
+    public static int read(RandomAccessInput in, double[] dest, int destOffset, int len, int byteOrder) throws IOException {
+        byte[] b = new byte[len * 8];
+        int cnt = in.read(b);
+
+        int r7 = cnt & 7;
+        if (r7 != 0) {
+            in.seek(in.getFilePointer() - r7);
+        }
+
+        final int count = cnt >> 3;
+        Transformer.byteToDouble(b, 0, count, dest, destOffset, byteOrder == RandomAccessInput.BIG_ENDIAN);
+        return count;
+    }
+}
Index: ocean/src/com/imagero/uio/Seekable.java
===================================================================
--- ocean/src/com/imagero/uio/Seekable.java	(revision 0)
+++ ocean/src/com/imagero/uio/Seekable.java	(revision 0)
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+ package com.imagero.uio;
+
+import java.io.IOException;
+
+/**
+ * Date: 01.03.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public interface Seekable {
+    long getFilePointer() throws IOException;
+
+    long length() throws IOException;
+
+    void seek(long offset) throws IOException;
+
+    void close() throws IOException;
+}
Index: ocean/src/com/imagero/uio/Sys.java
===================================================================
--- ocean/src/com/imagero/uio/Sys.java	(revision 0)
+++ ocean/src/com/imagero/uio/Sys.java	(revision 0)
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.imagero.uio;
+
+import java.io.IOException;
+import java.io.PrintStream;
+
+/**
+ * @author Andrei Kouznetsov
+ *         Date: 05.07.2004
+ *         Time: 15:02:01
+ */
+public class Sys {
+    public static final PrintStreamFilter out = new PrintStreamFilter(java.lang.System.out);
+    public static final PrintStreamFilter err = new PrintStreamFilter(java.lang.System.err);
+
+    public static PrintStreamFilter getErr() {
+        return err;
+    }
+
+    /**
+     * Set PrintStream used for output.
+     * Set to null to disable output.
+     * @param err PrintStream or null
+     */
+    public static void setErr(PrintStream err) {
+        Sys.err.setPs(err);
+    }
+
+    public static PrintStreamFilter getOut() {
+        return out;
+    }
+
+    /**
+     * Set PrintStream used for output.
+     * Set to null to disable output.
+     * @param out PrintStream or null
+     */
+    public static void setOut(PrintStream out) {
+        Sys.out.setPs(out);
+    }
+
+    public static class PrintStreamFilter {
+        PrintStream ps;
+
+        public PrintStreamFilter(PrintStream ps) {
+            this.ps = ps;
+        }
+
+        public PrintStream getPrintStream() {
+            return ps;
+        }
+
+        void setPs(PrintStream ps) {
+            this.ps = ps;
+        }
+
+        public void print(String x) {
+            if (ps == null) {
+                return;
+            }
+            if (x == null) {
+                ps.print(x);
+                return;
+            }
+            char[] chars = new char[x.length()];
+            x.getChars(0, x.length(), chars, 0);
+            int p = chars.length;
+            for (int j = 0; j < p; j++) {
+                char c = chars[j];
+                if (Character.isLetterOrDigit(c)) {
+                    chars[j] = c;
+                } else if (Character.isWhitespace(c)) {
+                    chars[j] = c;
+                } else if (Character.isISOControl(c)) {
+                    chars[j] = '.';
+                } else {
+                    chars[j] = c;
+                }
+            }
+            ps.print(chars);
+        }
+
+        public void println(String x) {
+            print(x);
+            println();
+        }
+
+        public void println() {
+            if (ps == null) {
+                return;
+            }
+            ps.println();
+        }
+
+        public void flush() {
+            if (ps == null) {
+                return;
+            }
+            ps.flush();
+        }
+
+        public void close() {
+            if (ps == null) {
+                return;
+            }
+            ps.close();
+        }
+
+        public boolean checkError() {
+            if (ps == null) {
+                return false;
+            }
+            return ps.checkError();
+        }
+
+        public void write(int b) {
+            if (ps == null) {
+                return;
+            }
+            ps.write(b);
+        }
+
+        public void write(byte buf[], int off, int len) {
+            if (ps == null) {
+                return;
+            }
+            ps.write(buf, off, len);
+        }
+
+        public void print(boolean b) {
+            if (ps == null) {
+                return;
+            }
+            ps.print(b);
+        }
+
+        public void print(char c) {
+            if (ps == null) {
+                return;
+            }
+            ps.print(c);
+        }
+
+        public void print(int i) {
+            if (ps == null) {
+                return;
+            }
+            ps.print(i);
+        }
+
+        public void print(long l) {
+            if (ps == null) {
+                return;
+            }
+            ps.print(l);
+        }
+
+        public void print(float f) {
+            if (ps == null) {
+                return;
+            }
+            ps.print(f);
+        }
+
+        public void print(double d) {
+            if (ps == null) {
+                return;
+            }
+            ps.print(d);
+        }
+
+        public void print(char s[]) {
+            if (ps == null) {
+                return;
+            }
+            ps.print(s);
+        }
+
+        public void print(Object obj) {
+            if (ps == null) {
+                return;
+            }
+            ps.print(obj);
+        }
+
+        public void println(boolean x) {
+            if (ps == null) {
+                return;
+            }
+            ps.println(x);
+        }
+
+        public void println(char x) {
+            if (ps == null) {
+                return;
+            }
+            ps.println(x);
+        }
+
+        public void println(int x) {
+            if (ps == null) {
+                return;
+            }
+            ps.println(x);
+        }
+
+        public void println(long x) {
+            if (ps == null) {
+                return;
+            }
+            ps.println(x);
+        }
+
+        public void println(float x) {
+            if (ps == null) {
+                return;
+            }
+            ps.println(x);
+        }
+
+        public void println(double x) {
+            if (ps == null) {
+                return;
+            }
+            ps.println(x);
+        }
+
+        public void println(char x[]) {
+            if (ps == null) {
+                return;
+            }
+            ps.println(x);
+        }
+
+        public void println(Object x) {
+            if (ps == null) {
+                return;
+            }
+            ps.println(x);
+        }
+
+        public void write(byte b[]) throws IOException {
+            if (ps == null) {
+                return;
+            }
+            ps.write(b);
+        }
+
+        public void println(Object[] objects) {
+            for (int i = 0; i < objects.length; i++) {
+                print(objects[i]);
+            }
+            println();
+        }
+
+        public void println(Object[] objects, String delimiter) {
+            for (int i = 0; i < objects.length; i++) {
+                print(objects[i]);
+                print(delimiter);
+            }
+            println();
+        }
+
+        public void println(long[] longs, String delimiter) {
+            for (int i = 0; i < longs.length; i++) {
+                print(longs[i]);
+                print(delimiter);
+            }
+            println();
+        }
+
+        public void println(int[] numbers, String delimiter) {
+            for (int i = 0; i < numbers.length; i++) {
+                print(numbers[i]);
+                print(delimiter);
+            }
+            println();
+        }
+
+        public void println(char[] chars, String delimiter) {
+            for (int i = 0; i < chars.length; i++) {
+                print(chars[i]);
+                print(delimiter);
+            }
+            println();
+        }
+
+        public void println(short[] shorts, String delimiter) {
+            for (int i = 0; i < shorts.length; i++) {
+                print(shorts[i]);
+                print(delimiter);
+            }
+            println();
+        }
+
+        public void println(byte[] bytes, String delimiter) {
+            for (int i = 0; i < bytes.length; i++) {
+                print(bytes[i]);
+                print(delimiter);
+            }
+            println();
+        }
+    }
+}
Index: ocean/src/com/imagero/uio/UIOStreamBuilder.java
===================================================================
--- ocean/src/com/imagero/uio/UIOStreamBuilder.java	(revision 0)
+++ ocean/src/com/imagero/uio/UIOStreamBuilder.java	(revision 0)
@@ -0,0 +1,782 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.imagero.uio;
+
+import com.imagero.uio.bio.BIOFactory;
+import com.imagero.uio.bio.BufferedRandomAccessIO;
+import com.imagero.uio.bio.ByteArrayRandomAccessIO;
+import com.imagero.uio.bio.IOController;
+import com.imagero.uio.bio.VariableSizeByteBuffer;
+import com.imagero.uio.bio.content.ByteArrayContent;
+import com.imagero.uio.bio.content.CharArrayContent;
+import com.imagero.uio.bio.content.Content;
+import com.imagero.uio.bio.content.DoubleArrayContent;
+import com.imagero.uio.bio.content.FloatArrayContent;
+import com.imagero.uio.bio.content.IntArrayContent;
+import com.imagero.uio.bio.content.LongArrayContent;
+import com.imagero.uio.bio.content.RandomAccessFileContent;
+import com.imagero.uio.bio.content.ShortArrayContent;
+import com.imagero.uio.bio.content.Span;
+import com.imagero.uio.bio.content.SpannedRandomAccessInputContent;
+import com.imagero.uio.bio.content.SpannedRandomAccessIOContent;
+import com.imagero.uio.impl.RandomAccessFileWrapper;
+import com.imagero.uio.impl.RandomAccessFileX;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.net.URL;
+
+/**
+ * <pre>
+ * UIOStreamBuilder is a builder pattern implementation and replacement for RandomAccessFactory.
+ * Usual process looks like
+ * File f = ...;
+ * RandomAccessIO ro = new UIOStreamBuilder(f).setByteOrder(RandomAccessIO.LITTLE_ENDIAN).setBuffered(true).create();
+ * or
+ * RandomAccessIO ra = (RandomAccessIO)new UIOStreamBuilder(f).setMode(UIOStreamBuilder.READ_WRITE).create();
+ *
+ * Defaul values are:
+ * mode - UIOStreamBuilder.READ_ONLY
+ * byte order - RandomAccessIO.BIG_ENDIAN
+ * buffered - false (however some streams are always buffered)
+ *
+ * </pre>
+ * @see #create
+ * @see #setBuffered
+ * @see #setByteOrder
+ * @see #setBufferSize
+ * @see #setCacheFile
+ * @see #setMaxBufferCount
+ * @see #setMode
+ * @see #setStart
+ * @see #setLength
+ *
+ * @author Andrey Kuznetsov
+ */
+public class UIOStreamBuilder {
+
+    public static final String READ_ONLY = "r";
+    public static final String READ_WRITE = "rw";
+
+    String mode = READ_ONLY;
+
+    int byteOrder = RandomAccessInput.BIG_ENDIAN;
+
+    Long start;
+    Long length;
+
+    private boolean buffered = true;
+
+    private Integer maxBufferCount;
+    private Integer bufferSize;
+
+    Creator creator;
+
+    File cache;
+
+    public static int DEFAULT_CHUNK_SIZE = 256 * 1024;
+    public static int DEFAULT_CHUNK_COUNT = 8;
+
+    public boolean isReadOnly() {
+        if (READ_WRITE.equals(mode)) {
+            return false;
+        }
+        return true;
+    }
+
+    public UIOStreamBuilder(String filename) {
+        this(new File(filename));
+    }
+
+    public UIOStreamBuilder(File file) {
+        this.creator = new FileCreator(file);
+    }
+
+    public UIOStreamBuilder(RandomAccessFile rafSource) {
+        this.creator = new RAFCreator(rafSource);
+    }
+
+    public UIOStreamBuilder(RandomAccessIO ra) {
+        this.creator = new RAIOCreator(ra);
+    }
+
+    public UIOStreamBuilder(RandomAccessInput ro) {
+        this.creator = new RAICreator(ro);
+    }
+
+    public UIOStreamBuilder(RandomAccessInput ro, Span [] spans) {
+        this.creator = new SpannedCreator(ro, spans);
+    }
+
+    public UIOStreamBuilder(byte[] byteSource) {
+        this.creator = new ByteCreator(byteSource);
+    }
+
+    public UIOStreamBuilder(byte[][] byteSource) {
+        if (byteSource.length == 1) {
+            this.creator = new ByteCreator(byteSource[0]);
+        } else {
+            this.creator = new Byte2DCreator(byteSource);
+        }
+    }
+
+    public UIOStreamBuilder(short[] shortSource) {
+        creator = new ShortCreator(shortSource);
+    }
+
+    public UIOStreamBuilder(short[][] shortSource) {
+        creator = new ShortCreator(shortSource);
+    }
+
+    public UIOStreamBuilder(char[] charSource) {
+        creator = new CharCreator(charSource);
+    }
+
+    public UIOStreamBuilder(char[][] charSource) {
+        creator = new CharCreator(charSource);
+    }
+
+    public UIOStreamBuilder(int[] intSource) {
+        creator = new IntCreator(intSource);
+    }
+
+    public UIOStreamBuilder(int[][] intSource) {
+        creator = new IntCreator(intSource);
+    }
+
+    public UIOStreamBuilder(long[] longSource) {
+        creator = new LongCreator(longSource);
+    }
+
+    public UIOStreamBuilder(long[][] longSource) {
+        creator = new LongCreator(longSource);
+    }
+
+    public UIOStreamBuilder(float[] floatSource) {
+        creator = new FloatCreator(floatSource);
+    }
+
+    public UIOStreamBuilder(float[][] floatSource) {
+        creator = new FloatCreator(floatSource);
+    }
+
+    public UIOStreamBuilder(double[] doubleSource) {
+        creator = new DoubleCreator(doubleSource);
+    }
+
+    public UIOStreamBuilder(double[][] doubleSource) {
+        creator = new DoubleCreator(doubleSource);
+    }
+
+    static private File getTmpDir() {
+        String name = System.getProperty("uio.temp.dir");
+        if (name != null && name.length() > 0) {
+            File f = new File(name);
+            if (!f.exists()) {
+                f.mkdirs();
+            }
+            if (f.isDirectory()) {
+                return f;
+            }
+        }
+        return null;
+    }
+
+    static File createTempFile(String prefix) {
+        File dir = getTmpDir();
+        if (dir != null) {
+            return new File(dir, prefix + Integer.toHexString(dir.hashCode()));
+        }
+        return null;
+    }
+
+    /**
+     * always buffered
+     * @param url
+     */
+    public UIOStreamBuilder(URL url) {
+        creator = new URLCreator(url);
+    }
+
+    /**
+     * always buffered
+     * @param in
+     */
+    public UIOStreamBuilder(InputStream in) {
+        creator = new ISCreator(in);
+    }
+
+    /**
+     * Always buffered.
+     * Two things are very important:
+     * 1. closing RandomAccessOutput created by this method does not close OutputStream
+     * 2. To write data to OutputStream RandomAccessOutput must be closed or flushed.
+     *
+     * @param out OutputStream
+     */
+    public UIOStreamBuilder(OutputStream out) {
+        creator = new OSCreator(out);
+    }
+
+    /**
+     * set mode (writeable or read only)
+     * @param mode READ_ONLY or READ_WRITE
+     * @return UIOStreamBuilder
+     */
+    public UIOStreamBuilder setMode(String mode) {
+        if (READ_ONLY.equals(mode) || READ_WRITE.equals(mode)) {
+            this.mode = mode;
+            return this;
+        } else {
+            throw new IllegalArgumentException(mode);
+        }
+    }
+
+    /**
+     * set byte order (big endian or little endian)
+     * @param byteOrder LITTLE_ENDIAN or BIG_ENDIAN (default value - BIG_ENDIAN)
+     * @return UIOStreamBuilder
+     */
+    public UIOStreamBuilder setByteOrder(int byteOrder) {
+        switch (byteOrder) {
+            case RandomAccessInput.LITTLE_ENDIAN:
+            case RandomAccessInput.BIG_ENDIAN:
+                this.byteOrder = byteOrder;
+                return this;
+            default:
+                throw new IllegalArgumentException("" + byteOrder);
+        }
+    }
+
+    /**
+     * set start offset
+     * @param start start offset of stream (default value - 0L)
+     * @return UIOStreamBuilder
+     */
+    public UIOStreamBuilder setStart(long start) {
+        if (start < 0) {
+            throw new IllegalArgumentException(" " + start);
+        }
+        this.start = new Long(start);
+        return this;
+    }
+
+    /**
+     * set stream length
+     * @param length stream length
+     * @return UIOStreamBuilder
+     */
+    public UIOStreamBuilder setLength(long length) {
+        if (length < 0) {
+            throw new IllegalArgumentException(" " + length);
+        }
+        this.length = new Long(length);
+        if (start == null) {
+            start = new Long(0L);
+        }
+        return this;
+    }
+
+    /**
+     * set if stream should be buffered or not (rather a hint because some streams are always buffered)
+     * @param buffered true or false (default value - false)
+     * @return UIOStreamBuilder
+     */
+    public UIOStreamBuilder setBuffered(boolean buffered) {
+        this.buffered = buffered;
+        return this;
+    }
+
+    /**
+     * set maxBufferCount for MemoryAccessManager - for unbuffered streams this parameter is ignored.
+     * @param max
+     * @return UIOStreamBuilder
+     */
+    public UIOStreamBuilder setMaxBufferCount(int max) {
+        this.maxBufferCount = new Integer(max);
+        return this;
+    }
+
+    /**
+     * set size for memory chunks used by MemoryAccessManager - for unbuffered streams this parameter is ignored.
+     * @param bufferSize
+     * @return UIOStreamBuilder
+     */
+    public UIOStreamBuilder setBufferSize(int bufferSize) {
+        this.bufferSize = new Integer(bufferSize);
+        return this;
+    }
+
+    /**
+     * Set file which can be used to cache data (only for Streams)
+     * @param f File
+     * @return UIOStreamBuilder
+     */
+    public UIOStreamBuilder setCacheFile(File f) {
+        this.cache = f;
+        return this;
+    }
+
+    /**
+     * finally create desired stream
+     * @return RandomAccessIinput
+     * @throws java.io.IOException
+     */
+    public RandomAccessInput create() throws IOException {
+        if (length != null && length.longValue() == 0) {
+            Sys.err.println("Warning: stream length is 0");
+        }
+        if (buffered) {
+            return creator.createBuffered();
+        } else {
+            return creator.create();
+        }
+    }
+
+    abstract class Creator {
+        abstract RandomAccessInput create() throws IOException;
+
+        abstract RandomAccessInput createBuffered() throws IOException;
+
+        protected int getByteOrder() {
+            return byteOrder != 0 ? byteOrder : RandomAccessInput.BIG_ENDIAN;
+        }
+
+        protected int getBufferCount() {
+            if (maxBufferCount != null) {
+                return maxBufferCount.intValue();
+            }
+            return DEFAULT_CHUNK_COUNT;
+        }
+
+        protected int getBufferSize() {
+            if (bufferSize != null) {
+                return bufferSize.intValue();
+            }
+            return DEFAULT_CHUNK_SIZE;
+        }
+    }
+
+    class FileCreator extends Creator {
+
+        File fileSource;
+
+        public FileCreator(File fileSource) {
+            this.fileSource = fileSource;
+        }
+
+        public RandomAccessInput create() throws IOException {
+            final RandomAccessFileX rafx = new RandomAccessFileX(fileSource, mode);
+            if (start == null && length == null) {
+                return new RandomAccessFileWrapper(rafx, getByteOrder());
+            } else if (length == null) {
+                return new RandomAccessFileWrapper(rafx, start.longValue(), getByteOrder());
+            } else {
+                return new RandomAccessFileWrapper(rafx, start.longValue(), length.longValue(), getByteOrder());
+            }
+        }
+
+        RandomAccessInput createBuffered() throws IOException {
+            Content bc = new RandomAccessFileContent(fileSource, mode);
+            IOController controller = new IOController(DEFAULT_CHUNK_SIZE, bc);
+            BufferedRandomAccessIO bio = new BufferedRandomAccessIO(controller);
+            bio.setByteOrder(byteOrder);
+            return bio;
+        }
+    }
+
+    class RAFCreator extends Creator {
+        RandomAccessFile rafSource;
+
+        public RAFCreator(RandomAccessFile rafSource) {
+            this.rafSource = rafSource;
+        }
+
+        public RandomAccessInput create() throws IOException {
+            if (start == null && length == null) {
+                return new RandomAccessFileWrapper(rafSource, getByteOrder());
+            } else if (length == null) {
+                return new RandomAccessFileWrapper(rafSource, start.longValue(), getByteOrder());
+            } else {
+                return new RandomAccessFileWrapper(rafSource, start.longValue(), length.longValue(), getByteOrder());
+            }
+        }
+
+        RandomAccessInput createBuffered() throws IOException {
+            Content bc = new RandomAccessFileContent(rafSource);
+            IOController controller = new IOController(DEFAULT_CHUNK_SIZE, bc);
+            return new BufferedRandomAccessIO(controller);
+        }
+    }
+
+    class RAICreator extends Creator {
+        RandomAccessInput roSource;
+
+        public RAICreator(RandomAccessInput roSource) {
+            this.roSource = roSource;
+        }
+
+        protected int getByteOrder() {
+            return byteOrder != 0 ? byteOrder : roSource.getByteOrder();
+        }
+
+        public RandomAccessInput create() throws IOException {
+            return roSource.createInputChild(start != null ? start.longValue() : 0L, 0, byteOrder, true);
+        }
+
+        RandomAccessInput createBuffered() throws IOException {
+            return create();
+        }
+    }
+
+    class RAIOCreator extends Creator {
+        RandomAccessIO raSource;
+
+        public RAIOCreator(RandomAccessIO raSource) {
+            this.raSource = raSource;
+        }
+
+        protected int getByteOrder() {
+            return byteOrder != 0 ? byteOrder : raSource.getByteOrder();
+        }
+
+        public RandomAccessInput create() throws IOException {
+            return raSource.createIOChild(start != null ? start.longValue() : 0L, 0, byteOrder, true);
+        }
+
+        RandomAccessInput createBuffered() throws IOException {
+            return create();
+        }
+    }
+
+    class Byte2DCreator extends Creator {
+
+        byte[][] byteSource;
+
+        public Byte2DCreator(byte[][] byteSource) {
+            this.byteSource = byteSource;
+        }
+
+        RandomAccessInput create() throws IOException {
+            return createBuffered();
+        }
+
+        RandomAccessInput createBuffered() throws IOException {
+            Content content = new ByteArrayContent(byteSource);
+            IOController controller = new IOController(DEFAULT_CHUNK_SIZE, content);
+            return new BufferedRandomAccessIO(controller, start != null ? start.longValue() : 0L);
+        }
+    }
+
+    class ByteCreator extends Creator {
+
+        byte[] byteSource;
+
+        public ByteCreator(byte[] byteSource) {
+            this.byteSource = byteSource;
+        }
+
+        RandomAccessInput create() throws IOException {
+            if (start != null) {
+                VariableSizeByteBuffer vsb;
+                vsb = new VariableSizeByteBuffer(byteSource);
+                return new ByteArrayRandomAccessIO(start.intValue(), length != null? length.intValue(): 0, vsb);
+            }
+            return new ByteArrayRandomAccessIO(byteSource);
+        }
+
+        RandomAccessInput createBuffered() throws IOException {
+            return create();
+        }
+    }
+
+    class ShortCreator extends Creator {
+        short[][] shortSource;
+
+        public ShortCreator(short[] shortSource) {
+            this(new short[][]{shortSource});
+        }
+
+        public ShortCreator(short[][] shortSource) {
+            this.shortSource = shortSource;
+        }
+
+        RandomAccessInput create() throws IOException {
+            Content content = new ShortArrayContent(shortSource);
+            IOController controller = new IOController(DEFAULT_CHUNK_SIZE, content);
+            return new BufferedRandomAccessIO(controller, start != null ? start.longValue() : 0L);
+        }
+
+        RandomAccessInput createBuffered() throws IOException {
+            return create();
+        }
+    }
+
+    class CharCreator extends Creator {
+        char[][] charSource;
+
+        public CharCreator(char[] charSource) {
+            this(new char[][]{charSource});
+        }
+
+        public CharCreator(char[][] charSource) {
+            this.charSource = charSource;
+        }
+
+        RandomAccessInput create() throws IOException {
+            Content content = new CharArrayContent(charSource);
+            IOController controller = new IOController(DEFAULT_CHUNK_SIZE, content);
+            return new BufferedRandomAccessIO(controller, start != null ? start.longValue() : 0L);
+        }
+
+        RandomAccessInput createBuffered() throws IOException {
+            return create();
+        }
+    }
+
+    class IntCreator extends Creator {
+        int[][] intSource;
+
+        public IntCreator(int[] intSource) {
+            this(new int[][]{intSource});
+        }
+
+        public IntCreator(int[][] intSource) {
+            this.intSource = intSource;
+        }
+
+        RandomAccessInput create() throws IOException {
+            Content content = new IntArrayContent(intSource);
+            IOController controller = new IOController(DEFAULT_CHUNK_SIZE, content);
+            return new BufferedRandomAccessIO(controller, start != null ? start.longValue() : 0L);
+        }
+
+        RandomAccessInput createBuffered() throws IOException {
+            return create();
+        }
+    }
+
+    class SpannedCreator extends Creator {
+        Span [] spans;
+        RandomAccessInput raiSource;
+
+        public SpannedCreator(RandomAccessInput raiSource, Span[] spans) {
+            this.raiSource = raiSource;
+            this.spans = spans;
+        }
+
+        RandomAccessInput create() throws IOException {
+            Content content;
+            if(mode == READ_ONLY) {
+                RandomAccessInput inputChild = raiSource.createInputChild(0, 0, getByteOrder(), false);
+                content = new SpannedRandomAccessInputContent(inputChild, spans);
+            }
+            else {
+                if(raiSource instanceof RandomAccessIO) {
+                    RandomAccessIO inputChild = ((RandomAccessIO)raiSource).createIOChild(0, 0, getByteOrder(), false);
+                    content = new SpannedRandomAccessIOContent(inputChild, spans);
+                }
+                else {
+                    RandomAccessInput inputChild = raiSource.createInputChild(0, 0, getByteOrder(), false);
+                    content = new SpannedRandomAccessInputContent(inputChild, spans);
+                }
+            }
+            IOController controller = new IOController(DEFAULT_CHUNK_SIZE, content);
+            return new BufferedRandomAccessIO(controller, 0L);
+        }
+
+        protected int getByteOrder() {
+            return byteOrder != 0 ? byteOrder : raiSource.getByteOrder();
+        }
+
+
+        RandomAccessInput createBuffered() throws IOException {
+            return create();
+        }
+    }
+
+    class LongCreator extends Creator {
+        long[][] longSource;
+
+        public LongCreator(long[] longSource) {
+            this(new long[][]{longSource});
+        }
+
+        public LongCreator(long[][] longSource) {
+            this.longSource = longSource;
+        }
+
+        RandomAccessInput create() throws IOException {
+            Content content = new LongArrayContent(longSource);
+            IOController controller = new IOController(DEFAULT_CHUNK_SIZE, content);
+            return new BufferedRandomAccessIO(controller, start != null ? start.longValue() : 0L);
+        }
+
+        RandomAccessInput createBuffered() throws IOException {
+            return create();
+        }
+    }
+
+    class FloatCreator extends Creator {
+        float[][] floatSource;
+
+        public FloatCreator(float[] floatSource) {
+            this(new float[][]{floatSource});
+        }
+
+        public FloatCreator(float[][] floatSource) {
+            this.floatSource = floatSource;
+        }
+
+        RandomAccessInput create() throws IOException {
+            Content content = new FloatArrayContent(floatSource);
+            IOController controller = new IOController(DEFAULT_CHUNK_SIZE, content);
+            return new BufferedRandomAccessIO(controller, start != null ? start.longValue() : 0L);
+        }
+
+        RandomAccessInput createBuffered() throws IOException {
+            return create();
+        }
+    }
+
+    class DoubleCreator extends Creator {
+        double[][] doubleSource;
+
+        public DoubleCreator(double[] doubleSource) {
+            this(new double[][]{doubleSource});
+        }
+
+        public DoubleCreator(double[][] doubleSource) {
+            this.doubleSource = doubleSource;
+        }
+
+        RandomAccessInput create() throws IOException {
+            Content content = new DoubleArrayContent(doubleSource);
+            IOController controller = new IOController(DEFAULT_CHUNK_SIZE, content);
+            return new BufferedRandomAccessIO(controller, start != null ? start.longValue() : 0L);
+        }
+
+        RandomAccessInput createBuffered() throws IOException {
+            return create();
+        }
+    }
+
+    class URLCreator extends Creator {
+        URL url;
+        FileCreator fileCreator;
+
+        public URLCreator(URL url) {
+            this.url = url;
+            final String protocol = url.getProtocol();
+            if ("file".equalsIgnoreCase(protocol)) {
+                File f = new File(url.getFile());
+                fileCreator = new FileCreator(f);
+            }
+        }
+
+        RandomAccessInput create() throws IOException {
+            if (fileCreator == null) {
+                return create0();
+            } else {
+                return fileCreator.create();
+            }
+        }
+
+        RandomAccessInput createBuffered() throws IOException {
+            if (fileCreator == null) {
+                return create0();
+            } else {
+                return fileCreator.createBuffered();
+            }
+        }
+
+        private RandomAccessInput create0() {
+            if (cache == null) {
+                cache = createTempFile("urc");
+            }
+            IOController controller = BIOFactory.createIOController(url, cache, getBufferSize());
+            BufferedRandomAccessIO rio = new BufferedRandomAccessIO(controller);
+            rio.setByteOrder(byteOrder);
+            return rio;
+        }
+    }
+
+    class ISCreator extends Creator {
+        InputStream inputStreamSource;
+
+        public ISCreator(InputStream inputStreamSource) {
+            this.inputStreamSource = inputStreamSource;
+        }
+
+        RandomAccessInput create() throws IOException {
+            if (inputStreamSource instanceof ByteArrayInputStream) {
+                return new BaisWrapper((ByteArrayInputStream) inputStreamSource);
+            } else {
+                if (cache == null) {
+                    cache = createTempFile("isc");
+                }
+                IOController controller = BIOFactory.createIOController(inputStreamSource, cache, getBufferSize());
+                BufferedRandomAccessIO bio = new BufferedRandomAccessIO(controller);
+                bio.setByteOrder(byteOrder);
+                return bio;
+            }
+        }
+
+        RandomAccessInput createBuffered() throws IOException {
+            return create();
+        }
+    }
+
+    class OSCreator extends Creator {
+        OutputStream outputStreamSource;
+
+        public OSCreator(OutputStream outputStreamSource) {
+            this.outputStreamSource = outputStreamSource;
+            setMode(READ_WRITE);
+        }
+
+        RandomAccessInput create() throws IOException {
+            final BufferedRandomAccessIO bio = BIOFactory.create(outputStreamSource);
+            bio.setByteOrder(byteOrder);
+            return bio;
+        }
+
+        RandomAccessInput createBuffered() throws IOException {
+            return create();
+        }
+    }
+}
Index: ocean/src/com/imagero/uio/BaisWrapper.java
===================================================================
--- ocean/src/com/imagero/uio/BaisWrapper.java	(revision 0)
+++ ocean/src/com/imagero/uio/BaisWrapper.java	(revision 0)
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+ package com.imagero.uio;
+
+import com.imagero.uio.impl.AbstractRandomAccessInput;
+import com.imagero.uio.io.IOutils;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+/**
+ * Wrapper for ByteArrayInputStream which gives possibility to use it as AbstractRandomAccessInput.
+ * Date: 19.12.2007
+ *
+ * @author Andrey Kuznetsov
+ */
+public class BaisWrapper extends AbstractRandomAccessInput {
+    long pos;
+    long mark;
+    long count;
+
+    long offset;
+
+    ByteArrayInputStream in;
+
+    public BaisWrapper(ByteArrayInputStream in) {
+        this.in = in;
+        in.mark(0);
+        count = in.available();
+    }
+
+    public BaisWrapper(BaisWrapper in, long offset, int byteOrder) {
+        this.in = in.in;
+        this.offset = offset;
+        count = in.count - (in.offset + (int)offset);
+        setByteOrder(byteOrder);
+    }
+
+    public BaisWrapper(BaisWrapper in, long offset, long length, int byteOrder) {
+        this.in = in.in;
+        this.offset = offset;
+        count = in.count - (in.offset + (int) offset);
+        if(length > 0) {
+            count = Math.min(count, length);
+        }
+        setByteOrder(byteOrder);
+    }
+
+    synchronized public int read() {
+        seek0(pos);
+        int k = in.read();
+        pos++;
+        return k;
+    }
+
+    synchronized public int read(byte b[], int off, int len) {
+        seek0(pos);
+        int read = in.read(b, off, len);
+        pos += read;
+        return read;
+    }
+
+    synchronized public long skip(long n) {
+        seek0(pos);
+        long length = in.skip(n);
+        pos += length;
+        return length;
+    }
+
+    synchronized public void seek(long position) {
+        seek0(position);
+    }
+
+    private void seek0(long position) {
+        in.reset();
+        pos = 0;
+        if(position + offset > 0) {
+            skip(position + offset);
+        }
+    }
+
+    public boolean markSupported() {
+        return true;
+    }
+
+    public synchronized void mark(int readlimit) {
+        mark = pos;
+    }
+
+    public synchronized void reset() {
+        seek0(mark);
+    }
+
+    public long getFilePointer() {
+        return pos;
+    }
+
+    public long length() {
+        return count - offset;
+    }
+
+    public RandomAccessInput createInputChild(long offset, long length, int byteOrder, boolean syncPointer) {
+        return new BaisWrapper(this, offset, length, byteOrder);
+    }
+
+    public void close() {
+        IOutils.closeStream(in);
+    }
+
+    public InputStream createInputStream(long offset) {
+        return new BaisWrapper(this, offset, byteOrder);
+    }
+
+    public long getChildPosition(InputStream child) {
+        if(child instanceof BaisWrapper) {
+            BaisWrapper wrapper = (BaisWrapper) child;
+            return wrapper.getFilePointer();
+        }
+        return -1;
+    }
+
+    public void setChildPosition(InputStream child, long position) {
+        if (child instanceof BaisWrapper) {
+            BaisWrapper wrapper = (BaisWrapper) child;
+            wrapper.seek(position);
+        }
+    }
+}
Index: ocean/src/com/imagero/uio/RandomAccessInput.java
===================================================================
--- ocean/src/com/imagero/uio/RandomAccessInput.java	(revision 0)
+++ ocean/src/com/imagero/uio/RandomAccessInput.java	(revision 0)
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.DataInput;
+
+/**
+ * @author Andrey Kuznetsov
+ */
+public interface RandomAccessInput extends Input, DataInput, Endian, Seekable {
+
+    /**
+     * Create RandomAccessInput child from given offset
+     * @param offset offset in parent stream
+     * @param byteOrder byte order for new stream
+     * @param syncPointer if true then streams will share same stream position
+     * @return RandomAccessInput
+     * @throws IOException
+     */
+    RandomAccessInput createInputChild(long offset, long length, int byteOrder, boolean syncPointer) throws IOException;
+
+    /**
+     * Create InputStream starting from given offset.
+     * @param offset offset in parent (for created InputStream) stream
+     * @return InputStream
+     */
+    InputStream createInputStream(long offset);
+
+    /**
+     * get stream position of child InputStream (created with createInputStream());
+     * @param child child InputStream
+     * @return Stream position or -1 if supplied InputStream is not a child of this stream
+     */
+    long getChildPosition(InputStream child);
+
+    /**
+     * set stream position of child InputStream.
+     * @param child child InputStream
+     * @param position new stream position
+     */ 
+    void setChildPosition(InputStream child, long position);
+
+    /**
+     * Same as readLine, but returns byte array instead of String
+     * @return byte array
+     * @throws IOException
+     */
+    byte [] readByteLine() throws IOException;
+
+    /**
+     * Same as readByteLine(), but read in given byte array and returns how much was read.
+     * @param dest byte array
+     * @return how much bytes read
+     * @throws IOException
+     */
+    int readByteLine(byte [] dest) throws IOException;
+
+    /**
+     * Same as readShort() from DataInput, but uses given byte order instead of streams byte order
+     * @param byteOrder byte order
+     * @return short
+     * @throws IOException
+     */
+    short readShort(int byteOrder) throws IOException;
+
+    /**
+     * Same as readUnsignedShort() from DataInput, but uses given byte order instead of streams byte order
+     * @param byteOrder byte order
+     * @return int
+     * @throws IOException
+     */
+    int readUnsignedShort(int byteOrder) throws IOException;
+
+    /**
+     * Same as readChar() from DataInput, but uses given byte order instead of streams byte order
+     * @param byteOrder byte order
+     * @return char
+     * @throws IOException
+     */
+    char readChar(int byteOrder) throws IOException;
+
+    /**
+     * Same as readInt() from DataInput, but uses given byte order instead of streams byte order
+     * @param byteOrder byte order
+     * @return int
+     * @throws IOException
+     */
+    int readInt(int byteOrder) throws IOException;
+
+    /**
+     * Same as readLong() from DataInput, but uses given byte order instead of streams byte order
+     * @param byteOrder byte order
+     * @return long
+     * @throws IOException
+     */
+    long readLong(int byteOrder) throws IOException;
+
+    /**
+     * Same as readFloat() from DataInput, but uses given byte order instead of streams byte order
+     * @param byteOrder byte order
+     * @return float
+     * @throws IOException
+     */
+    float readFloat(int byteOrder) throws IOException;
+
+    /**
+     * Same as readDouble() from DataInput, but uses given byte order instead of streams byte order
+     * @param byteOrder byte order
+     * @return double
+     * @throws IOException
+     */
+    double readDouble(int byteOrder) throws IOException;
+
+    /**
+     * Reads four input bytes and returns an long value in the range 0 through 0xFFFFFFFF.
+     * @return long
+     * @throws IOException
+     */
+    long readUnsignedInt() throws IOException;
+
+    /**
+     * Same as readUnsignedInt(), but uses given byte order instead of streams byte order
+     * @param byteOrder byte order
+     * @return long
+     * @throws IOException
+     */
+    long readUnsignedInt(int byteOrder) throws IOException;
+
+    boolean isBuffered();
+}
Index: ocean/src/com/imagero/uio/RandomAccessOutput.java
===================================================================
--- ocean/src/com/imagero/uio/RandomAccessOutput.java	(revision 0)
+++ ocean/src/com/imagero/uio/RandomAccessOutput.java	(revision 0)
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.DataOutput;
+
+
+/**
+ * @author Andrey Kuznetsov
+ */
+public interface RandomAccessOutput extends DataOutput, Endian, Seekable {
+
+    RandomAccessOutput createOutputChild(long offset, int byteOrder, boolean syncPointer) throws IOException;
+    OutputStream createOutputStream(long offset);
+    void flush() throws IOException;
+
+    void writeShort(int v, int byteOrder) throws IOException;
+
+    void writeChar(int v, int byteOrder) throws IOException;
+
+    void writeInt(int v, int byteOrder) throws IOException;
+
+    void writeLong(long v, int byteOrder) throws IOException;
+
+    void writeFloat(float v, int byteOrder) throws IOException;
+
+    void writeDouble(double v, int byteOrder) throws IOException;
+
+    /**
+     * Set length of stream.
+     * @param newLength new stream length
+     * @throws IOException
+     */
+    void setLength(long newLength) throws IOException;
+}
Index: ocean/src/com/imagero/uio/WriteUtil.java
===================================================================
--- ocean/src/com/imagero/uio/WriteUtil.java	(revision 0)
+++ ocean/src/com/imagero/uio/WriteUtil.java	(revision 0)
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+ package com.imagero.uio;
+
+import com.imagero.uio.xform.XtoByteBE;
+import com.imagero.uio.xform.XtoByteLE;
+
+import java.io.IOException;
+
+/**
+ * Methods to write data from primitive arrays.
+ *
+ * Date: 01.03.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class WriteUtil {
+    public static void write(RandomAccessOutput io, short[] data) throws IOException {
+        write(io, data, 0, data.length, io.getByteOrder());
+    }
+
+    public static void write(RandomAccessOutput io, short[] data, int byteOrder) throws IOException {
+        write(io, data, 0, data.length, byteOrder);
+    }
+
+    public static void write(RandomAccessOutput io, short[] data, int offset, int length) throws IOException {
+        write(io, data, offset, length, io.getByteOrder());
+    }
+
+    public static void write(RandomAccessOutput io, char[] data) throws IOException {
+        write(io, data, 0, data.length, io.getByteOrder());
+    }
+
+    public static void write(RandomAccessOutput io, char[] data, int byteOrder) throws IOException {
+        write(io, data, 0, data.length, byteOrder);
+    }
+
+    public static void write(RandomAccessOutput io, char[] data, int offset, int length) throws IOException {
+        write(io, data, offset, length, io.getByteOrder());
+    }
+
+    public static void write(RandomAccessOutput io, int[] data) throws IOException {
+        write(io, data, 0, data.length, io.getByteOrder());
+    }
+
+    public static void write(RandomAccessOutput io, int[] data, int byteOrder) throws IOException {
+        write(io, data, 0, data.length, byteOrder);
+    }
+
+    public static void write(RandomAccessOutput io, int[] data, int offset, int length) throws IOException {
+        write(io, data, offset, length, io.getByteOrder());
+    }
+
+    public static void write(RandomAccessOutput io, float[] data) throws IOException {
+        write(io, data, 0, data.length, io.getByteOrder());
+    }
+
+    public static void write(RandomAccessOutput io, float[] data, int byteOrder) throws IOException {
+        write(io, data, 0, data.length, byteOrder);
+    }
+
+    public static void write(RandomAccessOutput io, float[] data, int offset, int length) throws IOException {
+        write(io, data, offset, length, io.getByteOrder());
+    }
+
+    public static void write(RandomAccessOutput io, long[] data) throws IOException {
+        write(io, data, 0, data.length, io.getByteOrder());
+    }
+
+    public static void write(RandomAccessOutput io, long[] data, int byteOrder) throws IOException {
+        write(io, data, 0, data.length, byteOrder);
+    }
+
+    public static void write(RandomAccessOutput io, long[] data, int offset, int length) throws IOException {
+        write(io, data, offset, length, io.getByteOrder());
+    }
+
+    public static void write(RandomAccessOutput io, double[] data) throws IOException {
+        write(io, data, 0, data.length, io.getByteOrder());
+    }
+
+    public static void write(RandomAccessOutput io, double[] data, int byteOrder) throws IOException {
+        write(io, data, 0, data.length, byteOrder);
+    }
+
+    public static void write(RandomAccessOutput io, double[] data, int offset, int length) throws IOException {
+        write(io, data, offset, length, io.getByteOrder());
+    }
+
+    public static void write(RandomAccessOutput io, short[] sh, int offset, int length, int byteOrder) throws IOException {
+        byte[] b = new byte[length << 1];
+        if(byteOrder == RandomAccessInput.BIG_ENDIAN) {
+            XtoByteBE.shortToByteBE(sh, offset, length, b, 0);
+        }
+        else {
+            XtoByteLE.shortToByteLE(sh, offset, length, b, 0);
+        }
+        io.write(b);
+    }
+
+    public static void write(RandomAccessOutput io, char[] sh, int offset, int length, int byteOrder) throws IOException {
+        byte[] b = transform(sh, offset, length, byteOrder);
+        io.write(b);
+    }
+
+    protected static byte[] transform(char[] sh, int offset, int length, int byteOrder) {
+        byte[] b = new byte[length << 1];
+        if(byteOrder == RandomAccessInput.BIG_ENDIAN) {
+            XtoByteBE.charToByte(sh, offset, length, b, 0);
+        }
+        else {
+            XtoByteLE.charToByte(sh, offset, length, b, 0);
+        }
+        return b;
+    }
+
+    public static void write(RandomAccessOutput io, int[] source, int offset, int length, int byteOrder) throws IOException {
+        byte[] b = transform(source, offset, length, byteOrder);
+        io.write(b);
+    }
+
+    protected static byte[] transform(int[] source, int offset, int length, int byteOrder) {
+        byte[] b = new byte[length << 2];
+        if(byteOrder == RandomAccessInput.BIG_ENDIAN) {
+            XtoByteBE.intToByte(source, offset, length, b, 0);
+        }
+        else {
+            XtoByteLE.intToByte(source, offset, length, b, 0);
+        }
+        return b;
+    }
+
+    public static void write(RandomAccessOutput io, float[] source, int offset, int length, int byteOrder) throws IOException {
+        byte[] b = transform(source, offset, length, byteOrder);
+        io.write(b);
+    }
+
+    protected static byte[] transform(float[] source, int offset, int length, int byteOrder) {
+        byte[] b = new byte[length << 2];
+        if(byteOrder == RandomAccessInput.BIG_ENDIAN) {
+            XtoByteBE.floatToByteBE(source, offset, length, b, 0);
+        }
+        else {
+            XtoByteLE.floatToByteLE(source, offset, length, b, 0);
+        }
+        return b;
+    }
+
+    public static void write(RandomAccessOutput io, long[] source, int offset, int length, int byteOrder) throws IOException {
+        byte[] b = transform(source, offset, length, byteOrder);
+        io.write(b);
+    }
+
+    protected static byte[] transform(long[] source, int offset, int length, int byteOrder) {
+        byte[] b = new byte[length << 3];
+        if(byteOrder == RandomAccessInput.BIG_ENDIAN) {
+            XtoByteBE.longToByteBE(source, offset, length, b, 0);
+        }
+        else {
+            XtoByteLE.longToByteLE(source, offset, length, b, 0);
+        }
+        return b;
+    }
+
+    public static void write(RandomAccessOutput io, double[] source, int offset, int length, int byteOrder) throws IOException {
+        byte[] b = transform(source, offset, length, byteOrder);
+        io.write(b);
+    }
+
+    protected static byte[] transform(double[] source, int offset, int length, int byteOrder) {
+        byte[] b = new byte[length << 3];
+        if(byteOrder == RandomAccessInput.BIG_ENDIAN) {
+            XtoByteBE.doubleToByteBE(source, offset, length, b, 0);
+        }
+        else {
+            XtoByteLE.doubleToByteLE(source, offset, length, b, 0);
+        }
+        return b;
+    }
+}
Index: ocean/src/com/imagero/uio/ByteArrayIO.java
===================================================================
--- ocean/src/com/imagero/uio/ByteArrayIO.java	(revision 0)
+++ ocean/src/com/imagero/uio/ByteArrayIO.java	(revision 0)
@@ -0,0 +1,187 @@
+package com.imagero.uio;
+
+
+/**
+ * ByteArrayIO - this class gives the possibility to read from and write to given ByteArray.
+ * It supports bit offsets and bitwise writing.
+ * It does not extends InputStream or OutputStream, but has similar methods.
+ *
+ * Date: 09.04.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class ByteArrayIO {
+
+    public static int read(ByteArray ba) {
+        return read(ba, 8);
+    }
+
+    /**
+     * read up to 8 bits from given ByteArray
+     * @param ba ByteArray
+     * @param nbits bit count to read
+     * @return int
+     */
+    public static int read(ByteArray ba, final int nbits) {
+        int ret = 0;
+        //nothing to read
+        if (nbits == 0) {
+            return 0;
+        }
+        //too many bits requested
+        if (nbits > 8) {
+            throw new IllegalArgumentException("no more then 8 bits can be read at once");
+        }
+        if(ba.bitOffset == 0 && nbits == 8) {
+            return ba.buffer[ba.position++];
+        }
+        ret = ba.buffer[ba.position] & ByteArray.N_MASK[ba.bitOffset];
+        int rshift = (8 - ba.bitOffset) - nbits;
+        if(rshift > 0) {
+            ret = ret >> rshift;
+        }
+        int bitOffset = ba.bitOffset + nbits;
+        if (bitOffset > 7) {
+            bitOffset -= 8;
+            ba.bitOffset = bitOffset;
+            ba.position++;
+            if (bitOffset > 0) {
+                ret = (ret << bitOffset) | (ba.buffer[ba.position] & ByteArray.N_MASK[ba.bitOffset]);
+            }
+        }
+        return ret;
+    }
+
+    public static int read(ByteArray ba, byte b[]) {
+        return read(ba, b, 0, b.length);
+    }
+
+    /**
+     * Reads data from input stream into an byte array.
+     *
+     * @param b the buffer into which the data is read.
+     * @param off the start offset of the data.
+     * @param len the maximum number of bytes read.
+     * @return the total number of bytes read into the buffer, or -1 if the EOF has been reached.
+     * @exception NullPointerException if supplied byte array is null
+     */
+    public static int read(ByteArray ba, byte b[], int off, int len) {
+        if (len <= 0) {
+            return 0;
+        }
+        int c = read(ba);
+        if (c == -1) {
+            return -1;
+        }
+        b[off] = (byte) c;
+
+        int i = 1;
+        for (; i < len; ++i) {
+            c = read(ba);
+            if (c == -1) {
+                break;
+            }
+            b[off + i] = (byte) c;
+        }
+        return i;
+    }
+
+    /**
+     * Skips some bytes from the input stream.
+     * @param n the number of bytes to be skipped.
+     * @return the actual number of bytes skipped.
+     */
+    public static long skipBytes(ByteArray ba, long n) {
+        int max = (int) Math.min(ba.buffer.length - ba.position, n);
+        ba.position += max;
+        return max;
+    }
+
+    /**
+     * skip some bits
+     * @param n bits to skip
+     * @return number of bits skipped
+     */
+    public static int skipBits(ByteArray ba, int n) {
+        int k = n;
+        int nbits = k % 8;
+        int nbytes = k / 8;
+        int bitOffset = ba.bitOffset + nbits;
+        if (bitOffset > 7) {
+            nbytes++;
+            ba.bitOffset = bitOffset - 8;
+        }
+        ba.position += nbytes;
+        return n;
+    }
+
+    public static void seek(ByteArray ba, int pos) {
+        ba.position = pos;
+    }
+
+    public static void seek(ByteArray ba, int pos, int bitPos) {
+        ba.position = pos + bitPos / 8;
+        ba.bitOffset = bitPos % 8;
+    }
+
+    public static int skipToByteBoundary(ByteArray ba) {
+        if (ba.bitOffset > 0) {
+            int ret = 8 - ba.bitOffset;
+            ba.bitOffset = 0;
+            ba.position++;
+            return ret;
+        }
+        return 0;
+    }
+
+    /**
+     * Writes some bits from the specified int to stream.
+     * @param b int which should be written
+     */
+    public static void write(ByteArray ba, int b) {
+        write(ba, b, 8);
+    }
+
+    /**
+     * Writes up to 8 bits from the specified int to stream.
+     * @param N int which should be written
+     * @param N_BITS bit count to write
+     */
+    public static void write(ByteArray ba, int N, int N_BITS) {
+        if (N_BITS == 0) {
+            return;
+        }
+        if (N_BITS > 8) {
+            throw new IllegalArgumentException("no more then 8 bits can be written at once");
+        }
+        if(ba.bitOffset == 0 && N_BITS == 8) {
+            ba.buffer[ba.position++] = (byte) N;
+        }
+        else {
+            write0(ba, N, N_BITS);
+        }
+    }
+
+    protected final static void write0(ByteArray ba, int N, int N_BITS) {
+        int available = 8 - ba.bitOffset;
+        int a = ba.buffer[ba.position] & 0xFF;
+        int b = a;
+
+        a = ((a >> available) << N_BITS) | (N & ByteArray.K_MASK[N_BITS]);
+        if (N_BITS > available) {
+            a = a >> (N_BITS - available);
+            ba.buffer[ba.position++] = (byte) a;
+            ba.bitOffset = 0;
+            N_BITS -= available;
+            write0(ba, N & ByteArray.N_MASK[N_BITS], N_BITS);
+        } else if (available > N_BITS) {
+            a = a << (available - N_BITS);
+            a |= b & ByteArray.K_MASK[available - N_BITS];
+            ba.buffer[ba.position] = (byte) a;
+            ba.bitOffset += N_BITS;
+        } else {
+            ba.buffer[ba.position++] = (byte) a;
+            ba.bitOffset = 0;
+        }
+    }
+}
Index: ocean/src/com/imagero/uio/RandomAccessIO.java
===================================================================
--- ocean/src/com/imagero/uio/RandomAccessIO.java	(revision 0)
+++ ocean/src/com/imagero/uio/RandomAccessIO.java	(revision 0)
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio;
+
+import java.io.IOException;
+
+
+/**
+ * @author Andrey Kuznetsov
+ */
+public interface RandomAccessIO extends RandomAccessInput, RandomAccessOutput {
+    RandomAccessIO createIOChild(long offset, long length, int byteOrder, boolean syncPointer) throws IOException;
+}
Index: ocean/src/com/imagero/uio/Endian.java
===================================================================
--- ocean/src/com/imagero/uio/Endian.java	(revision 0)
+++ ocean/src/com/imagero/uio/Endian.java	(revision 0)
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+ package com.imagero.uio;
+
+/**
+ * Date: 01.03.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public interface Endian {
+    int BIG_ENDIAN = 0x4D4D;
+    int LITTLE_ENDIAN = 0x4949;
+
+    int getByteOrder();
+
+    void setByteOrder(int byteOrder);
+}
Index: ocean/src/com/imagero/uio/BaisWrapper.java
===================================================================
--- ocean/src/com/imagero/uio/BaisWrapper.java	(revision 0)
+++ ocean/src/com/imagero/uio/BaisWrapper.java	(revision 0)
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+ package com.imagero.uio;
+
+import com.imagero.uio.impl.AbstractRandomAccessInput;
+import com.imagero.uio.io.IOutils;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+/**
+ * Wrapper for ByteArrayInputStream which gives possibility to use it as AbstractRandomAccessInput.
+ * Date: 19.12.2007
+ *
+ * @author Andrey Kuznetsov
+ */
+public class BaisWrapper extends AbstractRandomAccessInput {
+    long pos;
+    long mark;
+    long count;
+
+    long offset;
+
+    ByteArrayInputStream in;
+
+    public BaisWrapper(ByteArrayInputStream in) {
+        this.in = in;
+        in.mark(0);
+        count = in.available();
+    }
+
+    public BaisWrapper(BaisWrapper in, long offset, int byteOrder) {
+        this.in = in.in;
+        this.offset = offset;
+        count = in.count - (in.offset + (int)offset);
+        setByteOrder(byteOrder);
+    }
+
+    public BaisWrapper(BaisWrapper in, long offset, long length, int byteOrder) {
+        this.in = in.in;
+        this.offset = offset;
+        count = in.count - (in.offset + (int) offset);
+        if(length > 0) {
+            count = Math.min(count, length);
+        }
+        setByteOrder(byteOrder);
+    }
+
+    synchronized public int read() {
+        seek0(pos);
+        int k = in.read();
+        pos++;
+        return k;
+    }
+
+    synchronized public int read(byte b[], int off, int len) {
+        seek0(pos);
+        int read = in.read(b, off, len);
+        pos += read;
+        return read;
+    }
+
+    synchronized public long skip(long n) {
+        seek0(pos);
+        long length = in.skip(n);
+        pos += length;
+        return length;
+    }
+
+    synchronized public void seek(long position) {
+        seek0(position);
+    }
+
+    private void seek0(long position) {
+        in.reset();
+        pos = 0;
+        if(position + offset > 0) {
+            skip(position + offset);
+        }
+    }
+
+    public boolean markSupported() {
+        return true;
+    }
+
+    public synchronized void mark(int readlimit) {
+        mark = pos;
+    }
+
+    public synchronized void reset() {
+        seek0(mark);
+    }
+
+    public long getFilePointer() {
+        return pos;
+    }
+
+    public long length() {
+        return count - offset;
+    }
+
+    public RandomAccessInput createInputChild(long offset, long length, int byteOrder, boolean syncPointer) {
+        return new BaisWrapper(this, offset, length, byteOrder);
+    }
+
+    public void close() {
+        IOutils.closeStream(in);
+    }
+
+    public InputStream createInputStream(long offset) {
+        return new BaisWrapper(this, offset, byteOrder);
+    }
+
+    public long getChildPosition(InputStream child) {
+        if(child instanceof BaisWrapper) {
+            BaisWrapper wrapper = (BaisWrapper) child;
+            return wrapper.getFilePointer();
+        }
+        return -1;
+    }
+
+    public void setChildPosition(InputStream child, long position) {
+        if (child instanceof BaisWrapper) {
+            BaisWrapper wrapper = (BaisWrapper) child;
+            wrapper.seek(position);
+        }
+    }
+}
Index: ocean/src/com/imagero/uio/bio/Ring.java
===================================================================
--- ocean/src/com/imagero/uio/bio/Ring.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/Ring.java	(revision 0)
@@ -0,0 +1,32 @@
+package com.imagero.uio.bio;
+
+/**
+ * Ring - minimalistic Ring implementation.
+ *
+ * Date: 29.08.2007
+ * @author Andrey Kuznetsov
+ */
+class Ring {
+
+    Object[] elements;
+
+    int size;
+    int index;
+
+    public Ring(int size) {
+        this.size = size;
+        this.elements = new Object[size];
+    }
+
+    /**
+     * add Object to ring
+     * @param o Object
+     * @return Object removed from ring (replaced by new Object) or null
+     */
+    public Object add(Object o) {
+        Object tmp = elements[index];
+        elements[index] = o;
+        index = (index + 1) % size;
+        return tmp;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/BIOFactory.java
===================================================================
--- ocean/src/com/imagero/uio/bio/BIOFactory.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/BIOFactory.java	(revision 0)
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio;
+
+import com.imagero.uio.UIOStreamBuilder;
+import com.imagero.uio.bio.content.Content;
+import com.imagero.uio.bio.content.DummyContent;
+import com.imagero.uio.bio.content.FileCachedInputStreamContent;
+import com.imagero.uio.bio.content.MemoryCachedInputStreamContent;
+import com.imagero.uio.bio.content.FileCachedHTTPContent;
+import com.imagero.uio.bio.content.HTTPContent;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.InputStream;
+import java.io.File;
+import java.net.URL;
+
+/**
+ * This class could be removed in the future.
+ * Use UIOStreamBuilder to create uio streams.
+ * @author Andrey Kuznetsov
+ */
+public class BIOFactory {
+
+    public static BufferedRandomAccessIO create(int chunkSize) {
+        IOController controller = createIOController(chunkSize);
+        BufferedRandomAccessIO out = new BufferedRandomAccessIO(controller);
+        return out;
+    }
+
+    public static BufferedRandomAccessIO create(OutputStream out) {
+        return create(out, UIOStreamBuilder.DEFAULT_CHUNK_SIZE);
+    }
+
+    public static BufferedRandomAccessIO create(final OutputStream out, int chunkSize) {
+        IOController ctrl = createIOController(chunkSize);
+        BufferedRandomAccessIO bio = new BufferedRandomAccessIO(ctrl) {
+            public void close() throws IOException {
+                controller.writeTo(out);
+                super.close();
+            }
+        };
+        return bio;
+    }
+
+    public static BufferedRandomAccessIO create(DataOutput out) {
+        return create(out, UIOStreamBuilder.DEFAULT_CHUNK_SIZE);
+    }
+
+    public static BufferedRandomAccessIO create(final DataOutput out, int chunkSize) {
+        IOController ctrl = createIOController(chunkSize);
+        BufferedRandomAccessIO bio = new BufferedRandomAccessIO(ctrl) {
+            public void close() throws IOException {
+                controller.writeTo(out);
+                super.close();
+            }
+        };
+        return bio;
+    }
+
+    public static IOController createIOController(int chunkSize) {
+        Content bc = new DummyContent();
+        IOController sb = new IOController(chunkSize, bc);
+        return sb;
+    }
+
+    public static IOController createIOController(InputStream in, File tmp, int chunkSize) {
+        Content bc;
+        if (tmp != null) {
+            try {
+                bc = new FileCachedInputStreamContent(in, tmp);
+            } catch (IOException ex) {
+                System.err.println("Unable to use file cache, switching to memory cache.");
+                ex.printStackTrace();
+                bc = new MemoryCachedInputStreamContent(in, chunkSize);
+            }
+        } else {
+            bc = new MemoryCachedInputStreamContent(in, chunkSize);
+        }
+        IOController sb = new IOController(chunkSize, bc);
+        return sb;
+    }
+
+    public static IOController createIOController(URL url) {
+        return createIOController(url, UIOStreamBuilder.DEFAULT_CHUNK_SIZE);
+    }
+
+    public static IOController createIOController(URL url, int chunkSize) {
+        return createIOController(url, null, chunkSize);
+    }
+
+    public static IOController createIOController(URL url, File tmp) {
+        return createIOController(url, tmp, UIOStreamBuilder.DEFAULT_CHUNK_SIZE);
+    }
+
+    public static IOController createIOController(URL url, File tmp, int chunkSize) {
+        Content bc;
+        if (tmp != null) {
+            try {
+                bc = new FileCachedHTTPContent(url, tmp);
+            } catch (IOException ex) {
+                ex.printStackTrace();
+                System.err.println("Unable to use file cache, switching to memory cache.");
+                bc = new HTTPContent(url);
+            }
+        } else {
+            bc = new HTTPContent(url);
+        }
+        IOController sb = new IOController(chunkSize, bc);
+        return sb;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/BufferIndex.java
===================================================================
--- ocean/src/com/imagero/uio/bio/BufferIndex.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/BufferIndex.java	(revision 0)
@@ -0,0 +1,35 @@
+package com.imagero.uio.bio;
+
+/**
+ * Index of Object in 2D array
+ * Date: 14.12.2007
+ *
+ * @author Andrey Kuznetsov
+ */
+class BufferIndex {
+    /**
+     * index of array in 2D array
+     */
+    int arrayIndex;
+    /**
+     * index of object in 1D array
+     */
+    int index;
+
+    /**
+     * @param arrayIndex index of array
+     * @param index index of object
+     */
+    public BufferIndex(int arrayIndex, int index) {
+        this.arrayIndex = arrayIndex;
+        this.index = index;
+    }
+
+    public boolean equals(Object obj) {
+        if(obj != null && obj instanceof BufferIndex) {
+            BufferIndex bi = (BufferIndex) obj;
+            return bi.arrayIndex == arrayIndex && bi.index == index;
+        }
+        return false;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/FSBInputStream.java
===================================================================
--- ocean/src/com/imagero/uio/bio/FSBInputStream.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/FSBInputStream.java	(revision 0)
@@ -0,0 +1,68 @@
+package com.imagero.uio.bio;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * Date: 01.03.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+class FSBInputStream extends InputStream {
+    FixedSizeByteBuffer buffer;
+    BufferPosition position;
+    int mark;
+    long offset;
+
+    public FSBInputStream(FixedSizeByteBuffer buffer) {
+        this.buffer = buffer;
+        position = new BufferPosition(buffer.buf.length);
+    }
+
+    public FSBInputStream(int offset, FixedSizeByteBuffer buffer) {
+        this.buffer = buffer;
+        position = new BufferPosition(Integer.MAX_VALUE);
+        position.pos = offset;
+        this.offset = offset;
+    }
+
+    public int read() {
+        return buffer.read(position);
+    }
+
+    public int read(byte b[]) throws IOException {
+        return buffer.read(b, 0, b.length, position);
+    }
+
+    public int read(byte b[], int off, int len) {
+        return buffer.read(b, off, len, position);
+    }
+
+    public long skip(long n) {
+        return buffer.skip(n, position);
+    }
+
+    public int available() {
+        return buffer.availableForReading(position);
+    }
+
+    public synchronized void mark(int readlimit) {
+        this.mark = position.pos;
+    }
+
+    public synchronized void reset() throws IOException {
+        position.pos = mark;
+    }
+
+    public boolean markSupported() {
+        return true;
+    }
+
+    public long getPosition() {
+        return position.pos - offset;
+    }
+
+    public void setPosition(long pos) {
+        position.pos = (int) (pos + offset);
+    }
+}
Index: ocean/src/com/imagero/uio/bio/IOCInputStream.java
===================================================================
--- ocean/src/com/imagero/uio/bio/IOCInputStream.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/IOCInputStream.java	(revision 0)
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Independent InputStream with shared IOController.
+ *
+ * @author Andrey Kuznetsov
+ */
+public class IOCInputStream extends InputStream {
+
+    FixedSizeByteBuffer buffer;
+    BufferIndex bufferIndex;
+    BufferPosition bufferPosition;
+    IOController controller;
+
+    StreamPosition streamPosition = new StreamPosition();
+    long offset;
+
+    long mark;
+
+    public IOCInputStream(IOController controller) {
+        this.controller = controller;
+    }
+
+    public IOCInputStream(IOController controller, long offset) {
+        this.controller = controller;
+        this.offset = offset;
+        bufferPosition = new BufferPosition(controller.bufferSize);
+        seek(0);
+    }
+
+    public void seek(long offset) {
+        streamPosition.pos = offset + this.offset;
+        bufferPosition.pos = (int) ((streamPosition.pos) % controller.bufferSize);
+ }
+
+    private void prepareBufferForReading() {
+        BufferIndex index = controller.getBufferIndex(streamPosition.pos);
+
+        if (!index.equals(bufferIndex) || buffer == null || buffer.buf == null) {
+            bufferIndex = index;
+            try {
+                buffer = controller.getBuffer(streamPosition.pos, true);
+            } catch (IOException ex) {
+                //ignore
+            }
+        }
+        if (buffer != null) {
+            bufferPosition.pos = (int) ((streamPosition.pos) % controller.bufferSize);
+        }
+    }
+
+    public int read() throws IOException {
+        checkBuffer();
+
+        if (buffer != null) {
+            streamPosition.pos++;
+            return buffer.read(bufferPosition);
+        }
+        return -1;
+    }
+
+    public int available() throws IOException {
+        if(buffer != null) {
+            return buffer.availableForReading(bufferPosition);
+        }
+        return 0;
+    }
+
+    private void checkBuffer() {
+        if (buffer == null || bufferPosition.available() <= 0) {
+            prepareBufferForReading();
+        }
+    }
+
+    public long skip(long n) throws IOException {
+        checkBuffer();
+        if (buffer == null) {
+            return 0;
+        }
+        long skp = buffer.skip(n, bufferPosition);
+        streamPosition.pos += skp;
+        return skp;
+    }
+
+    public int read(byte b[]) throws IOException {
+        return read(b, 0, b.length);
+    }
+
+    public int read(byte[] b, int offset, int length) throws IOException {
+        checkBuffer();
+        if (buffer == null) {
+            return -1;
+        }
+        int rc = buffer.read(b, offset, length, bufferPosition);
+        if (rc > 0) {
+            streamPosition.pos += rc;
+        }
+        return rc;
+    }
+
+    public boolean markSupported() {
+        return true;
+    }
+
+    public synchronized void mark(int readlimit) {
+        this.mark = streamPosition.pos;
+    }
+
+    public synchronized void reset() throws IOException {
+        seek(mark);
+    }
+
+    public void close() throws IOException {
+        if (controller != null) {
+            controller = null;
+        }
+    }
+
+    public long getPosition() {
+        return streamPosition.pos - offset;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/FSBOutputStream.java
===================================================================
--- ocean/src/com/imagero/uio/bio/FSBOutputStream.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/FSBOutputStream.java	(revision 0)
@@ -0,0 +1,40 @@
+package com.imagero.uio.bio;
+
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ * Date: 01.03.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+class FSBOutputStream extends OutputStream {
+    FixedSizeByteBuffer buffer;
+    BufferPosition position;
+
+    public FSBOutputStream(FixedSizeByteBuffer buffer) {
+        this(0, buffer);
+    }
+
+    public FSBOutputStream(int offset, FixedSizeByteBuffer buffer) {
+        this.buffer = buffer;
+        position = new BufferPosition(Integer.MAX_VALUE);
+        position.pos = offset;
+    }
+
+    public void write(int b) throws IOException {
+        buffer.write(b, position);
+    }
+
+    public void write(byte b[]) throws IOException {
+        buffer.write(b, 0, b.length, position);
+    }
+
+    public void write(byte b[], int off, int len) throws IOException {
+        buffer.write(b, off, len, position);
+    }
+
+    public void close() throws IOException {
+        buffer = null;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/IOCOutputStream.java
===================================================================
--- ocean/src/com/imagero/uio/bio/IOCOutputStream.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/IOCOutputStream.java	(revision 0)
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio;
+
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ * OutputStream with shared IOController.
+ *
+ * @author Andrey Kuznetsov
+ */
+public class IOCOutputStream extends OutputStream {
+
+    FixedSizeByteBuffer buffer;
+    BufferIndex bufferIndex;
+    BufferPosition bufferPosition;
+    IOController controller;
+
+    StreamPosition streamPosition = new StreamPosition();
+    long offset;
+
+    long mark;
+
+    public IOCOutputStream(IOController controller) {
+        this.controller = controller;
+    }
+
+    public IOCOutputStream(IOController controller, long offset) {
+        this.controller = controller;
+        this.offset = offset;
+        bufferPosition = new BufferPosition(controller.bufferSize);
+        seek(0);
+    }
+
+    public void seek(long offset) {
+        streamPosition.pos = offset + this.offset;
+    }
+
+    protected void prepareBufferForWriting() throws IOException {
+        BufferIndex index = controller.getBufferIndex(streamPosition.pos);
+
+        if (!index.equals(bufferIndex) || buffer == null || buffer.buf == null) {
+            bufferIndex = index;
+            buffer = controller.getBuffer(streamPosition.pos, false);
+        }
+        bufferPosition.pos = (int) ((streamPosition.pos) % controller.bufferSize);
+        buffer.changed = true;
+    }
+
+    private void checkBuffer() throws IOException {
+        if (buffer == null || !(bufferPosition.available() > 0)) {
+            prepareBufferForWriting();
+        }
+    }
+
+    public void write(int b) throws IOException {
+        checkBuffer();
+        buffer.write(b, bufferPosition);
+        streamPosition.pos++;
+    }
+
+    public void write(byte b[]) throws IOException {
+        write(b, 0, b.length);
+    }
+
+    public void write(byte b[], int offset, int length) throws IOException {
+        while (length > 0) {
+            checkBuffer();
+            int written = buffer.write(b, offset, length, bufferPosition);
+            length -= written;
+            offset += written;
+            streamPosition.pos += written;
+        }
+    }
+
+    public void flush() throws IOException {
+        if (controller != null) {
+            controller.sync();
+        }
+    }
+
+    public void close() throws IOException {
+        flush();
+        controller = null;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/FixedSizeByteBuffer.java
===================================================================
--- ocean/src/com/imagero/uio/bio/FixedSizeByteBuffer.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/FixedSizeByteBuffer.java	(revision 0)
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio;
+
+import com.imagero.uio.RandomAccessIO;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * This class can be used to read from and write to byte array.
+ *
+ * @author Andrey Kuznetsov
+ */
+public class FixedSizeByteBuffer {
+
+    protected byte[] buf;
+    protected int count;
+
+    boolean changed;
+    BufferIndex index;
+
+    protected FixedSizeByteBuffer(byte buf[]) {
+        this.buf = buf;
+    }
+
+    public int read(BufferPosition position) {
+        if (availableForReading(position) > 0) {
+            int v = buf[position.pos++] & 0xFF;
+            return v;
+        }
+        return -1;
+    }
+
+    public long skip(long n, BufferPosition position) {
+        long p = Math.max(0, Math.min(count - position.pos, n));
+        position.pos += p;
+        return p;
+    }
+
+    public BufferPosition createPosition() {
+        return new BufferPosition(buf.length);
+    }
+
+    public int availableForReading(BufferPosition position) {
+        return Math.max(0, count - position.pos);
+    }
+
+    public int availableForWriting(BufferPosition position) {
+        return Math.max(0, buf.length - position.pos);
+    }
+
+    public int read(byte[] dest, int offset, int length, BufferPosition position) {
+        final int available = availableForReading(position);
+        int toCopy = Math.max(0, Math.min(length, available));
+        if (toCopy > 0) {
+            System.arraycopy(buf, position.pos, dest, offset, toCopy);
+            position.pos += toCopy;
+        }
+        return toCopy;
+    }
+
+    /**
+     * write given byte to buffer.
+     *
+     * @param b int to write
+     */
+    public void write(int b, BufferPosition position) {
+        buf[position.pos++] = (byte) b;
+        count = Math.max(position.pos, count);
+    }
+
+    public int getCount() {
+        return count;
+    }
+
+    public int getPosition(BufferPosition position) {
+        return position.pos;
+    }
+
+    public void setCount(int count) {
+        this.count = Math.min(Math.max(count, 0), buf.length);
+    }
+
+    /**
+     * write buffer contents to OutputStream
+     * @param wholeBuffer if true then whole buffer is written, otherwise only getCount() bytes are written
+     */
+    public void writeBuffer(OutputStream out, boolean wholeBuffer) throws IOException {
+        if (wholeBuffer) {
+            out.write(buf);
+        } else {
+            out.write(buf, 0, count);
+        }
+    }
+
+    public void writeBuffer(DataOutput out, boolean wholeBuffer) throws IOException {
+        if (wholeBuffer) {
+            out.write(buf);
+        } else {
+            out.write(buf, 0, count);
+        }
+    }
+
+    /**
+     * write whole buffer contents to OutputStream (count is ignored)
+     */
+    public void writeBuffer(OutputStream out) throws IOException {
+        out.write(buf);
+    }
+
+    public void writeBuffer(DataOutput out) throws IOException {
+        out.write(buf);
+    }
+
+    public int write(byte src[], int offset, int length, BufferPosition position) {
+        int available = availableForWriting(position);
+        int toCopy = Math.max(0, Math.min(length, available));
+        if (toCopy > 0) {
+            System.arraycopy(src, offset, buf, position.pos, toCopy);
+            position.pos += toCopy;
+            count = Math.max(count, position.pos);
+        }
+        return toCopy;
+    }
+
+    public RandomAccessIO create() {
+        return new FSBRandomAccessIO(this);
+    }
+
+    public RandomAccessIO create(int offset, int length) {
+        return new FSBRandomAccessIO(this, offset, length);
+    }
+
+    public static FixedSizeByteBuffer createBuffer(byte buf[]) {
+        return new FixedSizeByteBuffer(buf);
+    }
+}
Index: ocean/src/com/imagero/uio/bio/Buffer.java
===================================================================
--- ocean/src/com/imagero/uio/bio/Buffer.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/Buffer.java	(revision 0)
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio;
+
+/**
+ * @author Andrey Kuznetsov
+ */
+class Buffer {
+    byte [] buffer;
+
+    public Buffer(byte[] buffer) {
+        this.buffer = buffer;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/IOController.java
===================================================================
--- ocean/src/com/imagero/uio/bio/IOController.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/IOController.java	(revision 0)
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio;
+
+import com.imagero.util.OpenVector;
+import com.imagero.uio.bio.content.SynchronizedContent;
+import com.imagero.uio.bio.content.Content;
+import com.imagero.uio.UIOStreamBuilder;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+/**
+ * Buffer controller.
+ * IOController loads data from Content and maintains buffers (FixedSizeBuffer).
+ * @author Andrey Kuznetsov
+ */
+public class IOController {
+
+    OpenVector bufs = new OpenVector(100);
+
+    int bufferSize = UIOStreamBuilder.DEFAULT_CHUNK_SIZE;
+    int arrayLength = 1000;
+
+    Content content;
+
+    Ring rs;
+    int maxBufferCount = UIOStreamBuilder.DEFAULT_CHUNK_COUNT;
+
+    long explicitLength;
+
+    public IOController(int bufferSize, Content content) {
+        this.bufferSize = bufferSize;
+        this.content = content;
+        this.rs = new Ring(maxBufferCount);
+    }
+
+    final void setLength(long newLength) {
+        explicitLength = newLength;
+    }
+
+    private Enumeration buffers(final boolean allowNullValues) {
+        return new Enumeration() {
+            final FixedSizeByteBuffer empty = FixedSizeByteBuffer.createBuffer(new byte[bufferSize]);
+            final long length = length();
+            final BufferIndex max = getBufferIndex(length);
+            final BufferIndex bi = new BufferIndex(0, 0);
+
+            public boolean hasMoreElements() {
+                boolean b = bi.arrayIndex < max.arrayIndex;
+                boolean b2 = bi.index <= max.index;
+                return b || b2;
+            }
+
+            public Object nextElement() {
+                if (hasMoreElements()) {
+                    if (!(bi.index < arrayLength)) {
+                        bi.index = 0;
+                        bi.arrayIndex++;
+                    }
+                    FixedSizeByteBuffer fb = getBuffer(bi.arrayIndex, bi.index++);
+                    return fb != null || allowNullValues ? fb : empty;
+                } else {
+                    throw new NoSuchElementException();
+                }
+            }
+        };
+    }
+
+    private Enumeration buffers(final long maxPos, final boolean allowNullValues) {
+        return new Enumeration() {
+            final FixedSizeByteBuffer empty = FixedSizeByteBuffer.createBuffer(new byte[bufferSize]);
+            final long length = Math.min(maxPos, length());
+            final BufferIndex max = getBufferIndex(length);
+            final BufferIndex bi = new BufferIndex(0, 0);
+
+            public boolean hasMoreElements() {
+                boolean b = bi.arrayIndex < max.arrayIndex;
+                boolean b2 = bi.index <= max.index;
+                return b || b2;
+            }
+
+            public Object nextElement() {
+                if (hasMoreElements()) {
+                    if (!(bi.index < arrayLength)) {
+                        bi.index = 0;
+                        bi.arrayIndex++;
+                    }
+                    FixedSizeByteBuffer fb = getBuffer(bi.arrayIndex, bi.index++);
+                    return fb != null || allowNullValues ? fb : empty;
+                } else {
+                    throw new NoSuchElementException();
+                }
+            }
+        };
+    }
+
+
+    long length() {
+        if (explicitLength > 0) {
+            return explicitLength;
+        }
+
+        long contentLength = 0;
+        try {
+            contentLength = content.length();
+        } catch (IOException ex) {
+            ex.printStackTrace();
+        }
+
+        Object[] elements = bufs.getElements();
+        int maxI = elements.length - 1;
+        for (int i = maxI; i >= 0; i--) {
+            BufferArray ba = (BufferArray) elements[i];
+            if (ba != null) {
+                FixedSizeByteBuffer[] buffers = ba.buffers;
+                int maxJ = buffers.length - 1;
+                for (int j = maxJ; j >= 0; j--) {
+                    FixedSizeByteBuffer buffer = buffers[j];
+                    if (buffer != null) {
+                        int count = buffer.getCount();
+                        long startOffset = getStartOffset(buffer.index, bufferSize);
+                        if (count > 0) {
+                            return Math.max(contentLength, startOffset + count);
+                        }
+                    }
+                }
+            }
+        }
+        return contentLength;
+    }
+
+    void writeTo(OutputStream out) throws IOException {
+        Enumeration enums = buffers(false);
+        while (enums.hasMoreElements()) {
+            FixedSizeByteBuffer buffer = (FixedSizeByteBuffer) enums.nextElement();
+            buffer.writeBuffer(out, enums.hasMoreElements());
+        }
+    }
+
+    void writeTo(DataOutput out) throws IOException {
+        Enumeration enums = buffers(false);
+        while (enums.hasMoreElements()) {
+            FixedSizeByteBuffer buffer = (FixedSizeByteBuffer) enums.nextElement();
+            buffer.writeBuffer(out, enums.hasMoreElements());
+        }
+    }
+
+    private class BufferArray {
+        FixedSizeByteBuffer[] buffers;
+
+        public BufferArray() {
+            buffers = new FixedSizeByteBuffer[arrayLength];
+        }
+    }
+
+    private FixedSizeByteBuffer getBuffer(BufferIndex bi) {
+        return getBuffer(bi.arrayIndex, bi.index);
+    }
+
+    private FixedSizeByteBuffer getBuffer(int aIndex, int index) {
+        Object[] objects = bufs.checkSize(aIndex);
+        BufferArray ba = (BufferArray) objects[aIndex];
+        if (ba == null) {
+            ba = new BufferArray();
+            objects[aIndex] = ba;
+        }
+        return ba.buffers[index];
+    }
+
+    protected void setBuffer(BufferIndex index, FixedSizeByteBuffer buffer) {
+        Object[] objects = bufs.checkSize(index.arrayIndex);
+        BufferArray ba = (BufferArray) objects[index.arrayIndex];
+        ba.buffers[index.index] = buffer;
+    }
+
+    public long flushBefore(long pos) {
+        pos = (pos / bufferSize) * bufferSize;
+        Enumeration enums = buffers(pos, true);
+        while (enums.hasMoreElements()) {
+            FixedSizeByteBuffer o = (FixedSizeByteBuffer) enums.nextElement();
+            if (o != null) {
+                o.buf = null;
+            }
+        }
+        return pos;
+    }
+
+    public BufferIndex getBufferIndex(long pos) {
+        if(pos < 0) {
+            throw new IllegalArgumentException("Negative stream position");
+        }
+        long count = pos / bufferSize;
+        long aIndex = count / arrayLength;
+        int index = (int) (count % arrayLength);
+        if (aIndex > Integer.MAX_VALUE) {
+            throw new IndexOutOfBoundsException("Please increase buffer size");
+        }
+        if(index < 0) {
+            throw new IndexOutOfBoundsException("Please increase buffer size");
+        }
+        BufferIndex bi = new BufferIndex((int) aIndex, index);
+        return bi;
+    }
+
+    public FixedSizeByteBuffer getBuffer(long pos) {
+        return getBuffer(getBufferIndex(pos));
+    }
+
+    FixedSizeByteBuffer getBuffer(long pos, boolean load) throws IOException {
+        BufferIndex bi = getBufferIndex(pos);
+        long startOffset = getStartOffset(bi, bufferSize);
+        FixedSizeByteBuffer sb = getBuffer(bi);
+        if (sb == null) {
+            sb = FixedSizeByteBuffer.createBuffer(new byte[bufferSize]);
+            setBuffer(bi, sb);
+            sb.index = bi;
+            if (load) {
+                long max = content.length();
+                if (pos > max) {
+                    return null;
+                }
+
+                int size = content.load(startOffset, sb.buf);
+                sb.count = size;
+            }
+            if (content.canReload()) {
+                checkBuffers(sb);
+            }
+        } else {
+            if (sb.buf == null) {
+                long max = content.length();
+                if (pos > max) {
+                    return null;
+                }
+                sb.buf = new byte[bufferSize];
+                int size = content.load(startOffset, sb.buf);
+                sb.count = size;
+            }
+        }
+        return sb;
+    }
+
+    private void checkBuffers(FixedSizeByteBuffer buffer0) {
+        FixedSizeByteBuffer buffer = (FixedSizeByteBuffer) rs.add(buffer0);
+        if (buffer != null && content.writable()) {
+            if (buffer.changed) {
+                try {
+                    long offset = getStartOffset(buffer.index, bufferSize);
+                    content.save(offset, 0, buffer.buf, buffer.getCount());
+                    buffer.changed = false;
+                } catch (IOException ex) {
+                    ex.printStackTrace();
+                }
+            }
+            setBuffer(buffer.index, null);
+            buffer.buf = null;
+        }
+    }
+
+    void sync() throws IOException {
+        boolean canWrite = content.writable();
+        if (!canWrite) {
+            return;
+        }
+
+        try {
+            long length = content.length(); //may be already closed
+        }
+        catch(IOException ex) {
+            //stream was already closed, so just return
+            return;
+        }
+        Enumeration enums = buffers(true);
+        while (enums.hasMoreElements()) {
+            FixedSizeByteBuffer buffer = (FixedSizeByteBuffer) enums.nextElement();
+            if (buffer != null && buffer.changed) {
+                long offset = getStartOffset(buffer.index, bufferSize);
+                content.save(offset, 0, buffer.buf, buffer.getCount());
+                buffer.changed = false;
+            }
+        }
+    }
+
+    public long getStartOffset(BufferIndex bufferIndex, int bufferSize) {
+        long res = bufferIndex.arrayIndex * arrayLength * bufferSize;
+        res += bufferIndex.index * bufferSize;
+        return res;
+    }
+
+    /**
+     * determine if access to stream content is synchronized
+     */
+    public boolean isSynchronizedContent() {
+        return content instanceof SynchronizedContent;
+    }
+
+    /**
+     * define if access to content should be syncronized.
+     */
+    public void setSynchronizedContent(boolean b) {
+        if(b) {
+            if(!isSynchronizedContent()) {
+                content = new SynchronizedContent(content);
+            }
+        }
+        else {
+            if(isSynchronizedContent()) {
+                SynchronizedContent synchronizedContent = (SynchronizedContent) content;
+                content = synchronizedContent.getContent();
+            }
+        }
+    }
+
+    protected void finalize() throws Throwable {
+        super.finalize();
+        content = null;
+        rs = null;
+        bufs = null;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/ByteArrayRandomAccessIO.java
===================================================================
--- ocean/src/com/imagero/uio/bio/ByteArrayRandomAccessIO.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/ByteArrayRandomAccessIO.java	(revision 0)
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio;
+
+import com.imagero.uio.RandomAccessIO;
+import com.imagero.uio.RandomAccessInput;
+import com.imagero.uio.RandomAccessOutput;
+import com.imagero.uio.impl.AbstractRandomAccessIO;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * ByteArrayRandomAccessIO is like ByteArrayOutputStream and ByteArrayInputStream together.
+ * It implements also DataInput/DataOutput and other advanced interfaces.
+ * @author Andrey Kuznetsov
+ */
+public class ByteArrayRandomAccessIO extends AbstractRandomAccessIO implements RandomAccessIO {
+
+    VariableSizeByteBuffer buffer;
+    BufferPosition position;
+    int _offset;
+    Integer length;
+
+    private static VariableSizeByteBuffer createBuffer(int size) {
+        return new VariableSizeByteBuffer(size);
+    }
+
+    private static VariableSizeByteBuffer createBuffer(byte [] data) {
+        return new VariableSizeByteBuffer(data);
+    }
+    public ByteArrayRandomAccessIO(int initialSize) {
+        this(createBuffer(initialSize));
+    }
+
+    public ByteArrayRandomAccessIO(int offset, int length, VariableSizeByteBuffer buffer) {
+        this._offset = offset;
+        this.buffer = buffer;
+        position = new BufferPosition(Integer.MAX_VALUE);
+        position.pos = offset;
+        if(length > 0) {
+            this.length = new Integer(length);
+        }
+    }
+
+    public ByteArrayRandomAccessIO(byte [] data) {
+        this(createBuffer(data));
+    }
+
+    public ByteArrayRandomAccessIO(VariableSizeByteBuffer buffer) {
+        this.buffer = buffer;
+        position = new BufferPosition(Integer.MAX_VALUE);
+    }
+
+    public int read() throws IOException {
+        return buffer.read(position);
+    }
+
+    public long skip(long n) throws IOException {
+        return buffer.skip(n, position);
+    }
+
+    public int read(byte[] b, int off, int len) throws IOException {
+        return buffer.read(b, off, len, position);
+    }
+
+    public long getFilePointer() throws IOException {
+        return position.pos - _offset;
+    }
+
+    public long length() throws IOException {
+        if(length != null) {
+            return Math.min(length.intValue(), buffer.getCount() - _offset);
+        }
+        else {
+            return buffer.getCount() - _offset;
+        }
+    }
+
+    public void seek(long offset) throws IOException {
+        if (offset + _offset > Integer.MAX_VALUE) {
+            throw new IOException("Offset too big: 0x" + Long.toHexString(offset));
+        }
+        buffer.seek((int) offset + _offset, position);
+    }
+
+    public void setLength(long newLength) throws IOException {
+        if (newLength > Integer.MAX_VALUE) {
+            throw new IOException();
+        }
+        buffer.setCount((int) newLength);
+    }
+
+    public void write(int b) throws IOException {
+        buffer.write(b, position);
+    }
+
+    public void write(byte b[], int offset, int length) throws IOException {
+        buffer.write(b, offset, length, position);
+    }
+
+    public RandomAccessIO createIOChild(long offset, long length, int byteOrder, boolean syncPointer) {
+        ByteArrayRandomAccessIO io = new ByteArrayRandomAccessIO((int) offset, (int) length, buffer);
+        if(syncPointer) {
+            io.buffer = buffer;
+        }
+        io.setByteOrder(byteOrder);
+        return io;
+    }
+
+    public RandomAccessInput createInputChild(long offset, long length, int byteOrder, boolean syncPointer) {
+        return createIOChild(offset, length, byteOrder, syncPointer);
+    }
+
+    public RandomAccessOutput createOutputChild(long offset, int byteOrder, boolean syncPointer) {
+        return createIOChild(offset, 0, byteOrder, syncPointer);
+    }
+
+    public byte [] toByteArray() throws IOException {
+        byte [] b = new byte[(int)length()];
+        int pos = position.pos;
+        buffer.seek(0, position);
+        buffer.read(b, 0, b.length, position);
+        buffer.seek(pos, position);
+        return b;
+    }
+
+    public InputStream createInputStream(long offset) {
+        return buffer.getInputStream((int) offset);
+    }
+
+    public long getChildPosition(InputStream child) {
+        if(child instanceof VSBInputStream) {
+            VSBInputStream vsbis = (VSBInputStream) child;
+            return vsbis.getPosition();
+        }
+        return -1;
+    }
+
+    public void setChildPosition(InputStream child, long pos) {
+        if (child instanceof VSBInputStream) {
+            VSBInputStream vsbis = (VSBInputStream) child;
+            vsbis.setPosition(pos);
+        }
+    }
+
+    public OutputStream createOutputStream(long offset) {
+        return buffer.getOutputStream((int) offset);
+    }
+}
Index: ocean/src/com/imagero/uio/bio/StreamPosition.java
===================================================================
--- ocean/src/com/imagero/uio/bio/StreamPosition.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/StreamPosition.java	(revision 0)
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio;
+
+/**
+ * @author Andrey Kuznetsov
+ */
+public class StreamPosition {
+    public long pos;
+}
Index: ocean/src/com/imagero/uio/bio/FSBRandomAccessIO.java
===================================================================
--- ocean/src/com/imagero/uio/bio/FSBRandomAccessIO.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/FSBRandomAccessIO.java	(revision 0)
@@ -0,0 +1,111 @@
+package com.imagero.uio.bio;
+
+import com.imagero.uio.impl.AbstractRandomAccessIO;
+import com.imagero.uio.RandomAccessIO;
+import com.imagero.uio.RandomAccessInput;
+import com.imagero.uio.RandomAccessOutput;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Date: 01.03.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+class FSBRandomAccessIO extends AbstractRandomAccessIO {
+
+    FixedSizeByteBuffer buffer;
+    BufferPosition position;
+
+    int offset;
+    int length;
+
+    public FSBRandomAccessIO(FixedSizeByteBuffer buffer) {
+        this(buffer, 0, buffer.buf.length);
+    }
+
+    public FSBRandomAccessIO(FixedSizeByteBuffer buffer, int offset) {
+        this(buffer, offset, buffer.buf.length - offset);
+    }
+
+    public FSBRandomAccessIO(FixedSizeByteBuffer buffer, int offset, int length) {
+        this.buffer = buffer;
+        this.offset = offset;
+        if(length > 0) {
+            this.length = length;
+        }
+        else {
+            this.length = buffer.availableForReading(buffer.createPosition());
+        }
+    }
+
+    public int read() throws IOException {
+        return buffer.read(position);
+    }
+
+    public void seek(long pos) {
+        position.pos = (int) Math.min(length, pos + offset);
+    }
+
+    public long length() throws IOException {
+        return length;
+    }
+
+    public long getFilePointer() throws IOException {
+        return position.pos - offset;
+    }
+
+    public void setLength(long newLength) throws IOException {
+        this.length = (int) Math.min(buffer.buf.length, newLength);
+    }
+
+    public void write(int b) throws IOException {
+        buffer.write(b, position);
+    }
+
+    public void write(byte b[], int off, int len) throws IOException {
+        buffer.write(b, off, len, position);
+    }
+
+    public InputStream createInputStream(long offset) {
+        return new FSBInputStream((int) offset, buffer);
+    }
+
+    public long getChildPosition(InputStream child) {
+        if(child instanceof FSBInputStream) {
+            FSBInputStream fsbis = (FSBInputStream) child;
+            return fsbis.getPosition();
+        }
+        return -1;
+    }
+
+    public void setChildPosition(InputStream child, long position) {
+        if (child instanceof FSBInputStream) {
+            FSBInputStream fsbis = (FSBInputStream) child;
+            fsbis.setPosition(position);
+        }
+    }
+
+    public OutputStream createOutputStream(long offset) {
+        return new FSBOutputStream((int) offset, buffer) ;
+    }
+
+    public RandomAccessIO createIOChild(long offset, long length, int byteOrder, boolean syncPointer) throws IOException {
+        FSBRandomAccessIO rio = new FSBRandomAccessIO(buffer, (int) offset, (int) length);
+        if(syncPointer) {
+            rio.position = position;
+        }
+        rio.setByteOrder(byteOrder);
+        return rio;
+    }
+
+    public RandomAccessInput createInputChild(long offset, long length, int byteOrder, boolean syncPointer) throws IOException {
+        return createIOChild(offset, 0, byteOrder, syncPointer);
+    }
+
+    public RandomAccessOutput createOutputChild(long offset, int byteOrder, boolean syncPointer) throws IOException {
+        return createIOChild(offset, 0, byteOrder, syncPointer);
+    }
+}
Index: ocean/src/com/imagero/uio/bio/VSBInputStream.java
===================================================================
--- ocean/src/com/imagero/uio/bio/VSBInputStream.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/VSBInputStream.java	(revision 0)
@@ -0,0 +1,68 @@
+package com.imagero.uio.bio;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * Date: 01.03.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+class VSBInputStream extends InputStream {
+    VariableSizeByteBuffer buffer;
+    BufferPosition position;
+    int mark;
+    long offset;
+
+    public VSBInputStream(VariableSizeByteBuffer buffer) {
+        this.buffer = buffer;
+        position = new BufferPosition(Integer.MAX_VALUE);
+    }
+
+    public VSBInputStream(int offset, VariableSizeByteBuffer buffer) {
+        this.buffer = buffer;
+        position = new BufferPosition(Integer.MAX_VALUE);
+        buffer.seek(offset, position);
+        this.offset = offset;
+    }
+
+    public int read() {
+        return buffer.read(position);
+    }
+
+    public int read(byte b[]) throws IOException {
+        return buffer.read(b, 0, b.length, position);
+    }
+
+    public int read(byte b[], int off, int len) {
+        return buffer.read(b, off, len, position);
+    }
+
+    public long skip(long n) {
+        return buffer.skip(n, position);
+    }
+
+    public int available() {
+        return buffer.availableForReading(position);
+    }
+
+    public synchronized void mark(int readlimit) {
+        this.mark = position.pos;
+    }
+
+    public synchronized void reset() throws IOException {
+        buffer.seek(mark, position);
+    }
+
+    public boolean markSupported() {
+        return true;
+    }
+
+    public long getPosition() {
+        return position.pos - offset;
+    }
+
+    public void setPosition(long pos) {
+        buffer.seek((int) pos, position);
+    }
+}
Index: ocean/src/com/imagero/uio/bio/VSBOutputStream.java
===================================================================
--- ocean/src/com/imagero/uio/bio/VSBOutputStream.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/VSBOutputStream.java	(revision 0)
@@ -0,0 +1,40 @@
+package com.imagero.uio.bio;
+
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ * Date: 01.03.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+class VSBOutputStream extends OutputStream {
+    VariableSizeByteBuffer buffer;
+    BufferPosition position;
+
+    public VSBOutputStream(VariableSizeByteBuffer buffer) {
+        this(0, buffer);
+    }
+
+    public VSBOutputStream(int offset, VariableSizeByteBuffer buffer) {
+        this.buffer = buffer;
+        position = new BufferPosition(Integer.MAX_VALUE);
+        buffer.seek(offset, position);
+    }
+
+    public void write(int b) throws IOException {
+        buffer.write(b, position);
+    }
+
+    public void write(byte b[]) throws IOException {
+        buffer.write(b, 0, b.length, position);
+    }
+
+    public void write(byte b[], int off, int len) throws IOException {
+        buffer.write(b, off, len, position);
+    }
+
+    public void close() throws IOException {
+        buffer = null;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/BufferedRandomAccessIO.java
===================================================================
--- ocean/src/com/imagero/uio/bio/BufferedRandomAccessIO.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/BufferedRandomAccessIO.java	(revision 0)
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio;
+
+import com.imagero.uio.RandomAccessIO;
+import com.imagero.uio.RandomAccessInput;
+import com.imagero.uio.RandomAccessOutput;
+import com.imagero.uio.impl.AbstractRandomAccessIO;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * BufferedRandomAccessIO - buffered readable and writable stream with random access.
+ * @author Andrey Kuznetsov
+ */
+public class BufferedRandomAccessIO extends AbstractRandomAccessIO {
+
+    FixedSizeByteBuffer buffer;
+    BufferIndex bufferIndex;
+    BufferPosition bufferPosition;
+    IOController controller;
+
+    StreamPosition streamPosition = new StreamPosition();
+    long offset;
+
+
+    public BufferedRandomAccessIO(IOController controller) {
+        this(controller, 0L);
+    }
+
+    public BufferedRandomAccessIO(IOController controller, long offset) {
+        this.controller = controller;
+        this.offset = offset;
+        bufferPosition = new BufferPosition(controller.bufferSize);
+        seek(0);
+    }
+
+    public final void setLength(long newLength) throws IOException {
+        controller.setLength(newLength);
+    }
+
+    public long flushBefore(long pos) {
+        return controller.flushBefore(pos);
+    }
+
+    protected void prepareBufferForReading(BufferIndex index) throws IOException {
+        if (!index.equals(bufferIndex) || buffer == null || buffer.buf == null) {
+            bufferIndex = index;
+            buffer = controller.getBuffer(streamPosition.pos, true);
+        }
+        bufferPosition.pos = (int) ((streamPosition.pos) % controller.bufferSize);
+    }
+
+    protected void prepareBufferForWriting(BufferIndex index) throws IOException {
+        if (!index.equals(bufferIndex) || buffer == null || buffer.buf == null) {
+            bufferIndex = index;
+            buffer = controller.getBuffer(streamPosition.pos, false);
+        }
+        bufferPosition.pos = (int) ((streamPosition.pos) % controller.bufferSize);
+        buffer.changed = true;
+    }
+
+    public long getFilePointer() {
+        return streamPosition.pos - offset;
+    }
+
+    public long length() throws IOException {
+        return controller.length() - offset;
+    }
+
+    public void seek(long pos) {
+        if(pos < 0) {
+            throw new IllegalArgumentException("Negative seek offset");
+        }
+        streamPosition.pos = pos + offset;
+        bufferPosition.pos = (int) ((streamPosition.pos) % controller.bufferSize);
+    }
+
+    public int available() throws IOException {
+        if (buffer != null) {
+            return buffer.availableForReading(bufferPosition);
+        }
+        return 0;
+    }
+
+    public void write(int b) throws IOException {
+        ensureBuffer(false);
+        buffer.write(b, bufferPosition);
+        streamPosition.pos++;
+    }
+
+    public void write(byte b[], int offset, int length) throws IOException {
+        while (length > 0) {
+            ensureBuffer(false);
+            int written = buffer.write(b, offset, length, bufferPosition);
+            length -= written;
+            offset += written;
+            streamPosition.pos += written;
+        }
+    }
+
+    public void close() throws IOException {
+        if (controller != null) {
+            controller.sync();
+            controller = null;
+        }
+    }
+
+    /**
+     * write buffer contents to given OutputStream
+     * @param out OutputStream
+     */
+    public void writeBuffer(OutputStream out) throws IOException {
+        controller.writeTo(out);
+    }
+
+    /**
+     * write buffer contents to DataOutput
+     * @param out OutputStream
+     */
+    public void writeBuffer(DataOutput out) throws IOException {
+        controller.writeTo(out);
+    }
+
+    private void ensureBuffer(boolean read) throws IOException {
+        BufferIndex index = controller.getBufferIndex(streamPosition.pos);
+        if (read) {
+            if (buffer == null || buffer.availableForReading(bufferPosition) <= 0 || bufferIndex != index) {
+                prepareBufferForReading(index);
+            }
+        } else {
+            if (buffer == null || buffer.availableForWriting(bufferPosition) <= 0 || bufferIndex != index) {
+                prepareBufferForWriting(index);
+            }
+        }
+    }
+
+    public int read() throws IOException {
+        try {
+            ensureBuffer(true);
+        } catch (IOException ex) {
+            return -1;
+        }
+        if (buffer != null) {
+            streamPosition.pos++;
+            return buffer.read(bufferPosition);
+        }
+        return -1;
+    }
+
+    public long skip(long n) throws IOException {
+        ensureBuffer(true);
+        if (buffer == null) {
+            return 0;
+        }
+        long skipped = buffer.skip(n, bufferPosition);
+        streamPosition.pos += skipped;
+        return skipped;
+    }
+
+    public int read(byte[] b, int offset, int length) throws IOException {
+        ensureBuffer(true);
+        if (buffer == null) {
+            return 0;
+        }
+        int rc = buffer.read(b, offset, length, bufferPosition);
+        if (rc > 0) {
+            streamPosition.pos += rc;
+        }
+        return rc;
+    }
+
+    public RandomAccessIO createIOChild(long offset, long length, int byteOrder, boolean syncPointer) {
+        BufferedRandomAccessIO io = new BufferedRandomAccessIO(controller, this.offset + offset);
+        io.setByteOrder(byteOrder);
+        if (syncPointer) {
+            io.streamPosition = streamPosition;
+        }
+        return io;
+    }
+
+    public RandomAccessInput createInputChild(long offset, long length, int byteOrder, boolean syncPointer) {
+        return createIOChild(offset, 0, byteOrder, syncPointer);
+    }
+
+    public InputStream createInputStream(long offset) {
+        return new IOCInputStream(controller, this.offset + offset);
+    }
+
+    public RandomAccessOutput createOutputChild(long offset, int byteOrder, boolean syncPointer) {
+        return createIOChild(offset, 0, byteOrder, syncPointer);
+    }
+
+    public OutputStream createOutputStream(long offset) {
+        return new IOCOutputStream(controller, this.offset + offset);
+    }
+
+    public void flush() throws IOException {
+        controller.sync();
+    }
+
+    public boolean isBuffered() {
+        return true;
+    }
+
+    public long getChildPosition(InputStream child) {
+        if(child instanceof IOCInputStream) {
+            IOCInputStream in = (IOCInputStream) child;
+            return in.getPosition();
+        }
+        return -1;
+    }
+
+    public void setChildPosition(InputStream child, long position) {
+        if (child instanceof IOCInputStream) {
+            IOCInputStream in = (IOCInputStream) child;
+            in.seek(position);
+        }
+    }
+}
Index: ocean/src/com/imagero/uio/bio/BufferPosition.java
===================================================================
--- ocean/src/com/imagero/uio/bio/BufferPosition.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/BufferPosition.java	(revision 0)
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio;
+
+/**
+ * @author Andrey Kuznetsov
+ */
+public class BufferPosition {
+
+    public BufferPosition(int bufferSize) {
+        this.bufferSize = bufferSize;
+    }
+
+    int bufferSize;
+    public int pos;
+
+    public int available() {
+        return bufferSize - pos;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/VariableSizeByteBuffer.java
===================================================================
--- ocean/src/com/imagero/uio/bio/VariableSizeByteBuffer.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/VariableSizeByteBuffer.java	(revision 0)
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * This class can be used to read from and write to byte array.
+ *
+ * @author Andrey Kuznetsov
+ */
+public class VariableSizeByteBuffer {
+
+    protected Buffer buf;
+    int count;
+
+    boolean changed;
+
+    public VariableSizeByteBuffer(int size) {
+        this(new byte[size]);
+    }
+
+    public VariableSizeByteBuffer(byte buf[]) {
+        this(new Buffer(buf));
+    }
+
+    VariableSizeByteBuffer(Buffer buf) {
+        this.buf = buf;
+        count = buf.buffer.length;
+    }
+
+    public VariableSizeByteBuffer create() {
+        return new VariableSizeByteBuffer(buf);
+    }
+
+    public void seek(int pos, BufferPosition position) {
+        position.pos = pos;
+    }
+
+    public int read(BufferPosition position) {
+        if (position.pos >= count) {
+            return -1;
+        }
+        return buf.buffer[position.pos++] & 0xFF;
+    }
+
+    public long skip(long n, BufferPosition position) {
+        long p = Math.max(0L, Math.min(n, Integer.MAX_VALUE));
+        position.pos += p;
+        return p;
+    }
+
+    /**
+     * get amount of bytes which may be written without changing buffer size
+     */
+    public int availableForWriting(BufferPosition position) {
+        return buf.buffer.length - position.pos;
+    }
+
+    public int availableForReading(BufferPosition position) {
+        return Math.max(0, count - position.pos);
+    }
+
+    public int read(byte[] dest, int offset, int length, BufferPosition position) {
+        final int available = availableForReading(position);
+        int toRead = Math.max(0, Math.min(length, available));
+        if (toRead > 0) {
+            System.arraycopy(buf.buffer, position.pos, dest, offset, toRead);
+            position.pos += toRead;
+        }
+        return toRead;
+    }
+
+    public int getCount() {
+        return count;
+    }
+
+    public void setCount(int count) {
+        this.count = Math.min(Math.max(count, 0), buf.buffer.length);
+    }
+
+    public void writeBuffer(OutputStream out) throws IOException {
+        out.write(buf.buffer, 0, count);
+    }
+
+    public void writeBuffer(DataOutput out) throws IOException {
+        out.write(buf.buffer, 0, count);
+    }
+
+    public void write(byte b[], int offset, int length, BufferPosition position) {
+        if (length > 0) {
+            checkSize(length, position);
+            System.arraycopy(b, offset, buf.buffer, position.pos, length);
+            position.pos += length;
+            count = Math.max(count, position.pos);
+        }
+    }
+
+    public void write(int b, BufferPosition position) {
+        checkSize(1, position);
+        buf.buffer[position.pos++] = (byte) b;
+        count = Math.max(count, position.pos);
+    }
+
+    private synchronized void checkSize(int k, BufferPosition position) {
+        if (position.pos + k > buf.buffer.length) {
+            byte newbuf[] = new byte[Math.max(buf.buffer.length << 1, position.pos + k)];
+            System.arraycopy(buf.buffer, 0, newbuf, 0, count);
+            buf.buffer = newbuf;
+        }
+    }
+
+    public InputStream getInputStream(int offset) {
+        return new VSBInputStream(offset, this);
+    }
+
+    public OutputStream getOutputStream(int offset) {
+        return new VSBOutputStream(offset, this);
+    }
+}
Index: ocean/src/com/imagero/uio/bio/BIOFactory.java
===================================================================
--- ocean/src/com/imagero/uio/bio/BIOFactory.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/BIOFactory.java	(revision 0)
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio;
+
+import com.imagero.uio.UIOStreamBuilder;
+import com.imagero.uio.bio.content.Content;
+import com.imagero.uio.bio.content.DummyContent;
+import com.imagero.uio.bio.content.FileCachedInputStreamContent;
+import com.imagero.uio.bio.content.MemoryCachedInputStreamContent;
+import com.imagero.uio.bio.content.FileCachedHTTPContent;
+import com.imagero.uio.bio.content.HTTPContent;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.InputStream;
+import java.io.File;
+import java.net.URL;
+
+/**
+ * This class could be removed in the future.
+ * Use UIOStreamBuilder to create uio streams.
+ * @author Andrey Kuznetsov
+ */
+public class BIOFactory {
+
+    public static BufferedRandomAccessIO create(int chunkSize) {
+        IOController controller = createIOController(chunkSize);
+        BufferedRandomAccessIO out = new BufferedRandomAccessIO(controller);
+        return out;
+    }
+
+    public static BufferedRandomAccessIO create(OutputStream out) {
+        return create(out, UIOStreamBuilder.DEFAULT_CHUNK_SIZE);
+    }
+
+    public static BufferedRandomAccessIO create(final OutputStream out, int chunkSize) {
+        IOController ctrl = createIOController(chunkSize);
+        BufferedRandomAccessIO bio = new BufferedRandomAccessIO(ctrl) {
+            public void close() throws IOException {
+                controller.writeTo(out);
+                super.close();
+            }
+        };
+        return bio;
+    }
+
+    public static BufferedRandomAccessIO create(DataOutput out) {
+        return create(out, UIOStreamBuilder.DEFAULT_CHUNK_SIZE);
+    }
+
+    public static BufferedRandomAccessIO create(final DataOutput out, int chunkSize) {
+        IOController ctrl = createIOController(chunkSize);
+        BufferedRandomAccessIO bio = new BufferedRandomAccessIO(ctrl) {
+            public void close() throws IOException {
+                controller.writeTo(out);
+                super.close();
+            }
+        };
+        return bio;
+    }
+
+    public static IOController createIOController(int chunkSize) {
+        Content bc = new DummyContent();
+        IOController sb = new IOController(chunkSize, bc);
+        return sb;
+    }
+
+    public static IOController createIOController(InputStream in, File tmp, int chunkSize) {
+        Content bc;
+        if (tmp != null) {
+            try {
+                bc = new FileCachedInputStreamContent(in, tmp);
+            } catch (IOException ex) {
+                System.err.println("Unable to use file cache, switching to memory cache.");
+                ex.printStackTrace();
+                bc = new MemoryCachedInputStreamContent(in, chunkSize);
+            }
+        } else {
+            bc = new MemoryCachedInputStreamContent(in, chunkSize);
+        }
+        IOController sb = new IOController(chunkSize, bc);
+        return sb;
+    }
+
+    public static IOController createIOController(URL url) {
+        return createIOController(url, UIOStreamBuilder.DEFAULT_CHUNK_SIZE);
+    }
+
+    public static IOController createIOController(URL url, int chunkSize) {
+        return createIOController(url, null, chunkSize);
+    }
+
+    public static IOController createIOController(URL url, File tmp) {
+        return createIOController(url, tmp, UIOStreamBuilder.DEFAULT_CHUNK_SIZE);
+    }
+
+    public static IOController createIOController(URL url, File tmp, int chunkSize) {
+        Content bc;
+        if (tmp != null) {
+            try {
+                bc = new FileCachedHTTPContent(url, tmp);
+            } catch (IOException ex) {
+                ex.printStackTrace();
+                System.err.println("Unable to use file cache, switching to memory cache.");
+                bc = new HTTPContent(url);
+            }
+        } else {
+            bc = new HTTPContent(url);
+        }
+        IOController sb = new IOController(chunkSize, bc);
+        return sb;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/Buffer.java
===================================================================
--- ocean/src/com/imagero/uio/bio/Buffer.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/Buffer.java	(revision 0)
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio;
+
+/**
+ * @author Andrey Kuznetsov
+ */
+class Buffer {
+    byte [] buffer;
+
+    public Buffer(byte[] buffer) {
+        this.buffer = buffer;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/BufferedRandomAccessIO.java
===================================================================
--- ocean/src/com/imagero/uio/bio/BufferedRandomAccessIO.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/BufferedRandomAccessIO.java	(revision 0)
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio;
+
+import com.imagero.uio.RandomAccessIO;
+import com.imagero.uio.RandomAccessInput;
+import com.imagero.uio.RandomAccessOutput;
+import com.imagero.uio.impl.AbstractRandomAccessIO;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * BufferedRandomAccessIO - buffered readable and writable stream with random access.
+ * @author Andrey Kuznetsov
+ */
+public class BufferedRandomAccessIO extends AbstractRandomAccessIO {
+
+    FixedSizeByteBuffer buffer;
+    BufferIndex bufferIndex;
+    BufferPosition bufferPosition;
+    IOController controller;
+
+    StreamPosition streamPosition = new StreamPosition();
+    long offset;
+
+
+    public BufferedRandomAccessIO(IOController controller) {
+        this(controller, 0L);
+    }
+
+    public BufferedRandomAccessIO(IOController controller, long offset) {
+        this.controller = controller;
+        this.offset = offset;
+        bufferPosition = new BufferPosition(controller.bufferSize);
+        seek(0);
+    }
+
+    public final void setLength(long newLength) throws IOException {
+        controller.setLength(newLength);
+    }
+
+    public long flushBefore(long pos) {
+        return controller.flushBefore(pos);
+    }
+
+    protected void prepareBufferForReading(BufferIndex index) throws IOException {
+        if (!index.equals(bufferIndex) || buffer == null || buffer.buf == null) {
+            bufferIndex = index;
+            buffer = controller.getBuffer(streamPosition.pos, true);
+        }
+        bufferPosition.pos = (int) ((streamPosition.pos) % controller.bufferSize);
+    }
+
+    protected void prepareBufferForWriting(BufferIndex index) throws IOException {
+        if (!index.equals(bufferIndex) || buffer == null || buffer.buf == null) {
+            bufferIndex = index;
+            buffer = controller.getBuffer(streamPosition.pos, false);
+        }
+        bufferPosition.pos = (int) ((streamPosition.pos) % controller.bufferSize);
+        buffer.changed = true;
+    }
+
+    public long getFilePointer() {
+        return streamPosition.pos - offset;
+    }
+
+    public long length() throws IOException {
+        return controller.length() - offset;
+    }
+
+    public void seek(long pos) {
+        if(pos < 0) {
+            throw new IllegalArgumentException("Negative seek offset");
+        }
+        streamPosition.pos = pos + offset;
+        bufferPosition.pos = (int) ((streamPosition.pos) % controller.bufferSize);
+    }
+
+    public int available() throws IOException {
+        if (buffer != null) {
+            return buffer.availableForReading(bufferPosition);
+        }
+        return 0;
+    }
+
+    public void write(int b) throws IOException {
+        ensureBuffer(false);
+        buffer.write(b, bufferPosition);
+        streamPosition.pos++;
+    }
+
+    public void write(byte b[], int offset, int length) throws IOException {
+        while (length > 0) {
+            ensureBuffer(false);
+            int written = buffer.write(b, offset, length, bufferPosition);
+            length -= written;
+            offset += written;
+            streamPosition.pos += written;
+        }
+    }
+
+    public void close() throws IOException {
+        if (controller != null) {
+            controller.sync();
+            controller = null;
+        }
+    }
+
+    /**
+     * write buffer contents to given OutputStream
+     * @param out OutputStream
+     */
+    public void writeBuffer(OutputStream out) throws IOException {
+        controller.writeTo(out);
+    }
+
+    /**
+     * write buffer contents to DataOutput
+     * @param out OutputStream
+     */
+    public void writeBuffer(DataOutput out) throws IOException {
+        controller.writeTo(out);
+    }
+
+    private void ensureBuffer(boolean read) throws IOException {
+        BufferIndex index = controller.getBufferIndex(streamPosition.pos);
+        if (read) {
+            if (buffer == null || buffer.availableForReading(bufferPosition) <= 0 || bufferIndex != index) {
+                prepareBufferForReading(index);
+            }
+        } else {
+            if (buffer == null || buffer.availableForWriting(bufferPosition) <= 0 || bufferIndex != index) {
+                prepareBufferForWriting(index);
+            }
+        }
+    }
+
+    public int read() throws IOException {
+        try {
+            ensureBuffer(true);
+        } catch (IOException ex) {
+            return -1;
+        }
+        if (buffer != null) {
+            streamPosition.pos++;
+            return buffer.read(bufferPosition);
+        }
+        return -1;
+    }
+
+    public long skip(long n) throws IOException {
+        ensureBuffer(true);
+        if (buffer == null) {
+            return 0;
+        }
+        long skipped = buffer.skip(n, bufferPosition);
+        streamPosition.pos += skipped;
+        return skipped;
+    }
+
+    public int read(byte[] b, int offset, int length) throws IOException {
+        ensureBuffer(true);
+        if (buffer == null) {
+            return 0;
+        }
+        int rc = buffer.read(b, offset, length, bufferPosition);
+        if (rc > 0) {
+            streamPosition.pos += rc;
+        }
+        return rc;
+    }
+
+    public RandomAccessIO createIOChild(long offset, long length, int byteOrder, boolean syncPointer) {
+        BufferedRandomAccessIO io = new BufferedRandomAccessIO(controller, this.offset + offset);
+        io.setByteOrder(byteOrder);
+        if (syncPointer) {
+            io.streamPosition = streamPosition;
+        }
+        return io;
+    }
+
+    public RandomAccessInput createInputChild(long offset, long length, int byteOrder, boolean syncPointer) {
+        return createIOChild(offset, 0, byteOrder, syncPointer);
+    }
+
+    public InputStream createInputStream(long offset) {
+        return new IOCInputStream(controller, this.offset + offset);
+    }
+
+    public RandomAccessOutput createOutputChild(long offset, int byteOrder, boolean syncPointer) {
+        return createIOChild(offset, 0, byteOrder, syncPointer);
+    }
+
+    public OutputStream createOutputStream(long offset) {
+        return new IOCOutputStream(controller, this.offset + offset);
+    }
+
+    public void flush() throws IOException {
+        controller.sync();
+    }
+
+    public boolean isBuffered() {
+        return true;
+    }
+
+    public long getChildPosition(InputStream child) {
+        if(child instanceof IOCInputStream) {
+            IOCInputStream in = (IOCInputStream) child;
+            return in.getPosition();
+        }
+        return -1;
+    }
+
+    public void setChildPosition(InputStream child, long position) {
+        if (child instanceof IOCInputStream) {
+            IOCInputStream in = (IOCInputStream) child;
+            in.seek(position);
+        }
+    }
+}
Index: ocean/src/com/imagero/uio/bio/BufferIndex.java
===================================================================
--- ocean/src/com/imagero/uio/bio/BufferIndex.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/BufferIndex.java	(revision 0)
@@ -0,0 +1,35 @@
+package com.imagero.uio.bio;
+
+/**
+ * Index of Object in 2D array
+ * Date: 14.12.2007
+ *
+ * @author Andrey Kuznetsov
+ */
+class BufferIndex {
+    /**
+     * index of array in 2D array
+     */
+    int arrayIndex;
+    /**
+     * index of object in 1D array
+     */
+    int index;
+
+    /**
+     * @param arrayIndex index of array
+     * @param index index of object
+     */
+    public BufferIndex(int arrayIndex, int index) {
+        this.arrayIndex = arrayIndex;
+        this.index = index;
+    }
+
+    public boolean equals(Object obj) {
+        if(obj != null && obj instanceof BufferIndex) {
+            BufferIndex bi = (BufferIndex) obj;
+            return bi.arrayIndex == arrayIndex && bi.index == index;
+        }
+        return false;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/BufferPosition.java
===================================================================
--- ocean/src/com/imagero/uio/bio/BufferPosition.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/BufferPosition.java	(revision 0)
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio;
+
+/**
+ * @author Andrey Kuznetsov
+ */
+public class BufferPosition {
+
+    public BufferPosition(int bufferSize) {
+        this.bufferSize = bufferSize;
+    }
+
+    int bufferSize;
+    public int pos;
+
+    public int available() {
+        return bufferSize - pos;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/ByteArrayRandomAccessIO.java
===================================================================
--- ocean/src/com/imagero/uio/bio/ByteArrayRandomAccessIO.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/ByteArrayRandomAccessIO.java	(revision 0)
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio;
+
+import com.imagero.uio.RandomAccessIO;
+import com.imagero.uio.RandomAccessInput;
+import com.imagero.uio.RandomAccessOutput;
+import com.imagero.uio.impl.AbstractRandomAccessIO;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * ByteArrayRandomAccessIO is like ByteArrayOutputStream and ByteArrayInputStream together.
+ * It implements also DataInput/DataOutput and other advanced interfaces.
+ * @author Andrey Kuznetsov
+ */
+public class ByteArrayRandomAccessIO extends AbstractRandomAccessIO implements RandomAccessIO {
+
+    VariableSizeByteBuffer buffer;
+    BufferPosition position;
+    int _offset;
+    Integer length;
+
+    private static VariableSizeByteBuffer createBuffer(int size) {
+        return new VariableSizeByteBuffer(size);
+    }
+
+    private static VariableSizeByteBuffer createBuffer(byte [] data) {
+        return new VariableSizeByteBuffer(data);
+    }
+    public ByteArrayRandomAccessIO(int initialSize) {
+        this(createBuffer(initialSize));
+    }
+
+    public ByteArrayRandomAccessIO(int offset, int length, VariableSizeByteBuffer buffer) {
+        this._offset = offset;
+        this.buffer = buffer;
+        position = new BufferPosition(Integer.MAX_VALUE);
+        position.pos = offset;
+        if(length > 0) {
+            this.length = new Integer(length);
+        }
+    }
+
+    public ByteArrayRandomAccessIO(byte [] data) {
+        this(createBuffer(data));
+    }
+
+    public ByteArrayRandomAccessIO(VariableSizeByteBuffer buffer) {
+        this.buffer = buffer;
+        position = new BufferPosition(Integer.MAX_VALUE);
+    }
+
+    public int read() throws IOException {
+        return buffer.read(position);
+    }
+
+    public long skip(long n) throws IOException {
+        return buffer.skip(n, position);
+    }
+
+    public int read(byte[] b, int off, int len) throws IOException {
+        return buffer.read(b, off, len, position);
+    }
+
+    public long getFilePointer() throws IOException {
+        return position.pos - _offset;
+    }
+
+    public long length() throws IOException {
+        if(length != null) {
+            return Math.min(length.intValue(), buffer.getCount() - _offset);
+        }
+        else {
+            return buffer.getCount() - _offset;
+        }
+    }
+
+    public void seek(long offset) throws IOException {
+        if (offset + _offset > Integer.MAX_VALUE) {
+            throw new IOException("Offset too big: 0x" + Long.toHexString(offset));
+        }
+        buffer.seek((int) offset + _offset, position);
+    }
+
+    public void setLength(long newLength) throws IOException {
+        if (newLength > Integer.MAX_VALUE) {
+            throw new IOException();
+        }
+        buffer.setCount((int) newLength);
+    }
+
+    public void write(int b) throws IOException {
+        buffer.write(b, position);
+    }
+
+    public void write(byte b[], int offset, int length) throws IOException {
+        buffer.write(b, offset, length, position);
+    }
+
+    public RandomAccessIO createIOChild(long offset, long length, int byteOrder, boolean syncPointer) {
+        ByteArrayRandomAccessIO io = new ByteArrayRandomAccessIO((int) offset, (int) length, buffer);
+        if(syncPointer) {
+            io.buffer = buffer;
+        }
+        io.setByteOrder(byteOrder);
+        return io;
+    }
+
+    public RandomAccessInput createInputChild(long offset, long length, int byteOrder, boolean syncPointer) {
+        return createIOChild(offset, length, byteOrder, syncPointer);
+    }
+
+    public RandomAccessOutput createOutputChild(long offset, int byteOrder, boolean syncPointer) {
+        return createIOChild(offset, 0, byteOrder, syncPointer);
+    }
+
+    public byte [] toByteArray() throws IOException {
+        byte [] b = new byte[(int)length()];
+        int pos = position.pos;
+        buffer.seek(0, position);
+        buffer.read(b, 0, b.length, position);
+        buffer.seek(pos, position);
+        return b;
+    }
+
+    public InputStream createInputStream(long offset) {
+        return buffer.getInputStream((int) offset);
+    }
+
+    public long getChildPosition(InputStream child) {
+        if(child instanceof VSBInputStream) {
+            VSBInputStream vsbis = (VSBInputStream) child;
+            return vsbis.getPosition();
+        }
+        return -1;
+    }
+
+    public void setChildPosition(InputStream child, long pos) {
+        if (child instanceof VSBInputStream) {
+            VSBInputStream vsbis = (VSBInputStream) child;
+            vsbis.setPosition(pos);
+        }
+    }
+
+    public OutputStream createOutputStream(long offset) {
+        return buffer.getOutputStream((int) offset);
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/FloatArrayContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/FloatArrayContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/FloatArrayContent.java	(revision 0)
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.Transformer;
+
+import java.io.IOException;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class FloatArrayContent extends Content {
+    float[][] data;
+    boolean bigEndian;
+
+    static final int SHIFT = 2;
+
+    public FloatArrayContent(float[][] data) {
+        this(data, true);
+    }
+
+    public FloatArrayContent(float[][] data, boolean bigEndian) {
+        this.data = data;
+        this.bigEndian = bigEndian;
+    }
+
+    public int load(long offset, int bpos, byte[] dest) throws IOException {
+        //to simplify float to byte converting, we require 4 bytes boundary
+        if ((offset & 3) != 0 || ((dest.length - bpos) & 3) != 0) {
+            throw new IOException("Illegal offset or length");
+        }
+        final long off = offset >> SHIFT;
+        final int len = (dest.length - bpos) >> SHIFT;
+
+        IndexAndStart ias = getIAS(off);
+        if (ias == null) {
+            return 0;
+        }
+        int index = ias.index;
+        int pos = (int) (off - ias.start);
+        float[] src = data[index];
+        int toCopy = Math.min(src.length - pos, len);
+        if (toCopy > 0) {
+            com.imagero.uio.Transformer.floatToByte(src, pos, toCopy, dest, bpos, bigEndian);
+            if ((toCopy < len)) {
+                int copiedBytes = (toCopy << SHIFT);
+                return toCopy + load(offset + copiedBytes, bpos + copiedBytes, dest);
+            }
+        }
+        return toCopy;
+    }
+
+    public void close() {
+    }
+
+    private IndexAndStart getIAS(long offset) {
+        long start = 0;
+        for (int i = 0; i < data.length; i++) {
+            float[] src = data[i];
+            int length = src.length;
+            long end = start + length;
+            if (offset >= start && offset <= end) {
+                return new IndexAndStart(i, start);
+            }
+            start += length;
+        }
+        return null;
+    }
+
+    /**
+     * Save data to current content (char array).
+     * All offsets and lengths must be
+     * @param offset
+     * @param spos
+     * @param src
+     * @param length
+     * @throws IOException
+     */
+    public void save(long offset, int spos, byte[] src, int length) throws IOException {
+        //to simplify byte to float converting we require 4 byte boundary
+        if ((offset & 3) != 0 || ((src.length - spos) & 3) != 0) {
+            throw new IOException("Illegal offset or length");
+        }
+        final long off = offset >> SHIFT;
+        final int len = Math.min((src.length - spos), length) >> SHIFT;
+
+        IndexAndStart ias = getIAS(off);
+        if (ias == null) {
+            return;
+        }
+        int index = ias.index;
+        long start = ias.start;
+        int dpos = (int) (off - start);
+        float[] dest = data[index];
+        int request = len;
+        int toCopy = Math.min((dest.length - dpos), request);
+        if (request > 0 && toCopy > 0) {
+            com.imagero.uio.Transformer.byteToFloat(src, spos, toCopy, dest, dpos, true);
+            if (toCopy < request) {
+                int copiedBytes = (toCopy << SHIFT);
+                save(offset + copiedBytes, spos + copiedBytes, src, (request - toCopy) << SHIFT);
+            }
+        }
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+
+    public long length() throws IOException {
+        long length = 0;
+        for (int i = 0; i < data.length; i++) {
+            float[] dest = data[i];
+            length += dest.length;
+        }
+        return length << SHIFT;
+    }
+
+    public boolean writable() {
+        return true;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/Span.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/Span.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/Span.java	(revision 0)
@@ -0,0 +1,16 @@
+package com.imagero.uio.bio.content;
+
+/**
+ * Date: 19.03.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class Span {
+    long offset;
+    long length;
+
+    public Span(long offset, long length) {
+        this.offset = offset;
+        this.length = length;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/FileCachedHTTPContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/FileCachedHTTPContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/FileCachedHTTPContent.java	(revision 0)
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.bio.content.HTTPContent;
+import com.imagero.uio.impl.TmpRandomAccessFile;
+import com.imagero.util.Vector;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class FileCachedHTTPContent extends HTTPContent {
+
+    File tmp;
+    TmpRandomAccessFile tmpRaf;
+
+    Vector ranges = new Vector();
+
+    public FileCachedHTTPContent(URL url, File tmp) throws IOException {
+        super(url);
+        this.tmp = tmp;
+        tmpRaf = new TmpRandomAccessFile(tmp, "rw");
+    }
+
+    public int load(long offset, int bpos, byte[] buffer) throws IOException {
+        if (contains(offset, buffer.length - bpos)) {
+            tmpRaf.seek(offset);
+            tmpRaf.readFully(buffer, bpos, buffer.length - bpos);
+            return buffer.length - bpos;
+        }
+        int k = super.load(offset, bpos, buffer);
+        if (k > 0) {
+            tmpRaf.seek(offset);
+            tmpRaf.write(buffer, bpos, k);
+            addRange(offset, k);
+        }
+        return k;
+    }
+
+    boolean contains(long offset, int length) {
+        Range r0 = new Range(offset, offset + length);
+        for (int i = 0; i < ranges.size(); i++) {
+            Range r = (Range) ranges.elementAt(i);
+            if (r.contains(r0)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    void addRange(long offset, int length) {
+        Range r0 = new Range(offset, offset + length);
+
+        for (int i = 0; i < ranges.size(); i++) {
+            Range r = (Range) ranges.elementAt(i);
+            if (r.canJoin(r0)) {
+                r.join(r0);
+                for (int j = 0; j < ranges.size(); j++) {
+                    Range r1 = (Range) ranges.elementAt(j);
+                    if (r != r1 && r.canJoin(r1)) {
+                        r.join(r1);
+                        j--;
+                        ranges.remove(r1);
+                    }
+                }
+                return;
+            }
+        }
+        ranges.add(r0);
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/DoubleArrayContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/DoubleArrayContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/DoubleArrayContent.java	(revision 0)
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.Transformer;
+
+import java.io.IOException;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class DoubleArrayContent extends Content {
+    double[][] data;
+    boolean bigEndian;
+
+    static final int SHIFT = 3;
+
+    public DoubleArrayContent(double[][] data) {
+        this(data, true);
+    }
+
+    public DoubleArrayContent(double[][] data, boolean bigEndian) {
+        this.data = data;
+        this.bigEndian = bigEndian;
+    }
+
+    public int load(long offset, int bpos, byte[] dest) throws IOException {
+        //to simplify long to byte converting, we require 8 bytes boundary
+        if ((offset & 7) != 0 || ((dest.length - bpos) & 7) != 0) {
+            throw new IOException("Illegal offset or length");
+        }
+        final long off = offset >> SHIFT;
+        final int len = (dest.length - bpos) >> SHIFT;
+
+        IndexAndStart ias = getIAS(off);
+        if (ias == null) {
+            return 0;
+        }
+        int index = ias.index;
+        int pos = (int) (off - ias.start);
+        double[] src = data[index];
+        int toCopy = Math.min(src.length - pos, len);
+        if (toCopy > 0) {
+            com.imagero.uio.Transformer.doubleToByte(src, pos, toCopy, dest, bpos, bigEndian);
+            if ((toCopy < len)) {
+                int copiedBytes = (toCopy << SHIFT);
+                return toCopy + load(offset + copiedBytes, bpos + copiedBytes, dest);
+            }
+        }
+        return toCopy;
+    }
+
+    public void close() {
+    }
+
+    private IndexAndStart getIAS(long offset) {
+        long start = 0;
+        for (int i = 0; i < data.length; i++) {
+            double[] src = data[i];
+            int length = src.length;
+            long end = start + length;
+            if (offset >= start && offset <= end) {
+                return new IndexAndStart(i, start);
+            }
+            start += length;
+        }
+        return null;
+    }
+
+    /**
+     * Save data to current content (char array).
+     * All offsets and lengths must be
+     * @param offset
+     * @param spos
+     * @param src
+     * @param length
+     * @throws IOException
+     */
+    public void save(long offset, int spos, byte[] src, int length) throws IOException {
+        //to simplify byte to long converting we require 8 byte boundary
+        if ((offset & 7) != 0 || ((src.length - spos) & 7) != 0) {
+            throw new IOException("Illegal offset or length");
+        }
+        final long off = offset >> SHIFT;
+        final int len = Math.min((src.length - spos), length) >> SHIFT;
+
+        IndexAndStart ias = getIAS(off);
+        if (ias == null) {
+            return;
+        }
+        int index = ias.index;
+        long start = ias.start;
+        int dpos = (int) (off - start);
+        double[] dest = data[index];
+        int request = len;
+        int toCopy = Math.min((dest.length - dpos), request);
+        if (request > 0 && toCopy > 0) {
+            com.imagero.uio.Transformer.byteToDouble(src, spos, toCopy, dest, dpos, true);
+            if (toCopy < request) {
+                int copiedBytes = (toCopy << SHIFT);
+                save(offset + copiedBytes, spos + copiedBytes, src, (request - toCopy) << SHIFT);
+            }
+        }
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+
+    public long length() throws IOException {
+        long length = 0;
+        for (int i = 0; i < data.length; i++) {
+            double[] dest = data[i];
+            length += dest.length;
+        }
+        return length << SHIFT;
+    }
+
+    public boolean writable() {
+        return true;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/SpannedRandomAccessFileContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/SpannedRandomAccessFileContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/SpannedRandomAccessFileContent.java	(revision 0)
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.impl.RandomAccessFileX;
+import com.imagero.uio.io.IOutils;
+import com.imagero.uio.io.UnexpectedEOFException;
+
+import java.io.RandomAccessFile;
+import java.io.File;
+import java.io.IOException;
+import java.io.EOFException;
+
+/**
+ * Content with access to one or more predefined areas in File or RandomAccessFile.
+ * Length can not be changed.
+ * UnexpectedEOFException is thrown if we try to write outside our length.
+ *
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class SpannedRandomAccessFileContent extends Content {
+    private RandomAccessFile raf;
+
+    Span[] spans;
+    long length;
+
+    public SpannedRandomAccessFileContent(File f, Span[] spans) throws IOException {
+        this(f, getMode(f), spans);
+    }
+
+    public SpannedRandomAccessFileContent(File f, String mode, Span[] spans) throws IOException {
+        this(new RandomAccessFileX(f, mode), spans);
+
+    }
+
+    static String getMode(File f) {
+        if (!f.exists() || f.canWrite()) {
+            return "rw";
+        } else {
+            return "r";
+        }
+    }
+
+    public SpannedRandomAccessFileContent(RandomAccessFile raf, Span[] spans) throws IOException {
+        this.raf = raf;
+        this.spans = spans;
+        long rafLength = raf.length();
+        for (int i = 0; i < spans.length; i++) {
+            Span span = spans[i];
+            if (span.offset > rafLength || span.offset + span.length > rafLength) {
+                throw new IOException("Illegal span: " + span.offset + " " + span.length);
+            }
+        }
+        for (int i = 0; i < spans.length; i++) {
+            length += spans[i].length;
+        }
+    }
+
+    Span currentSpan;
+    long spanOffset;
+
+    void seek(long offset) throws IOException {
+        int sp = 0;
+        while (offset > 0) {
+            Span span = spans[sp++];
+            if (offset < span.length) {
+                currentSpan = span;
+                spanOffset = offset;
+                raf.seek(span.offset + offset);
+                return;
+            } else {
+                offset -= span.length;
+            }
+        }
+    }
+
+    public int load(long offset, int bpos, byte[] b) throws IOException {
+        seek(offset);
+
+        long available = currentSpan.length - spanOffset;
+        int len = (int) Math.min(available, b.length - bpos);
+        if (len > 0) {
+            raf.readFully(b, bpos, len);
+            return len;
+        }
+        throw new EOFException();
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+
+    public void close() {
+        IOutils.closeStream(raf);
+    }
+
+    public boolean writable() {
+        return true;
+    }
+
+    public void save(long offset, int bpos, byte[] buffer, int length) throws IOException {
+        long len = length;
+        while (len > 0) {
+            seek(offset);
+            long available = currentSpan.length - spanOffset;
+            int w = (int) Math.min(available, len);
+            if (w == 0) {
+                throw new UnexpectedEOFException(length - len);
+            }
+            raf.write(buffer, bpos, w);
+            offset += w;
+            bpos += w;
+            len -= w;
+        }
+    }
+
+    public long length() throws IOException {
+        return length;
+    }
+
+    protected void finalize() throws Throwable {
+        super.finalize();
+        raf = null;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/CharArrayContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/CharArrayContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/CharArrayContent.java	(revision 0)
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.Transformer;
+
+import java.io.IOException;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class CharArrayContent extends Content {
+    char[][] data;
+    boolean bigEndian;
+
+    static final int SHIFT = 1;
+
+    public CharArrayContent(char[][] data) {
+        this(data, true);
+    }
+
+    public CharArrayContent(char[][] data, boolean bigEndian) {
+        this.data = data;
+        this.bigEndian = bigEndian;
+    }
+
+    public int load(long offset, int bpos, byte[] dest) throws IOException {
+        //to simplify char to byte converting, we require two bytes boundary
+        if ((offset & 1) != 0 || ((dest.length - bpos) & 1) != 0) {
+            throw new IOException("Illegal offset or length");
+        }
+        final long off = offset >> SHIFT;
+        final int len = (dest.length - bpos) >> SHIFT;
+
+        IndexAndStart ias = getIAS(off);
+        if (ias == null) {
+            return 0;
+        }
+        int index = ias.index;
+        int pos = (int) (off - ias.start);
+        char[] src = data[index];
+        int toCopy = Math.min(src.length - pos, len);
+        if (toCopy > 0) {
+            com.imagero.uio.Transformer.charToByte(src, pos, toCopy, dest, bpos, bigEndian);
+            if ((toCopy < len)) {
+                int copiedBytes = (toCopy << SHIFT);
+                return toCopy + load(offset + copiedBytes, bpos + copiedBytes, dest);
+            }
+        }
+        return toCopy;
+    }
+
+    public void close() {
+    }
+
+    private IndexAndStart getIAS(long offset) {
+        long start = 0;
+        for (int i = 0; i < data.length; i++) {
+            char[] src = data[i];
+            int length = src.length;
+            long end = start + length;
+            if (offset >= start && offset <= end) {
+                return new IndexAndStart(i, start);
+            }
+            start += length;
+        }
+        return null;
+    }
+
+    /**
+     * Save data to current content (char array).
+     * All offsets and lengths must be
+     * @param offset
+     * @param spos
+     * @param src
+     * @param length
+     * @throws IOException
+     */
+    public void save(long offset, int spos, byte[] src, int length) throws IOException {
+        //to simplify char to byte converting we require two byte boundary
+        if ((offset & 1) != 0 || ((src.length - spos) & 1) != 0) {
+            throw new IOException("Illegal offset or length");
+        }
+        final long off = offset >> SHIFT;
+        final int len = Math.min((src.length - spos), length) >> SHIFT;
+
+        IndexAndStart ias = getIAS(off);
+        if (ias == null) {
+            return;
+        }
+        int index = ias.index;
+        long start = ias.start;
+        int dpos = (int) (off - start);
+        char[] dest = data[index];
+        int request = len;
+        int toCopy = Math.min((dest.length - dpos), request);
+        if (request > 0 && toCopy > 0) {
+            com.imagero.uio.Transformer.byteToChar(src, spos, toCopy, dest, dpos, true);
+            if (toCopy < request) {
+                int copiedBytes = (toCopy << SHIFT);
+                save(offset + copiedBytes, spos + copiedBytes, src, (request - toCopy) << SHIFT);
+            }
+        }
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+
+    public long length() throws IOException {
+        long length = 0;
+        for (int i = 0; i < data.length; i++) {
+            char[] dest = data[i];
+            length += dest.length;
+        }
+        return length << SHIFT;
+    }
+
+    public boolean writable() {
+        return true;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/IndexAndStart.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/IndexAndStart.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/IndexAndStart.java	(revision 0)
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+/**
+ * Date: 06.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+class IndexAndStart {
+    //array index
+    int index;
+    //offset of first byte of array in stream
+    long start;
+
+    public IndexAndStart(int index, long start) {
+        this.index = index;
+        this.start = start;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/SynchronizedContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/SynchronizedContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/SynchronizedContent.java	(revision 0)
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import java.io.IOException;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class SynchronizedContent extends Content {
+
+    Content content;
+
+    public SynchronizedContent(Content content) {
+        this.content = content;
+    }
+
+    public synchronized int load(long offset, int bpos, byte[] buffer) throws IOException {
+        return content.load(offset, bpos, buffer);
+    }
+
+    public synchronized void save(long offset, int bpos, byte[] buffer, int length) throws IOException {
+        content.save(offset, bpos, buffer, length);
+    }
+
+    public synchronized long length() throws IOException {
+        return content.length();
+    }
+
+    public void close() {
+        content.close();
+    }
+
+    public boolean canReload() {
+        return content.canReload();
+    }
+
+    public boolean writable() {
+        return content.writable();
+    }
+
+    public Content getContent() {
+        return content;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/HTTPContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/HTTPContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/HTTPContent.java	(revision 0)
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.io.IOutils;
+import com.imagero.uio.io.UnexpectedEOFException;
+
+import java.net.URL;
+import java.net.HttpURLConnection;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class HTTPContent extends Content {
+    URL url;
+
+    long length;
+
+    public HTTPContent(URL url) {
+        String protocol = url.getProtocol();
+        if (!"http".equalsIgnoreCase(protocol)) {
+            throw new IllegalArgumentException("http protokol only");
+        }
+        this.url = url;
+    }
+
+    public int load(long offset, int bpos, byte[] buffer) throws IOException {
+        HttpURLConnection httpcon = (HttpURLConnection) url.openConnection();
+        httpcon.setAllowUserInteraction(true);
+        httpcon.setDoInput(true);
+        httpcon.setDoOutput(true);
+        httpcon.setRequestMethod("GET");
+        httpcon.setUseCaches(false);
+        httpcon.setRequestProperty("Range", "bytes=" + offset + "-" + (offset + buffer.length - bpos));
+        httpcon.connect();
+
+        int responseCode = httpcon.getResponseCode();
+        if (responseCode != 206) {
+            httpcon.disconnect();
+            throw new IOException("byteserving not supported by server");
+        }
+        InputStream in = httpcon.getInputStream();
+
+        int count = 0;
+        try {
+            int len = buffer.length - bpos;
+            IOutils.readFully(in, buffer, bpos, len);
+            count = len;
+        } catch (UnexpectedEOFException ex) {
+            count = (int) ex.getCount();
+        } finally {
+            httpcon.disconnect();
+        }
+        return count;
+    }
+
+    public void close() {
+    }
+
+    public void save(long offset, int bpos, byte[] buffer, int length) throws IOException {
+    }
+
+    public long length() throws IOException {
+        if (length == 0) {
+            HttpURLConnection httpcon = (HttpURLConnection) url.openConnection();
+            httpcon.setRequestMethod("HEAD");
+            httpcon.setUseCaches(false);
+            httpcon.connect();
+            length = httpcon.getContentLength();
+            httpcon.disconnect();
+        }
+        return length;
+    }
+
+    public boolean canReload() {
+        return false;
+    }
+
+    public boolean writable() {
+        return false;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/Content.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/Content.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/Content.java	(revision 0)
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import java.io.IOException;
+
+/**
+ *
+ * @author Andrey Kuznetsov
+ */
+public abstract class Content {
+
+    /**
+     * Load stream content to specified buffer
+     * @param offset stream offset
+     * @param buffer byte array
+     * @return how much bytes were loaded
+     * @throws java.io.IOException
+     */
+    public final int load(long offset, byte[] buffer) throws IOException {
+        return load(offset, 0, buffer);
+    }
+
+    /**
+     * Load stream content to specified buffer
+     * @param offset stream offset
+     * @param bpos buffer position
+     * @param buffer byte array
+     * @return how much bytes were loaded
+     * @throws java.io.IOException
+     */
+    public abstract int load(long offset, int bpos, byte[] buffer) throws IOException;
+
+    /**
+     * Save buffer content to stream.
+     * Not always supported.
+     * @param offset stream offset
+     * @param bpos buffer position
+     * @param buffer byte array
+     * @param length how much bytes should be saved
+     * @throws java.io.IOException
+     */
+    public abstract void save(long offset, int bpos, byte[] buffer, int length) throws IOException;
+
+    /**
+     * Get stream length. Not always known.
+     * @return
+     * @throws java.io.IOException
+     */
+    public abstract long length() throws IOException;
+
+    /**
+     * close stream
+     */
+    public abstract void close();
+
+    /**
+     * Determine if data may be reloaded or not.
+     * For example: data from InputStream cannot be reloaded,
+     * however if content uses file or memory based cache then it is possible to reload data.
+     * @return true if data can be reloaded.
+     */
+    public abstract boolean canReload();
+
+    public abstract boolean writable();
+
+    protected void finalize() throws Throwable {
+        super.finalize();
+        close();
+    }
+
+
+}
Index: ocean/src/com/imagero/uio/bio/content/IntArrayContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/IntArrayContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/IntArrayContent.java	(revision 0)
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.Transformer;
+
+import java.io.IOException;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class IntArrayContent extends Content {
+    int[][] data;
+    boolean bigEndian;
+
+    static final int SHIFT = 2;
+
+    public IntArrayContent(int[][] data) {
+        this(data, true);
+    }
+
+    public IntArrayContent(int[][] data, boolean bigEndian) {
+        this.data = data;
+        this.bigEndian = bigEndian;
+    }
+
+    public int load(long offset, int bpos, byte[] dest) throws IOException {
+        //to simplify int to byte converting, we require 4 bytes boundary
+        if ((offset & 3) != 0 || ((dest.length - bpos) & 3) != 0) {
+            throw new IOException("Illegal offset or length");
+        }
+        final long off = offset >> SHIFT;
+        final int len = (dest.length - bpos) >> SHIFT;
+
+        IndexAndStart ias = getIAS(off);
+        if (ias == null) {
+            return 0;
+        }
+        int index = ias.index;
+        int pos = (int) (off - ias.start);
+        int[] src = data[index];
+        int toCopy = Math.min(src.length - pos, len);
+        if (toCopy > 0) {
+            com.imagero.uio.Transformer.intToByte(src, pos, toCopy, dest, bpos, bigEndian);
+            if ((toCopy < len)) {
+                int copiedBytes = (toCopy << SHIFT);
+                return toCopy + load(offset + copiedBytes, bpos + copiedBytes, dest);
+            }
+        }
+        return toCopy;
+    }
+
+    public void close() {
+    }
+
+    private IndexAndStart getIAS(long offset) {
+        long start = 0;
+        for (int i = 0; i < data.length; i++) {
+            int[] src = data[i];
+            int length = src.length;
+            long end = start + length;
+            if (offset >= start && offset <= end) {
+                return new IndexAndStart(i, start);
+            }
+            start += length;
+        }
+        return null;
+    }
+
+    /**
+     * Save data to current content (char array).
+     * All offsets and lengths must be
+     * @param offset
+     * @param spos
+     * @param src
+     * @param length
+     * @throws IOException
+     */
+    public void save(long offset, int spos, byte[] src, int length) throws IOException {
+        //to simplify byte to int converting we require 4 byte boundary
+        if ((offset & 3) != 0 || ((src.length - spos) & 3) != 0) {
+            throw new IOException("Illegal offset or length");
+        }
+        final long off = offset >> SHIFT;
+        final int len = Math.min((src.length - spos), length) >> SHIFT;
+
+        IndexAndStart ias = getIAS(off);
+        if (ias == null) {
+            return;
+        }
+        int index = ias.index;
+        long start = ias.start;
+        int dpos = (int) (off - start);
+        int[] dest = data[index];
+        int request = len;
+        int toCopy = Math.min((dest.length - dpos), request);
+        if (request > 0 && toCopy > 0) {
+            com.imagero.uio.Transformer.byteToInt(src, spos, toCopy, dest, dpos, true);
+            if (toCopy < request) {
+                int copiedBytes = (toCopy << SHIFT);
+                save(offset + copiedBytes, spos + copiedBytes, src, (request - toCopy) << SHIFT);
+            }
+        }
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+
+    public long length() throws IOException {
+        long length = 0;
+        for (int i = 0; i < data.length; i++) {
+            int[] dest = data[i];
+            length += dest.length;
+        }
+        return length << SHIFT;
+    }
+
+    public boolean writable() {
+        return true;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/RandomAccessIOContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/RandomAccessIOContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/RandomAccessIOContent.java	(revision 0)
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.RandomAccessIO;
+import com.imagero.uio.RandomAccessInput;
+import com.imagero.uio.io.IOutils;
+
+import java.io.IOException;
+import java.io.EOFException;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class RandomAccessIOContent extends Content {
+    private RandomAccessIO rio;
+
+    public RandomAccessIOContent(RandomAccessIO rio) throws IOException {
+        this.rio = rio;
+    }
+
+    public int load(long offset, int bpos, byte[] b) throws IOException {
+        long max = rio.length() - offset;
+        int len = (int) Math.min(max, b.length - bpos);
+        if (len > 0) {
+            rio.seek(offset);
+            rio.readFully(b, bpos, len);
+            return len;
+        }
+        throw new EOFException();
+//            return 0;
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+
+    public void close() {
+        IOutils.closeStream((RandomAccessInput) rio);
+        rio = null;
+    }
+
+    public boolean writable() {
+        return true;
+    }
+
+    public void save(long offset, int bpos, byte[] buffer, int length) throws IOException {
+        rio.seek(offset);
+        try {
+            rio.write(buffer, bpos, length);
+        } catch (IndexOutOfBoundsException ex) {
+            ex.printStackTrace();
+        }
+    }
+
+    public long length() throws IOException {
+        return rio.length();
+    }
+
+    protected void finalize() throws Throwable {
+        super.finalize();
+        rio = null;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/SpannedRandomAccessInputContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/SpannedRandomAccessInputContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/SpannedRandomAccessInputContent.java	(revision 0)
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.RandomAccessInput;
+import com.imagero.uio.io.IOutils;
+
+import java.io.EOFException;
+import java.io.IOException;
+
+/**
+ * Content with access to one or more predefined areas in RandomAccessIO.
+ * Length can not be changed.
+ * UnexpectedEOFException is thrown if we try to write outside our length.
+ *
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class SpannedRandomAccessInputContent extends Content {
+    private RandomAccessInput rio;
+
+    Span[] spans;
+    long length;
+
+
+    public SpannedRandomAccessInputContent(RandomAccessInput rio, Span[] spans) throws IOException {
+        this.rio = rio;
+        this.spans = spans;
+        long rafLength = rio.length();
+        for (int i = 0; i < spans.length; i++) {
+            Span span = spans[i];
+            if (span.offset > rafLength || span.offset + span.length > rafLength) {
+                throw new IOException("Illegal span: " + span.offset + " " + span.length);
+            }
+        }
+        for (int i = 0; i < spans.length; i++) {
+            length += spans[i].length;
+        }
+    }
+
+    Span currentSpan;
+    long spanOffset;
+
+    void seek(long offset) throws IOException {
+        int sp = 0;
+        while (offset > 0) {
+            Span span = spans[sp++];
+            if (offset < span.length) {
+                currentSpan = span;
+                spanOffset = offset;
+                rio.seek(span.offset + offset);
+                return;
+            } else {
+                offset -= span.length;
+            }
+        }
+    }
+
+    public int load(long offset, int bpos, byte[] b) throws IOException {
+        seek(offset);
+
+        long available = currentSpan.length - spanOffset;
+        int len = (int) Math.min(available, b.length - bpos);
+        if (len > 0) {
+            rio.readFully(b, bpos, len);
+            return len;
+        }
+        throw new EOFException();
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+
+    public void close() {
+        IOutils.closeStream(rio);
+    }
+
+    public boolean writable() {
+        return false;
+    }
+
+    public void save(long offset, int bpos, byte[] buffer, int length) throws IOException {
+
+    }
+
+    public long length() throws IOException {
+        return length;
+    }
+
+    protected void finalize() throws Throwable {
+        super.finalize();
+        rio = null;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/DummyContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/DummyContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/DummyContent.java	(revision 0)
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import java.io.IOException;
+
+/**
+ * empty content which just tracks length.
+ */
+public class DummyContent extends Content {
+    long length;
+
+    public int load(long offset, int bpos, byte[] buffer) throws IOException {
+        return 0;
+    }
+
+    public void save(long offset, int bpos, byte[] buffer, int length) throws IOException {
+        this.length = Math.max(this.length, offset + length);
+    }
+
+    public long length() throws IOException {
+        return length;
+    }
+
+    public void close() {
+    }
+
+    public boolean canReload() {
+        return false;
+    }
+
+    public boolean writable() {
+        return true;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/MemoryCachedInputStreamContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/MemoryCachedInputStreamContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/MemoryCachedInputStreamContent.java	(revision 0)
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.io.IOutils;
+import com.imagero.uio.io.UnexpectedEOFException;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.EOFException;
+import java.util.Hashtable;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class MemoryCachedInputStreamContent extends Content {
+    InputStream in;
+    int chunkSize;
+    boolean finished;
+
+    int overflow = 5;
+
+    Hashtable ht = new Hashtable();
+
+    int lastChunkSize;
+
+    public MemoryCachedInputStreamContent(InputStream in, int chunkSize) {
+        this.in = in;
+        this.chunkSize = chunkSize;
+    }
+
+    public int load(long offset, int destOffset, byte[] dest) throws IOException {
+        long index = offset / chunkSize;
+        if (finished && index >= readCount) {
+            throw new EOFException();
+        }
+        if (!finished) {
+            try {
+                for (long i = readCount; i <= index; i++) {
+                    byte[] b = new byte[chunkSize];
+                    Chunk chunk = addChunk(b, i);   //first add then read
+                    int length = b.length;
+                    lastChunkSize = chunkSize;
+                    try {
+                        IOutils.readFully(in, b);
+                    } catch (UnexpectedEOFException ex) {
+                        length = (int) ex.getCount();
+                    }
+                    if (chunk.src.length != length) {
+                        if (length > 0) {
+                            b = new byte[length];
+                            System.arraycopy(chunk.src, 0, b, 0, length);
+                            chunk.src = b;
+                            lastChunkSize = length;
+                        } else {
+                            ht.remove(new Long(i));
+                        }
+                        finished = true;
+                        break;
+                    }
+                }
+            } catch (IOException ex) {
+                finished = true;
+            }
+        }
+        if (index <= readCount) {
+            return copyData(dest, destOffset, offset);
+        }
+        return 0;
+    }
+
+    protected void prepare() {
+        try {
+            for (long i = readCount; !finished; i++) {
+                byte[] b = new byte[chunkSize];
+                Chunk chunk = addChunk(b, i);   //first add then read
+                int length = b.length;
+                lastChunkSize = chunkSize;
+                try {
+                    IOutils.readFully(in, b);
+                } catch (UnexpectedEOFException ex) {
+                    length = (int) ex.getCount();
+                }
+                if (chunk.src.length != length) {
+                    if (length > 0) {
+                        b = new byte[length];
+                        System.arraycopy(chunk.src, 0, b, 0, length);
+                        chunk.src = b;
+                        lastChunkSize = length;
+                    } else {
+                        ht.remove(new Long(i));
+                    }
+                    finished = true;
+                    break;
+                }
+            }
+        } catch (IOException ex) {
+            finished = true;
+        }
+    }
+
+    private int copyData(byte[] dest, int destOffset, long streamOffset) {
+        long index = streamOffset / chunkSize;
+        Chunk chunk = (Chunk) ht.get(new Long(index));
+        if (chunk != null) {
+            return chunk.copyInterval(dest, destOffset, streamOffset);
+        }
+        return 0;
+    }
+
+    public void close() {
+    }
+
+    long readCount;
+
+    private Chunk addChunk(byte[] buf, long index) {
+        long start = index * chunkSize;
+        Chunk helper = new Chunk(buf, index, start);
+        readCount++;
+        ht.put(new Long(index), helper);
+        return helper;
+    }
+
+    class Chunk {
+        byte[] src;
+        long index;
+
+        long start;
+
+        private Chunk parent;
+
+        private Chunk left;
+        private Chunk right;
+
+        public Chunk(byte[] buf, long index, long start) {
+            this.src = buf;
+            this.index = index;
+            this.start = start;
+        }
+
+        /**
+         *
+         * @param dest destination array
+         * @param destOffset start offset in destination array
+         * @param absOffset absolute offset in stream
+         * @return how much bytes was copied
+         */
+        int copyInterval(byte[] dest, int destOffset, long absOffset) {
+            if (src != null) {
+                if ((start > absOffset) || (absOffset > start + src.length)) {
+                    throw new IndexOutOfBoundsException("Given offset is out of chunk bounds");
+                }
+                if (destOffset < 0 || destOffset > dest.length) {
+                    throw new IndexOutOfBoundsException("Illegal destination offset: " + destOffset);
+                }
+                int srcOffset = (int) (absOffset - start);
+                int length = Math.min(dest.length - destOffset, src.length - srcOffset);
+                System.arraycopy(src, srcOffset, dest, destOffset, length);
+                if (srcOffset == 0 && length == src.length) {
+                    free();
+                } else {
+                    if (srcOffset == 0) {
+                        //right part leftover
+                        byte[] buf = new byte[src.length - length];
+                        System.arraycopy(src, length, buf, 0, buf.length);
+                        src = buf;
+                    } else if (srcOffset + length == src.length) {
+                        //left part leftover
+                        byte[] buf = new byte[src.length - length];
+                        System.arraycopy(src, 0, buf, 0, buf.length);
+                        src = buf;
+                    } else {
+                        byte[] leftBuf = new byte[srcOffset];
+                        System.arraycopy(src, 0, leftBuf, 0, leftBuf.length);
+                        left = new Chunk(leftBuf, -1, start);
+                        left.parent = this;
+
+                        byte[] rightBuf = new byte[src.length - (srcOffset + length)];
+                        System.arraycopy(src, srcOffset + length, rightBuf, 0, rightBuf.length);
+                        right = new Chunk(rightBuf, -1, start + srcOffset + length);
+                        right.parent = this;
+
+                        src = null;
+                    }
+                }
+                return length;
+            } else {
+                Chunk chunk = getChild(absOffset);
+                if (chunk != null) {
+                    return chunk.copyInterval(dest, destOffset, absOffset);
+                }
+            }
+            return 0;
+        }
+
+        private Chunk getChild(long absOffset) {
+            if (right.start <= absOffset) {
+                if (right.src != null) {
+                    return right;
+                } else {
+                    return right.getChild(absOffset);
+                }
+            } else if (left.start <= absOffset) {
+                if (left.src != null) {
+                    return left;
+                } else {
+                    return left.getChild(absOffset);
+                }
+            }
+            return null;
+        }
+
+        private void free() {
+            if (parent != null) {
+                parent.removeChild(this);
+            } else {
+                ht.remove(new Long(index));
+            }
+        }
+
+        private void removeChild(Chunk c) {
+            if (c == null && c.parent != this) {
+                return;
+            }
+            if (c == left) {
+                left = null;
+            } else if (c == right) {
+                right = null;
+            } else {
+                return;
+            }
+
+            if (left == null && right == null) {
+                free();
+            } else {
+                if (left != null) {
+                    connectChild(left);
+                } else if (right != null) {
+                    connectChild(right);
+                }
+            }
+        }
+
+        private void connectChild(Chunk c) {
+            src = c.src;
+            left = c.left;
+            right = c.right;
+        }
+    }
+
+
+    public boolean canReload() {
+        return false;
+    }
+
+    public void save(long offset, int bpos, byte[] buffer, int length) throws IOException {
+    }
+
+    public long length() throws IOException {
+        if (!finished) {
+            prepare();
+        }
+        return (readCount - 1) * chunkSize + lastChunkSize;
+    }
+
+    public boolean writable() {
+        return false;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/ShortArrayContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/ShortArrayContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/ShortArrayContent.java	(revision 0)
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.Transformer;
+
+import java.io.IOException;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class ShortArrayContent extends Content {
+    short[][] data;
+    boolean bigEndian;
+
+    static final int SHIFT = 1;
+
+    public ShortArrayContent(short[][] data) {
+        this(data, true);
+    }
+
+    public ShortArrayContent(short[][] data, boolean bigEndian) {
+        this.data = data;
+        this.bigEndian = bigEndian;
+    }
+
+    public int load(long offset, int bpos, byte[] dest) throws IOException {
+        //to simplify short to byte converting, we require two bytes boundary
+        if ((offset & 1) != 0 || ((dest.length - bpos) & 1) != 0) {
+            throw new IOException("Illegal offset or length");
+        }
+        final long off = offset >> SHIFT;
+        final int len = (dest.length - bpos) >> SHIFT;
+
+        IndexAndStart ias = getIAS(off);
+        if (ias == null) {
+            return 0;
+        }
+        int index = ias.index;
+        int pos = (int) (off - ias.start);
+        short[] src = data[index];
+        int toCopy = Math.min(src.length - pos, len);
+        if (toCopy > 0) {
+            com.imagero.uio.Transformer.shortToByte(src, pos, toCopy, dest, bpos, bigEndian);
+            if ((toCopy < len)) {
+                int copiedBytes = (toCopy << SHIFT);
+                return toCopy + load(offset + copiedBytes, bpos + copiedBytes, dest);
+            }
+        }
+        return toCopy;
+    }
+
+    public void close() {
+    }
+
+    private IndexAndStart getIAS(long offset) {
+        long start = 0;
+        for (int i = 0; i < data.length; i++) {
+            short[] src = data[i];
+            int length = src.length;
+            long end = start + length;
+            if (offset >= start && offset <= end) {
+                return new IndexAndStart(i, start);
+            }
+            start += length;
+        }
+        return null;
+    }
+
+    /**
+     * Save data to current content (char array).
+     * All offsets and lengths must be
+     * @param offset
+     * @param spos
+     * @param src
+     * @param length
+     * @throws IOException
+     */
+    public void save(long offset, int spos, byte[] src, int length) throws IOException {
+        //to simplify byte to short converting we require two byte boundary
+        if ((offset & 1) != 0 || ((src.length - spos) & 1) != 0) {
+            throw new IOException("Illegal offset or length");
+        }
+        final long off = offset >> SHIFT;
+        final int len = Math.min((src.length - spos), length) >> SHIFT;
+
+        IndexAndStart ias = getIAS(off);
+        if (ias == null) {
+            return;
+        }
+        int index = ias.index;
+        long start = ias.start;
+        int dpos = (int) (off - start);
+        short[] dest = data[index];
+        int request = len;
+        int toCopy = Math.min((dest.length - dpos), request);
+        if (request > 0 && toCopy > 0) {
+            com.imagero.uio.Transformer.byteToShort(src, spos, toCopy, dest, dpos, true);
+            if (toCopy < request) {
+                int copiedBytes = (toCopy << SHIFT);
+                save(offset + copiedBytes, spos + copiedBytes, src, (request - toCopy) << SHIFT);
+            }
+        }
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+
+    public long length() throws IOException {
+        long length = 0;
+        for (int i = 0; i < data.length; i++) {
+            short[] dest = data[i];
+            length += dest.length;
+        }
+        return length << SHIFT;
+    }
+
+    public boolean writable() {
+        return true;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/LongArrayContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/LongArrayContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/LongArrayContent.java	(revision 0)
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.Transformer;
+
+import java.io.IOException;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class LongArrayContent extends Content {
+    long[][] data;
+    boolean bigEndian;
+
+    static final int SHIFT = 3;
+
+    public LongArrayContent(long[][] data) {
+        this(data, true);
+    }
+
+    public LongArrayContent(long[][] data, boolean bigEndian) {
+        this.data = data;
+        this.bigEndian = bigEndian;
+    }
+
+    public int load(long offset, int bpos, byte[] dest) throws IOException {
+        //to simplify long to byte converting, we require 8 bytes boundary
+        if ((offset & 7) != 0 || ((dest.length - bpos) & 7) != 0) {
+            throw new IOException("Illegal offset or length");
+        }
+        final long off = offset >> SHIFT;
+        final int len = (dest.length - bpos) >> SHIFT;
+
+        IndexAndStart ias = getIAS(off);
+        if (ias == null) {
+            return 0;
+        }
+        int index = ias.index;
+        int pos = (int) (off - ias.start);
+        long[] src = data[index];
+        int toCopy = Math.min(src.length - pos, len);
+        if (toCopy > 0) {
+            com.imagero.uio.Transformer.longToByte(src, pos, toCopy, dest, bpos, bigEndian);
+            if ((toCopy < len)) {
+                int copiedBytes = (toCopy << SHIFT);
+                return toCopy + load(offset + copiedBytes, bpos + copiedBytes, dest);
+            }
+        }
+        return toCopy;
+    }
+
+    public void close() {
+    }
+
+    private IndexAndStart getIAS(long offset) {
+        long start = 0;
+        for (int i = 0; i < data.length; i++) {
+            long[] src = data[i];
+            int length = src.length;
+            long end = start + length;
+            if (offset >= start && offset <= end) {
+                return new IndexAndStart(i, start);
+            }
+            start += length;
+        }
+        return null;
+    }
+
+    /**
+     * Save data to current content (char array).
+     * All offsets and lengths must be
+     * @param offset
+     * @param spos
+     * @param src
+     * @param length
+     * @throws IOException
+     */
+    public void save(long offset, int spos, byte[] src, int length) throws IOException {
+        //to simplify byte to long converting we require 8 byte boundary
+        if ((offset & 7) != 0 || ((src.length - spos) & 7) != 0) {
+            throw new IOException("Illegal offset or length");
+        }
+        final long off = offset >> SHIFT;
+        final int len = Math.min((src.length - spos), length) >> SHIFT;
+
+        IndexAndStart ias = getIAS(off);
+        if (ias == null) {
+            return;
+        }
+        int index = ias.index;
+        long start = ias.start;
+        int dpos = (int) (off - start);
+        long[] dest = data[index];
+        int request = len;
+        int toCopy = Math.min((dest.length - dpos), request);
+        if (request > 0 && toCopy > 0) {
+            com.imagero.uio.Transformer.byteToLong(src, spos, toCopy, dest, dpos, true);
+            if (toCopy < request) {
+                int copiedBytes = (toCopy << SHIFT);
+                save(offset + copiedBytes, spos + copiedBytes, src, (request - toCopy) << SHIFT);
+            }
+        }
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+
+    public long length() throws IOException {
+        long length = 0;
+        for (int i = 0; i < data.length; i++) {
+            long[] dest = data[i];
+            length += dest.length;
+        }
+        return length << SHIFT;
+    }
+
+    public boolean writable() {
+        return true;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/Range.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/Range.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/Range.java	(revision 0)
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+/**
+ * This class helps to track which interval was already filed with data.
+ * @author Andrey Kuznetsov
+ */
+public class Range {
+    long first;
+    long last;
+
+    public Range(long first, long last) {
+        if(first == last) {
+            throw new IllegalArgumentException("Empty range");
+        }
+        this.first = Math.min(first, last);
+        this.last = Math.max(first, last);
+    }
+
+    public boolean contains(Range r) {
+        return (r.first >= first && r.last <= last);
+    }
+
+    public boolean isOverlap(Range r) {
+        return ((r.first >= first && r.first <= last) || (r.last >= first && r.last <= last));
+    }
+
+    public boolean isNeighbor(Range r) {
+        return ((r.last + 1 == first) || (r.first - 1 == last));
+    }
+
+    public boolean canJoin(Range r) {
+        return isOverlap(r) || isNeighbor(r);
+    }
+
+    public void join(Range r) {
+        if(canJoin(r)) {
+            this.first = Math.min(first, r.first);
+            this.last = Math.max(last, r.last);
+        }
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/RandomAccessFileContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/RandomAccessFileContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/RandomAccessFileContent.java	(revision 0)
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.impl.RandomAccessFileX;
+import com.imagero.uio.io.IOutils;
+
+import java.io.RandomAccessFile;
+import java.io.File;
+import java.io.IOException;
+import java.io.EOFException;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class RandomAccessFileContent extends Content {
+    private RandomAccessFile raf;
+
+    public RandomAccessFileContent(File f) throws IOException {
+        this(f, getMode(f));
+    }
+
+    public RandomAccessFileContent(File f, String mode) throws IOException {
+        this(new RandomAccessFileX(f, mode));
+    }
+
+    static String getMode(File f) {
+        if (!f.exists() || f.canWrite()) {
+            return "rw";
+        } else {
+            return "r";
+        }
+    }
+
+    public RandomAccessFileContent(RandomAccessFile raf) {
+        this.raf = raf;
+    }
+
+    public int load(long offset, int bpos, byte[] b) throws IOException {
+        long max = raf.length() - offset;
+        int len = (int) Math.min(max, b.length - bpos);
+        if (len > 0) {
+            raf.seek(offset);
+            raf.readFully(b, bpos, len);
+            return len;
+        }
+        throw new EOFException();
+//            return 0;
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+
+    public void close() {
+        IOutils.closeStream(raf);
+    }
+
+    public boolean writable() {
+        return true;
+    }
+
+    public void save(long offset, int bpos, byte[] buffer, int length) throws IOException {
+        raf.seek(offset);
+        try {
+            raf.write(buffer, bpos, length);
+        } catch (IndexOutOfBoundsException ex) {
+            ex.printStackTrace();
+        }
+    }
+
+    public long length() throws IOException {
+        return raf.length();
+    }
+
+    protected void finalize() throws Throwable {
+        super.finalize();
+        raf = null;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/ByteArrayContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/ByteArrayContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/ByteArrayContent.java	(revision 0)
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import java.io.IOException;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class ByteArrayContent extends Content {
+    byte[][] data;
+
+    public ByteArrayContent(byte[][] data) {
+        this.data = data;
+    }
+
+    public int load(long offset, int bpos, byte[] buffer) throws IOException {
+        IndexAndStart ias = getIAS(offset);
+        if (ias == null) {
+            return 0;
+        }
+        int index = ias.index;
+        long start = ias.start;
+        int pos = (int) (offset - start);
+        byte[] src = data[index];
+        int toCopy = Math.min(src.length - pos, buffer.length - bpos);
+        if (toCopy > 0) {
+            System.arraycopy(src, pos, buffer, bpos, toCopy);
+            if ((toCopy < buffer.length - bpos)) {
+                return toCopy + load(offset + toCopy, bpos + toCopy, buffer);
+            }
+        }
+        return toCopy;
+    }
+
+    public void close() {
+    }
+
+    private IndexAndStart getIAS(long offset) {
+        long start = 0;
+        for (int i = 0; i < data.length; i++) {
+            byte[] src = data[i];
+            int length = src.length;
+            long end = start + length;
+            if (offset >= start && offset <= end) {
+                return new IndexAndStart(i, start);
+            }
+            start += length;
+        }
+        return null;
+    }
+
+    public void save(long offset, int spos, byte[] src, int length) throws IOException {
+        IndexAndStart ias = getIAS(offset);
+        if (ias == null) {
+            return;
+        }
+        int index = ias.index;
+        long start = ias.start;
+        int dpos = (int) (offset - start);
+        byte[] dest = data[index];
+        int request = Math.min(length, src.length - spos);
+        int toCopy = Math.min(dest.length - dpos, request);
+        if (request > 0 && toCopy > 0) {
+            System.arraycopy(src, spos, dest, dpos, toCopy);
+            if (toCopy < request) {
+                save(offset + toCopy, spos + toCopy, src, request - toCopy);
+            }
+        }
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+
+    public long length() throws IOException {
+        long length = 0;
+        for (int i = 0; i < data.length; i++) {
+            byte[] dest = data[i];
+            length += dest.length;
+        }
+        return length;
+    }
+
+    public boolean writable() {
+        return true;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/FileCachedInputStreamContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/FileCachedInputStreamContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/FileCachedInputStreamContent.java	(revision 0)
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.impl.TmpRandomAccessFile;
+import com.imagero.uio.io.IOutils;
+
+import java.io.InputStream;
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class FileCachedInputStreamContent extends Content {
+    InputStream in;
+    File tmp;
+    TmpRandomAccessFile tmpRaf;
+
+    public FileCachedInputStreamContent(InputStream in, File tmp) throws IOException {
+        this.in = in;
+        this.tmp = tmp;
+        tmpRaf = new TmpRandomAccessFile(tmp, "rw");
+    }
+
+    public int load(long offset, int bpos, byte[] buffer) throws IOException {
+        long length = tmpRaf.length();
+        long nl = offset + buffer.length - bpos;
+        if (length < nl) {
+            tmpRaf.seek(length);
+            IOutils.copy(nl - length, in, tmpRaf);
+        }
+        tmpRaf.seek(offset);
+        tmpRaf.readFully(buffer, bpos, buffer.length - bpos);
+        return buffer.length - bpos;
+    }
+
+    public void save(long offset, int bpos, byte[] buffer, int length) throws IOException {
+    }
+
+    public long length() throws IOException {
+        return tmp.length() + in.available();
+    }
+
+    public void close() {
+        IOutils.closeStream(tmpRaf);
+    }
+
+    protected void finalize() throws Throwable {
+        super.finalize();
+        IOutils.closeStream(tmpRaf);
+        tmpRaf = null;
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+
+    public boolean writable() {
+        return false;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/SpannedRandomAccessIOContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/SpannedRandomAccessIOContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/SpannedRandomAccessIOContent.java	(revision 0)
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.RandomAccessIO;
+import com.imagero.uio.RandomAccessInput;
+import com.imagero.uio.io.IOutils;
+import com.imagero.uio.io.UnexpectedEOFException;
+
+import java.io.EOFException;
+import java.io.IOException;
+
+/**
+ * Content with access to one or more predefined areas in RandomAccessIO.
+ * Length can not be changed.
+ * UnexpectedEOFException is thrown if we try to write outside our length.
+ *
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class SpannedRandomAccessIOContent extends Content {
+    private RandomAccessIO rio;
+
+    Span[] spans;
+    long length;
+
+
+    public SpannedRandomAccessIOContent(RandomAccessIO rio, Span[] spans) throws IOException {
+        this.rio = rio;
+        this.spans = spans;
+        long rafLength = rio.length();
+        for (int i = 0; i < spans.length; i++) {
+            Span span = spans[i];
+            if (span.offset > rafLength || span.offset + span.length > rafLength) {
+                throw new IOException("Illegal span: " + span.offset + " " + span.length);
+            }
+        }
+        for (int i = 0; i < spans.length; i++) {
+            length += spans[i].length;
+        }
+    }
+
+    Span currentSpan;
+    long spanOffset;
+
+    void seek(long offset) throws IOException {
+        int sp = 0;
+        while (offset >= 0) {
+            Span span = spans[sp++];
+            if (offset < span.length) {
+                currentSpan = span;
+                spanOffset = offset;
+                rio.seek(span.offset + offset);
+                return;
+            } else {
+                offset -= span.length;
+            }
+        }
+    }
+
+    public int load(long offset, int bpos, byte[] b) throws IOException {
+        seek(offset);
+
+        long available = currentSpan.length - spanOffset;
+        int len = (int) Math.min(available, b.length - bpos);
+        if (len > 0) {
+            rio.readFully(b, bpos, len);
+            return len;
+        }
+        throw new EOFException();
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+
+    public void close() {
+        IOutils.closeStream((RandomAccessInput) rio);
+    }
+
+    public boolean writable() {
+        return true;
+    }
+
+    public void save(long offset, int bpos, byte[] buffer, int length) throws IOException {
+        long len = length;
+        while (len > 0) {
+            seek(offset);
+            long available = currentSpan.length - spanOffset;
+            int w = (int) Math.min(available, len);
+            if (w == 0) {
+                throw new UnexpectedEOFException(length - len);
+            }
+            rio.write(buffer, bpos, w);
+            offset += w;
+            bpos += w;
+            len -= w;
+        }
+    }
+
+    public long length() throws IOException {
+        return length;
+    }
+
+    protected void finalize() throws Throwable {
+        super.finalize();
+        rio = null;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/ByteArrayContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/ByteArrayContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/ByteArrayContent.java	(revision 0)
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import java.io.IOException;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class ByteArrayContent extends Content {
+    byte[][] data;
+
+    public ByteArrayContent(byte[][] data) {
+        this.data = data;
+    }
+
+    public int load(long offset, int bpos, byte[] buffer) throws IOException {
+        IndexAndStart ias = getIAS(offset);
+        if (ias == null) {
+            return 0;
+        }
+        int index = ias.index;
+        long start = ias.start;
+        int pos = (int) (offset - start);
+        byte[] src = data[index];
+        int toCopy = Math.min(src.length - pos, buffer.length - bpos);
+        if (toCopy > 0) {
+            System.arraycopy(src, pos, buffer, bpos, toCopy);
+            if ((toCopy < buffer.length - bpos)) {
+                return toCopy + load(offset + toCopy, bpos + toCopy, buffer);
+            }
+        }
+        return toCopy;
+    }
+
+    public void close() {
+    }
+
+    private IndexAndStart getIAS(long offset) {
+        long start = 0;
+        for (int i = 0; i < data.length; i++) {
+            byte[] src = data[i];
+            int length = src.length;
+            long end = start + length;
+            if (offset >= start && offset <= end) {
+                return new IndexAndStart(i, start);
+            }
+            start += length;
+        }
+        return null;
+    }
+
+    public void save(long offset, int spos, byte[] src, int length) throws IOException {
+        IndexAndStart ias = getIAS(offset);
+        if (ias == null) {
+            return;
+        }
+        int index = ias.index;
+        long start = ias.start;
+        int dpos = (int) (offset - start);
+        byte[] dest = data[index];
+        int request = Math.min(length, src.length - spos);
+        int toCopy = Math.min(dest.length - dpos, request);
+        if (request > 0 && toCopy > 0) {
+            System.arraycopy(src, spos, dest, dpos, toCopy);
+            if (toCopy < request) {
+                save(offset + toCopy, spos + toCopy, src, request - toCopy);
+            }
+        }
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+
+    public long length() throws IOException {
+        long length = 0;
+        for (int i = 0; i < data.length; i++) {
+            byte[] dest = data[i];
+            length += dest.length;
+        }
+        return length;
+    }
+
+    public boolean writable() {
+        return true;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/CharArrayContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/CharArrayContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/CharArrayContent.java	(revision 0)
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.Transformer;
+
+import java.io.IOException;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class CharArrayContent extends Content {
+    char[][] data;
+    boolean bigEndian;
+
+    static final int SHIFT = 1;
+
+    public CharArrayContent(char[][] data) {
+        this(data, true);
+    }
+
+    public CharArrayContent(char[][] data, boolean bigEndian) {
+        this.data = data;
+        this.bigEndian = bigEndian;
+    }
+
+    public int load(long offset, int bpos, byte[] dest) throws IOException {
+        //to simplify char to byte converting, we require two bytes boundary
+        if ((offset & 1) != 0 || ((dest.length - bpos) & 1) != 0) {
+            throw new IOException("Illegal offset or length");
+        }
+        final long off = offset >> SHIFT;
+        final int len = (dest.length - bpos) >> SHIFT;
+
+        IndexAndStart ias = getIAS(off);
+        if (ias == null) {
+            return 0;
+        }
+        int index = ias.index;
+        int pos = (int) (off - ias.start);
+        char[] src = data[index];
+        int toCopy = Math.min(src.length - pos, len);
+        if (toCopy > 0) {
+            com.imagero.uio.Transformer.charToByte(src, pos, toCopy, dest, bpos, bigEndian);
+            if ((toCopy < len)) {
+                int copiedBytes = (toCopy << SHIFT);
+                return toCopy + load(offset + copiedBytes, bpos + copiedBytes, dest);
+            }
+        }
+        return toCopy;
+    }
+
+    public void close() {
+    }
+
+    private IndexAndStart getIAS(long offset) {
+        long start = 0;
+        for (int i = 0; i < data.length; i++) {
+            char[] src = data[i];
+            int length = src.length;
+            long end = start + length;
+            if (offset >= start && offset <= end) {
+                return new IndexAndStart(i, start);
+            }
+            start += length;
+        }
+        return null;
+    }
+
+    /**
+     * Save data to current content (char array).
+     * All offsets and lengths must be
+     * @param offset
+     * @param spos
+     * @param src
+     * @param length
+     * @throws IOException
+     */
+    public void save(long offset, int spos, byte[] src, int length) throws IOException {
+        //to simplify char to byte converting we require two byte boundary
+        if ((offset & 1) != 0 || ((src.length - spos) & 1) != 0) {
+            throw new IOException("Illegal offset or length");
+        }
+        final long off = offset >> SHIFT;
+        final int len = Math.min((src.length - spos), length) >> SHIFT;
+
+        IndexAndStart ias = getIAS(off);
+        if (ias == null) {
+            return;
+        }
+        int index = ias.index;
+        long start = ias.start;
+        int dpos = (int) (off - start);
+        char[] dest = data[index];
+        int request = len;
+        int toCopy = Math.min((dest.length - dpos), request);
+        if (request > 0 && toCopy > 0) {
+            com.imagero.uio.Transformer.byteToChar(src, spos, toCopy, dest, dpos, true);
+            if (toCopy < request) {
+                int copiedBytes = (toCopy << SHIFT);
+                save(offset + copiedBytes, spos + copiedBytes, src, (request - toCopy) << SHIFT);
+            }
+        }
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+
+    public long length() throws IOException {
+        long length = 0;
+        for (int i = 0; i < data.length; i++) {
+            char[] dest = data[i];
+            length += dest.length;
+        }
+        return length << SHIFT;
+    }
+
+    public boolean writable() {
+        return true;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/Content.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/Content.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/Content.java	(revision 0)
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import java.io.IOException;
+
+/**
+ *
+ * @author Andrey Kuznetsov
+ */
+public abstract class Content {
+
+    /**
+     * Load stream content to specified buffer
+     * @param offset stream offset
+     * @param buffer byte array
+     * @return how much bytes were loaded
+     * @throws java.io.IOException
+     */
+    public final int load(long offset, byte[] buffer) throws IOException {
+        return load(offset, 0, buffer);
+    }
+
+    /**
+     * Load stream content to specified buffer
+     * @param offset stream offset
+     * @param bpos buffer position
+     * @param buffer byte array
+     * @return how much bytes were loaded
+     * @throws java.io.IOException
+     */
+    public abstract int load(long offset, int bpos, byte[] buffer) throws IOException;
+
+    /**
+     * Save buffer content to stream.
+     * Not always supported.
+     * @param offset stream offset
+     * @param bpos buffer position
+     * @param buffer byte array
+     * @param length how much bytes should be saved
+     * @throws java.io.IOException
+     */
+    public abstract void save(long offset, int bpos, byte[] buffer, int length) throws IOException;
+
+    /**
+     * Get stream length. Not always known.
+     * @return
+     * @throws java.io.IOException
+     */
+    public abstract long length() throws IOException;
+
+    /**
+     * close stream
+     */
+    public abstract void close();
+
+    /**
+     * Determine if data may be reloaded or not.
+     * For example: data from InputStream cannot be reloaded,
+     * however if content uses file or memory based cache then it is possible to reload data.
+     * @return true if data can be reloaded.
+     */
+    public abstract boolean canReload();
+
+    public abstract boolean writable();
+
+    protected void finalize() throws Throwable {
+        super.finalize();
+        close();
+    }
+
+
+}
Index: ocean/src/com/imagero/uio/bio/content/DoubleArrayContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/DoubleArrayContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/DoubleArrayContent.java	(revision 0)
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.Transformer;
+
+import java.io.IOException;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class DoubleArrayContent extends Content {
+    double[][] data;
+    boolean bigEndian;
+
+    static final int SHIFT = 3;
+
+    public DoubleArrayContent(double[][] data) {
+        this(data, true);
+    }
+
+    public DoubleArrayContent(double[][] data, boolean bigEndian) {
+        this.data = data;
+        this.bigEndian = bigEndian;
+    }
+
+    public int load(long offset, int bpos, byte[] dest) throws IOException {
+        //to simplify long to byte converting, we require 8 bytes boundary
+        if ((offset & 7) != 0 || ((dest.length - bpos) & 7) != 0) {
+            throw new IOException("Illegal offset or length");
+        }
+        final long off = offset >> SHIFT;
+        final int len = (dest.length - bpos) >> SHIFT;
+
+        IndexAndStart ias = getIAS(off);
+        if (ias == null) {
+            return 0;
+        }
+        int index = ias.index;
+        int pos = (int) (off - ias.start);
+        double[] src = data[index];
+        int toCopy = Math.min(src.length - pos, len);
+        if (toCopy > 0) {
+            com.imagero.uio.Transformer.doubleToByte(src, pos, toCopy, dest, bpos, bigEndian);
+            if ((toCopy < len)) {
+                int copiedBytes = (toCopy << SHIFT);
+                return toCopy + load(offset + copiedBytes, bpos + copiedBytes, dest);
+            }
+        }
+        return toCopy;
+    }
+
+    public void close() {
+    }
+
+    private IndexAndStart getIAS(long offset) {
+        long start = 0;
+        for (int i = 0; i < data.length; i++) {
+            double[] src = data[i];
+            int length = src.length;
+            long end = start + length;
+            if (offset >= start && offset <= end) {
+                return new IndexAndStart(i, start);
+            }
+            start += length;
+        }
+        return null;
+    }
+
+    /**
+     * Save data to current content (char array).
+     * All offsets and lengths must be
+     * @param offset
+     * @param spos
+     * @param src
+     * @param length
+     * @throws IOException
+     */
+    public void save(long offset, int spos, byte[] src, int length) throws IOException {
+        //to simplify byte to long converting we require 8 byte boundary
+        if ((offset & 7) != 0 || ((src.length - spos) & 7) != 0) {
+            throw new IOException("Illegal offset or length");
+        }
+        final long off = offset >> SHIFT;
+        final int len = Math.min((src.length - spos), length) >> SHIFT;
+
+        IndexAndStart ias = getIAS(off);
+        if (ias == null) {
+            return;
+        }
+        int index = ias.index;
+        long start = ias.start;
+        int dpos = (int) (off - start);
+        double[] dest = data[index];
+        int request = len;
+        int toCopy = Math.min((dest.length - dpos), request);
+        if (request > 0 && toCopy > 0) {
+            com.imagero.uio.Transformer.byteToDouble(src, spos, toCopy, dest, dpos, true);
+            if (toCopy < request) {
+                int copiedBytes = (toCopy << SHIFT);
+                save(offset + copiedBytes, spos + copiedBytes, src, (request - toCopy) << SHIFT);
+            }
+        }
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+
+    public long length() throws IOException {
+        long length = 0;
+        for (int i = 0; i < data.length; i++) {
+            double[] dest = data[i];
+            length += dest.length;
+        }
+        return length << SHIFT;
+    }
+
+    public boolean writable() {
+        return true;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/DummyContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/DummyContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/DummyContent.java	(revision 0)
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import java.io.IOException;
+
+/**
+ * empty content which just tracks length.
+ */
+public class DummyContent extends Content {
+    long length;
+
+    public int load(long offset, int bpos, byte[] buffer) throws IOException {
+        return 0;
+    }
+
+    public void save(long offset, int bpos, byte[] buffer, int length) throws IOException {
+        this.length = Math.max(this.length, offset + length);
+    }
+
+    public long length() throws IOException {
+        return length;
+    }
+
+    public void close() {
+    }
+
+    public boolean canReload() {
+        return false;
+    }
+
+    public boolean writable() {
+        return true;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/FileCachedHTTPContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/FileCachedHTTPContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/FileCachedHTTPContent.java	(revision 0)
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.bio.content.HTTPContent;
+import com.imagero.uio.impl.TmpRandomAccessFile;
+import com.imagero.util.Vector;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class FileCachedHTTPContent extends HTTPContent {
+
+    File tmp;
+    TmpRandomAccessFile tmpRaf;
+
+    Vector ranges = new Vector();
+
+    public FileCachedHTTPContent(URL url, File tmp) throws IOException {
+        super(url);
+        this.tmp = tmp;
+        tmpRaf = new TmpRandomAccessFile(tmp, "rw");
+    }
+
+    public int load(long offset, int bpos, byte[] buffer) throws IOException {
+        if (contains(offset, buffer.length - bpos)) {
+            tmpRaf.seek(offset);
+            tmpRaf.readFully(buffer, bpos, buffer.length - bpos);
+            return buffer.length - bpos;
+        }
+        int k = super.load(offset, bpos, buffer);
+        if (k > 0) {
+            tmpRaf.seek(offset);
+            tmpRaf.write(buffer, bpos, k);
+            addRange(offset, k);
+        }
+        return k;
+    }
+
+    boolean contains(long offset, int length) {
+        Range r0 = new Range(offset, offset + length);
+        for (int i = 0; i < ranges.size(); i++) {
+            Range r = (Range) ranges.elementAt(i);
+            if (r.contains(r0)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    void addRange(long offset, int length) {
+        Range r0 = new Range(offset, offset + length);
+
+        for (int i = 0; i < ranges.size(); i++) {
+            Range r = (Range) ranges.elementAt(i);
+            if (r.canJoin(r0)) {
+                r.join(r0);
+                for (int j = 0; j < ranges.size(); j++) {
+                    Range r1 = (Range) ranges.elementAt(j);
+                    if (r != r1 && r.canJoin(r1)) {
+                        r.join(r1);
+                        j--;
+                        ranges.remove(r1);
+                    }
+                }
+                return;
+            }
+        }
+        ranges.add(r0);
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/FileCachedInputStreamContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/FileCachedInputStreamContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/FileCachedInputStreamContent.java	(revision 0)
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.impl.TmpRandomAccessFile;
+import com.imagero.uio.io.IOutils;
+
+import java.io.InputStream;
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class FileCachedInputStreamContent extends Content {
+    InputStream in;
+    File tmp;
+    TmpRandomAccessFile tmpRaf;
+
+    public FileCachedInputStreamContent(InputStream in, File tmp) throws IOException {
+        this.in = in;
+        this.tmp = tmp;
+        tmpRaf = new TmpRandomAccessFile(tmp, "rw");
+    }
+
+    public int load(long offset, int bpos, byte[] buffer) throws IOException {
+        long length = tmpRaf.length();
+        long nl = offset + buffer.length - bpos;
+        if (length < nl) {
+            tmpRaf.seek(length);
+            IOutils.copy(nl - length, in, tmpRaf);
+        }
+        tmpRaf.seek(offset);
+        tmpRaf.readFully(buffer, bpos, buffer.length - bpos);
+        return buffer.length - bpos;
+    }
+
+    public void save(long offset, int bpos, byte[] buffer, int length) throws IOException {
+    }
+
+    public long length() throws IOException {
+        return tmp.length() + in.available();
+    }
+
+    public void close() {
+        IOutils.closeStream(tmpRaf);
+    }
+
+    protected void finalize() throws Throwable {
+        super.finalize();
+        IOutils.closeStream(tmpRaf);
+        tmpRaf = null;
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+
+    public boolean writable() {
+        return false;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/FloatArrayContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/FloatArrayContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/FloatArrayContent.java	(revision 0)
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.Transformer;
+
+import java.io.IOException;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class FloatArrayContent extends Content {
+    float[][] data;
+    boolean bigEndian;
+
+    static final int SHIFT = 2;
+
+    public FloatArrayContent(float[][] data) {
+        this(data, true);
+    }
+
+    public FloatArrayContent(float[][] data, boolean bigEndian) {
+        this.data = data;
+        this.bigEndian = bigEndian;
+    }
+
+    public int load(long offset, int bpos, byte[] dest) throws IOException {
+        //to simplify float to byte converting, we require 4 bytes boundary
+        if ((offset & 3) != 0 || ((dest.length - bpos) & 3) != 0) {
+            throw new IOException("Illegal offset or length");
+        }
+        final long off = offset >> SHIFT;
+        final int len = (dest.length - bpos) >> SHIFT;
+
+        IndexAndStart ias = getIAS(off);
+        if (ias == null) {
+            return 0;
+        }
+        int index = ias.index;
+        int pos = (int) (off - ias.start);
+        float[] src = data[index];
+        int toCopy = Math.min(src.length - pos, len);
+        if (toCopy > 0) {
+            com.imagero.uio.Transformer.floatToByte(src, pos, toCopy, dest, bpos, bigEndian);
+            if ((toCopy < len)) {
+                int copiedBytes = (toCopy << SHIFT);
+                return toCopy + load(offset + copiedBytes, bpos + copiedBytes, dest);
+            }
+        }
+        return toCopy;
+    }
+
+    public void close() {
+    }
+
+    private IndexAndStart getIAS(long offset) {
+        long start = 0;
+        for (int i = 0; i < data.length; i++) {
+            float[] src = data[i];
+            int length = src.length;
+            long end = start + length;
+            if (offset >= start && offset <= end) {
+                return new IndexAndStart(i, start);
+            }
+            start += length;
+        }
+        return null;
+    }
+
+    /**
+     * Save data to current content (char array).
+     * All offsets and lengths must be
+     * @param offset
+     * @param spos
+     * @param src
+     * @param length
+     * @throws IOException
+     */
+    public void save(long offset, int spos, byte[] src, int length) throws IOException {
+        //to simplify byte to float converting we require 4 byte boundary
+        if ((offset & 3) != 0 || ((src.length - spos) & 3) != 0) {
+            throw new IOException("Illegal offset or length");
+        }
+        final long off = offset >> SHIFT;
+        final int len = Math.min((src.length - spos), length) >> SHIFT;
+
+        IndexAndStart ias = getIAS(off);
+        if (ias == null) {
+            return;
+        }
+        int index = ias.index;
+        long start = ias.start;
+        int dpos = (int) (off - start);
+        float[] dest = data[index];
+        int request = len;
+        int toCopy = Math.min((dest.length - dpos), request);
+        if (request > 0 && toCopy > 0) {
+            com.imagero.uio.Transformer.byteToFloat(src, spos, toCopy, dest, dpos, true);
+            if (toCopy < request) {
+                int copiedBytes = (toCopy << SHIFT);
+                save(offset + copiedBytes, spos + copiedBytes, src, (request - toCopy) << SHIFT);
+            }
+        }
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+
+    public long length() throws IOException {
+        long length = 0;
+        for (int i = 0; i < data.length; i++) {
+            float[] dest = data[i];
+            length += dest.length;
+        }
+        return length << SHIFT;
+    }
+
+    public boolean writable() {
+        return true;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/HTTPContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/HTTPContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/HTTPContent.java	(revision 0)
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.io.IOutils;
+import com.imagero.uio.io.UnexpectedEOFException;
+
+import java.net.URL;
+import java.net.HttpURLConnection;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class HTTPContent extends Content {
+    URL url;
+
+    long length;
+
+    public HTTPContent(URL url) {
+        String protocol = url.getProtocol();
+        if (!"http".equalsIgnoreCase(protocol)) {
+            throw new IllegalArgumentException("http protokol only");
+        }
+        this.url = url;
+    }
+
+    public int load(long offset, int bpos, byte[] buffer) throws IOException {
+        HttpURLConnection httpcon = (HttpURLConnection) url.openConnection();
+        httpcon.setAllowUserInteraction(true);
+        httpcon.setDoInput(true);
+        httpcon.setDoOutput(true);
+        httpcon.setRequestMethod("GET");
+        httpcon.setUseCaches(false);
+        httpcon.setRequestProperty("Range", "bytes=" + offset + "-" + (offset + buffer.length - bpos));
+        httpcon.connect();
+
+        int responseCode = httpcon.getResponseCode();
+        if (responseCode != 206) {
+            httpcon.disconnect();
+            throw new IOException("byteserving not supported by server");
+        }
+        InputStream in = httpcon.getInputStream();
+
+        int count = 0;
+        try {
+            int len = buffer.length - bpos;
+            IOutils.readFully(in, buffer, bpos, len);
+            count = len;
+        } catch (UnexpectedEOFException ex) {
+            count = (int) ex.getCount();
+        } finally {
+            httpcon.disconnect();
+        }
+        return count;
+    }
+
+    public void close() {
+    }
+
+    public void save(long offset, int bpos, byte[] buffer, int length) throws IOException {
+    }
+
+    public long length() throws IOException {
+        if (length == 0) {
+            HttpURLConnection httpcon = (HttpURLConnection) url.openConnection();
+            httpcon.setRequestMethod("HEAD");
+            httpcon.setUseCaches(false);
+            httpcon.connect();
+            length = httpcon.getContentLength();
+            httpcon.disconnect();
+        }
+        return length;
+    }
+
+    public boolean canReload() {
+        return false;
+    }
+
+    public boolean writable() {
+        return false;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/IndexAndStart.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/IndexAndStart.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/IndexAndStart.java	(revision 0)
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+/**
+ * Date: 06.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+class IndexAndStart {
+    //array index
+    int index;
+    //offset of first byte of array in stream
+    long start;
+
+    public IndexAndStart(int index, long start) {
+        this.index = index;
+        this.start = start;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/IntArrayContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/IntArrayContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/IntArrayContent.java	(revision 0)
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.Transformer;
+
+import java.io.IOException;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class IntArrayContent extends Content {
+    int[][] data;
+    boolean bigEndian;
+
+    static final int SHIFT = 2;
+
+    public IntArrayContent(int[][] data) {
+        this(data, true);
+    }
+
+    public IntArrayContent(int[][] data, boolean bigEndian) {
+        this.data = data;
+        this.bigEndian = bigEndian;
+    }
+
+    public int load(long offset, int bpos, byte[] dest) throws IOException {
+        //to simplify int to byte converting, we require 4 bytes boundary
+        if ((offset & 3) != 0 || ((dest.length - bpos) & 3) != 0) {
+            throw new IOException("Illegal offset or length");
+        }
+        final long off = offset >> SHIFT;
+        final int len = (dest.length - bpos) >> SHIFT;
+
+        IndexAndStart ias = getIAS(off);
+        if (ias == null) {
+            return 0;
+        }
+        int index = ias.index;
+        int pos = (int) (off - ias.start);
+        int[] src = data[index];
+        int toCopy = Math.min(src.length - pos, len);
+        if (toCopy > 0) {
+            com.imagero.uio.Transformer.intToByte(src, pos, toCopy, dest, bpos, bigEndian);
+            if ((toCopy < len)) {
+                int copiedBytes = (toCopy << SHIFT);
+                return toCopy + load(offset + copiedBytes, bpos + copiedBytes, dest);
+            }
+        }
+        return toCopy;
+    }
+
+    public void close() {
+    }
+
+    private IndexAndStart getIAS(long offset) {
+        long start = 0;
+        for (int i = 0; i < data.length; i++) {
+            int[] src = data[i];
+            int length = src.length;
+            long end = start + length;
+            if (offset >= start && offset <= end) {
+                return new IndexAndStart(i, start);
+            }
+            start += length;
+        }
+        return null;
+    }
+
+    /**
+     * Save data to current content (char array).
+     * All offsets and lengths must be
+     * @param offset
+     * @param spos
+     * @param src
+     * @param length
+     * @throws IOException
+     */
+    public void save(long offset, int spos, byte[] src, int length) throws IOException {
+        //to simplify byte to int converting we require 4 byte boundary
+        if ((offset & 3) != 0 || ((src.length - spos) & 3) != 0) {
+            throw new IOException("Illegal offset or length");
+        }
+        final long off = offset >> SHIFT;
+        final int len = Math.min((src.length - spos), length) >> SHIFT;
+
+        IndexAndStart ias = getIAS(off);
+        if (ias == null) {
+            return;
+        }
+        int index = ias.index;
+        long start = ias.start;
+        int dpos = (int) (off - start);
+        int[] dest = data[index];
+        int request = len;
+        int toCopy = Math.min((dest.length - dpos), request);
+        if (request > 0 && toCopy > 0) {
+            com.imagero.uio.Transformer.byteToInt(src, spos, toCopy, dest, dpos, true);
+            if (toCopy < request) {
+                int copiedBytes = (toCopy << SHIFT);
+                save(offset + copiedBytes, spos + copiedBytes, src, (request - toCopy) << SHIFT);
+            }
+        }
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+
+    public long length() throws IOException {
+        long length = 0;
+        for (int i = 0; i < data.length; i++) {
+            int[] dest = data[i];
+            length += dest.length;
+        }
+        return length << SHIFT;
+    }
+
+    public boolean writable() {
+        return true;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/LongArrayContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/LongArrayContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/LongArrayContent.java	(revision 0)
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.Transformer;
+
+import java.io.IOException;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class LongArrayContent extends Content {
+    long[][] data;
+    boolean bigEndian;
+
+    static final int SHIFT = 3;
+
+    public LongArrayContent(long[][] data) {
+        this(data, true);
+    }
+
+    public LongArrayContent(long[][] data, boolean bigEndian) {
+        this.data = data;
+        this.bigEndian = bigEndian;
+    }
+
+    public int load(long offset, int bpos, byte[] dest) throws IOException {
+        //to simplify long to byte converting, we require 8 bytes boundary
+        if ((offset & 7) != 0 || ((dest.length - bpos) & 7) != 0) {
+            throw new IOException("Illegal offset or length");
+        }
+        final long off = offset >> SHIFT;
+        final int len = (dest.length - bpos) >> SHIFT;
+
+        IndexAndStart ias = getIAS(off);
+        if (ias == null) {
+            return 0;
+        }
+        int index = ias.index;
+        int pos = (int) (off - ias.start);
+        long[] src = data[index];
+        int toCopy = Math.min(src.length - pos, len);
+        if (toCopy > 0) {
+            com.imagero.uio.Transformer.longToByte(src, pos, toCopy, dest, bpos, bigEndian);
+            if ((toCopy < len)) {
+                int copiedBytes = (toCopy << SHIFT);
+                return toCopy + load(offset + copiedBytes, bpos + copiedBytes, dest);
+            }
+        }
+        return toCopy;
+    }
+
+    public void close() {
+    }
+
+    private IndexAndStart getIAS(long offset) {
+        long start = 0;
+        for (int i = 0; i < data.length; i++) {
+            long[] src = data[i];
+            int length = src.length;
+            long end = start + length;
+            if (offset >= start && offset <= end) {
+                return new IndexAndStart(i, start);
+            }
+            start += length;
+        }
+        return null;
+    }
+
+    /**
+     * Save data to current content (char array).
+     * All offsets and lengths must be
+     * @param offset
+     * @param spos
+     * @param src
+     * @param length
+     * @throws IOException
+     */
+    public void save(long offset, int spos, byte[] src, int length) throws IOException {
+        //to simplify byte to long converting we require 8 byte boundary
+        if ((offset & 7) != 0 || ((src.length - spos) & 7) != 0) {
+            throw new IOException("Illegal offset or length");
+        }
+        final long off = offset >> SHIFT;
+        final int len = Math.min((src.length - spos), length) >> SHIFT;
+
+        IndexAndStart ias = getIAS(off);
+        if (ias == null) {
+            return;
+        }
+        int index = ias.index;
+        long start = ias.start;
+        int dpos = (int) (off - start);
+        long[] dest = data[index];
+        int request = len;
+        int toCopy = Math.min((dest.length - dpos), request);
+        if (request > 0 && toCopy > 0) {
+            com.imagero.uio.Transformer.byteToLong(src, spos, toCopy, dest, dpos, true);
+            if (toCopy < request) {
+                int copiedBytes = (toCopy << SHIFT);
+                save(offset + copiedBytes, spos + copiedBytes, src, (request - toCopy) << SHIFT);
+            }
+        }
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+
+    public long length() throws IOException {
+        long length = 0;
+        for (int i = 0; i < data.length; i++) {
+            long[] dest = data[i];
+            length += dest.length;
+        }
+        return length << SHIFT;
+    }
+
+    public boolean writable() {
+        return true;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/MemoryCachedInputStreamContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/MemoryCachedInputStreamContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/MemoryCachedInputStreamContent.java	(revision 0)
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.io.IOutils;
+import com.imagero.uio.io.UnexpectedEOFException;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.EOFException;
+import java.util.Hashtable;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class MemoryCachedInputStreamContent extends Content {
+    InputStream in;
+    int chunkSize;
+    boolean finished;
+
+    int overflow = 5;
+
+    Hashtable ht = new Hashtable();
+
+    int lastChunkSize;
+
+    public MemoryCachedInputStreamContent(InputStream in, int chunkSize) {
+        this.in = in;
+        this.chunkSize = chunkSize;
+    }
+
+    public int load(long offset, int destOffset, byte[] dest) throws IOException {
+        long index = offset / chunkSize;
+        if (finished && index >= readCount) {
+            throw new EOFException();
+        }
+        if (!finished) {
+            try {
+                for (long i = readCount; i <= index; i++) {
+                    byte[] b = new byte[chunkSize];
+                    Chunk chunk = addChunk(b, i);   //first add then read
+                    int length = b.length;
+                    lastChunkSize = chunkSize;
+                    try {
+                        IOutils.readFully(in, b);
+                    } catch (UnexpectedEOFException ex) {
+                        length = (int) ex.getCount();
+                    }
+                    if (chunk.src.length != length) {
+                        if (length > 0) {
+                            b = new byte[length];
+                            System.arraycopy(chunk.src, 0, b, 0, length);
+                            chunk.src = b;
+                            lastChunkSize = length;
+                        } else {
+                            ht.remove(new Long(i));
+                        }
+                        finished = true;
+                        break;
+                    }
+                }
+            } catch (IOException ex) {
+                finished = true;
+            }
+        }
+        if (index <= readCount) {
+            return copyData(dest, destOffset, offset);
+        }
+        return 0;
+    }
+
+    protected void prepare() {
+        try {
+            for (long i = readCount; !finished; i++) {
+                byte[] b = new byte[chunkSize];
+                Chunk chunk = addChunk(b, i);   //first add then read
+                int length = b.length;
+                lastChunkSize = chunkSize;
+                try {
+                    IOutils.readFully(in, b);
+                } catch (UnexpectedEOFException ex) {
+                    length = (int) ex.getCount();
+                }
+                if (chunk.src.length != length) {
+                    if (length > 0) {
+                        b = new byte[length];
+                        System.arraycopy(chunk.src, 0, b, 0, length);
+                        chunk.src = b;
+                        lastChunkSize = length;
+                    } else {
+                        ht.remove(new Long(i));
+                    }
+                    finished = true;
+                    break;
+                }
+            }
+        } catch (IOException ex) {
+            finished = true;
+        }
+    }
+
+    private int copyData(byte[] dest, int destOffset, long streamOffset) {
+        long index = streamOffset / chunkSize;
+        Chunk chunk = (Chunk) ht.get(new Long(index));
+        if (chunk != null) {
+            return chunk.copyInterval(dest, destOffset, streamOffset);
+        }
+        return 0;
+    }
+
+    public void close() {
+    }
+
+    long readCount;
+
+    private Chunk addChunk(byte[] buf, long index) {
+        long start = index * chunkSize;
+        Chunk helper = new Chunk(buf, index, start);
+        readCount++;
+        ht.put(new Long(index), helper);
+        return helper;
+    }
+
+    class Chunk {
+        byte[] src;
+        long index;
+
+        long start;
+
+        private Chunk parent;
+
+        private Chunk left;
+        private Chunk right;
+
+        public Chunk(byte[] buf, long index, long start) {
+            this.src = buf;
+            this.index = index;
+            this.start = start;
+        }
+
+        /**
+         *
+         * @param dest destination array
+         * @param destOffset start offset in destination array
+         * @param absOffset absolute offset in stream
+         * @return how much bytes was copied
+         */
+        int copyInterval(byte[] dest, int destOffset, long absOffset) {
+            if (src != null) {
+                if ((start > absOffset) || (absOffset > start + src.length)) {
+                    throw new IndexOutOfBoundsException("Given offset is out of chunk bounds");
+                }
+                if (destOffset < 0 || destOffset > dest.length) {
+                    throw new IndexOutOfBoundsException("Illegal destination offset: " + destOffset);
+                }
+                int srcOffset = (int) (absOffset - start);
+                int length = Math.min(dest.length - destOffset, src.length - srcOffset);
+                System.arraycopy(src, srcOffset, dest, destOffset, length);
+                if (srcOffset == 0 && length == src.length) {
+                    free();
+                } else {
+                    if (srcOffset == 0) {
+                        //right part leftover
+                        byte[] buf = new byte[src.length - length];
+                        System.arraycopy(src, length, buf, 0, buf.length);
+                        src = buf;
+                    } else if (srcOffset + length == src.length) {
+                        //left part leftover
+                        byte[] buf = new byte[src.length - length];
+                        System.arraycopy(src, 0, buf, 0, buf.length);
+                        src = buf;
+                    } else {
+                        byte[] leftBuf = new byte[srcOffset];
+                        System.arraycopy(src, 0, leftBuf, 0, leftBuf.length);
+                        left = new Chunk(leftBuf, -1, start);
+                        left.parent = this;
+
+                        byte[] rightBuf = new byte[src.length - (srcOffset + length)];
+                        System.arraycopy(src, srcOffset + length, rightBuf, 0, rightBuf.length);
+                        right = new Chunk(rightBuf, -1, start + srcOffset + length);
+                        right.parent = this;
+
+                        src = null;
+                    }
+                }
+                return length;
+            } else {
+                Chunk chunk = getChild(absOffset);
+                if (chunk != null) {
+                    return chunk.copyInterval(dest, destOffset, absOffset);
+                }
+            }
+            return 0;
+        }
+
+        private Chunk getChild(long absOffset) {
+            if (right.start <= absOffset) {
+                if (right.src != null) {
+                    return right;
+                } else {
+                    return right.getChild(absOffset);
+                }
+            } else if (left.start <= absOffset) {
+                if (left.src != null) {
+                    return left;
+                } else {
+                    return left.getChild(absOffset);
+                }
+            }
+            return null;
+        }
+
+        private void free() {
+            if (parent != null) {
+                parent.removeChild(this);
+            } else {
+                ht.remove(new Long(index));
+            }
+        }
+
+        private void removeChild(Chunk c) {
+            if (c == null && c.parent != this) {
+                return;
+            }
+            if (c == left) {
+                left = null;
+            } else if (c == right) {
+                right = null;
+            } else {
+                return;
+            }
+
+            if (left == null && right == null) {
+                free();
+            } else {
+                if (left != null) {
+                    connectChild(left);
+                } else if (right != null) {
+                    connectChild(right);
+                }
+            }
+        }
+
+        private void connectChild(Chunk c) {
+            src = c.src;
+            left = c.left;
+            right = c.right;
+        }
+    }
+
+
+    public boolean canReload() {
+        return false;
+    }
+
+    public void save(long offset, int bpos, byte[] buffer, int length) throws IOException {
+    }
+
+    public long length() throws IOException {
+        if (!finished) {
+            prepare();
+        }
+        return (readCount - 1) * chunkSize + lastChunkSize;
+    }
+
+    public boolean writable() {
+        return false;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/RandomAccessFileContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/RandomAccessFileContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/RandomAccessFileContent.java	(revision 0)
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.impl.RandomAccessFileX;
+import com.imagero.uio.io.IOutils;
+
+import java.io.RandomAccessFile;
+import java.io.File;
+import java.io.IOException;
+import java.io.EOFException;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class RandomAccessFileContent extends Content {
+    private RandomAccessFile raf;
+
+    public RandomAccessFileContent(File f) throws IOException {
+        this(f, getMode(f));
+    }
+
+    public RandomAccessFileContent(File f, String mode) throws IOException {
+        this(new RandomAccessFileX(f, mode));
+    }
+
+    static String getMode(File f) {
+        if (!f.exists() || f.canWrite()) {
+            return "rw";
+        } else {
+            return "r";
+        }
+    }
+
+    public RandomAccessFileContent(RandomAccessFile raf) {
+        this.raf = raf;
+    }
+
+    public int load(long offset, int bpos, byte[] b) throws IOException {
+        long max = raf.length() - offset;
+        int len = (int) Math.min(max, b.length - bpos);
+        if (len > 0) {
+            raf.seek(offset);
+            raf.readFully(b, bpos, len);
+            return len;
+        }
+        throw new EOFException();
+//            return 0;
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+
+    public void close() {
+        IOutils.closeStream(raf);
+    }
+
+    public boolean writable() {
+        return true;
+    }
+
+    public void save(long offset, int bpos, byte[] buffer, int length) throws IOException {
+        raf.seek(offset);
+        try {
+            raf.write(buffer, bpos, length);
+        } catch (IndexOutOfBoundsException ex) {
+            ex.printStackTrace();
+        }
+    }
+
+    public long length() throws IOException {
+        return raf.length();
+    }
+
+    protected void finalize() throws Throwable {
+        super.finalize();
+        raf = null;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/RandomAccessIOContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/RandomAccessIOContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/RandomAccessIOContent.java	(revision 0)
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.RandomAccessIO;
+import com.imagero.uio.RandomAccessInput;
+import com.imagero.uio.io.IOutils;
+
+import java.io.IOException;
+import java.io.EOFException;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class RandomAccessIOContent extends Content {
+    private RandomAccessIO rio;
+
+    public RandomAccessIOContent(RandomAccessIO rio) throws IOException {
+        this.rio = rio;
+    }
+
+    public int load(long offset, int bpos, byte[] b) throws IOException {
+        long max = rio.length() - offset;
+        int len = (int) Math.min(max, b.length - bpos);
+        if (len > 0) {
+            rio.seek(offset);
+            rio.readFully(b, bpos, len);
+            return len;
+        }
+        throw new EOFException();
+//            return 0;
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+
+    public void close() {
+        IOutils.closeStream((RandomAccessInput) rio);
+        rio = null;
+    }
+
+    public boolean writable() {
+        return true;
+    }
+
+    public void save(long offset, int bpos, byte[] buffer, int length) throws IOException {
+        rio.seek(offset);
+        try {
+            rio.write(buffer, bpos, length);
+        } catch (IndexOutOfBoundsException ex) {
+            ex.printStackTrace();
+        }
+    }
+
+    public long length() throws IOException {
+        return rio.length();
+    }
+
+    protected void finalize() throws Throwable {
+        super.finalize();
+        rio = null;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/Range.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/Range.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/Range.java	(revision 0)
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+/**
+ * This class helps to track which interval was already filed with data.
+ * @author Andrey Kuznetsov
+ */
+public class Range {
+    long first;
+    long last;
+
+    public Range(long first, long last) {
+        if(first == last) {
+            throw new IllegalArgumentException("Empty range");
+        }
+        this.first = Math.min(first, last);
+        this.last = Math.max(first, last);
+    }
+
+    public boolean contains(Range r) {
+        return (r.first >= first && r.last <= last);
+    }
+
+    public boolean isOverlap(Range r) {
+        return ((r.first >= first && r.first <= last) || (r.last >= first && r.last <= last));
+    }
+
+    public boolean isNeighbor(Range r) {
+        return ((r.last + 1 == first) || (r.first - 1 == last));
+    }
+
+    public boolean canJoin(Range r) {
+        return isOverlap(r) || isNeighbor(r);
+    }
+
+    public void join(Range r) {
+        if(canJoin(r)) {
+            this.first = Math.min(first, r.first);
+            this.last = Math.max(last, r.last);
+        }
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/ShortArrayContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/ShortArrayContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/ShortArrayContent.java	(revision 0)
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.Transformer;
+
+import java.io.IOException;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class ShortArrayContent extends Content {
+    short[][] data;
+    boolean bigEndian;
+
+    static final int SHIFT = 1;
+
+    public ShortArrayContent(short[][] data) {
+        this(data, true);
+    }
+
+    public ShortArrayContent(short[][] data, boolean bigEndian) {
+        this.data = data;
+        this.bigEndian = bigEndian;
+    }
+
+    public int load(long offset, int bpos, byte[] dest) throws IOException {
+        //to simplify short to byte converting, we require two bytes boundary
+        if ((offset & 1) != 0 || ((dest.length - bpos) & 1) != 0) {
+            throw new IOException("Illegal offset or length");
+        }
+        final long off = offset >> SHIFT;
+        final int len = (dest.length - bpos) >> SHIFT;
+
+        IndexAndStart ias = getIAS(off);
+        if (ias == null) {
+            return 0;
+        }
+        int index = ias.index;
+        int pos = (int) (off - ias.start);
+        short[] src = data[index];
+        int toCopy = Math.min(src.length - pos, len);
+        if (toCopy > 0) {
+            com.imagero.uio.Transformer.shortToByte(src, pos, toCopy, dest, bpos, bigEndian);
+            if ((toCopy < len)) {
+                int copiedBytes = (toCopy << SHIFT);
+                return toCopy + load(offset + copiedBytes, bpos + copiedBytes, dest);
+            }
+        }
+        return toCopy;
+    }
+
+    public void close() {
+    }
+
+    private IndexAndStart getIAS(long offset) {
+        long start = 0;
+        for (int i = 0; i < data.length; i++) {
+            short[] src = data[i];
+            int length = src.length;
+            long end = start + length;
+            if (offset >= start && offset <= end) {
+                return new IndexAndStart(i, start);
+            }
+            start += length;
+        }
+        return null;
+    }
+
+    /**
+     * Save data to current content (char array).
+     * All offsets and lengths must be
+     * @param offset
+     * @param spos
+     * @param src
+     * @param length
+     * @throws IOException
+     */
+    public void save(long offset, int spos, byte[] src, int length) throws IOException {
+        //to simplify byte to short converting we require two byte boundary
+        if ((offset & 1) != 0 || ((src.length - spos) & 1) != 0) {
+            throw new IOException("Illegal offset or length");
+        }
+        final long off = offset >> SHIFT;
+        final int len = Math.min((src.length - spos), length) >> SHIFT;
+
+        IndexAndStart ias = getIAS(off);
+        if (ias == null) {
+            return;
+        }
+        int index = ias.index;
+        long start = ias.start;
+        int dpos = (int) (off - start);
+        short[] dest = data[index];
+        int request = len;
+        int toCopy = Math.min((dest.length - dpos), request);
+        if (request > 0 && toCopy > 0) {
+            com.imagero.uio.Transformer.byteToShort(src, spos, toCopy, dest, dpos, true);
+            if (toCopy < request) {
+                int copiedBytes = (toCopy << SHIFT);
+                save(offset + copiedBytes, spos + copiedBytes, src, (request - toCopy) << SHIFT);
+            }
+        }
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+
+    public long length() throws IOException {
+        long length = 0;
+        for (int i = 0; i < data.length; i++) {
+            short[] dest = data[i];
+            length += dest.length;
+        }
+        return length << SHIFT;
+    }
+
+    public boolean writable() {
+        return true;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/Span.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/Span.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/Span.java	(revision 0)
@@ -0,0 +1,16 @@
+package com.imagero.uio.bio.content;
+
+/**
+ * Date: 19.03.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class Span {
+    long offset;
+    long length;
+
+    public Span(long offset, long length) {
+        this.offset = offset;
+        this.length = length;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/SpannedRandomAccessFileContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/SpannedRandomAccessFileContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/SpannedRandomAccessFileContent.java	(revision 0)
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.impl.RandomAccessFileX;
+import com.imagero.uio.io.IOutils;
+import com.imagero.uio.io.UnexpectedEOFException;
+
+import java.io.RandomAccessFile;
+import java.io.File;
+import java.io.IOException;
+import java.io.EOFException;
+
+/**
+ * Content with access to one or more predefined areas in File or RandomAccessFile.
+ * Length can not be changed.
+ * UnexpectedEOFException is thrown if we try to write outside our length.
+ *
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class SpannedRandomAccessFileContent extends Content {
+    private RandomAccessFile raf;
+
+    Span[] spans;
+    long length;
+
+    public SpannedRandomAccessFileContent(File f, Span[] spans) throws IOException {
+        this(f, getMode(f), spans);
+    }
+
+    public SpannedRandomAccessFileContent(File f, String mode, Span[] spans) throws IOException {
+        this(new RandomAccessFileX(f, mode), spans);
+
+    }
+
+    static String getMode(File f) {
+        if (!f.exists() || f.canWrite()) {
+            return "rw";
+        } else {
+            return "r";
+        }
+    }
+
+    public SpannedRandomAccessFileContent(RandomAccessFile raf, Span[] spans) throws IOException {
+        this.raf = raf;
+        this.spans = spans;
+        long rafLength = raf.length();
+        for (int i = 0; i < spans.length; i++) {
+            Span span = spans[i];
+            if (span.offset > rafLength || span.offset + span.length > rafLength) {
+                throw new IOException("Illegal span: " + span.offset + " " + span.length);
+            }
+        }
+        for (int i = 0; i < spans.length; i++) {
+            length += spans[i].length;
+        }
+    }
+
+    Span currentSpan;
+    long spanOffset;
+
+    void seek(long offset) throws IOException {
+        int sp = 0;
+        while (offset > 0) {
+            Span span = spans[sp++];
+            if (offset < span.length) {
+                currentSpan = span;
+                spanOffset = offset;
+                raf.seek(span.offset + offset);
+                return;
+            } else {
+                offset -= span.length;
+            }
+        }
+    }
+
+    public int load(long offset, int bpos, byte[] b) throws IOException {
+        seek(offset);
+
+        long available = currentSpan.length - spanOffset;
+        int len = (int) Math.min(available, b.length - bpos);
+        if (len > 0) {
+            raf.readFully(b, bpos, len);
+            return len;
+        }
+        throw new EOFException();
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+
+    public void close() {
+        IOutils.closeStream(raf);
+    }
+
+    public boolean writable() {
+        return true;
+    }
+
+    public void save(long offset, int bpos, byte[] buffer, int length) throws IOException {
+        long len = length;
+        while (len > 0) {
+            seek(offset);
+            long available = currentSpan.length - spanOffset;
+            int w = (int) Math.min(available, len);
+            if (w == 0) {
+                throw new UnexpectedEOFException(length - len);
+            }
+            raf.write(buffer, bpos, w);
+            offset += w;
+            bpos += w;
+            len -= w;
+        }
+    }
+
+    public long length() throws IOException {
+        return length;
+    }
+
+    protected void finalize() throws Throwable {
+        super.finalize();
+        raf = null;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/SpannedRandomAccessInputContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/SpannedRandomAccessInputContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/SpannedRandomAccessInputContent.java	(revision 0)
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.RandomAccessInput;
+import com.imagero.uio.io.IOutils;
+
+import java.io.EOFException;
+import java.io.IOException;
+
+/**
+ * Content with access to one or more predefined areas in RandomAccessIO.
+ * Length can not be changed.
+ * UnexpectedEOFException is thrown if we try to write outside our length.
+ *
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class SpannedRandomAccessInputContent extends Content {
+    private RandomAccessInput rio;
+
+    Span[] spans;
+    long length;
+
+
+    public SpannedRandomAccessInputContent(RandomAccessInput rio, Span[] spans) throws IOException {
+        this.rio = rio;
+        this.spans = spans;
+        long rafLength = rio.length();
+        for (int i = 0; i < spans.length; i++) {
+            Span span = spans[i];
+            if (span.offset > rafLength || span.offset + span.length > rafLength) {
+                throw new IOException("Illegal span: " + span.offset + " " + span.length);
+            }
+        }
+        for (int i = 0; i < spans.length; i++) {
+            length += spans[i].length;
+        }
+    }
+
+    Span currentSpan;
+    long spanOffset;
+
+    void seek(long offset) throws IOException {
+        int sp = 0;
+        while (offset > 0) {
+            Span span = spans[sp++];
+            if (offset < span.length) {
+                currentSpan = span;
+                spanOffset = offset;
+                rio.seek(span.offset + offset);
+                return;
+            } else {
+                offset -= span.length;
+            }
+        }
+    }
+
+    public int load(long offset, int bpos, byte[] b) throws IOException {
+        seek(offset);
+
+        long available = currentSpan.length - spanOffset;
+        int len = (int) Math.min(available, b.length - bpos);
+        if (len > 0) {
+            rio.readFully(b, bpos, len);
+            return len;
+        }
+        throw new EOFException();
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+
+    public void close() {
+        IOutils.closeStream(rio);
+    }
+
+    public boolean writable() {
+        return false;
+    }
+
+    public void save(long offset, int bpos, byte[] buffer, int length) throws IOException {
+
+    }
+
+    public long length() throws IOException {
+        return length;
+    }
+
+    protected void finalize() throws Throwable {
+        super.finalize();
+        rio = null;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/SpannedRandomAccessIOContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/SpannedRandomAccessIOContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/SpannedRandomAccessIOContent.java	(revision 0)
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import com.imagero.uio.RandomAccessIO;
+import com.imagero.uio.RandomAccessInput;
+import com.imagero.uio.io.IOutils;
+import com.imagero.uio.io.UnexpectedEOFException;
+
+import java.io.EOFException;
+import java.io.IOException;
+
+/**
+ * Content with access to one or more predefined areas in RandomAccessIO.
+ * Length can not be changed.
+ * UnexpectedEOFException is thrown if we try to write outside our length.
+ *
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class SpannedRandomAccessIOContent extends Content {
+    private RandomAccessIO rio;
+
+    Span[] spans;
+    long length;
+
+
+    public SpannedRandomAccessIOContent(RandomAccessIO rio, Span[] spans) throws IOException {
+        this.rio = rio;
+        this.spans = spans;
+        long rafLength = rio.length();
+        for (int i = 0; i < spans.length; i++) {
+            Span span = spans[i];
+            if (span.offset > rafLength || span.offset + span.length > rafLength) {
+                throw new IOException("Illegal span: " + span.offset + " " + span.length);
+            }
+        }
+        for (int i = 0; i < spans.length; i++) {
+            length += spans[i].length;
+        }
+    }
+
+    Span currentSpan;
+    long spanOffset;
+
+    void seek(long offset) throws IOException {
+        int sp = 0;
+        while (offset >= 0) {
+            Span span = spans[sp++];
+            if (offset < span.length) {
+                currentSpan = span;
+                spanOffset = offset;
+                rio.seek(span.offset + offset);
+                return;
+            } else {
+                offset -= span.length;
+            }
+        }
+    }
+
+    public int load(long offset, int bpos, byte[] b) throws IOException {
+        seek(offset);
+
+        long available = currentSpan.length - spanOffset;
+        int len = (int) Math.min(available, b.length - bpos);
+        if (len > 0) {
+            rio.readFully(b, bpos, len);
+            return len;
+        }
+        throw new EOFException();
+    }
+
+    public boolean canReload() {
+        return true;
+    }
+
+    public void close() {
+        IOutils.closeStream((RandomAccessInput) rio);
+    }
+
+    public boolean writable() {
+        return true;
+    }
+
+    public void save(long offset, int bpos, byte[] buffer, int length) throws IOException {
+        long len = length;
+        while (len > 0) {
+            seek(offset);
+            long available = currentSpan.length - spanOffset;
+            int w = (int) Math.min(available, len);
+            if (w == 0) {
+                throw new UnexpectedEOFException(length - len);
+            }
+            rio.write(buffer, bpos, w);
+            offset += w;
+            bpos += w;
+            len -= w;
+        }
+    }
+
+    public long length() throws IOException {
+        return length;
+    }
+
+    protected void finalize() throws Throwable {
+        super.finalize();
+        rio = null;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/content/SynchronizedContent.java
===================================================================
--- ocean/src/com/imagero/uio/bio/content/SynchronizedContent.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/content/SynchronizedContent.java	(revision 0)
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio.content;
+
+import java.io.IOException;
+
+/**
+ * Date: 05.01.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class SynchronizedContent extends Content {
+
+    Content content;
+
+    public SynchronizedContent(Content content) {
+        this.content = content;
+    }
+
+    public synchronized int load(long offset, int bpos, byte[] buffer) throws IOException {
+        return content.load(offset, bpos, buffer);
+    }
+
+    public synchronized void save(long offset, int bpos, byte[] buffer, int length) throws IOException {
+        content.save(offset, bpos, buffer, length);
+    }
+
+    public synchronized long length() throws IOException {
+        return content.length();
+    }
+
+    public void close() {
+        content.close();
+    }
+
+    public boolean canReload() {
+        return content.canReload();
+    }
+
+    public boolean writable() {
+        return content.writable();
+    }
+
+    public Content getContent() {
+        return content;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/FixedSizeByteBuffer.java
===================================================================
--- ocean/src/com/imagero/uio/bio/FixedSizeByteBuffer.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/FixedSizeByteBuffer.java	(revision 0)
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio;
+
+import com.imagero.uio.RandomAccessIO;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * This class can be used to read from and write to byte array.
+ *
+ * @author Andrey Kuznetsov
+ */
+public class FixedSizeByteBuffer {
+
+    protected byte[] buf;
+    protected int count;
+
+    boolean changed;
+    BufferIndex index;
+
+    protected FixedSizeByteBuffer(byte buf[]) {
+        this.buf = buf;
+    }
+
+    public int read(BufferPosition position) {
+        if (availableForReading(position) > 0) {
+            int v = buf[position.pos++] & 0xFF;
+            return v;
+        }
+        return -1;
+    }
+
+    public long skip(long n, BufferPosition position) {
+        long p = Math.max(0, Math.min(count - position.pos, n));
+        position.pos += p;
+        return p;
+    }
+
+    public BufferPosition createPosition() {
+        return new BufferPosition(buf.length);
+    }
+
+    public int availableForReading(BufferPosition position) {
+        return Math.max(0, count - position.pos);
+    }
+
+    public int availableForWriting(BufferPosition position) {
+        return Math.max(0, buf.length - position.pos);
+    }
+
+    public int read(byte[] dest, int offset, int length, BufferPosition position) {
+        final int available = availableForReading(position);
+        int toCopy = Math.max(0, Math.min(length, available));
+        if (toCopy > 0) {
+            System.arraycopy(buf, position.pos, dest, offset, toCopy);
+            position.pos += toCopy;
+        }
+        return toCopy;
+    }
+
+    /**
+     * write given byte to buffer.
+     *
+     * @param b int to write
+     */
+    public void write(int b, BufferPosition position) {
+        buf[position.pos++] = (byte) b;
+        count = Math.max(position.pos, count);
+    }
+
+    public int getCount() {
+        return count;
+    }
+
+    public int getPosition(BufferPosition position) {
+        return position.pos;
+    }
+
+    public void setCount(int count) {
+        this.count = Math.min(Math.max(count, 0), buf.length);
+    }
+
+    /**
+     * write buffer contents to OutputStream
+     * @param wholeBuffer if true then whole buffer is written, otherwise only getCount() bytes are written
+     */
+    public void writeBuffer(OutputStream out, boolean wholeBuffer) throws IOException {
+        if (wholeBuffer) {
+            out.write(buf);
+        } else {
+            out.write(buf, 0, count);
+        }
+    }
+
+    public void writeBuffer(DataOutput out, boolean wholeBuffer) throws IOException {
+        if (wholeBuffer) {
+            out.write(buf);
+        } else {
+            out.write(buf, 0, count);
+        }
+    }
+
+    /**
+     * write whole buffer contents to OutputStream (count is ignored)
+     */
+    public void writeBuffer(OutputStream out) throws IOException {
+        out.write(buf);
+    }
+
+    public void writeBuffer(DataOutput out) throws IOException {
+        out.write(buf);
+    }
+
+    public int write(byte src[], int offset, int length, BufferPosition position) {
+        int available = availableForWriting(position);
+        int toCopy = Math.max(0, Math.min(length, available));
+        if (toCopy > 0) {
+            System.arraycopy(src, offset, buf, position.pos, toCopy);
+            position.pos += toCopy;
+            count = Math.max(count, position.pos);
+        }
+        return toCopy;
+    }
+
+    public RandomAccessIO create() {
+        return new FSBRandomAccessIO(this);
+    }
+
+    public RandomAccessIO create(int offset, int length) {
+        return new FSBRandomAccessIO(this, offset, length);
+    }
+
+    public static FixedSizeByteBuffer createBuffer(byte buf[]) {
+        return new FixedSizeByteBuffer(buf);
+    }
+}
Index: ocean/src/com/imagero/uio/bio/FSBInputStream.java
===================================================================
--- ocean/src/com/imagero/uio/bio/FSBInputStream.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/FSBInputStream.java	(revision 0)
@@ -0,0 +1,68 @@
+package com.imagero.uio.bio;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * Date: 01.03.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+class FSBInputStream extends InputStream {
+    FixedSizeByteBuffer buffer;
+    BufferPosition position;
+    int mark;
+    long offset;
+
+    public FSBInputStream(FixedSizeByteBuffer buffer) {
+        this.buffer = buffer;
+        position = new BufferPosition(buffer.buf.length);
+    }
+
+    public FSBInputStream(int offset, FixedSizeByteBuffer buffer) {
+        this.buffer = buffer;
+        position = new BufferPosition(Integer.MAX_VALUE);
+        position.pos = offset;
+        this.offset = offset;
+    }
+
+    public int read() {
+        return buffer.read(position);
+    }
+
+    public int read(byte b[]) throws IOException {
+        return buffer.read(b, 0, b.length, position);
+    }
+
+    public int read(byte b[], int off, int len) {
+        return buffer.read(b, off, len, position);
+    }
+
+    public long skip(long n) {
+        return buffer.skip(n, position);
+    }
+
+    public int available() {
+        return buffer.availableForReading(position);
+    }
+
+    public synchronized void mark(int readlimit) {
+        this.mark = position.pos;
+    }
+
+    public synchronized void reset() throws IOException {
+        position.pos = mark;
+    }
+
+    public boolean markSupported() {
+        return true;
+    }
+
+    public long getPosition() {
+        return position.pos - offset;
+    }
+
+    public void setPosition(long pos) {
+        position.pos = (int) (pos + offset);
+    }
+}
Index: ocean/src/com/imagero/uio/bio/FSBOutputStream.java
===================================================================
--- ocean/src/com/imagero/uio/bio/FSBOutputStream.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/FSBOutputStream.java	(revision 0)
@@ -0,0 +1,40 @@
+package com.imagero.uio.bio;
+
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ * Date: 01.03.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+class FSBOutputStream extends OutputStream {
+    FixedSizeByteBuffer buffer;
+    BufferPosition position;
+
+    public FSBOutputStream(FixedSizeByteBuffer buffer) {
+        this(0, buffer);
+    }
+
+    public FSBOutputStream(int offset, FixedSizeByteBuffer buffer) {
+        this.buffer = buffer;
+        position = new BufferPosition(Integer.MAX_VALUE);
+        position.pos = offset;
+    }
+
+    public void write(int b) throws IOException {
+        buffer.write(b, position);
+    }
+
+    public void write(byte b[]) throws IOException {
+        buffer.write(b, 0, b.length, position);
+    }
+
+    public void write(byte b[], int off, int len) throws IOException {
+        buffer.write(b, off, len, position);
+    }
+
+    public void close() throws IOException {
+        buffer = null;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/FSBRandomAccessIO.java
===================================================================
--- ocean/src/com/imagero/uio/bio/FSBRandomAccessIO.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/FSBRandomAccessIO.java	(revision 0)
@@ -0,0 +1,111 @@
+package com.imagero.uio.bio;
+
+import com.imagero.uio.impl.AbstractRandomAccessIO;
+import com.imagero.uio.RandomAccessIO;
+import com.imagero.uio.RandomAccessInput;
+import com.imagero.uio.RandomAccessOutput;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Date: 01.03.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+class FSBRandomAccessIO extends AbstractRandomAccessIO {
+
+    FixedSizeByteBuffer buffer;
+    BufferPosition position;
+
+    int offset;
+    int length;
+
+    public FSBRandomAccessIO(FixedSizeByteBuffer buffer) {
+        this(buffer, 0, buffer.buf.length);
+    }
+
+    public FSBRandomAccessIO(FixedSizeByteBuffer buffer, int offset) {
+        this(buffer, offset, buffer.buf.length - offset);
+    }
+
+    public FSBRandomAccessIO(FixedSizeByteBuffer buffer, int offset, int length) {
+        this.buffer = buffer;
+        this.offset = offset;
+        if(length > 0) {
+            this.length = length;
+        }
+        else {
+            this.length = buffer.availableForReading(buffer.createPosition());
+        }
+    }
+
+    public int read() throws IOException {
+        return buffer.read(position);
+    }
+
+    public void seek(long pos) {
+        position.pos = (int) Math.min(length, pos + offset);
+    }
+
+    public long length() throws IOException {
+        return length;
+    }
+
+    public long getFilePointer() throws IOException {
+        return position.pos - offset;
+    }
+
+    public void setLength(long newLength) throws IOException {
+        this.length = (int) Math.min(buffer.buf.length, newLength);
+    }
+
+    public void write(int b) throws IOException {
+        buffer.write(b, position);
+    }
+
+    public void write(byte b[], int off, int len) throws IOException {
+        buffer.write(b, off, len, position);
+    }
+
+    public InputStream createInputStream(long offset) {
+        return new FSBInputStream((int) offset, buffer);
+    }
+
+    public long getChildPosition(InputStream child) {
+        if(child instanceof FSBInputStream) {
+            FSBInputStream fsbis = (FSBInputStream) child;
+            return fsbis.getPosition();
+        }
+        return -1;
+    }
+
+    public void setChildPosition(InputStream child, long position) {
+        if (child instanceof FSBInputStream) {
+            FSBInputStream fsbis = (FSBInputStream) child;
+            fsbis.setPosition(position);
+        }
+    }
+
+    public OutputStream createOutputStream(long offset) {
+        return new FSBOutputStream((int) offset, buffer) ;
+    }
+
+    public RandomAccessIO createIOChild(long offset, long length, int byteOrder, boolean syncPointer) throws IOException {
+        FSBRandomAccessIO rio = new FSBRandomAccessIO(buffer, (int) offset, (int) length);
+        if(syncPointer) {
+            rio.position = position;
+        }
+        rio.setByteOrder(byteOrder);
+        return rio;
+    }
+
+    public RandomAccessInput createInputChild(long offset, long length, int byteOrder, boolean syncPointer) throws IOException {
+        return createIOChild(offset, 0, byteOrder, syncPointer);
+    }
+
+    public RandomAccessOutput createOutputChild(long offset, int byteOrder, boolean syncPointer) throws IOException {
+        return createIOChild(offset, 0, byteOrder, syncPointer);
+    }
+}
Index: ocean/src/com/imagero/uio/bio/IOCInputStream.java
===================================================================
--- ocean/src/com/imagero/uio/bio/IOCInputStream.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/IOCInputStream.java	(revision 0)
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Independent InputStream with shared IOController.
+ *
+ * @author Andrey Kuznetsov
+ */
+public class IOCInputStream extends InputStream {
+
+    FixedSizeByteBuffer buffer;
+    BufferIndex bufferIndex;
+    BufferPosition bufferPosition;
+    IOController controller;
+
+    StreamPosition streamPosition = new StreamPosition();
+    long offset;
+
+    long mark;
+
+    public IOCInputStream(IOController controller) {
+        this.controller = controller;
+    }
+
+    public IOCInputStream(IOController controller, long offset) {
+        this.controller = controller;
+        this.offset = offset;
+        bufferPosition = new BufferPosition(controller.bufferSize);
+        seek(0);
+    }
+
+    public void seek(long offset) {
+        streamPosition.pos = offset + this.offset;
+        bufferPosition.pos = (int) ((streamPosition.pos) % controller.bufferSize);
+ }
+
+    private void prepareBufferForReading() {
+        BufferIndex index = controller.getBufferIndex(streamPosition.pos);
+
+        if (!index.equals(bufferIndex) || buffer == null || buffer.buf == null) {
+            bufferIndex = index;
+            try {
+                buffer = controller.getBuffer(streamPosition.pos, true);
+            } catch (IOException ex) {
+                //ignore
+            }
+        }
+        if (buffer != null) {
+            bufferPosition.pos = (int) ((streamPosition.pos) % controller.bufferSize);
+        }
+    }
+
+    public int read() throws IOException {
+        checkBuffer();
+
+        if (buffer != null) {
+            streamPosition.pos++;
+            return buffer.read(bufferPosition);
+        }
+        return -1;
+    }
+
+    public int available() throws IOException {
+        if(buffer != null) {
+            return buffer.availableForReading(bufferPosition);
+        }
+        return 0;
+    }
+
+    private void checkBuffer() {
+        if (buffer == null || bufferPosition.available() <= 0) {
+            prepareBufferForReading();
+        }
+    }
+
+    public long skip(long n) throws IOException {
+        checkBuffer();
+        if (buffer == null) {
+            return 0;
+        }
+        long skp = buffer.skip(n, bufferPosition);
+        streamPosition.pos += skp;
+        return skp;
+    }
+
+    public int read(byte b[]) throws IOException {
+        return read(b, 0, b.length);
+    }
+
+    public int read(byte[] b, int offset, int length) throws IOException {
+        checkBuffer();
+        if (buffer == null) {
+            return -1;
+        }
+        int rc = buffer.read(b, offset, length, bufferPosition);
+        if (rc > 0) {
+            streamPosition.pos += rc;
+        }
+        return rc;
+    }
+
+    public boolean markSupported() {
+        return true;
+    }
+
+    public synchronized void mark(int readlimit) {
+        this.mark = streamPosition.pos;
+    }
+
+    public synchronized void reset() throws IOException {
+        seek(mark);
+    }
+
+    public void close() throws IOException {
+        if (controller != null) {
+            controller = null;
+        }
+    }
+
+    public long getPosition() {
+        return streamPosition.pos - offset;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/IOController.java
===================================================================
--- ocean/src/com/imagero/uio/bio/IOController.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/IOController.java	(revision 0)
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio;
+
+import com.imagero.util.OpenVector;
+import com.imagero.uio.bio.content.SynchronizedContent;
+import com.imagero.uio.bio.content.Content;
+import com.imagero.uio.UIOStreamBuilder;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+/**
+ * Buffer controller.
+ * IOController loads data from Content and maintains buffers (FixedSizeBuffer).
+ * @author Andrey Kuznetsov
+ */
+public class IOController {
+
+    OpenVector bufs = new OpenVector(100);
+
+    int bufferSize = UIOStreamBuilder.DEFAULT_CHUNK_SIZE;
+    int arrayLength = 1000;
+
+    Content content;
+
+    Ring rs;
+    int maxBufferCount = UIOStreamBuilder.DEFAULT_CHUNK_COUNT;
+
+    long explicitLength;
+
+    public IOController(int bufferSize, Content content) {
+        this.bufferSize = bufferSize;
+        this.content = content;
+        this.rs = new Ring(maxBufferCount);
+    }
+
+    final void setLength(long newLength) {
+        explicitLength = newLength;
+    }
+
+    private Enumeration buffers(final boolean allowNullValues) {
+        return new Enumeration() {
+            final FixedSizeByteBuffer empty = FixedSizeByteBuffer.createBuffer(new byte[bufferSize]);
+            final long length = length();
+            final BufferIndex max = getBufferIndex(length);
+            final BufferIndex bi = new BufferIndex(0, 0);
+
+            public boolean hasMoreElements() {
+                boolean b = bi.arrayIndex < max.arrayIndex;
+                boolean b2 = bi.index <= max.index;
+                return b || b2;
+            }
+
+            public Object nextElement() {
+                if (hasMoreElements()) {
+                    if (!(bi.index < arrayLength)) {
+                        bi.index = 0;
+                        bi.arrayIndex++;
+                    }
+                    FixedSizeByteBuffer fb = getBuffer(bi.arrayIndex, bi.index++);
+                    return fb != null || allowNullValues ? fb : empty;
+                } else {
+                    throw new NoSuchElementException();
+                }
+            }
+        };
+    }
+
+    private Enumeration buffers(final long maxPos, final boolean allowNullValues) {
+        return new Enumeration() {
+            final FixedSizeByteBuffer empty = FixedSizeByteBuffer.createBuffer(new byte[bufferSize]);
+            final long length = Math.min(maxPos, length());
+            final BufferIndex max = getBufferIndex(length);
+            final BufferIndex bi = new BufferIndex(0, 0);
+
+            public boolean hasMoreElements() {
+                boolean b = bi.arrayIndex < max.arrayIndex;
+                boolean b2 = bi.index <= max.index;
+                return b || b2;
+            }
+
+            public Object nextElement() {
+                if (hasMoreElements()) {
+                    if (!(bi.index < arrayLength)) {
+                        bi.index = 0;
+                        bi.arrayIndex++;
+                    }
+                    FixedSizeByteBuffer fb = getBuffer(bi.arrayIndex, bi.index++);
+                    return fb != null || allowNullValues ? fb : empty;
+                } else {
+                    throw new NoSuchElementException();
+                }
+            }
+        };
+    }
+
+
+    long length() {
+        if (explicitLength > 0) {
+            return explicitLength;
+        }
+
+        long contentLength = 0;
+        try {
+            contentLength = content.length();
+        } catch (IOException ex) {
+            ex.printStackTrace();
+        }
+
+        Object[] elements = bufs.getElements();
+        int maxI = elements.length - 1;
+        for (int i = maxI; i >= 0; i--) {
+            BufferArray ba = (BufferArray) elements[i];
+            if (ba != null) {
+                FixedSizeByteBuffer[] buffers = ba.buffers;
+                int maxJ = buffers.length - 1;
+                for (int j = maxJ; j >= 0; j--) {
+                    FixedSizeByteBuffer buffer = buffers[j];
+                    if (buffer != null) {
+                        int count = buffer.getCount();
+                        long startOffset = getStartOffset(buffer.index, bufferSize);
+                        if (count > 0) {
+                            return Math.max(contentLength, startOffset + count);
+                        }
+                    }
+                }
+            }
+        }
+        return contentLength;
+    }
+
+    void writeTo(OutputStream out) throws IOException {
+        Enumeration enums = buffers(false);
+        while (enums.hasMoreElements()) {
+            FixedSizeByteBuffer buffer = (FixedSizeByteBuffer) enums.nextElement();
+            buffer.writeBuffer(out, enums.hasMoreElements());
+        }
+    }
+
+    void writeTo(DataOutput out) throws IOException {
+        Enumeration enums = buffers(false);
+        while (enums.hasMoreElements()) {
+            FixedSizeByteBuffer buffer = (FixedSizeByteBuffer) enums.nextElement();
+            buffer.writeBuffer(out, enums.hasMoreElements());
+        }
+    }
+
+    private class BufferArray {
+        FixedSizeByteBuffer[] buffers;
+
+        public BufferArray() {
+            buffers = new FixedSizeByteBuffer[arrayLength];
+        }
+    }
+
+    private FixedSizeByteBuffer getBuffer(BufferIndex bi) {
+        return getBuffer(bi.arrayIndex, bi.index);
+    }
+
+    private FixedSizeByteBuffer getBuffer(int aIndex, int index) {
+        Object[] objects = bufs.checkSize(aIndex);
+        BufferArray ba = (BufferArray) objects[aIndex];
+        if (ba == null) {
+            ba = new BufferArray();
+            objects[aIndex] = ba;
+        }
+        return ba.buffers[index];
+    }
+
+    protected void setBuffer(BufferIndex index, FixedSizeByteBuffer buffer) {
+        Object[] objects = bufs.checkSize(index.arrayIndex);
+        BufferArray ba = (BufferArray) objects[index.arrayIndex];
+        ba.buffers[index.index] = buffer;
+    }
+
+    public long flushBefore(long pos) {
+        pos = (pos / bufferSize) * bufferSize;
+        Enumeration enums = buffers(pos, true);
+        while (enums.hasMoreElements()) {
+            FixedSizeByteBuffer o = (FixedSizeByteBuffer) enums.nextElement();
+            if (o != null) {
+                o.buf = null;
+            }
+        }
+        return pos;
+    }
+
+    public BufferIndex getBufferIndex(long pos) {
+        if(pos < 0) {
+            throw new IllegalArgumentException("Negative stream position");
+        }
+        long count = pos / bufferSize;
+        long aIndex = count / arrayLength;
+        int index = (int) (count % arrayLength);
+        if (aIndex > Integer.MAX_VALUE) {
+            throw new IndexOutOfBoundsException("Please increase buffer size");
+        }
+        if(index < 0) {
+            throw new IndexOutOfBoundsException("Please increase buffer size");
+        }
+        BufferIndex bi = new BufferIndex((int) aIndex, index);
+        return bi;
+    }
+
+    public FixedSizeByteBuffer getBuffer(long pos) {
+        return getBuffer(getBufferIndex(pos));
+    }
+
+    FixedSizeByteBuffer getBuffer(long pos, boolean load) throws IOException {
+        BufferIndex bi = getBufferIndex(pos);
+        long startOffset = getStartOffset(bi, bufferSize);
+        FixedSizeByteBuffer sb = getBuffer(bi);
+        if (sb == null) {
+            sb = FixedSizeByteBuffer.createBuffer(new byte[bufferSize]);
+            setBuffer(bi, sb);
+            sb.index = bi;
+            if (load) {
+                long max = content.length();
+                if (pos > max) {
+                    return null;
+                }
+
+                int size = content.load(startOffset, sb.buf);
+                sb.count = size;
+            }
+            if (content.canReload()) {
+                checkBuffers(sb);
+            }
+        } else {
+            if (sb.buf == null) {
+                long max = content.length();
+                if (pos > max) {
+                    return null;
+                }
+                sb.buf = new byte[bufferSize];
+                int size = content.load(startOffset, sb.buf);
+                sb.count = size;
+            }
+        }
+        return sb;
+    }
+
+    private void checkBuffers(FixedSizeByteBuffer buffer0) {
+        FixedSizeByteBuffer buffer = (FixedSizeByteBuffer) rs.add(buffer0);
+        if (buffer != null && content.writable()) {
+            if (buffer.changed) {
+                try {
+                    long offset = getStartOffset(buffer.index, bufferSize);
+                    content.save(offset, 0, buffer.buf, buffer.getCount());
+                    buffer.changed = false;
+                } catch (IOException ex) {
+                    ex.printStackTrace();
+                }
+            }
+            setBuffer(buffer.index, null);
+            buffer.buf = null;
+        }
+    }
+
+    void sync() throws IOException {
+        boolean canWrite = content.writable();
+        if (!canWrite) {
+            return;
+        }
+
+        try {
+            long length = content.length(); //may be already closed
+        }
+        catch(IOException ex) {
+            //stream was already closed, so just return
+            return;
+        }
+        Enumeration enums = buffers(true);
+        while (enums.hasMoreElements()) {
+            FixedSizeByteBuffer buffer = (FixedSizeByteBuffer) enums.nextElement();
+            if (buffer != null && buffer.changed) {
+                long offset = getStartOffset(buffer.index, bufferSize);
+                content.save(offset, 0, buffer.buf, buffer.getCount());
+                buffer.changed = false;
+            }
+        }
+    }
+
+    public long getStartOffset(BufferIndex bufferIndex, int bufferSize) {
+        long res = bufferIndex.arrayIndex * arrayLength * bufferSize;
+        res += bufferIndex.index * bufferSize;
+        return res;
+    }
+
+    /**
+     * determine if access to stream content is synchronized
+     */
+    public boolean isSynchronizedContent() {
+        return content instanceof SynchronizedContent;
+    }
+
+    /**
+     * define if access to content should be syncronized.
+     */
+    public void setSynchronizedContent(boolean b) {
+        if(b) {
+            if(!isSynchronizedContent()) {
+                content = new SynchronizedContent(content);
+            }
+        }
+        else {
+            if(isSynchronizedContent()) {
+                SynchronizedContent synchronizedContent = (SynchronizedContent) content;
+                content = synchronizedContent.getContent();
+            }
+        }
+    }
+
+    protected void finalize() throws Throwable {
+        super.finalize();
+        content = null;
+        rs = null;
+        bufs = null;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/IOCOutputStream.java
===================================================================
--- ocean/src/com/imagero/uio/bio/IOCOutputStream.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/IOCOutputStream.java	(revision 0)
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio;
+
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ * OutputStream with shared IOController.
+ *
+ * @author Andrey Kuznetsov
+ */
+public class IOCOutputStream extends OutputStream {
+
+    FixedSizeByteBuffer buffer;
+    BufferIndex bufferIndex;
+    BufferPosition bufferPosition;
+    IOController controller;
+
+    StreamPosition streamPosition = new StreamPosition();
+    long offset;
+
+    long mark;
+
+    public IOCOutputStream(IOController controller) {
+        this.controller = controller;
+    }
+
+    public IOCOutputStream(IOController controller, long offset) {
+        this.controller = controller;
+        this.offset = offset;
+        bufferPosition = new BufferPosition(controller.bufferSize);
+        seek(0);
+    }
+
+    public void seek(long offset) {
+        streamPosition.pos = offset + this.offset;
+    }
+
+    protected void prepareBufferForWriting() throws IOException {
+        BufferIndex index = controller.getBufferIndex(streamPosition.pos);
+
+        if (!index.equals(bufferIndex) || buffer == null || buffer.buf == null) {
+            bufferIndex = index;
+            buffer = controller.getBuffer(streamPosition.pos, false);
+        }
+        bufferPosition.pos = (int) ((streamPosition.pos) % controller.bufferSize);
+        buffer.changed = true;
+    }
+
+    private void checkBuffer() throws IOException {
+        if (buffer == null || !(bufferPosition.available() > 0)) {
+            prepareBufferForWriting();
+        }
+    }
+
+    public void write(int b) throws IOException {
+        checkBuffer();
+        buffer.write(b, bufferPosition);
+        streamPosition.pos++;
+    }
+
+    public void write(byte b[]) throws IOException {
+        write(b, 0, b.length);
+    }
+
+    public void write(byte b[], int offset, int length) throws IOException {
+        while (length > 0) {
+            checkBuffer();
+            int written = buffer.write(b, offset, length, bufferPosition);
+            length -= written;
+            offset += written;
+            streamPosition.pos += written;
+        }
+    }
+
+    public void flush() throws IOException {
+        if (controller != null) {
+            controller.sync();
+        }
+    }
+
+    public void close() throws IOException {
+        flush();
+        controller = null;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/Ring.java
===================================================================
--- ocean/src/com/imagero/uio/bio/Ring.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/Ring.java	(revision 0)
@@ -0,0 +1,32 @@
+package com.imagero.uio.bio;
+
+/**
+ * Ring - minimalistic Ring implementation.
+ *
+ * Date: 29.08.2007
+ * @author Andrey Kuznetsov
+ */
+class Ring {
+
+    Object[] elements;
+
+    int size;
+    int index;
+
+    public Ring(int size) {
+        this.size = size;
+        this.elements = new Object[size];
+    }
+
+    /**
+     * add Object to ring
+     * @param o Object
+     * @return Object removed from ring (replaced by new Object) or null
+     */
+    public Object add(Object o) {
+        Object tmp = elements[index];
+        elements[index] = o;
+        index = (index + 1) % size;
+        return tmp;
+    }
+}
Index: ocean/src/com/imagero/uio/bio/StreamPosition.java
===================================================================
--- ocean/src/com/imagero/uio/bio/StreamPosition.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/StreamPosition.java	(revision 0)
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio;
+
+/**
+ * @author Andrey Kuznetsov
+ */
+public class StreamPosition {
+    public long pos;
+}
Index: ocean/src/com/imagero/uio/bio/VariableSizeByteBuffer.java
===================================================================
--- ocean/src/com/imagero/uio/bio/VariableSizeByteBuffer.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/VariableSizeByteBuffer.java	(revision 0)
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.imagero.uio.bio;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * This class can be used to read from and write to byte array.
+ *
+ * @author Andrey Kuznetsov
+ */
+public class VariableSizeByteBuffer {
+
+    protected Buffer buf;
+    int count;
+
+    boolean changed;
+
+    public VariableSizeByteBuffer(int size) {
+        this(new byte[size]);
+    }
+
+    public VariableSizeByteBuffer(byte buf[]) {
+        this(new Buffer(buf));
+    }
+
+    VariableSizeByteBuffer(Buffer buf) {
+        this.buf = buf;
+        count = buf.buffer.length;
+    }
+
+    public VariableSizeByteBuffer create() {
+        return new VariableSizeByteBuffer(buf);
+    }
+
+    public void seek(int pos, BufferPosition position) {
+        position.pos = pos;
+    }
+
+    public int read(BufferPosition position) {
+        if (position.pos >= count) {
+            return -1;
+        }
+        return buf.buffer[position.pos++] & 0xFF;
+    }
+
+    public long skip(long n, BufferPosition position) {
+        long p = Math.max(0L, Math.min(n, Integer.MAX_VALUE));
+        position.pos += p;
+        return p;
+    }
+
+    /**
+     * get amount of bytes which may be written without changing buffer size
+     */
+    public int availableForWriting(BufferPosition position) {
+        return buf.buffer.length - position.pos;
+    }
+
+    public int availableForReading(BufferPosition position) {
+        return Math.max(0, count - position.pos);
+    }
+
+    public int read(byte[] dest, int offset, int length, BufferPosition position) {
+        final int available = availableForReading(position);
+        int toRead = Math.max(0, Math.min(length, available));
+        if (toRead > 0) {
+            System.arraycopy(buf.buffer, position.pos, dest, offset, toRead);
+            position.pos += toRead;
+        }
+        return toRead;
+    }
+
+    public int getCount() {
+        return count;
+    }
+
+    public void setCount(int count) {
+        this.count = Math.min(Math.max(count, 0), buf.buffer.length);
+    }
+
+    public void writeBuffer(OutputStream out) throws IOException {
+        out.write(buf.buffer, 0, count);
+    }
+
+    public void writeBuffer(DataOutput out) throws IOException {
+        out.write(buf.buffer, 0, count);
+    }
+
+    public void write(byte b[], int offset, int length, BufferPosition position) {
+        if (length > 0) {
+            checkSize(length, position);
+            System.arraycopy(b, offset, buf.buffer, position.pos, length);
+            position.pos += length;
+            count = Math.max(count, position.pos);
+        }
+    }
+
+    public void write(int b, BufferPosition position) {
+        checkSize(1, position);
+        buf.buffer[position.pos++] = (byte) b;
+        count = Math.max(count, position.pos);
+    }
+
+    private synchronized void checkSize(int k, BufferPosition position) {
+        if (position.pos + k > buf.buffer.length) {
+            byte newbuf[] = new byte[Math.max(buf.buffer.length << 1, position.pos + k)];
+            System.arraycopy(buf.buffer, 0, newbuf, 0, count);
+            buf.buffer = newbuf;
+        }
+    }
+
+    public InputStream getInputStream(int offset) {
+        return new VSBInputStream(offset, this);
+    }
+
+    public OutputStream getOutputStream(int offset) {
+        return new VSBOutputStream(offset, this);
+    }
+}
Index: ocean/src/com/imagero/uio/bio/VSBInputStream.java
===================================================================
--- ocean/src/com/imagero/uio/bio/VSBInputStream.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/VSBInputStream.java	(revision 0)
@@ -0,0 +1,68 @@
+package com.imagero.uio.bio;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * Date: 01.03.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+class VSBInputStream extends InputStream {
+    VariableSizeByteBuffer buffer;
+    BufferPosition position;
+    int mark;
+    long offset;
+
+    public VSBInputStream(VariableSizeByteBuffer buffer) {
+        this.buffer = buffer;
+        position = new BufferPosition(Integer.MAX_VALUE);
+    }
+
+    public VSBInputStream(int offset, VariableSizeByteBuffer buffer) {
+        this.buffer = buffer;
+        position = new BufferPosition(Integer.MAX_VALUE);
+        buffer.seek(offset, position);
+        this.offset = offset;
+    }
+
+    public int read() {
+        return buffer.read(position);
+    }
+
+    public int read(byte b[]) throws IOException {
+        return buffer.read(b, 0, b.length, position);
+    }
+
+    public int read(byte b[], int off, int len) {
+        return buffer.read(b, off, len, position);
+    }
+
+    public long skip(long n) {
+        return buffer.skip(n, position);
+    }
+
+    public int available() {
+        return buffer.availableForReading(position);
+    }
+
+    public synchronized void mark(int readlimit) {
+        this.mark = position.pos;
+    }
+
+    public synchronized void reset() throws IOException {
+        buffer.seek(mark, position);
+    }
+
+    public boolean markSupported() {
+        return true;
+    }
+
+    public long getPosition() {
+        return position.pos - offset;
+    }
+
+    public void setPosition(long pos) {
+        buffer.seek((int) pos, position);
+    }
+}
Index: ocean/src/com/imagero/uio/bio/VSBOutputStream.java
===================================================================
--- ocean/src/com/imagero/uio/bio/VSBOutputStream.java	(revision 0)
+++ ocean/src/com/imagero/uio/bio/VSBOutputStream.java	(revision 0)
@@ -0,0 +1,40 @@
+package com.imagero.uio.bio;
+
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ * Date: 01.03.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+class VSBOutputStream extends OutputStream {
+    VariableSizeByteBuffer buffer;
+    BufferPosition position;
+
+    public VSBOutputStream(VariableSizeByteBuffer buffer) {
+        this(0, buffer);
+    }
+
+    public VSBOutputStream(int offset, VariableSizeByteBuffer buffer) {
+        this.buffer = buffer;
+        position = new BufferPosition(Integer.MAX_VALUE);
+        buffer.seek(offset, position);
+    }
+
+    public void write(int b) throws IOException {
+        buffer.write(b, position);
+    }
+
+    public void write(byte b[]) throws IOException {
+        buffer.write(b, 0, b.length, position);
+    }
+
+    public void write(byte b[], int off, int len) throws IOException {
+        buffer.write(b, off, len, position);
+    }
+
+    public void close() throws IOException {
+        buffer = null;
+    }
+}
Index: ocean/src/com/imagero/uio/blob/BlobInputStream.java
===================================================================
--- ocean/src/com/imagero/uio/blob/BlobInputStream.java	(revision 0)
+++ ocean/src/com/imagero/uio/blob/BlobInputStream.java	(revision 0)
@@ -0,0 +1,86 @@
+package com.imagero.uio.blob;
+
+import com.imagero.uio.blob.Blob;
+import com.imagero.uio.io.UnexpectedEOFException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+/**
+ * BlobInputStream.
+ * Read data from Blob.
+ *
+ * Data from Blob first filled into internal buffer and then read.
+ *
+ * Date: 23.07.2007
+ *
+ * @author Andrey Kuznetsov
+ */
+public class BlobInputStream extends ByteArrayInputStream {
+    Blob blob;
+
+    long start;
+
+    boolean finished;
+
+    public BlobInputStream(Blob blob) throws IOException {
+        super(new byte[2048]);
+        this.blob = blob;
+        fillBuffer();
+    }
+
+    public int read() {
+        if (finished) {
+            return -1;
+        }
+        if (available() > 0) {
+            return super.read();
+        }
+        return -1;
+    }
+
+    public synchronized long skip(long n) {
+        int k = available();
+        if (k < n) {
+            fillBuffer();
+        }
+        return super.skip(n);
+    }
+
+    public synchronized int available() {
+        int k = super.available();
+        if (k > 0) {
+            return k;
+        } else {
+            fillBuffer();
+            return super.available();
+        }
+    }
+
+    public synchronized int read(byte b[], int off, int len) {
+        if (finished) {
+            return -1;
+        }
+        int k = available();
+        return super.read(b, off, Math.min(k, len));
+    }
+
+    private void fillBuffer() {
+        try {
+            count = blob.get(start, buf);
+        } catch (UnexpectedEOFException ex) {
+            count = (int) ex.getCount();
+        } catch (IOException ex) {
+            finished = true;
+            ex.printStackTrace();
+        }
+        if (count <= 0) {
+            count = 0;
+            pos = 0;
+            finished = true;
+            return;
+        }
+        start += count;
+        pos = 0;
+    }
+}
Index: ocean/src/com/imagero/uio/blob/Blob.java
===================================================================
--- ocean/src/com/imagero/uio/blob/Blob.java	(revision 0)
+++ ocean/src/com/imagero/uio/blob/Blob.java	(revision 0)
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.imagero.uio.blob;
+
+import com.imagero.uio.RandomAccessIO;
+import com.imagero.uio.RandomAccessInput;
+import com.imagero.uio.io.UnexpectedEOFException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Hashtable;
+
+/**
+ * Blob - Object which encapsulates (possible deferred)
+ * data which may come from different sources.
+ *
+ * @author Andrey Kuznetsov
+ */
+public abstract class Blob {
+
+    long length;
+
+    private Hashtable properties = new Hashtable();
+
+    /**
+     * retrieve data from this Blob
+     * @param start start offset
+     * @param length how much bytes to get
+     * @return byte array with data
+     * @throws java.io.IOException
+     */
+    public abstract byte[] get(long start, int length) throws IOException;
+
+    /**
+     * retrieve data from this Blob
+     * @param start start offset
+     * @param dest where to copy data
+     * @return how much byte were copied
+     * @throws java.io.IOException
+     */
+    public abstract int get(long start, byte[] dest) throws IOException;
+
+    /**
+     * determine if this Blob is writable and method set(long, byte[]) can be used to change content of this Blob
+     * @return true if Blob is writable
+     */
+    public abstract boolean writable();
+
+    /**
+     * set data (work only if writable returns true)
+     * @param start start in destination
+     * @param data new data
+     * @throws java.io.IOException
+     */
+    public abstract void set(long start, byte[] data) throws IOException;
+
+    /**
+     * release reloadable resources
+     */
+    public abstract void clear();
+
+    protected boolean lengthKnown() {
+        return length > 0;
+    }
+
+    protected abstract long computeLength() throws IOException;
+
+    public long getLength() {
+        if (!lengthKnown()) {
+            try {
+                length = computeLength();
+            } catch (IOException ex) {
+                System.err.println("Can't compute Blob length. " + ex.getMessage());
+            }
+        }
+        return length;
+    }
+
+    public InputStream getInputStream() throws IOException {
+        return new BlobInputStream(this);
+    }
+
+    public Object getProperty(Object key) {
+        return properties.get(key);
+    }
+
+    public void setProperty(Object key, Object property) {
+        properties.put(key, property);
+    }
+
+    public static class BaBlob extends Blob {
+        int offset;
+
+        byte[] blob;
+
+        public BaBlob(byte[] blob) {
+            this(blob, 0, blob.length);
+        }
+
+        public BaBlob(byte[] blob, int offset, int length) {
+            this.offset = offset;
+            this.length = length;
+            this.blob = blob;
+        }
+
+        protected long computeLength() {
+            return length;
+        }
+
+        static final byte[] empty = new byte[0];
+
+        public byte[] get(long start, int length) {
+            int len = (int) Math.max(Math.min(length, this.length - start), 0);
+            if (len == 0) {
+                return empty;
+            }
+            byte[] dest = new byte[len];
+            System.arraycopy(blob, (int) start + offset, dest, 0, len);
+            return dest;
+        }
+
+        public int get(long start, byte[] dest) {
+            long max = Math.max(Math.min(dest.length, this.length - start), 0);
+            if (max > 0) {
+                System.arraycopy(blob, (int) start + offset, dest, 0, (int) max);
+            }
+            return (int) max;
+        }
+
+        public boolean writable() {
+            return true;
+        }
+
+        public void set(long start, byte[] data) {
+            System.arraycopy(data, 0, blob, (int) start, data.length);
+        }
+
+        public void clear() {
+        }
+
+        public InputStream getInputStream() throws IOException {
+            return new ByteArrayInputStream(blob, offset, (int) length);
+        }
+    }
+
+    public static class RoBlob extends Blob {
+        RandomAccessInput ro;
+        long start;
+
+        public RoBlob(RandomAccessInput ro, long start, long length) {
+            this.ro = ro;
+            this.start = start;
+            this.length = length;
+        }
+
+        protected long computeLength() {
+            return length;
+        }
+
+        public byte[] get(long start, int length) throws IOException {
+            long pos = ro.getFilePointer();
+            try {
+                byte[] dest = new byte[length];
+                ro.seek(this.start + start);
+                ro.readFully(dest);
+                return dest;
+            } finally {
+                ro.seek(pos);
+            }
+        }
+
+        public int get(long start, byte[] dest) throws IOException {
+            long max = Math.min(dest.length, length - start);
+            long pos = ro.getFilePointer();
+            try {
+                ro.seek(this.start + start);
+                ro.readFully(dest, 0, (int) max);
+            } catch (UnexpectedEOFException ex) {
+                return (int) ex.getCount();
+            } finally {
+                ro.seek(pos);
+            }
+            return (int) max;
+        }
+
+        public boolean writable() {
+            return ro instanceof RandomAccessIO;
+        }
+
+        public void set(long start, byte[] data) throws IOException {
+            if (writable()) {
+                RandomAccessIO ra = (RandomAccessIO) ro;
+                ra.seek(start);
+                ra.write(data);
+            }
+        }
+
+        public void clear() {
+        }
+    }
+
+//    public static class IaBlob extends Blob {
+//
+//        int offset;
+//        int length4;
+//        int[] blob;
+//
+////        int mask;
+////        private int bytesPerInt;
+//
+//        RandomAccessInput in;
+//        SkipBytesInputStream skip;
+//
+//        IntArrayContent content;
+//
+//        public IaBlob(int[] blob) throws IOException {
+//            this(blob, 0, blob.length);
+//        }
+//
+//        public IaBlob(int[] blob, int offset, int length/*, int mask, int[] shifts*/) throws IOException {
+//            this.offset = offset;
+//            this.length4 = length;
+//            this.blob = blob;
+////            this.mask = mask;
+//            final int[] shiftsBE = new int[4];
+//            final int[] shiftsLE = new int[4];
+////            for (int i = 0; i < shifts.length; i++) {
+////                shiftsBE[i] = shifts[i];
+////                shiftsLE[i] = shifts[3 - i];
+////            }
+//            in = new UIOStreamBuilder(blob).setStart(offset).setLength(length).setByteOrder(ISeekable.BIG_ENDIAN).create();
+//            in = new XIntArrayInputStream(blob, offset, length, shiftsBE, shiftsLE, ISeekable.BIG_ENDIAN);
+////            skip = new SkipBytesInputStream(in, mask, 4);
+////            if (mask == 0) {
+////                bytesPerInt = 4;
+////            } else {
+////                bytesPerInt = XtoByteBE.getBytesPerNumber(mask);
+////            }
+//        }
+//
+//        public boolean writable() {
+//            return false;
+//        }
+//
+//        protected long computeLength() throws IOException {
+//            InputStream in = getInputStream();
+//            long count = 0;
+//            while (in.read() >= 0) {
+//                count++;
+//            }
+//            return count;
+//        }
+//
+//        public byte[] get(long start, int length) throws IOException {
+//            int len = (int) Math.max(Math.min(this.length4 * 4 - start, length), 0);
+//            byte[] b = new byte[len];
+//            if (len > 0) {
+//                in.seek(start);
+//                IOutils.readFully(skip, b);
+//            }
+//            return b;
+//        }
+//
+//        public int get(long start, byte[] dest) throws IOException {
+//            int len = (int) Math.max(Math.min(this.length4 * 4 - start, dest.length), 0);
+//            if (len > 0) {
+//                in.seek(start);
+//                IOutils.readFully(skip, dest, 0, len);
+//            }
+//            return len;
+//        }
+//
+//        public InputStream getInputStream() throws IOException {
+//            InputStream in = new BlobInputStream(this);
+////            InputStream skip = new SkipBytesInputStream(in, mask, 4);
+//            return in;
+//        }
+//
+//        public void set(long start, byte[] data) throws IOException {
+//            throw new RuntimeException("Unsupported operation");
+//        }
+//
+//
+//        public void clear() {
+//        }
+//    }
+}
Index: ocean/src/com/imagero/uio/blob/Blob.java
===================================================================
--- ocean/src/com/imagero/uio/blob/Blob.java	(revision 0)
+++ ocean/src/com/imagero/uio/blob/Blob.java	(revision 0)
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.imagero.uio.blob;
+
+import com.imagero.uio.RandomAccessIO;
+import com.imagero.uio.RandomAccessInput;
+import com.imagero.uio.io.UnexpectedEOFException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Hashtable;
+
+/**
+ * Blob - Object which encapsulates (possible deferred)
+ * data which may come from different sources.
+ *
+ * @author Andrey Kuznetsov
+ */
+public abstract class Blob {
+
+    long length;
+
+    private Hashtable properties = new Hashtable();
+
+    /**
+     * retrieve data from this Blob
+     * @param start start offset
+     * @param length how much bytes to get
+     * @return byte array with data
+     * @throws java.io.IOException
+     */
+    public abstract byte[] get(long start, int length) throws IOException;
+
+    /**
+     * retrieve data from this Blob
+     * @param start start offset
+     * @param dest where to copy data
+     * @return how much byte were copied
+     * @throws java.io.IOException
+     */
+    public abstract int get(long start, byte[] dest) throws IOException;
+
+    /**
+     * determine if this Blob is writable and method set(long, byte[]) can be used to change content of this Blob
+     * @return true if Blob is writable
+     */
+    public abstract boolean writable();
+
+    /**
+     * set data (work only if writable returns true)
+     * @param start start in destination
+     * @param data new data
+     * @throws java.io.IOException
+     */
+    public abstract void set(long start, byte[] data) throws IOException;
+
+    /**
+     * release reloadable resources
+     */
+    public abstract void clear();
+
+    protected boolean lengthKnown() {
+        return length > 0;
+    }
+
+    protected abstract long computeLength() throws IOException;
+
+    public long getLength() {
+        if (!lengthKnown()) {
+            try {
+                length = computeLength();
+            } catch (IOException ex) {
+                System.err.println("Can't compute Blob length. " + ex.getMessage());
+            }
+        }
+        return length;
+    }
+
+    public InputStream getInputStream() throws IOException {
+        return new BlobInputStream(this);
+    }
+
+    public Object getProperty(Object key) {
+        return properties.get(key);
+    }
+
+    public void setProperty(Object key, Object property) {
+        properties.put(key, property);
+    }
+
+    public static class BaBlob extends Blob {
+        int offset;
+
+        byte[] blob;
+
+        public BaBlob(byte[] blob) {
+            this(blob, 0, blob.length);
+        }
+
+        public BaBlob(byte[] blob, int offset, int length) {
+            this.offset = offset;
+            this.length = length;
+            this.blob = blob;
+        }
+
+        protected long computeLength() {
+            return length;
+        }
+
+        static final byte[] empty = new byte[0];
+
+        public byte[] get(long start, int length) {
+            int len = (int) Math.max(Math.min(length, this.length - start), 0);
+            if (len == 0) {
+                return empty;
+            }
+            byte[] dest = new byte[len];
+            System.arraycopy(blob, (int) start + offset, dest, 0, len);
+            return dest;
+        }
+
+        public int get(long start, byte[] dest) {
+            long max = Math.max(Math.min(dest.length, this.length - start), 0);
+            if (max > 0) {
+                System.arraycopy(blob, (int) start + offset, dest, 0, (int) max);
+            }
+            return (int) max;
+        }
+
+        public boolean writable() {
+            return true;
+        }
+
+        public void set(long start, byte[] data) {
+            System.arraycopy(data, 0, blob, (int) start, data.length);
+        }
+
+        public void clear() {
+        }
+
+        public InputStream getInputStream() throws IOException {
+            return new ByteArrayInputStream(blob, offset, (int) length);
+        }
+    }
+
+    public static class RoBlob extends Blob {
+        RandomAccessInput ro;
+        long start;
+
+        public RoBlob(RandomAccessInput ro, long start, long length) {
+            this.ro = ro;
+            this.start = start;
+            this.length = length;
+        }
+
+        protected long computeLength() {
+            return length;
+        }
+
+        public byte[] get(long start, int length) throws IOException {
+            long pos = ro.getFilePointer();
+            try {
+                byte[] dest = new byte[length];
+                ro.seek(this.start + start);
+                ro.readFully(dest);
+                return dest;
+            } finally {
+                ro.seek(pos);
+            }
+        }
+
+        public int get(long start, byte[] dest) throws IOException {
+            long max = Math.min(dest.length, length - start);
+            long pos = ro.getFilePointer();
+            try {
+                ro.seek(this.start + start);
+                ro.readFully(dest, 0, (int) max);
+            } catch (UnexpectedEOFException ex) {
+                return (int) ex.getCount();
+            } finally {
+                ro.seek(pos);
+            }
+            return (int) max;
+        }
+
+        public boolean writable() {
+            return ro instanceof RandomAccessIO;
+        }
+
+        public void set(long start, byte[] data) throws IOException {
+            if (writable()) {
+                RandomAccessIO ra = (RandomAccessIO) ro;
+                ra.seek(start);
+                ra.write(data);
+            }
+        }
+
+        public void clear() {
+        }
+    }
+
+//    public static class IaBlob extends Blob {
+//
+//        int offset;
+//        int length4;
+//        int[] blob;
+//
+////        int mask;
+////        private int bytesPerInt;
+//
+//        RandomAccessInput in;
+//        SkipBytesInputStream skip;
+//
+//        IntArrayContent content;
+//
+//        public IaBlob(int[] blob) throws IOException {
+//            this(blob, 0, blob.length);
+//        }
+//
+//        public IaBlob(int[] blob, int offset, int length/*, int mask, int[] shifts*/) throws IOException {
+//            this.offset = offset;
+//            this.length4 = length;
+//            this.blob = blob;
+////            this.mask = mask;
+//            final int[] shiftsBE = new int[4];
+//            final int[] shiftsLE = new int[4];
+////            for (int i = 0; i < shifts.length; i++) {
+////                shiftsBE[i] = shifts[i];
+////                shiftsLE[i] = shifts[3 - i];
+////            }
+//            in = new UIOStreamBuilder(blob).setStart(offset).setLength(length).setByteOrder(ISeekable.BIG_ENDIAN).create();
+//            in = new XIntArrayInputStream(blob, offset, length, shiftsBE, shiftsLE, ISeekable.BIG_ENDIAN);
+////            skip = new SkipBytesInputStream(in, mask, 4);
+////            if (mask == 0) {
+////                bytesPerInt = 4;
+////            } else {
+////                bytesPerInt = XtoByteBE.getBytesPerNumber(mask);
+////            }
+//        }
+//
+//        public boolean writable() {
+//            return false;
+//        }
+//
+//        protected long computeLength() throws IOException {
+//            InputStream in = getInputStream();
+//            long count = 0;
+//            while (in.read() >= 0) {
+//                count++;
+//            }
+//            return count;
+//        }
+//
+//        public byte[] get(long start, int length) throws IOException {
+//            int len = (int) Math.max(Math.min(this.length4 * 4 - start, length), 0);
+//            byte[] b = new byte[len];
+//            if (len > 0) {
+//                in.seek(start);
+//                IOutils.readFully(skip, b);
+//            }
+//            return b;
+//        }
+//
+//        public int get(long start, byte[] dest) throws IOException {
+//            int len = (int) Math.max(Math.min(this.length4 * 4 - start, dest.length), 0);
+//            if (len > 0) {
+//                in.seek(start);
+//                IOutils.readFully(skip, dest, 0, len);
+//            }
+//            return len;
+//        }
+//
+//        public InputStream getInputStream() throws IOException {
+//            InputStream in = new BlobInputStream(this);
+////            InputStream skip = new SkipBytesInputStream(in, mask, 4);
+//            return in;
+//        }
+//
+//        public void set(long start, byte[] data) throws IOException {
+//            throw new RuntimeException("Unsupported operation");
+//        }
+//
+//
+//        public void clear() {
+//        }
+//    }
+}
Index: ocean/src/com/imagero/uio/blob/BlobInputStream.java
===================================================================
--- ocean/src/com/imagero/uio/blob/BlobInputStream.java	(revision 0)
+++ ocean/src/com/imagero/uio/blob/BlobInputStream.java	(revision 0)
@@ -0,0 +1,86 @@
+package com.imagero.uio.blob;
+
+import com.imagero.uio.blob.Blob;
+import com.imagero.uio.io.UnexpectedEOFException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+/**
+ * BlobInputStream.
+ * Read data from Blob.
+ *
+ * Data from Blob first filled into internal buffer and then read.
+ *
+ * Date: 23.07.2007
+ *
+ * @author Andrey Kuznetsov
+ */
+public class BlobInputStream extends ByteArrayInputStream {
+    Blob blob;
+
+    long start;
+
+    boolean finished;
+
+    public BlobInputStream(Blob blob) throws IOException {
+        super(new byte[2048]);
+        this.blob = blob;
+        fillBuffer();
+    }
+
+    public int read() {
+        if (finished) {
+            return -1;
+        }
+        if (available() > 0) {
+            return super.read();
+        }
+        return -1;
+    }
+
+    public synchronized long skip(long n) {
+        int k = available();
+        if (k < n) {
+            fillBuffer();
+        }
+        return super.skip(n);
+    }
+
+    public synchronized int available() {
+        int k = super.available();
+        if (k > 0) {
+            return k;
+        } else {
+            fillBuffer();
+            return super.available();
+        }
+    }
+
+    public synchronized int read(byte b[], int off, int len) {
+        if (finished) {
+            return -1;
+        }
+        int k = available();
+        return super.read(b, off, Math.min(k, len));
+    }
+
+    private void fillBuffer() {
+        try {
+            count = blob.get(start, buf);
+        } catch (UnexpectedEOFException ex) {
+            count = (int) ex.getCount();
+        } catch (IOException ex) {
+            finished = true;
+            ex.printStackTrace();
+        }
+        if (count <= 0) {
+            count = 0;
+            pos = 0;
+            finished = true;
+            return;
+        }
+        start += count;
+        pos = 0;
+    }
+}
Index: ocean/src/com/imagero/uio/ByteArray.java
===================================================================
--- ocean/src/com/imagero/uio/ByteArray.java	(revision 0)
+++ ocean/src/com/imagero/uio/ByteArray.java	(revision 0)
@@ -0,0 +1,22 @@
+package com.imagero.uio;
+
+/**
+ * Date: 09.04.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class ByteArray {
+
+    byte[] buffer;
+
+    int position;
+    int bitOffset;
+
+    public static final int[] N_MASK = {0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+    public static final int[] K_MASK = {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF};
+    public static final int[] I_MASK = {0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF};
+
+    public ByteArray(byte[] buffer) {
+        this.buffer = buffer;
+    }
+}
Index: ocean/src/com/imagero/uio/ByteArrayIO.java
===================================================================
--- ocean/src/com/imagero/uio/ByteArrayIO.java	(revision 0)
+++ ocean/src/com/imagero/uio/ByteArrayIO.java	(revision 0)
@@ -0,0 +1,187 @@
+package com.imagero.uio;
+
+
+/**
+ * ByteArrayIO - this class gives the possibility to read from and write to given ByteArray.
+ * It supports bit offsets and bitwise writing.
+ * It does not extends InputStream or OutputStream, but has similar methods.
+ *
+ * Date: 09.04.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public class ByteArrayIO {
+
+    public static int read(ByteArray ba) {
+        return read(ba, 8);
+    }
+
+    /**
+     * read up to 8 bits from given ByteArray
+     * @param ba ByteArray
+     * @param nbits bit count to read
+     * @return int
+     */
+    public static int read(ByteArray ba, final int nbits) {
+        int ret = 0;
+        //nothing to read
+        if (nbits == 0) {
+            return 0;
+        }
+        //too many bits requested
+        if (nbits > 8) {
+            throw new IllegalArgumentException("no more then 8 bits can be read at once");
+        }
+        if(ba.bitOffset == 0 && nbits == 8) {
+            return ba.buffer[ba.position++];
+        }
+        ret = ba.buffer[ba.position] & ByteArray.N_MASK[ba.bitOffset];
+        int rshift = (8 - ba.bitOffset) - nbits;
+        if(rshift > 0) {
+            ret = ret >> rshift;
+        }
+        int bitOffset = ba.bitOffset + nbits;
+        if (bitOffset > 7) {
+            bitOffset -= 8;
+            ba.bitOffset = bitOffset;
+            ba.position++;
+            if (bitOffset > 0) {
+                ret = (ret << bitOffset) | (ba.buffer[ba.position] & ByteArray.N_MASK[ba.bitOffset]);
+            }
+        }
+        return ret;
+    }
+
+    public static int read(ByteArray ba, byte b[]) {
+        return read(ba, b, 0, b.length);
+    }
+
+    /**
+     * Reads data from input stream into an byte array.
+     *
+     * @param b the buffer into which the data is read.
+     * @param off the start offset of the data.
+     * @param len the maximum number of bytes read.
+     * @return the total number of bytes read into the buffer, or -1 if the EOF has been reached.
+     * @exception NullPointerException if supplied byte array is null
+     */
+    public static int read(ByteArray ba, byte b[], int off, int len) {
+        if (len <= 0) {
+            return 0;
+        }
+        int c = read(ba);
+        if (c == -1) {
+            return -1;
+        }
+        b[off] = (byte) c;
+
+        int i = 1;
+        for (; i < len; ++i) {
+            c = read(ba);
+            if (c == -1) {
+                break;
+            }
+            b[off + i] = (byte) c;
+        }
+        return i;
+    }
+
+    /**
+     * Skips some bytes from the input stream.
+     * @param n the number of bytes to be skipped.
+     * @return the actual number of bytes skipped.
+     */
+    public static long skipBytes(ByteArray ba, long n) {
+        int max = (int) Math.min(ba.buffer.length - ba.position, n);
+        ba.position += max;
+        return max;
+    }
+
+    /**
+     * skip some bits
+     * @param n bits to skip
+     * @return number of bits skipped
+     */
+    public static int skipBits(ByteArray ba, int n) {
+        int k = n;
+        int nbits = k % 8;
+        int nbytes = k / 8;
+        int bitOffset = ba.bitOffset + nbits;
+        if (bitOffset > 7) {
+            nbytes++;
+            ba.bitOffset = bitOffset - 8;
+        }
+        ba.position += nbytes;
+        return n;
+    }
+
+    public static void seek(ByteArray ba, int pos) {
+        ba.position = pos;
+    }
+
+    public static void seek(ByteArray ba, int pos, int bitPos) {
+        ba.position = pos + bitPos / 8;
+        ba.bitOffset = bitPos % 8;
+    }
+
+    public static int skipToByteBoundary(ByteArray ba) {
+        if (ba.bitOffset > 0) {
+            int ret = 8 - ba.bitOffset;
+            ba.bitOffset = 0;
+            ba.position++;
+            return ret;
+        }
+        return 0;
+    }
+
+    /**
+     * Writes some bits from the specified int to stream.
+     * @param b int which should be written
+     */
+    public static void write(ByteArray ba, int b) {
+        write(ba, b, 8);
+    }
+
+    /**
+     * Writes up to 8 bits from the specified int to stream.
+     * @param N int which should be written
+     * @param N_BITS bit count to write
+     */
+    public static void write(ByteArray ba, int N, int N_BITS) {
+        if (N_BITS == 0) {
+            return;
+        }
+        if (N_BITS > 8) {
+            throw new IllegalArgumentException("no more then 8 bits can be written at once");
+        }
+        if(ba.bitOffset == 0 && N_BITS == 8) {
+            ba.buffer[ba.position++] = (byte) N;
+        }
+        else {
+            write0(ba, N, N_BITS);
+        }
+    }
+
+    protected final static void write0(ByteArray ba, int N, int N_BITS) {
+        int available = 8 - ba.bitOffset;
+        int a = ba.buffer[ba.position] & 0xFF;
+        int b = a;
+
+        a = ((a >> available) << N_BITS) | (N & ByteArray.K_MASK[N_BITS]);
+        if (N_BITS > available) {
+            a = a >> (N_BITS - available);
+            ba.buffer[ba.position++] = (byte) a;
+            ba.bitOffset = 0;
+            N_BITS -= available;
+            write0(ba, N & ByteArray.N_MASK[N_BITS], N_BITS);
+        } else if (available > N_BITS) {
+            a = a << (available - N_BITS);
+            a |= b & ByteArray.K_MASK[available - N_BITS];
+            ba.buffer[ba.position] = (byte) a;
+            ba.bitOffset += N_BITS;
+        } else {
+            ba.buffer[ba.position++] = (byte) a;
+            ba.bitOffset = 0;
+        }
+    }
+}
Index: ocean/src/com/imagero/uio/Endian.java
===================================================================
--- ocean/src/com/imagero/uio/Endian.java	(revision 0)
+++ ocean/src/com/imagero/uio/Endian.java	(revision 0)
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+ package com.imagero.uio;
+
+/**
+ * Date: 01.03.2008
+ *
+ * @author Andrey Kuznetsov
+ */
+public interface Endian {
+    int BIG_ENDIAN = 0x4D4D;
+    int LITTLE_ENDIAN = 0x4949;
+
+    int getByteOrder();
+
+    void setByteOrder(int byteOrder);
+}
Index: ocean/src/com/imagero/uio/FilterDataOutput.java
===================================================================
--- ocean/src/com/imagero/uio/FilterDataOutput.java	(revision 0)
+++ ocean/src/com/imagero/uio/FilterDataOutput.java	(revision 0)
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+ package com.imagero.uio;
+
+import java.io.DataOutput;
+import java.io.IOException;
+
+public class FilterDataOutput implements DataOutput {
+    /**
+     * The underlying output to be filtered.
+     */
+    protected DataOutput out;
+
+    public FilterDataOutput(DataOutput out) {
+        this.out = out;
+    }
+
+    public void write(int b) throws IOException {
+        out.write(b);
+    }
+
+    public void write(byte b[]) throws IOException {
+        write(b, 0, b.length);
+    }
+
+    public void write(byte b[], int off, int len) throws IOException {
+        if ((off | len | (b.length - (len + off)) | (off + len)) < 0)
+            throw new IndexOutOfBoundsException();
+
+        for (int i = 0; i < len; i++) {
+            write(b[off + i]);
+        }
+    }
+
+    public void writeBoolean(boolean v) throws IOException {
+        out.writeBoolean(v);
+    }
+
+    public void writeByte(int v) throws IOException {
+        out.writeByte(v);
+    }
+
+    public void writeShort(int v) throws IOException {
+        out.writeShort(v);
+    }
+
+    public void writeChar(int v) throws IOException {
+        out.writeChar(v);
+    }
+
+    public void writeInt(int v) throws IOException {
+        out.writeInt(v);
+    }
+
+    public void writeLong(long v) throws IOException {
+        out.writeLong(v);
+    }
+
+    public void writeFloat(float v) throws IOException {
+        out.writeFloat(v);
+    }
+
+    public void writeDouble(double v) throws IOException {
+        out.writeDouble(v);
+    }
+
+    public void writeBytes(String s) throws IOException {
+        out.writeBytes(s);
+    }
+
+    public void writeChars(String s) throws IOException {
+        out.writeChars(s);
+    }
+
+    public void writeUTF(String str) throws IOException {
+        out.writeUTF(str);
+    }
+}
Index: ocean/src/com/imagero/uio/impl/RandomAccessFileWrapper.java
===================================================================
--- ocean/src/com/imagero/uio/impl/RandomAccessFileWrapper.java	(revision 0)
+++ ocean/src/com/imagero/uio/impl/RandomAccessFileWrapper.java	(revision 0)
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
+ *
+ * http://uio.imagero.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  o Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  o Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  o Neither the name of Andrey Kuznetsov nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.imagero.uio.impl;
+
+import com.imagero.uio.RandomAccessIO;
+import com.imagero.uio.RandomAccessInput;
+import com.imagero.uio.RandomAccessOutput;
+import com.imagero.uio.UIOStreamBuilder;
+import com.imagero.uio.bio.BufferedRandomAccessIO;
+import com.imagero.uio.bio.IOCInputStream;
+import com.imagero.uio.bio.IOCOutputStream;
+import com.imagero.uio.bio.IOController;
+import com.imagero.uio.bio.content.Content;
+import com.imagero.uio.bio.content.RandomAccessFileContent;
+import com.imagero.uio.io.IOutils;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.lang.reflect.Method;
+
+/**
+ * Wrap RandomAccessFile in RandomAccessIO<br>
+ * Attention - this class is not buffered.
+ * That means if you make extensive use of writeInt, writeLong, writeChar, ...,
+ * then performance will be pretty poor. Use buffered classes instead.
+ *
+ * @author Andrei Kouznetsov
+ *         Date: 08.11.2003
+ *         Time: 13:04:44
+ */
+public class RandomAccessFileWrapper extends AbstractRandomAccessIO {
+
+    RandomAccessFile in;
+
+    IOController controller;
+
+    long offset;
+    Long length;
+
+    public RandomAccessFileWrapper(RandomAccessFile in, int byteOrder) throws IOException {
+        this.in = in;
+        setByteOrder(byteOrder);
+    }
+
+    public RandomAccessFileWrapper(RandomAccessFile in, long offset, int byteOrder) throws IOException {
+        if (offset < 0 || offset >= in.length()) {
+            throw new IndexOutOfBoundsException();
+        }
+        this.in = in;
+        this.offset = offset;
+        setByteOrder(byteOrder);
+    }
+
+    public RandomAccessFileWrapper(RandomAccessFile in, long offset, long length, int byteOrder) throws IOException {
+        if (offset < 0 || offset >= in.length()) {
+            throw new IndexOutOfBoundsException();
+        }
+        this.in = in;
+        this.offset = offset;
+        this.length = new Long(length);
+        setByteOrder(byteOrder);
+    }
+
+    protected int _read() throws IOException {
+        if (getFilePointer() >= length()) {
+            throw new EOFException();
+        }
+        int i = in.read();
+        if (i < 0) {
+            throw new EOFException();
+        }
+        return i;
+    }
+
+    public void write(int b) throws IOException {
+        in.write(b);
+    }
+
+    p