28package com.jogamp.common.nio;
30import java.io.IOException;
31import java.io.InputStream;
32import java.io.OutputStream;
33import java.io.PrintStream;
34import java.io.RandomAccessFile;
35import java.lang.ref.WeakReference;
36import java.nio.ByteBuffer;
37import java.nio.MappedByteBuffer;
38import java.nio.channels.FileChannel;
39import java.nio.channels.FileChannel.MapMode;
41import com.jogamp.common.os.Platform.OSType;
43import jogamp.common.Debug;
44import jogamp.common.os.PlatformPropsImpl;
114 public void setLength(
final long newSize)
throws IOException {
115 throw new IOException(
"file size change not supported");
137 static final boolean DEBUG;
140 if( PlatformPropsImpl.CPU_ARCH.is32Bit ) {
146 DEBUG = Debug.debug(
"ByteBufferInputStream");
149 private final int sliceShift;
150 private final FileChannel fc;
151 private final FileChannel.MapMode mmode;
154 private int sliceCount;
155 private ByteBuffer[] slices;
156 private WeakReference<ByteBuffer>[] slices2GC;
157 private long totalSize;
158 private int slicesEntries, slices2GCEntries;
159 private boolean synchronous;
161 private int refCount;
165 private int sliceIdx;
168 final void dbgDump(
final String prefix,
final PrintStream out) {
169 int _slicesEntries = 0;
170 for(
int i=0; i<sliceCount; i++) {
171 if(
null != slices[i] ) {
175 int _slices2GCEntries = 0;
176 int _slices2GCAliveEntries = 0;
177 for(
int i=0; i<sliceCount; i++) {
178 final WeakReference<ByteBuffer> ref = slices2GC[i];
181 if(
null != ref.get() ) {
182 _slices2GCAliveEntries++;
186 long fcSz = 0, pos = 0, rem = 0;
190 }
catch (
final IOException e) {
197 rem = totalSize - pos;
198 }
catch (
final IOException e) {
202 final int sliceCount2 =
null != slices ? slices.length : 0;
203 out.println(prefix+
" refCount "+refCount+
", fcSize "+fcSz+
", totalSize "+totalSize);
204 out.println(prefix+
" position "+pos+
", remaining "+rem);
205 out.println(prefix+
" mmode "+mmode+
", cmode "+cmode+
", fileResizeOp "+fileResizeOp);
206 out.println(prefix+
" slice "+sliceIdx+
" / "+sliceCount+
" ("+sliceCount2+
"), synchronous "+synchronous);
207 out.println(prefix+
" mapped "+slicesEntries+
" / "+_slicesEntries);
208 out.println(prefix+
" GC-queue "+slices2GCEntries+
" / "+_slices2GCEntries+
" (alive "+_slices2GCAliveEntries+
")");
209 out.println(prefix+
" sliceShift "+sliceShift+
" -> "+(1L << sliceShift));
212 MappedByteBufferInputStream(
final FileChannel fc,
final FileChannel.MapMode mmode,
final CacheMode cmode,
213 final int sliceShift,
final long totalSize,
final int currSliceIdx)
throws IOException {
214 this.sliceShift = sliceShift;
218 if( 0 > totalSize ) {
219 throw new IllegalArgumentException(
"Negative size "+totalSize);
229 this.sliceIdx = currSliceIdx;
234 if( MappedByteBufferInputStream.DEBUG ) {
235 this.dbgDump(
"CTOR", System.err);
251 final FileChannel.MapMode mmode,
253 final int sliceShift)
throws IOException {
254 this(fileChannel, mmode, cmode, sliceShift, fileChannel.size(), 0);
308 final synchronized void checkOpen() throws IOException {
309 if( 0 == refCount ) {
310 throw new IOException(
"stream closed");
315 public final synchronized void close() throws IOException {
318 if( 0 == refCount ) {
320 cleanAllSlices(
true );
322 flushImpl(
true ,
false );
331 this.dbgDump(
"Close", System.err);
335 final FileChannel.MapMode getMapMode() {
return mmode; }
342 if( NoFileResize != this.fileResizeOp && this.fileResizeOp != fileResizeOp ) {
343 throw new IllegalStateException(
"FileResizeOp already set, this value differs");
345 this.fileResizeOp =
null != fileResizeOp ? fileResizeOp : NoFileResize;
361 public final synchronized void setLength(
final long newTotalSize)
throws IOException {
362 final long currentPosition;
363 if( 0 != newTotalSize && totalSize != newTotalSize ) {
366 currentPosition = -1L;
368 if( fc.size() != newTotalSize ) {
374 cleanAllSlices( synchronous );
379 flushImpl(
true ,
false );
382 notifyLengthChangeImpl(newTotalSize, currentPosition);
395 notifyLengthChangeImpl(newTotalSize, -1L);
397 private final synchronized void notifyLengthChangeImpl(
final long newTotalSize,
final long currentPosition)
throws IOException {
402 if( totalSize == newTotalSize ) {
405 }
else if( 0 == newTotalSize ) {
407 cleanAllSlices( synchronous );
408 @SuppressWarnings(
"unchecked")
409 final WeakReference<ByteBuffer>[] newSlices2GC = new WeakReference[ 1 ];
410 slices2GC = newSlices2GC;
411 slices = new ByteBuffer[1];
412 slices[0] = ByteBuffer.allocate(0);
418 final long prePosition = 0 <= currentPosition ? currentPosition :
position();
420 final long sliceSize = 1L << sliceShift;
421 final int newSliceCount = (int)( ( newTotalSize + ( sliceSize - 1 ) ) / sliceSize );
422 @SuppressWarnings(
"unchecked")
423 final WeakReference<ByteBuffer>[] newSlices2GC = new WeakReference[ newSliceCount ];
424 final ByteBuffer[] newSlices = new ByteBuffer[ newSliceCount ];
425 final
int copySliceCount = Math.min(newSliceCount, sliceCount-1);
426 if( 0 <= copySliceCount ) {
427 if( 0 < copySliceCount ) {
428 System.arraycopy(slices2GC, 0, newSlices2GC, 0, copySliceCount);
429 System.arraycopy(slices, 0, newSlices, 0, copySliceCount);
431 for(
int i=copySliceCount; i<sliceCount; i++) {
432 cleanSlice(i, synchronous);
435 slices2GC = newSlices2GC;
437 sliceCount = newSliceCount;
438 totalSize = newTotalSize;
439 if( newTotalSize < mark ) {
442 position2( Math.min(prePosition, newTotalSize) );
444 if( MappedByteBufferInputStream.DEBUG ) {
445 this.dbgDump(
"NotifyLengthChange", System.err);
456 public final synchronized void flush(
final boolean metaData)
throws IOException {
458 flushImpl(metaData,
true);
460 private final synchronized void flushImpl(
final boolean metaData,
final boolean syncBuffer)
throws IOException {
461 if( FileChannel.MapMode.READ_ONLY != mmode ) {
462 if( syncBuffer && FileChannel.MapMode.READ_WRITE == mmode ) {
463 for(
int i=0; i<sliceCount; i++) {
464 syncSlice(slices[i],
true);
466 for(
int i=0; i<sliceCount; i++) {
467 final WeakReference<ByteBuffer> ref = slices2GC[i];
469 syncSlice(ref.get(),
true);
487 throws IllegalStateException, IOException
506 public final synchronized ByteBuffer
currentSlice() throws IOException {
507 final ByteBuffer s0 = slices[sliceIdx];
512 final WeakReference<ByteBuffer> ref = slices2GC[sliceIdx];
514 final ByteBuffer mbb = ref.get();
515 slices2GC[sliceIdx] =
null;
518 slices[sliceIdx] = mbb;
524 final long pos = (long)sliceIdx << sliceShift;
525 final MappedByteBuffer s1 = fc.map(mmode, pos, Math.min(1L << sliceShift, totalSize - pos));
526 slices[sliceIdx] = s1;
541 public final synchronized ByteBuffer
nextSlice() throws IOException {
542 if ( sliceIdx < sliceCount - 1 ) {
543 flushSlice(sliceIdx, synchronous);
558 if(
null != slices ) {
559 for(
int i=0; i<sliceCount; i++) {
560 flushSlice(i, synchronous);
564 this.dbgDump(
"FlushSlices", System.err);
568 synchronized void syncSlice(
final ByteBuffer s)
throws IOException {
569 syncSlice(s, synchronous);
571 synchronized void syncSlice(
final ByteBuffer s,
final boolean syncBuffer)
throws IOException {
572 if( syncBuffer &&
null != s && FileChannel.MapMode.READ_WRITE == mmode ) {
574 ((MappedByteBuffer)s).force();
575 }
catch(
final Throwable t ) {
581 System.err.println(
"Caught "+t.getMessage());
587 private synchronized void flushSlice(
final int i,
final boolean syncBuffer)
throws IOException {
588 final ByteBuffer s = slices[i];
590 if( CacheMode.FLUSH_NONE != cmode ) {
593 if( CacheMode.FLUSH_PRE_HARD == cmode ) {
594 if( !cleanBuffer(s, syncBuffer) ) {
596 slices2GC[i] =
new WeakReference<ByteBuffer>(s);
600 syncSlice(s, syncBuffer);
601 slices2GC[i] =
new WeakReference<ByteBuffer>(s);
605 syncSlice(s, syncBuffer);
609 private synchronized void cleanAllSlices(
final boolean syncBuffers)
throws IOException {
610 if(
null != slices ) {
611 for(
int i=0; i<sliceCount; i++) {
612 cleanSlice(i, syncBuffers);
614 if( 0 != slicesEntries || 0 != slices2GCEntries ) {
615 final String err =
"mappedSliceCount "+slicesEntries+
", slices2GCEntries "+slices2GCEntries;
616 dbgDump(err+
": ", System.err);
617 throw new InternalError(err);
622 private synchronized void cleanSlice(
final int i,
final boolean syncBuffer)
throws IOException {
623 final ByteBuffer s1 = slices[i];
626 final WeakReference<ByteBuffer> ref = slices2GC[i];
638 cleanBuffer(s1, syncBuffer);
640 throw new InternalError(
"XXX");
642 }
else if(
null != s2 ) {
643 cleanBuffer(s2, syncBuffer);
646 private synchronized boolean cleanBuffer(
final ByteBuffer mbb,
final boolean syncBuffer)
throws IOException {
647 syncSlice(mbb, syncBuffer);
648 if( !mbb.isDirect() ) {
651 if( !Buffers.Cleaner.clean(mbb) && CacheMode.FLUSH_PRE_HARD == cmode ) {
677 public final synchronized long length() {
693 public final synchronized long remaining() throws IOException {
694 return 0 < refCount ? totalSize -
position() : 0;
705 public final synchronized int available() throws IOException {
718 public final synchronized long position() throws IOException {
720 return ( (
long)sliceIdx << sliceShift ) +
currentSlice().position();
738 if ( totalSize < newPosition || 0 > newPosition ) {
739 throw new IllegalArgumentException(
"new position "+newPosition+
" not within [0.."+totalSize+
"]");
741 final int preSlice = sliceIdx;
743 if ( totalSize == newPosition ) {
745 sliceIdx = Math.max(0, sliceCount - 1);
746 if( preSlice != sliceIdx ) {
747 flushSlice(preSlice, synchronous);
750 s.position( s.capacity() );
752 sliceIdx = (int)( newPosition >>> sliceShift );
753 if( preSlice != sliceIdx ) {
754 flushSlice(preSlice, synchronous);
756 currentSlice().position( (
int)( newPosition - ( (
long)sliceIdx << sliceShift ) ) );
760 private final synchronized void position2(
final long newPosition )
throws IOException {
761 if ( totalSize == newPosition ) {
763 sliceIdx = Math.max(0, sliceCount - 1);
765 s.position( s.capacity() );
767 sliceIdx = (int)( newPosition >>> sliceShift );
768 currentSlice().position( (
int)( newPosition - ( (
long)sliceIdx << sliceShift ) ) );
785 public final synchronized void mark(
final int readlimit ) {
789 }
catch (
final IOException e) {
790 throw new RuntimeException(e);
801 public final synchronized void reset() throws IOException {
804 throw new IOException(
"mark not set");
814 public final synchronized long skip(
final long n )
throws IOException {
820 final long rem = totalSize - pos;
821 final long s = Math.min( rem, n );
827 public final synchronized int read() throws IOException {
830 if ( !slice.hasRemaining() ) {
835 return slice.get() & 0xFF;
839 public final synchronized int read(
final byte[] b,
final int off,
final int len )
throws IOException {
842 throw new NullPointerException();
843 }
else if( off < 0 ||
846 off + len > b.length ||
849 throw new IndexOutOfBoundsException(
"offset "+off+
", length "+len+
", b.length "+b.length);
850 }
else if ( 0 == len ) {
854 if ( 0 == totalRem ) {
857 final int maxLen = (int)Math.min( totalRem, len );
859 while(
read < maxLen ) {
861 int currRem = slice.remaining();
862 if ( 0 == currRem ) {
864 throw new InternalError(
"Unexpected EOT");
866 currRem = slice.remaining();
868 final int currLen = Math.min( maxLen -
read, currRem );
869 slice.get( b, off +
read, currLen );
884 public final synchronized int read(
final ByteBuffer b,
final int len)
throws IOException {
887 throw new NullPointerException();
888 }
else if (len < 0 || len > b.remaining()) {
889 throw new IndexOutOfBoundsException(
"length "+len+
", b "+b);
890 }
else if ( 0 == len ) {
894 if ( 0 == totalRem ) {
897 final int maxLen = (int)Math.min( totalRem, len );
899 while(
read < maxLen ) {
901 int currRem = slice.remaining();
902 if ( 0 == currRem ) {
904 throw new InternalError(
"Unexpected EOT");
906 currRem = slice.remaining();
908 final int currLen = Math.min( maxLen -
read, currRem );
909 if( slice.hasArray() && b.hasArray() ) {
910 System.arraycopy(slice.array(), slice.arrayOffset() + slice.position(),
911 b.array(), b.arrayOffset() + b.position(),
913 slice.position( slice.position() + currLen );
914 b.position( b.position() + currLen );
915 }
else if( currLen == currRem ) {
918 final int _limit = slice.limit();
919 slice.limit(currLen);
An OutputStream implementation based on an underlying FileChannel's memory mapped ByteBuffer.