com.jogamp.common.nio.MappedByteBufferOutputStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gluegen-rt Show documentation
Show all versions of gluegen-rt Show documentation
JNI binding generator (runtime)
/**
* 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;
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}.
*
* Implementation is based on {@link MappedByteBufferInputStream}, using it as its parent instance.
*
*
* An instance maybe created via its parent {@link MappedByteBufferInputStream#getOutputStream(FileResizeOp)}
* or directly {@link #MappedByteBufferOutputStream(FileChannel, MapMode, CacheMode, int, FileResizeOp)}.
*
* @since 2.3.0
*/
public class MappedByteBufferOutputStream extends OutputStream {
private final MappedByteBufferInputStream parent;
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}.
*
* The {@link ByteBuffer} slices will be mapped lazily at first usage.
*
* @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);
}
/**
* See {@link MappedByteBufferInputStream#setSynchronous(boolean)}.
*/
public final synchronized void setSynchronous(final boolean s) {
parent.setSynchronous(s);
}
/**
* See {@link MappedByteBufferInputStream#getSynchronous()}.
*/
public final synchronized boolean getSynchronous() {
return parent.getSynchronous();
}
/**
* 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( true /* metaData */);
}
/**
* See {@link MappedByteBufferInputStream#flush(boolean)}.
*/
// @Override
public final synchronized void flush(final boolean metaData) throws IOException {
parent.flush(metaData);
}
@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 );
}
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.currentSlice());
parent.dbgDump("EOT write:", System.err);
}
throw new IOException("EOT"); // 'end-of-tape'
}
}
slice.put( (byte)(b & 0xFF) );
// sync last buffer (happens only in synchronous mode)
if( null != slice ) {
parent.syncSlice(slice);
}
}
@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 ||
len < 0 ||
off > b.length ||
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;
ByteBuffer slice = null;
while( written < len ) {
slice = parent.currentSlice();
int currRem = slice.remaining();
if ( 0 == currRem ) {
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.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 );
slice.put( b, off + written, currLen );
written += currLen;
}
// sync last buffer (happens only in synchronous mode)
if( null != slice ) {
parent.syncSlice(slice);
}
}
/**
* 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;
ByteBuffer slice = null;
while( written < len ) {
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;
}
// sync last buffer (happens only in synchronous mode)
if( null != slice ) {
parent.syncSlice(slice);
}
}
/**
* Perform similar to {@link #write(ByteBuffer, int)}
* with {@link MappedByteBufferInputStream} instead of byte array.
*
* Method directly copies memory mapped {@link ByteBuffer}'ed data
* from the given input stream to this stream without extra data copy.
*
* @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;
ByteBuffer slice = null;
while( written < len ) {
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'
}
written += currLen;
}
// sync last buffer (happens only in synchronous mode)
if( null != slice ) {
parent.syncSlice(slice);
}
}
}