1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
|
package uk.ac.bristol.star.cdf.record;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
/**
* Buf implementation based on a single NIO ByteBuffer.
* This works fine as long as it doesn't need to be more than 2^31 bytes (2Gb),
* which is the maximum length of a ByteBuffer.
*
* @author Mark Taylor
* @since 18 Jun 2013
* @see java.nio.ByteBuffer
*/
public class SimpleNioBuf implements Buf {
private final ByteBuffer byteBuf_;
private final ByteBuffer dataBuf_;
private boolean isBit64_;
private boolean isBigendian_;
/**
* Constructor.
*
* @param byteBuf NIO byte buffer containing the byte data
* @param isBit64 64bit-ness of this buf
* @param isBigendian true for big-endian, false for little-endian
*/
public SimpleNioBuf( ByteBuffer byteBuf, boolean isBit64,
boolean isBigendian ) {
byteBuf_ = byteBuf;
dataBuf_ = byteBuf.duplicate();
setBit64( isBit64 );
setEncoding( isBigendian );
}
public long getLength() {
return byteBuf_.capacity();
}
public int readUnsignedByte( Pointer ptr ) {
return byteBuf_.get( toInt( ptr.getAndIncrement( 1 ) ) ) & 0xff;
}
public int readInt( Pointer ptr ) {
return byteBuf_.getInt( toInt( ptr.getAndIncrement( 4 ) ) );
}
public long readOffset( Pointer ptr ) {
return isBit64_
? byteBuf_.getLong( toInt( ptr.getAndIncrement( 8 ) ) )
: (long) byteBuf_.getInt( toInt( ptr.getAndIncrement( 4 ) ) );
}
public String readAsciiString( Pointer ptr, int nbyte ) {
return Bufs.readAsciiString( byteBuf_,
toInt( ptr.getAndIncrement( nbyte ) ),
nbyte );
}
public synchronized void setBit64( boolean isBit64 ) {
isBit64_ = isBit64;
}
public synchronized void setEncoding( boolean bigend ) {
// NIO buffers can do all the hard work - just tell them the
// endianness of the data buffer. Note however that the
// endianness of control data is not up for grabs, so maintain
// separate buffers for control data and application data.
dataBuf_.order( bigend ? ByteOrder.BIG_ENDIAN
: ByteOrder.LITTLE_ENDIAN );
isBigendian_ = bigend;
}
public boolean isBigendian() {
return isBigendian_;
}
public boolean isBit64() {
return isBit64_;
}
public void readDataBytes( long offset, int count, byte[] array ) {
Bufs.readBytes( dataBuf_, toInt( offset ), count, array );
}
public void readDataShorts( long offset, int count, short[] array ) {
Bufs.readShorts( dataBuf_, toInt( offset ), count, array );
}
public void readDataInts( long offset, int count, int[] array ) {
Bufs.readInts( dataBuf_, toInt( offset ), count, array );
}
public void readDataLongs( long offset, int count, long[] array ) {
Bufs.readLongs( dataBuf_, toInt( offset ), count, array );
}
public void readDataFloats( long offset, int count, float[] array ) {
Bufs.readFloats( dataBuf_, toInt( offset ), count, array );
}
public void readDataDoubles( long offset, int count, double[] array ) {
Bufs.readDoubles( dataBuf_, toInt( offset ), count, array );
}
public InputStream createInputStream( long offset ) {
ByteBuffer strmBuf = byteBuf_.duplicate();
strmBuf.position( (int) offset );
return Bufs.createByteBufferInputStream( strmBuf );
}
public Buf fillNewBuf( long count, InputStream in ) throws IOException {
int icount = toInt( count );
ByteBuffer bbuf = ByteBuffer.allocateDirect( icount );
ReadableByteChannel chan = Channels.newChannel( in );
while ( icount > 0 ) {
int nr = chan.read( bbuf );
if ( nr < 0 ) {
throw new EOFException();
}
else {
icount -= nr;
}
}
return new SimpleNioBuf( bbuf, isBit64_, isBigendian_ );
}
/**
* Downcasts a long to an int.
* If the value is too large, an unchecked exception is thrown.
* That shouldn't happen because the only values this is invoked on
* are offsets into a ByteBuffer.
*
* @param lvalue long value
* @return integer with the same value as <code>lvalue</code>
*/
private static int toInt( long lvalue ) {
int ivalue = (int) lvalue;
if ( ivalue != lvalue ) {
throw new IllegalArgumentException( "Pointer out of range: "
+ lvalue + " >32 bits" );
}
return ivalue;
}
}
|