diff options
Diffstat (limited to 'src/java/com/jogamp/common/nio/MappedByteBufferOutputStream.java')
-rw-r--r-- | src/java/com/jogamp/common/nio/MappedByteBufferOutputStream.java | 184 |
1 files changed, 169 insertions, 15 deletions
diff --git a/src/java/com/jogamp/common/nio/MappedByteBufferOutputStream.java b/src/java/com/jogamp/common/nio/MappedByteBufferOutputStream.java index 498e9f7..f84e6c2 100644 --- a/src/java/com/jogamp/common/nio/MappedByteBufferOutputStream.java +++ b/src/java/com/jogamp/common/nio/MappedByteBufferOutputStream.java @@ -29,12 +29,53 @@ package com.jogamp.common.nio; import java.io.IOException; import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.FileChannel.MapMode; +import com.jogamp.common.nio.MappedByteBufferInputStream.CacheMode; +import com.jogamp.common.nio.MappedByteBufferInputStream.FileResizeOp; + +/** + * An {@link OutputStream} implementation based on an underlying {@link FileChannel}'s memory mapped {@link ByteBuffer}. + * <p> + * Implementation is based on {@link MappedByteBufferInputStream}, using it as its parent instance. + * </p> + * <p> + * An instance maybe created via its parent {@link MappedByteBufferInputStream#getOutputStream(FileResizeOp)} + * or directly {@link #MappedByteBufferOutputStream(FileChannel, MapMode, CacheMode, int, FileResizeOp)}. + * </p> + * @since 2.3.0 + */ public class MappedByteBufferOutputStream extends OutputStream { private final MappedByteBufferInputStream parent; - MappedByteBufferOutputStream(final MappedByteBufferInputStream stream) throws IOException { - this.parent = stream; + MappedByteBufferOutputStream(final MappedByteBufferInputStream parent, + final FileResizeOp fileResizeOp) throws IOException { + if( FileChannel.MapMode.READ_ONLY == parent.getMapMode() ) { + throw new IOException("FileChannel map-mode is read-only"); + } + this.parent = parent; + this.parent.setFileResizeOp(fileResizeOp); + } + + /** + * Creates a new instance using the given {@link FileChannel}. + * <p> + * The {@link ByteBuffer} slices will be mapped lazily at first usage. + * </p> + * @param fileChannel the file channel to be mapped lazily. + * @param mmode the map mode, default is {@link FileChannel.MapMode#READ_WRITE}. + * @param cmode the caching mode, default is {@link MappedByteBufferInputStream.CacheMode#FLUSH_PRE_SOFT}. + * @param sliceShift the pow2 slice size, default is {@link MappedByteBufferInputStream#DEFAULT_SLICE_SHIFT}. + * @param fileResizeOp {@link MappedByteBufferInputStream.FileResizeOp} as described on {@link MappedByteBufferInputStream#setFileResizeOp(FileResizeOp)}. + * @throws IOException + */ + public MappedByteBufferOutputStream(final FileChannel fileChannel, + final FileChannel.MapMode mmode, + final CacheMode cmode, + final int sliceShift, final FileResizeOp fileResizeOp) throws IOException { + this(new MappedByteBufferInputStream(fileChannel, mmode, cmode, sliceShift, fileChannel.size(), 0), fileResizeOp); } /** @@ -103,16 +144,18 @@ public class MappedByteBufferOutputStream extends OutputStream { if ( totalRem < 1 ) { // grow if required parent.setLength( parent.length() + 1 ); } - if ( ! parent.slice( parent.currSlice ).hasRemaining() ) { - if ( !parent.nextSlice() ) { + ByteBuffer slice = parent.currentSlice(); + final int currRem = slice.remaining(); + if ( 0 == currRem ) { + if ( null == ( slice = parent.nextSlice() ) ) { if( MappedByteBufferInputStream.DEBUG ) { - System.err.println("EOT write: "+parent.slices[ parent.currSlice ]); + System.err.println("EOT write: "+parent.currentSlice()); parent.dbgDump("EOT write:", System.err); } - throw new IOException("EOT"); + throw new IOException("EOT"); // 'end-of-tape' } } - parent.slices[ parent.currSlice ].put( (byte)(b & 0xFF) ); + slice.put( (byte)(b & 0xFF) ); } @Override @@ -121,8 +164,8 @@ public class MappedByteBufferOutputStream extends OutputStream { if (b == null) { throw new NullPointerException(); } else if( off < 0 || - off > b.length || len < 0 || + off > b.length || off + len > b.length || off + len < 0 ) { @@ -136,21 +179,132 @@ public class MappedByteBufferOutputStream extends OutputStream { } int written = 0; while( written < len ) { - int currRem = parent.slice( parent.currSlice ).remaining(); + ByteBuffer slice = parent.currentSlice(); + int currRem = slice.remaining(); if ( 0 == currRem ) { - if ( !parent.nextSlice() ) { + if ( null == ( slice = 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 ]); + System.err.println("EOT write: "+parent.currentSlice()); parent.dbgDump("EOT write:", System.err); } - throw new InternalError("EOT"); + throw new InternalError("EOT"); // 'end-of-tape' } - currRem = parent.slice( parent.currSlice ).remaining(); + currRem = slice.remaining(); + } + final int currLen = Math.min( len - written, currRem ); + slice.put( b, off + written, currLen ); + written += currLen; + } + } + + /** + * Perform similar to {@link #write(byte[], int, int)} + * with {@link ByteBuffer} instead of byte array. + * @param b the {@link ByteBuffer} source, data is read from current {@link ByteBuffer#position()} + * @param len the number of bytes to write + * @throws IOException if a buffer slice operation failed or stream has been {@link #close() closed}. + */ + // @Override + public final synchronized void write(final ByteBuffer b, final int len) throws IOException { + parent.checkOpen(); + if (b == null) { + throw new NullPointerException(); + } else if (len < 0 || len > b.remaining()) { + throw new IndexOutOfBoundsException("length "+len+", b "+b); + } 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 ) { + ByteBuffer slice = parent.currentSlice(); + int currRem = slice.remaining(); + if ( 0 == currRem ) { + if ( null == ( slice = parent.nextSlice() ) ) { + if( MappedByteBufferInputStream.DEBUG ) { + System.err.println("EOT write: length "+len+", b "+b); + System.err.println("EOT write: written "+written+" / "+len+", currRem "+currRem); + System.err.println("EOT write: "+parent.currentSlice()); + parent.dbgDump("EOT write:", System.err); + } + throw new InternalError("EOT"); // 'end-of-tape' + } + currRem = slice.remaining(); + } + final int currLen = Math.min( len - written, currRem ); + + if( slice.hasArray() && b.hasArray() ) { + System.arraycopy(b.array(), b.arrayOffset() + b.position(), + slice.array(), slice.arrayOffset() + slice.position(), + currLen); + b.position( b.position() + currLen ); + slice.position( slice.position() + currLen ); + } else if( currLen == currRem ) { + slice.put(b); + } else { + final int _limit = b.limit(); + b.limit(currLen); + try { + slice.put(b); + } finally { + b.limit(_limit); + } + } + written += currLen; + } + } + + /** + * Perform similar to {@link #write(ByteBuffer, int)} + * with {@link MappedByteBufferInputStream} instead of byte array. + * <p> + * Method directly copies memory mapped {@link ByteBuffer}'ed data + * from the given input stream to this stream without extra data copy. + * </p> + * @param b the {@link ByteBuffer} source, data is read from current {@link MappedByteBufferInputStream#position()} + * @param len the number of bytes to write + * @throws IOException if a buffer slice operation failed or stream has been {@link #close() closed}. + */ + // @Override + public final synchronized void write(final MappedByteBufferInputStream b, final long len) throws IOException { + parent.checkOpen(); + if (b == null) { + throw new NullPointerException(); + } else if (len < 0 || len > b.remaining()) { + throw new IndexOutOfBoundsException("length "+len+", b "+b); + } else if( 0 == len ) { + return; + } + final long totalRem = parent.remaining(); + if ( totalRem < len ) { // grow if required + parent.setLength( parent.length() + len - totalRem ); + } + long written = 0; + while( written < len ) { + ByteBuffer slice = parent.currentSlice(); + int currRem = slice.remaining(); + if ( 0 == currRem ) { + if ( null == ( slice = parent.nextSlice() ) ) { + if( MappedByteBufferInputStream.DEBUG ) { + System.err.println("EOT write: length "+len+", b "+b); + System.err.println("EOT write: written "+written+" / "+len+", currRem "+currRem); + System.err.println("EOT write: "+parent.currentSlice()); + parent.dbgDump("EOT write:", System.err); + } + throw new InternalError("EOT"); // 'end-of-tape' + } + currRem = slice.remaining(); + } + final int currLen = b.read(slice, (int)Math.min( len - written, currRem )); + if( 0 > currLen ) { + throw new InternalError("Unexpected InputStream EOT"); // 'end-of-tape' } - parent.slices[ parent.currSlice ].put( b, off + written, Math.min( len - written, currRem ) ); - written += Math.min( len - written, currRem ); + written += currLen; } } } |