From 92a6d2c1476fd562721f231f89afba9342ed8a20 Mon Sep 17 00:00:00 2001
From: Sven Gothel
- * The {@link MappedByteBuffer} slices will be mapped {@link FileChannel.MapMode#READ_ONLY} lazily at first usage.
+ * The {@link MappedByteBuffer} slices will be mapped lazily at first usage.
*
- * The {@link MappedByteBuffer} slices will be mapped {@link FileChannel.MapMode#READ_ONLY} lazily at first usage.
+ * The {@link MappedByteBuffer} slices will be mapped lazily at first usage.
*
- * The {@link MappedByteBuffer} slices will be mapped lazily at first usage.
+ * The {@link MappedByteBuffer} slices will be mapped {@link FileChannel.MapMode#READ_ONLY} lazily at first usage.
*
@@ -110,7 +131,7 @@ public class MappedByteBufferInputStream extends InputStream {
*/
public static final int DEFAULT_SLICE_SHIFT;
- private static final boolean DEBUG;
+ static final boolean DEBUG;
static {
Platform.initSingleton();
@@ -126,10 +147,14 @@ public class MappedByteBufferInputStream extends InputStream {
private final int sliceShift;
private final FileChannel fc;
private final FileChannel.MapMode mmode;
- private final MappedByteBuffer[] slices;
- private final WeakReference
+ * Should be called by user API when aware of such event. + *
+ * @param newTotalSize the new total size + * @throws IOException if a buffer slice operation failed + */ + public final synchronized void notifyLengthChange(final long newTotalSize) throws IOException { + /* if( DEBUG ) { + System.err.println("notifyLengthChange.0: "+totalSize+" -> "+newTotalSize); + dbgDump("notifyLengthChange.0:", System.err); + } */ + if( totalSize == newTotalSize ) { + // NOP + return; + } else if( 0 == newTotalSize ) { + // ZERO - ensure one entry avoiding NULL checks + if( null != slices ) { + for(int i=0; i* {@inheritDoc} *
+ * @throws IOException if a buffer slice operation failed. */ @Override public final synchronized int available() throws IOException { @@ -372,11 +568,15 @@ public class MappedByteBufferInputStream extends InputStream { *
* 0 <= {@link #position()} <= {@link #length()}
*
- * @throws IOException
+ * @throws IOException if a buffer slice operation failed.
*/
// @Override
public final synchronized long position() throws IOException {
- return ( (long)currSlice << sliceShift ) + slice( currSlice ).position();
+ if( 0 < refCount ) {
+ return ( (long)currSlice << sliceShift ) + slice( currSlice ).position();
+ } else {
+ return 0;
+ }
}
/**
@@ -386,26 +586,31 @@ public class MappedByteBufferInputStream extends InputStream {
*
* @param newPosition The new position, which must be non-negative and ≤ {@link #length()}.
* @return this instance
- * @throws IOException
+ * @throws IOException if a buffer slice operation failed or stream is {@link #close() closed}.
*/
// @Override
public final synchronized MappedByteBufferInputStream position( final long newPosition ) throws IOException {
+ checkOpen();
if ( totalSize < newPosition || 0 > newPosition ) {
throw new IllegalArgumentException("new position "+newPosition+" not within [0.."+totalSize+"]");
}
final int preSlice = currSlice;
+ positionImpl( newPosition );
+ if( CacheMode.FLUSH_NONE != cmode && preSlice != currSlice) {
+ flushSlice(preSlice);
+ }
+ return this;
+ }
+ private final synchronized void positionImpl( final long newPosition ) throws IOException {
if ( totalSize == newPosition ) {
- currSlice = sliceCount - 1;
- final MappedByteBuffer s = slice( currSlice );
+ // EOF, pos == maxPos + 1
+ currSlice = Math.max(0, sliceCount - 1); // handle zero size
+ final ByteBuffer s = slice( currSlice );
s.position( s.capacity() );
} else {
currSlice = (int)( newPosition >>> sliceShift );
slice( currSlice ).position( (int)( newPosition - ( (long)currSlice << sliceShift ) ) );
}
- if( CacheMode.FLUSH_NONE != cmode && preSlice != currSlice) {
- flushSlice(preSlice);
- }
- return this;
}
@Override
@@ -413,25 +618,45 @@ public class MappedByteBufferInputStream extends InputStream {
return true;
}
+ /**
+ * {@inheritDoc}
+ * + * Parameter {@code readLimit} is not used in this implementation, + * since the whole file is memory mapped and no read limitation occurs. + *
+ */ @Override - public final synchronized void mark( final int unused ) { - try { - mark = position(); - } catch (final IOException e) { - throw new RuntimeException(e); // FIXME: oops + public final synchronized void mark( final int readlimit ) { + if( 0 < refCount ) { + try { + mark = position(); + } catch (final IOException e) { + throw new RuntimeException(e); // FIXME: oops + } } } + /** + * {@inheritDoc} + * @throws IOException if this stream has not been marked, + * a buffer slice operation failed or stream has been {@link #close() closed}. + */ @Override public final synchronized void reset() throws IOException { + checkOpen(); if ( mark == -1 ) { throw new IOException("mark not set"); } position( mark ); } + /** + * {@inheritDoc} + * @throws IOException if a buffer slice operation failed or stream is {@link #close() closed}. + */ @Override public final synchronized long skip( final long n ) throws IOException { + checkOpen(); if( 0 > n ) { return 0; } @@ -444,15 +669,9 @@ public class MappedByteBufferInputStream extends InputStream { @Override public final synchronized int read() throws IOException { + checkOpen(); if ( ! slice( currSlice ).hasRemaining() ) { - if ( currSlice < sliceCount - 1 ) { - final int preSlice = currSlice; - currSlice++; - slice( currSlice ).position( 0 ); - if( CacheMode.FLUSH_NONE != cmode ) { - flushSlice(preSlice); - } - } else { + if ( !nextSlice() ) { return -1; } } @@ -461,6 +680,7 @@ public class MappedByteBufferInputStream extends InputStream { @Override public final synchronized int read( final byte[] b, final int off, final int len ) throws IOException { + checkOpen(); if (b == null) { throw new NullPointerException(); } else if (off < 0 || len < 0 || len > b.length - off) { @@ -478,13 +698,10 @@ public class MappedByteBufferInputStream extends InputStream { while( read < maxLen ) { int currRem = slice( currSlice ).remaining(); if ( 0 == currRem ) { - final int preSlice = currSlice; - currSlice++; - slice( currSlice ).position( 0 ); - currRem = slice( currSlice ).remaining(); - if( CacheMode.FLUSH_NONE != cmode ) { - flushSlice(preSlice); + if ( !nextSlice() ) { + throw new InternalError("XX"); } + currRem = slice( currSlice ).remaining(); } slices[ currSlice ].get( b, off + read, Math.min( maxLen - read, currRem ) ); read += Math.min( maxLen - read, currRem ); diff --git a/src/java/com/jogamp/common/nio/MappedByteBufferOutputStream.java b/src/java/com/jogamp/common/nio/MappedByteBufferOutputStream.java new file mode 100644 index 0000000..498e9f7 --- /dev/null +++ b/src/java/com/jogamp/common/nio/MappedByteBufferOutputStream.java @@ -0,0 +1,156 @@ +/** + * 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.IOException; +import java.io.OutputStream; + +public class MappedByteBufferOutputStream extends OutputStream { + private final MappedByteBufferInputStream parent; + + MappedByteBufferOutputStream(final MappedByteBufferInputStream stream) throws IOException { + this.parent = stream; + } + + /** + * See {@link MappedByteBufferInputStream#setLength(long)}. + */ + public final synchronized void setLength(final long newTotalSize) throws IOException { + parent.setLength(newTotalSize); + } + + /** + * See {@link MappedByteBufferInputStream#notifyLengthChange(long)}. + */ + public final synchronized void notifyLengthChange(final long newTotalSize) throws IOException { + parent.notifyLengthChange(newTotalSize); + } + + /** + * See {@link MappedByteBufferInputStream#length()}. + */ + public final synchronized long length() { + return parent.length(); + } + + /** + * See {@link MappedByteBufferInputStream#remaining()}. + */ + public final synchronized long remaining() throws IOException { + return parent.remaining(); + } + + /** + * See {@link MappedByteBufferInputStream#position()}. + */ + public final synchronized long position() throws IOException { + return parent.position(); + } + + /** + * See {@link MappedByteBufferInputStream#position(long)}. + */ + public final synchronized MappedByteBufferInputStream position( final long newPosition ) throws IOException { + return parent.position(newPosition); + } + + /** + * See {@link MappedByteBufferInputStream#skip(long)}. + */ + public final synchronized long skip( final long n ) throws IOException { + return parent.skip(n); + } + + @Override + public final synchronized void flush() throws IOException { + parent.flush(); + } + + @Override + public final synchronized void close() throws IOException { + parent.close(); + } + + @Override + public final synchronized void write(final int b) throws IOException { + parent.checkOpen(); + final long totalRem = parent.remaining(); + if ( totalRem < 1 ) { // grow if required + parent.setLength( parent.length() + 1 ); + } + if ( ! parent.slice( parent.currSlice ).hasRemaining() ) { + if ( !parent.nextSlice() ) { + if( MappedByteBufferInputStream.DEBUG ) { + System.err.println("EOT write: "+parent.slices[ parent.currSlice ]); + parent.dbgDump("EOT write:", System.err); + } + throw new IOException("EOT"); + } + } + parent.slices[ parent.currSlice ].put( (byte)(b & 0xFF) ); + } + + @Override + public final synchronized void write(final byte b[], final int off, final int len) throws IOException { + parent.checkOpen(); + if (b == null) { + throw new NullPointerException(); + } else if( off < 0 || + off > b.length || + len < 0 || + off + len > b.length || + off + len < 0 + ) { + throw new IndexOutOfBoundsException("offset "+off+", length "+len+", b.length "+b.length); + } else if( 0 == len ) { + return; + } + final long totalRem = parent.remaining(); + if ( totalRem < len ) { // grow if required + parent.setLength( parent.length() + len - totalRem ); + } + int written = 0; + while( written < len ) { + int currRem = parent.slice( parent.currSlice ).remaining(); + if ( 0 == currRem ) { + if ( !parent.nextSlice() ) { + if( MappedByteBufferInputStream.DEBUG ) { + System.err.println("EOT write: offset "+off+", length "+len+", b.length "+b.length); + System.err.println("EOT write: written "+written+" / "+len+", currRem "+currRem); + System.err.println("EOT write: "+parent.slices[ parent.currSlice ]); + parent.dbgDump("EOT write:", System.err); + } + throw new InternalError("EOT"); + } + currRem = parent.slice( parent.currSlice ).remaining(); + } + parent.slices[ parent.currSlice ].put( b, off + written, Math.min( len - written, currRem ) ); + written += Math.min( len - written, currRem ); + } + } +} 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