aboutsummaryrefslogtreecommitdiffstats
path: root/src/junit/com/jogamp/common
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2014-09-26 12:29:04 +0200
committerSven Gothel <[email protected]>2014-09-26 12:29:04 +0200
commit92a6d2c1476fd562721f231f89afba9342ed8a20 (patch)
treeaefdae450078fdb6bd8422d487233abacbb64805 /src/junit/com/jogamp/common
parent95c4a3c7b6b256de4293ed1b31380d6af5ab59d0 (diff)
Bug 1080 - Add write support for memory mapped big file I/O via specialized OutputStream impl.
Added MappedByteBufferOutputStream as a child instance of MappedByteBufferInputStream, since the latter already manages the file's mapped buffer slices. Current design is: - MappedByteBufferInputStream (parent) - MappedByteBufferOutputStream this is due to InputStream and OutputStream not being interfaces, but most functionality is provided in one class. We could redesign both as follows: - MappedByteBufferIOStream (parent) - MappedByteBufferInputStream - MappedByteBufferOutputStream This might visualize things better .. dunno whether its worth the extra redirection. +++ MappedByteBufferInputStream: - Adding [file] resize support via custom FileResizeOp - All construction happens via ctors - Handle refCount, incr. by ctor and getOutputStream(..), decr by close - Check whether stream is closed already -> IOException - Simplify / Reuse code MappedByteBufferOutputStream: - Adding simple write operations
Diffstat (limited to 'src/junit/com/jogamp/common')
-rw-r--r--src/junit/com/jogamp/common/nio/TestByteBufferInputStream.java2
-rw-r--r--src/junit/com/jogamp/common/nio/TestByteBufferOutputStream.java275
2 files changed, 276 insertions, 1 deletions
diff --git a/src/junit/com/jogamp/common/nio/TestByteBufferInputStream.java b/src/junit/com/jogamp/common/nio/TestByteBufferInputStream.java
index 698ddf4..195bef3 100644
--- a/src/junit/com/jogamp/common/nio/TestByteBufferInputStream.java
+++ b/src/junit/com/jogamp/common/nio/TestByteBufferInputStream.java
@@ -228,7 +228,7 @@ public class TestByteBufferInputStream extends JunitTracer {
default: fis.close();
throw new InternalError("XX: "+srcType);
}
- final MappedByteBufferInputStream mis = MappedByteBufferInputStream.create(fis.getChannel(), FileChannel.MapMode.READ_ONLY, cmode);
+ final MappedByteBufferInputStream mis = new MappedByteBufferInputStream(fis.getChannel(), FileChannel.MapMode.READ_ONLY, cmode);
Assert.assertEquals(expSize, mis.remaining());
Assert.assertEquals(expSize, mis.length());
Assert.assertEquals(0, mis.position());
diff --git a/src/junit/com/jogamp/common/nio/TestByteBufferOutputStream.java b/src/junit/com/jogamp/common/nio/TestByteBufferOutputStream.java
new file mode 100644
index 0000000..10d7f50
--- /dev/null
+++ b/src/junit/com/jogamp/common/nio/TestByteBufferOutputStream.java
@@ -0,0 +1,275 @@
+/**
+ * Copyright 2014 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``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 JogAmp Community 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.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package com.jogamp.common.nio;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.channels.FileChannel;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.jogamp.junit.util.JunitTracer;
+
+import org.junit.FixMethodOrder;
+import org.junit.runners.MethodSorters;
+
+/**
+ * Testing {@link MappedByteBufferInputStream} and {@link MappedByteBufferOutputStream} editing functionality.
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class TestByteBufferOutputStream extends JunitTracer {
+
+ static void testImpl(final String fname,
+ final byte[] payLoad, final long payLoadOffset, final long postPayLoadFiller,
+ final byte[] endBytes,
+ final int sliceShift)
+ throws IOException
+ {
+ final File file = new File(fname);
+ file.delete();
+ file.createNewFile();
+ file.deleteOnExit();
+ final RandomAccessFile out = new RandomAccessFile(file, "rw");
+ final MappedByteBufferInputStream.FileResizeOp szOp = new MappedByteBufferInputStream.FileResizeOp() {
+ @Override
+ public void setLength(final long newSize) throws IOException {
+ out.setLength(newSize);
+ }
+ };
+ final MappedByteBufferInputStream mis = new MappedByteBufferInputStream(out.getChannel(),
+ FileChannel.MapMode.READ_WRITE,
+ MappedByteBufferInputStream.CacheMode.FLUSH_PRE_SOFT,
+ sliceShift);
+ final MappedByteBufferOutputStream mos = mis.getOutputStream(szOp);
+
+ // resize to payLoad start and position to it
+ mos.setLength(payLoadOffset);
+ Assert.assertEquals(payLoadOffset, out.length());
+ Assert.assertEquals(payLoadOffset, mos.length());
+ Assert.assertEquals(0, mos.position()); // no change
+ mos.position(payLoadOffset);
+ Assert.assertEquals(payLoadOffset, mos.position());
+
+ // mark, write-expand payLoad
+ mis.mark(1);
+ mos.write(payLoad);
+ Assert.assertEquals(payLoadOffset+payLoad.length, out.length());
+ Assert.assertEquals(payLoadOffset+payLoad.length, mos.length());
+ Assert.assertEquals(payLoadOffset+payLoad.length, mos.position());
+
+ // expand + 1
+ mos.setLength(payLoadOffset+payLoad.length+1);
+ Assert.assertEquals(payLoadOffset+payLoad.length+1, out.length());
+ Assert.assertEquals(payLoadOffset+payLoad.length+1, mos.length());
+ Assert.assertEquals(payLoadOffset+payLoad.length, mos.position()); // no change
+
+ // expand up-to very end, ahead of write - position to endBytes start
+ mos.setLength(payLoadOffset+payLoad.length+postPayLoadFiller+endBytes.length);
+ Assert.assertEquals(payLoadOffset+payLoad.length+postPayLoadFiller+endBytes.length, out.length());
+ Assert.assertEquals(payLoadOffset+payLoad.length+postPayLoadFiller+endBytes.length, mos.length());
+ Assert.assertEquals(payLoadOffset+payLoad.length, mos.position()); // no change
+ mos.skip(postPayLoadFiller);
+ Assert.assertEquals(payLoadOffset+payLoad.length+postPayLoadFiller, mos.position());
+
+ // write endBytes (no resize)
+ mos.write(endBytes);
+ Assert.assertEquals(payLoadOffset+payLoad.length+postPayLoadFiller+endBytes.length, mos.position());
+
+ // Reset to payLoad, read it and verify
+ mis.reset();
+ Assert.assertEquals(payLoadOffset, mos.position());
+ Assert.assertEquals(payLoadOffset, mis.position());
+ final byte[] tmp = new byte[payLoad.length];
+ Assert.assertEquals(payLoad.length, mis.read(tmp));
+ Assert.assertEquals(payLoadOffset+payLoad.length, mos.position());
+ Assert.assertEquals(payLoadOffset+payLoad.length, mis.position());
+ Assert.assertArrayEquals(payLoad, tmp);
+
+ // Shrink to end of payLoad, mark, read >= 0, reset .. redo
+ Assert.assertEquals(payLoadOffset+payLoad.length+postPayLoadFiller+endBytes.length, out.length());
+ Assert.assertEquals(payLoadOffset+payLoad.length+postPayLoadFiller+endBytes.length, mos.length());
+ mos.setLength(payLoadOffset+payLoad.length+1);
+ Assert.assertEquals(payLoadOffset+payLoad.length+1, out.length());
+ Assert.assertEquals(payLoadOffset+payLoad.length+1, mos.length());
+ Assert.assertEquals(payLoadOffset+payLoad.length, mos.position());
+ mis.mark(1);
+ Assert.assertTrue(mis.read()>=0);
+ Assert.assertEquals(payLoadOffset+payLoad.length+1, mos.position());
+ mis.reset();
+ Assert.assertEquals(payLoadOffset+payLoad.length, mos.position());
+ Assert.assertTrue(mis.read()>=0);
+ Assert.assertEquals(payLoadOffset+payLoad.length+1, mos.position());
+
+ // Shrink -1, read EOS
+ mos.setLength(payLoadOffset+payLoad.length);
+ Assert.assertEquals(payLoadOffset+payLoad.length, out.length());
+ Assert.assertEquals(payLoadOffset+payLoad.length, mos.length());
+ Assert.assertEquals(payLoadOffset+payLoad.length, mos.position());
+ Assert.assertEquals(-1, mis.read());
+
+ // Expand + 1, mark, read >= 0, reset .. redo
+ mos.setLength(payLoadOffset+payLoad.length+1);
+ Assert.assertEquals(payLoadOffset+payLoad.length+1, out.length());
+ Assert.assertEquals(payLoadOffset+payLoad.length+1, mos.length());
+ Assert.assertEquals(payLoadOffset+payLoad.length, mos.position());
+ mis.mark(1);
+ Assert.assertTrue(mis.read()>=0);
+ Assert.assertEquals(payLoadOffset+payLoad.length+1, mos.position());
+ mis.reset();
+ Assert.assertEquals(payLoadOffset+payLoad.length, mos.position());
+ Assert.assertTrue(mis.read()>=0);
+ Assert.assertEquals(payLoadOffset+payLoad.length+1, mos.position());
+
+ // Shrink -1, read EOS, write-expand, reset and verify
+ mos.setLength(payLoadOffset+payLoad.length);
+ Assert.assertEquals(payLoadOffset+payLoad.length, out.length());
+ Assert.assertEquals(payLoadOffset+payLoad.length, mos.length());
+ Assert.assertEquals(payLoadOffset+payLoad.length, mos.position());
+ Assert.assertEquals(-1, mis.read());
+ mos.write('Z'); // expand while writing ..
+ Assert.assertEquals(payLoadOffset+payLoad.length+1, out.length());
+ Assert.assertEquals(payLoadOffset+payLoad.length+1, mos.length());
+ Assert.assertEquals(payLoadOffset+payLoad.length+1, mos.position());
+ mis.reset();
+ Assert.assertEquals(payLoadOffset+payLoad.length, mos.position());
+ Assert.assertEquals(payLoadOffset+payLoad.length, mis.position());
+ Assert.assertEquals('Z', mis.read());
+ Assert.assertEquals(payLoadOffset+payLoad.length+1, mos.position());
+ Assert.assertEquals(payLoadOffset+payLoad.length+1, mis.position());
+
+ // Shrink -2, shall clear mark, test reset failure
+ mos.setLength(payLoadOffset+payLoad.length-1);
+ Assert.assertEquals(payLoadOffset+payLoad.length-1, out.length());
+ Assert.assertEquals(payLoadOffset+payLoad.length-1, mos.length());
+ Assert.assertEquals(payLoadOffset+payLoad.length-1, mos.position());
+ try {
+ mis.reset();
+ Assert.assertTrue(false); // shall not reach
+ } catch( final IOException ioe ) {
+ Assert.assertNotNull(ioe);
+ }
+ mis.mark(1);
+
+ // ZERO file, test reset failure, read EOS, write-expand
+ mos.setLength(0);
+ Assert.assertEquals(0, out.length());
+ Assert.assertEquals(0, mos.length());
+ Assert.assertEquals(0, mos.position());
+ try {
+ mis.reset();
+ Assert.assertTrue(false); // shall not reach
+ } catch( final IOException ioe ) {
+ Assert.assertNotNull(ioe);
+ }
+ Assert.assertEquals(-1, mis.read());
+ mos.write('Z'); // expand while writing ..
+ Assert.assertEquals(1, out.length());
+ Assert.assertEquals(1, mos.length());
+ Assert.assertEquals(1, mos.position());
+ mis.position(0);
+ Assert.assertEquals(0, mos.position());
+ Assert.assertEquals(0, mis.position());
+ Assert.assertEquals('Z', mis.read());
+
+ mos.close();
+ mis.close();
+ out.close();
+ file.delete();
+ }
+
+ @Test
+ public void test00() throws IOException {
+ final int sliceShift = 13; // 8192 bytes per slice
+ testImpl("./test01.bin", "123456789AB".getBytes(), 0L, 0L, "EOF".getBytes(), sliceShift);
+ }
+
+ @Test
+ public void test01() throws IOException {
+ final int sliceShift = 13; // 8192 bytes per slice
+ testImpl("./test01.bin", "123456789AB".getBytes(), 9000L, 100L, "EOF".getBytes(), sliceShift);
+ }
+
+ @Test
+ public void test02() throws IOException {
+ final int sliceShift = 13; // 8192 bytes per slice
+ testImpl("./test01.bin", "123456789AB".getBytes(), 8189L, 9001L, "EOF".getBytes(), sliceShift);
+ }
+
+ @Test
+ public void test03() throws IOException {
+ final int sliceShift = 13; // 8192 bytes per slice
+ testImpl("./test01.bin", "123456789AB".getBytes(), 58189L, 109001L, "EOF".getBytes(), sliceShift);
+ }
+
+ @Test
+ public void test10() throws IOException {
+ final int sliceShift = 10; // 1024 bytes per slice
+ final byte[] payLoad = new byte[4096];
+ for(int i=0; i<payLoad.length; i++) {
+ payLoad[i] = (byte)('A' + i%26);
+ }
+ testImpl("./test01.bin", payLoad, 0L, 0L, "EOF".getBytes(), sliceShift);
+ }
+
+ @Test
+ public void test11() throws IOException {
+ final int sliceShift = 10; // 1024 bytes per slice
+ final byte[] payLoad = new byte[4096];
+ for(int i=0; i<payLoad.length; i++) {
+ payLoad[i] = (byte)('A' + i%26);
+ }
+ testImpl("./test01.bin", payLoad, 1030L, 99L, "EOF".getBytes(), sliceShift);
+ }
+
+ @Test
+ public void test12() throws IOException {
+ final int sliceShift = 10; // 1024 bytes per slice
+ final byte[] payLoad = new byte[4096];
+ for(int i=0; i<payLoad.length; i++) {
+ payLoad[i] = (byte)('A' + i%26);
+ }
+ testImpl("./test01.bin", payLoad, 1021L, 1301L, "EOF".getBytes(), sliceShift);
+ }
+
+ @Test
+ public void test13() throws IOException {
+ final int sliceShift = 10; // 1024 bytes per slice
+ final byte[] payLoad = new byte[4096];
+ for(int i=0; i<payLoad.length; i++) {
+ payLoad[i] = (byte)('A' + i%26);
+ }
+ testImpl("./test01.bin", payLoad, 3021L, 6301L, "EOF".getBytes(), sliceShift);
+ }
+
+ public static void main(final String args[]) throws IOException {
+ final String tstname = TestByteBufferOutputStream.class.getName();
+ org.junit.runner.JUnitCore.main(tstname);
+ }
+}