diff options
author | Sven Gothel <[email protected]> | 2014-09-26 12:29:04 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2014-09-26 12:29:04 +0200 |
commit | 92a6d2c1476fd562721f231f89afba9342ed8a20 (patch) | |
tree | aefdae450078fdb6bd8422d487233abacbb64805 /src/junit/com/jogamp/common | |
parent | 95c4a3c7b6b256de4293ed1b31380d6af5ab59d0 (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.java | 2 | ||||
-rw-r--r-- | src/junit/com/jogamp/common/nio/TestByteBufferOutputStream.java | 275 |
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); + } +} |