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 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
|
package uk.ac.bristol.star.cdf.record;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.logging.Logger;
/**
* Factory and utility methods for use with Bufs.
*
* @author Mark Taylor
* @since 21 Jun 2013
*/
public class Bufs {
/** Preferred maximum size for a bank buffer. */
private static final int BANK_SIZE = 1 << 30;
private static Logger logger_ = Logger.getLogger( Bufs.class.getName() );
/**
* Private constructor prevents instantiation.
*/
private Bufs() {
}
/**
* Creates a buf based on a single NIO buffer.
*
* @param byteBuffer NIO buffer containing data
* @param isBit64 64bit-ness of buf
* @param isBigendian true for big-endian data, false for little-endian
*/
public static Buf createBuf( ByteBuffer byteBuffer,
boolean isBit64, boolean isBigendian ) {
return new SimpleNioBuf( byteBuffer, isBit64, isBigendian );
}
/**
* Creates a buf based on a sequence of NIO buffers.
*
* @param byteBuffers array of NIO buffers containing data
* @param isBit64 64bit-ness of buf
* @param isBigendian true for big-endian data, false for little-endian
*/
public static Buf createBuf( ByteBuffer[] byteBuffers,
boolean isBit64, boolean isBigendian ) {
return byteBuffers.length == 1
? createBuf( byteBuffers[ 0 ], isBit64, isBigendian )
: BankBuf.createMultiBankBuf( byteBuffers, isBit64, isBigendian );
}
/**
* Creates a buf based on a file.
*
* @param file file containing data
* @param isBit64 64bit-ness of buf
* @param isBigendian true for big-endian data, false for little-endian
*/
public static Buf createBuf( File file,
boolean isBit64, boolean isBigendian )
throws IOException {
FileChannel channel = new FileInputStream( file ).getChannel();
long leng = file.length();
if ( leng <= Integer.MAX_VALUE ) {
int ileng = (int) leng;
ByteBuffer bbuf =
channel.map( FileChannel.MapMode.READ_ONLY, 0, ileng );
return createBuf( bbuf, isBit64, isBigendian );
}
else {
return BankBuf.createMultiBankBuf( channel, leng, BANK_SIZE,
isBit64, isBigendian );
}
}
/**
* Decompresses part of an input Buf into an output Buf.
*
* @param compression compression format
* @param inBuf buffer containing input compressed data
* @param inOffset offset into <code>inBuf</code> at which the
* compressed data starts
* @param outSize byte count of the uncompressed data
* @return new buffer of size <code>outSize</code> containing
* uncompressed data
*/
public static Buf uncompress( Compression compression, Buf inBuf,
long inOffset, long outSize )
throws IOException {
logger_.config( "Uncompressing CDF data to new " + outSize
+ "-byte buffer" );
InputStream uin =
compression
.uncompressStream( new BufferedInputStream(
inBuf.createInputStream( inOffset ) ) );
Buf ubuf = inBuf.fillNewBuf( outSize, uin );
uin.close();
return ubuf;
}
/**
* Utility method to acquire the data from an NIO buffer in the form
* of an InputStream.
*
* @param bbuf NIO buffer
* @return stream
*/
public static InputStream createByteBufferInputStream( ByteBuffer bbuf ) {
return new ByteBufferInputStream( bbuf );
}
// Utility methods to read arrays of data from buffers.
// These essentially provide bulk absolute NIO buffer read operations;
// The NIO Buffer classes themselves only provide relative read operations
// for bulk reads.
//
// We work differently according to whether we are in fact reading
// single value or multiple values. This is because NIO Buffer
// classes have absolute read methods for scalar reads, but only
// relative read methods for array reads (i.e. you need to position
// a pointer and then do the read). For thread safety we need to
// synchronize in that case to make sure somebody else doesn't
// reposition before the read takes place.
//
// For the array reads, we also recast the ByteBuffer to a Buffer of
// the appropriate type for the data being read.
//
// Both these steps are taken on the assumption that the bulk reads
// are more efficient than multiple byte reads perhaps followed by
// bit manipulation where required. The NIO javadocs suggest that
// assumption is true, but I haven't tested it. Doing it the other
// way would avoid the need for synchronization.
/**
* Utility method to read a fixed length ASCII string from an NIO buffer.
* If a character 0x00 is encountered before the end of the byte sequence,
* it is considered to terminate the string.
*
* @param bbuf NIO buffer
* @param ioff offset into buffer of start of string
* @param nbyte number of bytes in string
*/
static String readAsciiString( ByteBuffer bbuf, int ioff, int nbyte ) {
byte[] abuf = new byte[ nbyte ];
synchronized ( bbuf ) {
bbuf.position( ioff );
bbuf.get( abuf, 0, nbyte );
}
StringBuffer sbuf = new StringBuffer( nbyte );
for ( int i = 0; i < nbyte; i++ ) {
byte b = abuf[ i ];
if ( b == 0 ) {
break;
}
else {
sbuf.append( (char) b );
}
}
return sbuf.toString();
}
/**
* Utility method to read an array of byte values from an NIO buffer
* into an array.
*
* @param bbuf buffer
* @param ioff offset into bbuf of data start
* @param count number of values to read
* @param a array into which values will be read, starting at element 0
*/
static void readBytes( ByteBuffer bbuf, int ioff, int count, byte[] a ) {
if ( count == 1 ) {
a[ 0 ] = bbuf.get( ioff );
}
else {
synchronized ( bbuf ) {
bbuf.position( ioff );
bbuf.get( a, 0, count );
}
}
}
/**
* Utility method to read an array of short values from an NIO buffer
* into an array.
*
* @param bbuf buffer
* @param ioff offset into bbuf of data start
* @param count number of values to read
* @param a array into which values will be read, starting at element 0
*/
static void readShorts( ByteBuffer bbuf, int ioff, int count, short[] a ) {
if ( count == 1 ) {
a[ 0 ] = bbuf.getShort( ioff );
}
else {
synchronized ( bbuf ) {
bbuf.position( ioff );
bbuf.asShortBuffer().get( a, 0, count );
}
}
}
/**
* Utility method to read an array of int values from an NIO buffer
* into an array.
*
* @param bbuf buffer
* @param ioff offset into bbuf of data start
* @param count number of values to read
* @param a array into which values will be read, starting at element 0
*/
static void readInts( ByteBuffer bbuf, int ioff, int count, int[] a ) {
if ( count == 1 ) {
a[ 0 ] = bbuf.getInt( ioff );
}
else {
synchronized ( bbuf ) {
bbuf.position( ioff );
bbuf.asIntBuffer().get( a, 0, count );
}
}
}
/**
* Utility method to read an array of long values from an NIO buffer
* into an array.
*
* @param bbuf buffer
* @param ioff offset into bbuf of data start
* @param count number of values to read
* @param a array into which values will be read, starting at element 0
*/
static void readLongs( ByteBuffer bbuf, int ioff, int count, long[] a ) {
if ( count == 1 ) {
a[ 0 ] = bbuf.getLong( ioff );
}
else {
synchronized ( bbuf ) {
bbuf.position( ioff );
bbuf.asLongBuffer().get( a, 0, count );
}
}
}
/**
* Utility method to read an array of float values from an NIO buffer
* into an array.
*
* @param bbuf buffer
* @param ioff offset into bbuf of data start
* @param count number of values to read
* @param a array into which values will be read, starting at element 0
*/
static void readFloats( ByteBuffer bbuf, int ioff, int count, float[] a ) {
if ( count == 1 ) {
a[ 0 ] = bbuf.getFloat( ioff );
}
else {
synchronized ( bbuf ) {
bbuf.position( ioff );
bbuf.asFloatBuffer().get( a, 0, count );
}
}
}
/**
* Utility method to read an array of double values from an NIO buffer
* into an array.
*
* @param bbuf buffer
* @param ioff offset into bbuf of data start
* @param count number of values to read
* @param a array into which values will be read, starting at element 0
*/
static void readDoubles( ByteBuffer bbuf, int ioff, int count,
double[] a ) {
if ( count == 1 ) {
a[ 0 ] = bbuf.getDouble( ioff );
}
else {
synchronized ( bbuf ) {
bbuf.position( ioff );
bbuf.asDoubleBuffer().get( a, 0, count );
}
}
}
/**
* Input stream that reads from an NIO buffer.
* You'd think there was an implementation of this in the J2SE somewhere,
* but I can't see one.
*/
private static class ByteBufferInputStream extends InputStream {
private final ByteBuffer bbuf_;
/**
* Constructor.
*
* @param bbuf NIO buffer supplying data
*/
ByteBufferInputStream( ByteBuffer bbuf ) {
bbuf_ = bbuf;
}
@Override
public int read() {
return bbuf_.remaining() > 0 ? bbuf_.get() : -1;
}
@Override
public int read( byte[] b ) {
return read( b, 0, b.length );
}
@Override
public int read( byte[] b, int off, int len ) {
if ( len == 0 ) {
return 0;
}
int remain = bbuf_.remaining();
if ( remain == 0 ) {
return -1;
}
else {
int nr = Math.min( remain, len );
bbuf_.get( b, off, nr );
return nr;
}
}
@Override
public boolean markSupported() {
return true;
}
@Override
public void mark( int readLimit ) {
bbuf_.mark();
}
@Override
public void reset() {
bbuf_.reset();
}
@Override
public long skip( long n ) {
int nsk = (int) Math.min( n, bbuf_.remaining() );
bbuf_.position( bbuf_.position() + nsk );
return nsk;
}
@Override
public int available() {
return bbuf_.remaining();
}
}
}
|