summaryrefslogtreecommitdiffstats
path: root/src/java/com/jogamp/common/nio/MappedByteBufferOutputStream.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/java/com/jogamp/common/nio/MappedByteBufferOutputStream.java')
-rw-r--r--src/java/com/jogamp/common/nio/MappedByteBufferOutputStream.java184
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;
}
}
}