All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ucar.unidata.io.RandomAccessFile Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */

package ucar.unidata.io;

import ucar.nc2.constants.CDM;
import ucar.nc2.dataset.DatasetUrl;
import ucar.nc2.util.CancelTask;
import ucar.nc2.util.cache.FileCache;
import ucar.nc2.util.cache.FileCacheIF;
import ucar.nc2.util.cache.FileCacheable;
import ucar.nc2.util.cache.FileFactory;
import ucar.unidata.util.StringUtil2;

import javax.annotation.concurrent.NotThreadSafe;
import java.io.*;
import java.nio.ByteOrder;
import java.nio.channels.WritableByteChannel;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;


/**
 * A buffered drop-in replacement for java.io.RandomAccessFile.
 * Instances of this class realise substantial speed increases over
 * java.io.RandomAccessFile through the use of buffering. This is a
 * subclass of Object, as it was not possible to subclass
 * java.io.RandomAccessFile because many of the methods are
 * final. However, if it is necessary to use RandomAccessFile and
 * java.io.RandomAccessFile interchangeably, both classes implement the
 * DataInput and DataOutput interfaces.
 * 

*

By Russ Rew, based on * BufferedRandomAccessFile by Alex McManus, based on Sun's source code * for java.io.RandomAccessFile. For Alex McManus version from which * this derives, see his * Freeware Java Classes. *

* Must be thread confined - that is, can only be used by a single thread at a time.. * * @author Alex McManus * @author Russ Rew * @author john caron * @see java.io.DataInput * @see java.io.DataOutput * @see java.io.RandomAccessFile */ @NotThreadSafe public class RandomAccessFile implements DataInput, DataOutput, FileCacheable, Closeable { static public final int BIG_ENDIAN = 0; static public final int LITTLE_ENDIAN = 1; static protected final int defaultBufferSize = 8092; // The default buffer size, in bytes. /////////////////////////////////////////////////////////////////////// // debug leaks - keep track of open files static protected boolean debugLeaks = false; static protected boolean debugAccess = false; static protected Set allFiles = null; static protected List openFiles = Collections.synchronizedList(new ArrayList<>()); // could keep map on file hashcode static private AtomicLong count_openFiles = new AtomicLong(); static private AtomicInteger maxOpenFiles = new AtomicInteger(); static private AtomicInteger debug_nseeks = new AtomicInteger(); static private AtomicLong debug_nbytes = new AtomicLong(); static protected boolean showOpen = false; static protected boolean showRead = false; /** * Debugging, do not use. * * @return true if debugLeaks is on */ static public boolean getDebugLeaks() { return debugLeaks; } /** * Debugging, do not use in production. * Set counters to zero, set debugging on * @param b set true to track java.io.RandomAccessFile */ static public void setDebugLeaks(boolean b) { if (b) { count_openFiles.set(0); maxOpenFiles.set(0); allFiles = new HashSet<>(1000); } debugLeaks = b; } /** * Debugging, do not use. * * @return list of open files. */ static public List getOpenFiles() { return Collections.unmodifiableList(openFiles); } static public long getOpenFileCount() { return count_openFiles.get(); } static public int getMaxOpenFileCount() { return maxOpenFiles.get(); } /** * Debugging, do not use. * * @return list of all files used. */ static public List getAllFiles() { if (null == allFiles) return null; List result = new ArrayList<>(); result.addAll(allFiles); Collections.sort(result); return result; } /** * Debugging, do not use. * * @param b to debug file reading */ static public void setDebugAccess(boolean b) { debugAccess = b; if (b) { debug_nseeks = new AtomicInteger(); debug_nbytes = new AtomicLong(); } } /** * Debugging, do not use. * * @return number of seeks */ static public int getDebugNseeks() { return (debug_nseeks == null) ? 0 : debug_nseeks.intValue(); } /** * Debugging, do not use. * * @return number of bytes read */ static public long getDebugNbytes() { return (debug_nbytes == null) ? 0 : debug_nbytes.longValue(); } ///////////////////////////////////////////////////////////////////////////////////////////// // internal File Caching. this allows a global pool of OS files. // note read only static private final ucar.nc2.util.cache.FileFactory factory = new FileFactory() { public FileCacheable open(DatasetUrl durl, int buffer_size, CancelTask cancelTask, Object iospMessage) throws IOException { String location = StringUtil2.replace(durl.trueurl, "\\", "/"); // canonicalize the name RandomAccessFile result = new RandomAccessFile(location, "r", buffer_size); result.cacheState = 1; // in use return result; } }; static private FileCacheIF cache = null; static public synchronized void enableDefaultGlobalFileCache() { if (cache != null) cache.disable(); cache = new FileCache("RandomAccessFile", 200, 300, 400, 60 * 60); // default; override for higher performance, or set to null for no caching; } static public synchronized void setGlobalFileCache(FileCacheIF _cache) { if (cache != null) cache.disable(); cache = _cache; } static public synchronized FileCacheIF getGlobalFileCache() { return cache; } static public RandomAccessFile acquire(String location) throws IOException { if (cache == null) return new RandomAccessFile(location, "r"); else return (RandomAccessFile) cache.acquire(factory, new DatasetUrl(null, location)); } static public RandomAccessFile acquire(String location, int buffer_size) throws IOException { if (cache == null) return new RandomAccessFile(location, "r", buffer_size); else return (RandomAccessFile) cache.acquire(factory, location, new DatasetUrl(null, location), buffer_size, null, null); } static public void eject(String location) { if (cache != null) cache.eject(location); } static public void shutdown() { if (cache != null) cache.clearCache(true); } ///////////////////////////////////////////////////////////////////////////////////////////// /** * File location */ protected String location; private int cacheState = 0; // 0 - not in cache, 1 = in cache && in use, 2 = in cache but not in use /** * The underlying java.io.RandomAccessFile. */ protected java.io.RandomAccessFile file; protected java.nio.channels.FileChannel fileChannel; /** * The offset in bytes from the file start, of the next read or * write operation. */ protected long filePosition; /** * The buffer used for reading the data. */ protected byte buffer[]; /** * The offset in bytes of the start of the buffer, from the start of the file. */ protected long bufferStart; /** * The offset in bytes of the end of the data in the buffer, from * the start of the file. This can be calculated from * bufferStart + dataSize, but it is cached to speed * up the read( ) method. */ protected long dataEnd; /** * The size of the data stored in the buffer, in bytes. This may be * less than the size of the buffer. */ protected int dataSize; /** * True if we are at the end of the file. */ protected boolean endOfFile; /** * The access mode of the file. */ protected boolean readonly; /** * The current endian (big or little) mode of the file. */ protected boolean bigEndian; /** * True if the data in the buffer has been modified. */ boolean bufferModified = false; /** * make sure file is at least this long when closed */ private long minLength = 0; /** * STUPID extendMode for truncated, yet valid files. old netcdf C library code allowed NOFILL to do this */ private boolean extendMode = false; /** * Constructor, for subclasses * * @param bufferSize size of read buffer */ protected RandomAccessFile(int bufferSize) { file = null; readonly = true; init(bufferSize); } /** * Constructor, default buffer size. * * @param location location of the file * @param mode same as for java.io.RandomAccessFile, usually "r" or "rw" * @throws IOException on open error */ public RandomAccessFile(String location, String mode) throws IOException { this(location, mode, defaultBufferSize); this.location = location; } /** * Constructor. * * @param location location of the file * @param mode same as for java.io.RandomAccessFile * @param bufferSize size of buffer to use. * @throws IOException on open error */ public RandomAccessFile(String location, String mode, int bufferSize) throws IOException { if (bufferSize < 0) bufferSize = defaultBufferSize; this.location = location; if (debugLeaks) { allFiles.add(location); } try { this.file = new java.io.RandomAccessFile(location, mode); } catch (IOException ioe) { if (ioe.getMessage().equals("Too many open files")) { System.out.printf("RandomAccessFile %s%n", ioe); try { Thread.currentThread().sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } this.file = new java.io.RandomAccessFile(location, mode); // Windows having troublke keeping up ?? } else { throw ioe; } } this.readonly = mode.equals("r"); init(bufferSize); if (debugLeaks) { openFiles.add(location); int max = Math.max(openFiles.size(), maxOpenFiles.get()); maxOpenFiles.set(max); count_openFiles.getAndIncrement(); if (showOpen) System.out.println(" DebugRAF open " + location); //if (openFiles.size() > 1000) // System.out.println("RandomAccessFile debugLeaks"); } } /** * Allow access to the underlying java.io.RandomAccessFile. * WARNING! BROKEN ENCAPSOLATION, DO NOT USE. May change implementation in the future. * * @return the underlying java.io.RandomAccessFile. */ public java.io.RandomAccessFile getRandomAccessFile() { return this.file; } private void init(int bufferSize) { // Initialise the buffer bufferStart = 0; dataEnd = 0; dataSize = 0; filePosition = 0; buffer = new byte[bufferSize]; endOfFile = false; } /** * Set the buffer size. * If writing, call flush() first. * * @param bufferSize length in bytes */ public void setBufferSize(int bufferSize) { init(bufferSize); } /** * Get the buffer size * * @return bufferSize length in bytes */ public int getBufferSize() { return buffer.length; } /** * Close the file, and release any associated system resources. * * @throws IOException if an I/O error occurrs. */ public synchronized void close() throws IOException { if (cache != null) { if (cacheState > 0) { if (cacheState == 1) { cacheState = 2; if (cache.release(this)) // return true if in the cache, otherwise was opened regular, so must be closed regular return; cacheState = 0; // release failed, bail out } else { return; // close has been called more than once - ok } } } if (debugLeaks) { openFiles.remove(location); if (showOpen) System.out.println(" close " + location); } if (file == null) return; // If we are writing and the buffer has been modified, flush the contents of the buffer. flush(); // may need to extend file, in case no fill is being used // may need to truncate file in case overwriting a longer file // use only if minLength is set (by N3iosp) long fileSize = file.length(); if (!readonly && (minLength != 0) && (minLength != fileSize)) { file.setLength(minLength); // System.out.println("TRUNCATE!!! minlength="+minLength); } // Close the underlying file object. file.close(); file = null; // help the gc } @Override public void release() { // one to one with java.io.RandomAccessFile cacheState = 2; } @Override public void reacquire() { cacheState = 1; } @Override public synchronized void setFileCache(FileCacheIF fileCache) { if (fileCache == null) cacheState = 0; } @Override public long getLastModified() { File file = new File(getLocation()); return file.lastModified(); } /** * Return true if file pointer is at end of file. * * @return true if file pointer is at end of file */ public boolean isAtEndOfFile() { return endOfFile; } /** * Set the position in the file for the next read or write. * * @param pos the offset (in bytes) from the start of the file. * @throws IOException if an I/O error occurrs. */ public void seek(long pos) throws IOException { if (pos < 0) throw new java.io.IOException("Negative seek offset"); // If the seek is into the buffer, just update the file pointer. if ((pos >= bufferStart) && (pos < dataEnd)) { filePosition = pos; return; } // need new buffer, starting at pos readBuffer(pos); } protected void readBuffer(long pos) throws IOException { // If the current buffer is modified, write it to disk. if (bufferModified) { flush(); } bufferStart = pos; filePosition = pos; dataSize = read_(pos, buffer, 0, buffer.length); if (dataSize <= 0) { dataSize = 0; endOfFile = true; } else { endOfFile = false; } // Cache the position of the buffer end. dataEnd = bufferStart + dataSize; } /** * Returns the current position in the file, where the next read or * write will occur. * * @return the offset from the start of the file in bytes. * @throws IOException if an I/O error occurrs. */ public long getFilePointer() throws IOException { return filePosition; } /** * Get the file location, or name. * * @return file location */ public String getLocation() { return location; } /** * Get the length of the file. The data in the buffer (which may not * have been written the disk yet) is taken into account. * * @return the length of the file in bytes. * @throws IOException if an I/O error occurrs. */ public long length() throws IOException { long fileLength = (file == null) ? -1L : file.length(); // GRIB has closed the data raf if (fileLength < dataEnd) { return dataEnd; } else { return fileLength; } } /** * Change the current endian mode. Subsequent reads of short, int, float, double, long, char will * use this. Does not currently affect writes. * Default values is BIG_ENDIAN. * * @param endian RandomAccessFile.BIG_ENDIAN or RandomAccessFile.LITTLE_ENDIAN */ public void order(int endian) { if (endian < 0) return; this.bigEndian = (endian == BIG_ENDIAN); } public void order(ByteOrder bo) { if (bo == null) return; this.bigEndian = bo.equals(ByteOrder.BIG_ENDIAN); } /** * Copy the contents of the buffer to the disk. * * @throws IOException if an I/O error occurs. */ public void flush() throws IOException { if (bufferModified) { file.seek(bufferStart); file.write(buffer, 0, dataSize); //System.out.println("--flush at "+bufferStart+" dataSize= "+dataSize+ " filePosition= "+filePosition); bufferModified = false; } /* check min length if (!readonly && (minLength != 0) && (minLength != file.length())) { file.setLength(minLength); } */ } /** * Make sure file is at least this long when its closed. * needed when not using fill mode, and not all data is written. * * @param minLength minimum length of the file. */ public synchronized void setMinLength(long minLength) { this.minLength = minLength; } /** * Set extendMode for truncated, yet valid files - old NetCDF code allowed this * when NOFILL on, and user doesnt write all variables. */ public void setExtendMode() { this.extendMode = true; } ////////////////////////////////////////////////////////////////////////////////////////////// // Read primitives. // /** * Read a byte of data from the file, blocking until data is * available. * * @return the next byte of data, or -1 if the end of the file is * reached. * @throws IOException if an I/O error occurrs. */ public int read() throws IOException { // If the file position is within the data, return the byte... if (filePosition < dataEnd) { int pos = (int) (filePosition - bufferStart); filePosition++; return (buffer[pos] & 0xff); // ...or should we indicate EOF... } else if (endOfFile) { return -1; // ...or seek to fill the buffer, and try again. } else { seek(filePosition); return read(); } } /** * Read up to len bytes into an array, at a specified * offset. This will block until at least one byte has been read. * * @param b the byte array to receive the bytes. * @param off the offset in the array where copying will start. * @param len the number of bytes to copy. * @return the actual number of bytes read, or -1 if there is not * more data due to the end of the file being reached. * @throws IOException if an I/O error occurrs. */ public int readBytes(byte b[], int off, int len) throws IOException { // Check for end of file. if (endOfFile) { return -1; } // See how many bytes are available in the buffer - if none, // seek to the file position to update the buffer and try again. int bytesAvailable = (int) (dataEnd - filePosition); if (bytesAvailable < 1) { seek(filePosition); return readBytes(b, off, len); } // Copy as much as we can. int copyLength = (bytesAvailable >= len) ? len : bytesAvailable; System.arraycopy(buffer, (int) (filePosition - bufferStart), b, off, copyLength); filePosition += copyLength; // If there is more to copy... if (copyLength < len) { int extraCopy = len - copyLength; // If the amount remaining is more than a buffer's length, read it // directly from the file. if (extraCopy > buffer.length) { extraCopy = read_(filePosition, b, off + copyLength, len - copyLength); // ...or read a new buffer full, and copy as much as possible... } else { seek(filePosition); if (!endOfFile) { extraCopy = (extraCopy > dataSize) ? dataSize : extraCopy; System.arraycopy(buffer, 0, b, off + copyLength, extraCopy); } else { extraCopy = -1; } } // If we did manage to copy any more, update the file position and // return the amount copied. if (extraCopy > 0) { filePosition += extraCopy; return copyLength + extraCopy; } } // Return the amount copied. return copyLength; } /** * Read nbytes bytes, at the specified file offset, send to a WritableByteChannel. * This will block until all bytes are read. * This uses the underlying file channel directly, bypassing all user buffers. * * @param dest write to this WritableByteChannel. * @param offset the offset in the file where copying will start. * @param nbytes the number of bytes to read. * @return the actual number of bytes read and transfered * @throws IOException if an I/O error occurs. */ public long readToByteChannel(WritableByteChannel dest, long offset, long nbytes) throws IOException { if (fileChannel == null) fileChannel = file.getChannel(); long need = nbytes; while (need > 0) { long count = fileChannel.transferTo(offset, need, dest); //if (count == 0) break; // LOOK not sure what the EOF condition is need -= count; offset += count; } return nbytes - need; } /** * Read directly from file, without going through the buffer. * All reading goes through here or readToByteChannel; * * @param pos start here in the file * @param b put data into this buffer * @param offset buffer offset * @param len this number of bytes * @return actual number of bytes read * @throws IOException on io error */ protected int read_(long pos, byte[] b, int offset, int len) throws IOException { file.seek(pos); int n = file.read(b, offset, len); if (debugAccess) { if (showRead) System.out.println(" **read_ " + location + " = " + len + " bytes at " + pos + "; block = " + (pos / buffer.length)); debug_nseeks.incrementAndGet(); debug_nbytes.addAndGet(len); } if (extendMode && (n < len)) { //System.out.println(" read_ = "+len+" at "+pos+"; got = "+n); n = len; } return n; } /** * Read up to len bytes into an array, at a specified * offset. This will block until at least one byte has been read. * * @param b the byte array to receive the bytes. * @param off the offset in the array where copying will start. * @param len the number of bytes to copy. * @return the actual number of bytes read, or -1 if there is not * more data due to the end of the file being reached. * @throws IOException if an I/O error occurrs. */ public int read(byte b[], int off, int len) throws IOException { return readBytes(b, off, len); } /** * Read up to b.length( ) bytes into an array. This * will block until at least one byte has been read. * * @param b the byte array to receive the bytes. * @return the actual number of bytes read, or -1 if there is not * more data due to the end of the file being reached. * @throws IOException if an I/O error occurrs. */ public int read(byte b[]) throws IOException { return readBytes(b, 0, b.length); } /** * Read fully count number of bytes * * @param count how many bytes tp read * @return a byte array of length count, fully read in * @throws IOException if an I/O error occurrs. */ public byte[] readBytes(int count) throws IOException { byte[] b = new byte[count]; readFully(b); return b; } /** * Reads b.length bytes from this file into the byte * array. This method reads repeatedly from the file until all the * bytes are read. This method blocks until all the bytes are read, * the end of the stream is detected, or an exception is thrown. * * @param b the buffer into which the data is read. * @throws EOFException if this file reaches the end before reading * all the bytes. * @throws IOException if an I/O error occurs. */ public final void readFully(byte b[]) throws IOException { readFully(b, 0, b.length); } /** * Reads exactly len bytes from this file into the byte * array. This method reads repeatedly from the file until all the * bytes are read. This method blocks until all the bytes are read, * the end of the stream is detected, or an exception is thrown. * * @param b the buffer into which the data is read. * @param off the start offset of the data. * @param len the number of bytes to read. * @throws EOFException if this file reaches the end before reading * all the bytes. * @throws IOException if an I/O error occurs. */ public final void readFully(byte b[], int off, int len) throws IOException { int n = 0; while (n < len) { int count = this.read(b, off + n, len - n); if (count < 0) { throw new EOFException("Reading "+location+" at "+filePosition+" file length = "+length()); } n += count; } } /** * Skips exactly n bytes of input. * This method blocks until all the bytes are skipped, the end of * the stream is detected, or an exception is thrown. * * @param n the number of bytes to be skipped. * @return the number of bytes skipped, which is always n. * @throws EOFException if this file reaches the end before skipping * all the bytes. * @throws IOException if an I/O error occurs. */ public int skipBytes(int n) throws IOException { seek(getFilePointer() + n); return n; } public long skipBytes(long n) throws IOException { seek(getFilePointer() + n); return n; } /* public void skipToMultiple( int multipleOfBytes) throws IOException { long pos = getFilePointer(); int pad = (int) (pos % multipleOfBytes); if (pad != 0) pad = multipleOfBytes - pad; if (pad > 0) skipBytes(pad); } */ /** * Unread the last byte read. * This method should not be used more than once * between reading operations, or strange things might happen. */ public void unread() { filePosition--; } // // Write primitives. // /** * Write a byte to the file. If the file has not been opened for * writing, an IOException will be raised only when an attempt is * made to write the buffer to the file. *

* Caveat: the effects of seek( )ing beyond the end of the file are * undefined. * * @param b write this byte * @throws IOException if an I/O error occurrs. */ public void write(int b) throws IOException { // If the file position is within the block of data... if (filePosition < dataEnd) { int pos = (int) (filePosition - bufferStart); buffer[pos] = (byte) b; bufferModified = true; filePosition++; // ...or (assuming that seek will not allow the file pointer // to move beyond the end of the file) get the correct block of // data... } else { // If there is room in the buffer, expand it... if (dataSize != buffer.length) { int pos = (int) (filePosition - bufferStart); buffer[pos] = (byte) b; bufferModified = true; filePosition++; dataSize++; dataEnd++; // ...or do another seek to get a new buffer, and start again... } else { seek(filePosition); write(b); } } } /** * Write len bytes from an array to the file. * * @param b the array containing the data. * @param off the offset in the array to the data. * @param len the length of the data. * @throws IOException if an I/O error occurrs. */ public void writeBytes(byte b[], int off, int len) throws IOException { // If the amount of data is small (less than a full buffer)... if (len < buffer.length) { // If any of the data fits within the buffer... int spaceInBuffer = 0; int copyLength = 0; if (filePosition >= bufferStart) { spaceInBuffer = (int) ((bufferStart + buffer.length) - filePosition); } if (spaceInBuffer > 0) { // Copy as much as possible to the buffer. copyLength = (spaceInBuffer > len) ? len : spaceInBuffer; System.arraycopy(b, off, buffer, (int) (filePosition - bufferStart), copyLength); bufferModified = true; long myDataEnd = filePosition + copyLength; dataEnd = (myDataEnd > dataEnd) ? myDataEnd : dataEnd; dataSize = (int) (dataEnd - bufferStart); filePosition += copyLength; ///System.out.println("--copy to buffer "+copyLength+" "+len); } // If there is any data remaining, move to the new position and copy to // the new buffer. if (copyLength < len) { //System.out.println("--need more "+copyLength+" "+len+" space= "+spaceInBuffer); seek(filePosition); // triggers a flush System.arraycopy(b, off + copyLength, buffer, (int) (filePosition - bufferStart), len - copyLength); bufferModified = true; long myDataEnd = filePosition + (len - copyLength); dataEnd = (myDataEnd > dataEnd) ? myDataEnd : dataEnd; dataSize = (int) (dataEnd - bufferStart); filePosition += (len - copyLength); } // ...or write a lot of data... } else { // Flush the current buffer, and write this data to the file. if (bufferModified) { flush(); } file.seek(filePosition); // moved per Steve Cerruti; Jan 14, 2005 file.write(b, off, len); //System.out.println("--write at "+filePosition+" "+len); filePosition += len; bufferStart = filePosition; // an empty buffer dataSize = 0; dataEnd = bufferStart + dataSize; } } /** * Writes b.length bytes from the specified byte array * starting at offset off to this file. * * @param b the data. * @throws IOException if an I/O error occurs. */ public void write(byte b[]) throws IOException { writeBytes(b, 0, b.length); } /** * Writes len bytes from the specified byte array * starting at offset off to this file. * * @param b the data. * @param off the start offset in the data. * @param len the number of bytes to write. * @throws IOException if an I/O error occurs. */ public void write(byte b[], int off, int len) throws IOException { writeBytes(b, off, len); } // // DataInput methods. // /** * Reads a boolean from this file. This method reads a * single byte from the file. A value of 0 represents * false. Any other value represents true. * This method blocks until the byte is read, the end of the stream * is detected, or an exception is thrown. * * @return the boolean value read. * @throws EOFException if this file has reached the end. * @throws IOException if an I/O error occurs. */ public final boolean readBoolean() throws IOException { int ch = this.read(); if (ch < 0) { throw new EOFException(); } return (ch != 0); } /** * Reads a signed 8-bit value from this file. This method reads a * byte from the file. If the byte read is b, where * 0 <= b <= 255, * then the result is: *

    * (byte)(b) *
*

* This method blocks until the byte is read, the end of the stream * is detected, or an exception is thrown. * * @return the next byte of this file as a signed 8-bit * byte. * @throws EOFException if this file has reached the end. * @throws IOException if an I/O error occurs. */ public final byte readByte() throws IOException { int ch = this.read(); if (ch < 0) { throw new EOFException(); } return (byte) (ch); } /** * Reads an unsigned 8-bit number from this file. This method reads * a byte from this file and returns that byte. *

* This method blocks until the byte is read, the end of the stream * is detected, or an exception is thrown. * * @return the next byte of this file, interpreted as an unsigned * 8-bit number. * @throws EOFException if this file has reached the end. * @throws IOException if an I/O error occurs. */ public final int readUnsignedByte() throws IOException { int ch = this.read(); if (ch < 0) { throw new EOFException(); } return ch; } /** * Reads a signed 16-bit number from this file. The method reads 2 * bytes from this file. If the two bytes read, in order, are * b1 and b2, where each of the two values is * between 0 and 255, inclusive, then the * result is equal to: *

    * (short)((b1 << 8) | b2) *
*

* This method blocks until the two bytes are read, the end of the * stream is detected, or an exception is thrown. * * @return the next two bytes of this file, interpreted as a signed * 16-bit number. * @throws EOFException if this file reaches the end before reading * two bytes. * @throws IOException if an I/O error occurs. */ public final short readShort() throws IOException { int ch1 = this.read(); int ch2 = this.read(); if ((ch1 | ch2) < 0) { throw new EOFException(); } if (bigEndian) { return (short) ((ch1 << 8) + (ch2)); } else { return (short) ((ch2 << 8) + (ch1)); } } /** * Read an array of shorts * * @param pa read into this array * @param start starting at pa[start] * @param n read this many elements * @throws IOException on read error */ public final void readShort(short[] pa, int start, int n) throws IOException { for (int i = 0; i < n; i++) { pa[start + i] = readShort(); } } /** * Reads an unsigned 16-bit number from this file. This method reads * two bytes from the file. If the bytes read, in order, are * b1 and b2, where * 0 <= b1, b2 <= 255, * then the result is equal to: *

    * (b1 << 8) | b2 *
*

* This method blocks until the two bytes are read, the end of the * stream is detected, or an exception is thrown. * * @return the next two bytes of this file, interpreted as an unsigned * 16-bit integer. * @throws EOFException if this file reaches the end before reading * two bytes. * @throws IOException if an I/O error occurs. */ public final int readUnsignedShort() throws IOException { int ch1 = this.read(); int ch2 = this.read(); if ((ch1 | ch2) < 0) { throw new EOFException(); } if (bigEndian) { return ((ch1 << 8) + (ch2)); } else { return ((ch2 << 8) + (ch1)); } } /* * Reads a signed 24-bit integer from this file. This method reads 3 * bytes from the file. If the bytes read, in order, are b1, * b2, and b3, where * 0 <= b1, b2, b3 <= 255, * then the result is equal to: *

    * (b1 << 16) | (b2 << 8) + (b3 << 0) *
*

* This method blocks until the three bytes are read, the end of the * stream is detected, or an exception is thrown. */ /** * Reads a Unicode character from this file. This method reads two * bytes from the file. If the bytes read, in order, are * b1 and b2, where * 0 <= b1, b2 <= 255, * then the result is equal to: *

    * (char)((b1 << 8) | b2) *
*

* This method blocks until the two bytes are read, the end of the * stream is detected, or an exception is thrown. * * @return the next two bytes of this file as a Unicode character. * @throws EOFException if this file reaches the end before reading * two bytes. * @throws IOException if an I/O error occurs. */ public final char readChar() throws IOException { int ch1 = this.read(); int ch2 = this.read(); if ((ch1 | ch2) < 0) { throw new EOFException(); } if (bigEndian) { return (char) ((ch1 << 8) + (ch2)); } else { return (char) ((ch2 << 8) + (ch1)); } } /** * Reads a signed 32-bit integer from this file. This method reads 4 * bytes from the file. If the bytes read, in order, are b1, * b2, b3, and b4, where * 0 <= b1, b2, b3, b4 <= 255, * then the result is equal to: *

    * (b1 << 24) | (b2 << 16) + (b3 << 8) + b4 *
*

* This method blocks until the four bytes are read, the end of the * stream is detected, or an exception is thrown. * * @return the next four bytes of this file, interpreted as an * int. * @throws EOFException if this file reaches the end before reading * four bytes. * @throws IOException if an I/O error occurs. */ public final int readInt() throws IOException { int ch1 = this.read(); int ch2 = this.read(); int ch3 = this.read(); int ch4 = this.read(); if ((ch1 | ch2 | ch3 | ch4) < 0) { throw new EOFException(); } if (bigEndian) { return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4)); } else { return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1)); } } /** * Read an integer at the given position, bypassing all buffering. * * @param pos read a byte at this position * @return The int that was read * @throws IOException if an I/O error occurs. */ public final int readIntUnbuffered(long pos) throws IOException { byte[] bb = new byte[4]; read_(pos, bb, 0, 4); int ch1 = bb[0] & 0xff; int ch2 = bb[1] & 0xff; int ch3 = bb[2] & 0xff; int ch4 = bb[3] & 0xff; if ((ch1 | ch2 | ch3 | ch4) < 0) { throw new EOFException(); } if (bigEndian) { return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4)); } else { return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1)); } } /** * Read an array of ints * * @param pa read into this array * @param start starting at pa[start] * @param n read this many elements * @throws IOException on read error */ public final void readInt(int[] pa, int start, int n) throws IOException { for (int i = 0; i < n; i++) { pa[start + i] = readInt(); } } /** * Reads a signed 64-bit integer from this file. This method reads eight * bytes from the file. If the bytes read, in order, are * b1, b2, b3, * b4, b5, b6, * b7, and b8, where: *

    * 0 <= b1, b2, b3, b4, b5, b6, b7, b8 <=255, *
*

* then the result is equal to: *

   *     ((long)b1 << 56) + ((long)b2 << 48)
   *     + ((long)b3 << 40) + ((long)b4 << 32)
   *     + ((long)b5 << 24) + ((long)b6 << 16)
   *     + ((long)b7 << 8) + b8
   * 
*

* This method blocks until the eight bytes are read, the end of the * stream is detected, or an exception is thrown. * * @return the next eight bytes of this file, interpreted as a * long. * @throws EOFException if this file reaches the end before reading * eight bytes. * @throws IOException if an I/O error occurs. */ public final long readLong() throws IOException { if (bigEndian) { return ((long) (readInt()) << 32) + (readInt() & 0xFFFFFFFFL); // tested ok } else { return ((readInt() & 0xFFFFFFFFL) + ((long) readInt() << 32)); // not tested yet ?? } /* int ch1 = this.read(); int ch2 = this.read(); int ch3 = this.read(); int ch4 = this.read(); int ch5 = this.read(); int ch6 = this.read(); int ch7 = this.read(); int ch8 = this.read(); if ((ch1 | ch2 | ch3 | ch4 | ch5 | ch6 | ch7 | ch8) < 0) throw new EOFException(); if (bigEndian) return ((long)(ch1 << 56)) + (ch2 << 48) + (ch3 << 40) + (ch4 << 32) + (ch5 << 24) + (ch6 << 16) + (ch7 << 8) + (ch8 << 0)); else return ((long)(ch8 << 56) + (ch7 << 48) + (ch6 << 40) + (ch5 << 32) + (ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0)); */ } /** * Read an array of longs * * @param pa read into this array * @param start starting at pa[start] * @param n read this many elements * @throws IOException on read error */ public final void readLong(long[] pa, int start, int n) throws IOException { for (int i = 0; i < n; i++) { pa[start + i] = readLong(); } } /** * Reads a float from this file. This method reads an * int value as if by the readInt method * and then converts that int to a float * using the intBitsToFloat method in class * Float. *

* This method blocks until the four bytes are read, the end of the * stream is detected, or an exception is thrown. * * @return the next four bytes of this file, interpreted as a * float. * @throws EOFException if this file reaches the end before reading * four bytes. * @throws IOException if an I/O error occurs. * @see java.io.RandomAccessFile#readInt() * @see java.lang.Float#intBitsToFloat(int) */ public final float readFloat() throws IOException { return Float.intBitsToFloat(readInt()); } /** * Read an array of floats * * @param pa read into this array * @param start starting at pa[start] * @param n read this many elements * @throws IOException on read error */ public final void readFloat(float[] pa, int start, int n) throws IOException { for (int i = 0; i < n; i++) { pa[start + i] = Float.intBitsToFloat(readInt()); } } /** * Reads a double from this file. This method reads a * long value as if by the readLong method * and then converts that long to a double * using the longBitsToDouble method in * class Double. *

* This method blocks until the eight bytes are read, the end of the * stream is detected, or an exception is thrown. * * @return the next eight bytes of this file, interpreted as a * double. * @throws EOFException if this file reaches the end before reading * eight bytes. * @throws IOException if an I/O error occurs. * @see java.io.RandomAccessFile#readLong() * @see java.lang.Double#longBitsToDouble(long) */ public final double readDouble() throws IOException { return Double.longBitsToDouble(readLong()); } /** * Read an array of doubles * * @param pa read into this array * @param start starting at pa[start] * @param n read this many elements * @throws IOException on read error */ public final void readDouble(double[] pa, int start, int n) throws IOException { for (int i = 0; i < n; i++) { pa[start + i] = Double.longBitsToDouble(readLong()); } } /** * Reads the next line of text from this file. This method successively * reads bytes from the file, starting at the current file pointer, * until it reaches a line terminator or the end * of the file. Each byte is converted into a character by taking the * byte's value for the lower eight bits of the character and setting the * high eight bits of the character to zero. This method does not, * therefore, support the full Unicode character set. * *

A line of text is terminated by a carriage-return character * ('\r'), a newline character ('\n'), a * carriage-return character immediately followed by a newline character, * or the end of the file. Line-terminating characters are discarded and * are not included as part of the string returned. * *

This method blocks until a newline character is read, a carriage * return and the byte following it are read (to see if it is a newline), * the end of the file is reached, or an exception is thrown. * * @return the next line of text from this file, or null if end * of file is encountered before even one byte is read. * @exception IOException if an I/O error occurs. */ public final String readLine() throws IOException { StringBuilder input = new StringBuilder(); int c = -1; boolean eol = false; while (!eol) { switch (c = read()) { case -1: case '\n': eol = true; break; case '\r': eol = true; long cur = getFilePointer(); if ((read()) != '\n') { seek(cur); } break; default: input.append((char) c); break; } } if ((c == -1) && (input.length() == 0)) { return null; } return input.toString(); } /** * Reads in a string from this file. The string has been encoded * using a modified UTF-8 format. *

* The first two bytes are read as if by * readUnsignedShort. This value gives the number of * following bytes that are in the encoded string, not * the length of the resulting string. The following bytes are then * interpreted as bytes encoding characters in the UTF-8 format * and are converted into characters. *

* This method blocks until all the bytes are read, the end of the * stream is detected, or an exception is thrown. * * @return a Unicode string. * @throws EOFException if this file reaches the end before * reading all the bytes. * @throws IOException if an I/O error occurs. * @throws UTFDataFormatException if the bytes do not represent * valid UTF-8 encoding of a Unicode string. * @see java.io.RandomAccessFile#readUnsignedShort() */ public final String readUTF() throws IOException { return DataInputStream.readUTF(this); } /** * Read a String of known length. * * @param nbytes number of bytes to read * @return String wrapping the bytes. * @throws IOException if an I/O error occurs. */ public String readString(int nbytes) throws IOException { byte[] data = new byte[nbytes]; readFully(data); return new String(data, CDM.utf8Charset); } /** * Read a String of max length, zero terminate. * * @param nbytes number of bytes to read * @return String wrapping the bytes. * @throws IOException if an I/O error occurs. */ public String readStringMax(int nbytes) throws IOException { byte[] b = new byte[nbytes]; readFully(b); int count; for (count = 0; count < nbytes; count++) if (b[count] == 0) break; return new String(b, 0, count, CDM.utf8Charset); } // // DataOutput methods. // /** * Writes a boolean to the file as a 1-byte value. The * value true is written out as the value * (byte)1; the value false is written out * as the value (byte)0. * * @param v a boolean value to be written. * @throws IOException if an I/O error occurs. */ public final void writeBoolean(boolean v) throws IOException { write(v ? 1 : 0); } /** * Write an array of booleans * * @param pa write from this array * @param start starting with this element in the array * @param n write this number of elements * @throws IOException on read error */ public final void writeBoolean(boolean[] pa, int start, int n) throws IOException { for (int i = 0; i < n; i++) { writeBoolean(pa[start + i]); } } /** * Writes a byte to the file as a 1-byte value. * * @param v a byte value to be written. * @throws IOException if an I/O error occurs. */ public final void writeByte(int v) throws IOException { write(v); } /** * Writes a short to the file as two bytes, high byte first. * * @param v a short to be written. * @throws IOException if an I/O error occurs. */ public final void writeShort(int v) throws IOException { write((v >>> 8) & 0xFF); write((v) & 0xFF); } /** * Write an array of shorts * * @param pa write from this array * @param start starting with this element in the array * @param n this number of elements * @throws IOException on read error */ public final void writeShort(short[] pa, int start, int n) throws IOException { for (int i = 0; i < n; i++) { writeShort(pa[start + i]); } } /** * Writes a char to the file as a 2-byte value, high * byte first. * * @param v a char value to be written. * @throws IOException if an I/O error occurs. */ public final void writeChar(int v) throws IOException { write((v >>> 8) & 0xFF); write((v) & 0xFF); } /** * Write an array of chars * * @param pa write from this array * @param start starting with this element in the array * @param n this number of elements * @throws IOException on read error */ public final void writeChar(char[] pa, int start, int n) throws IOException { for (int i = 0; i < n; i++) { writeChar(pa[start + i]); } } /** * Writes an int to the file as four bytes, high byte first. * * @param v an int to be written. * @throws IOException if an I/O error occurs. */ public final void writeInt(int v) throws IOException { write((v >>> 24) & 0xFF); write((v >>> 16) & 0xFF); write((v >>> 8) & 0xFF); write((v) & 0xFF); } /** * Write an array of ints * * @param pa write from this array * @param start starting with this element in the array * @param n write this number of elements * @throws IOException on read error */ public final void writeInt(int[] pa, int start, int n) throws IOException { for (int i = 0; i < n; i++) { writeInt(pa[start + i]); } } /** * Writes a long to the file as eight bytes, high byte first. * * @param v a long to be written. * @throws IOException if an I/O error occurs. */ public final void writeLong(long v) throws IOException { write((int) (v >>> 56) & 0xFF); write((int) (v >>> 48) & 0xFF); write((int) (v >>> 40) & 0xFF); write((int) (v >>> 32) & 0xFF); write((int) (v >>> 24) & 0xFF); write((int) (v >>> 16) & 0xFF); write((int) (v >>> 8) & 0xFF); write((int) (v) & 0xFF); } /** * Write an array of longs * * @param pa write from this array * @param start starting with this element in the array * @param n write this number of elements * @throws IOException on read error */ public final void writeLong(long[] pa, int start, int n) throws IOException { for (int i = 0; i < n; i++) { writeLong(pa[start + i]); } } /** * Converts the float argument to an int using the * floatToIntBits method in class Float, * and then writes that int value to the file as a * 4-byte quantity, high byte first. * * @param v a float value to be written. * @throws IOException if an I/O error occurs. * @see java.lang.Float#floatToIntBits(float) */ public final void writeFloat(float v) throws IOException { writeInt(Float.floatToIntBits(v)); } /** * Write an array of floats * * @param pa write from this array * @param start starting with this element in the array * @param n write this number of elements * @throws IOException on read error */ public final void writeFloat(float[] pa, int start, int n) throws IOException { for (int i = 0; i < n; i++) { writeFloat(pa[start + i]); } } /** * Converts the double argument to a long using the * doubleToLongBits method in class Double, * and then writes that long value to the file as an * 8-byte quantity, high byte first. * * @param v a double value to be written. * @throws IOException if an I/O error occurs. * @see java.lang.Double#doubleToLongBits(double) */ public final void writeDouble(double v) throws IOException { writeLong(Double.doubleToLongBits(v)); } /** * Write an array of doubles * * @param pa write from this array * @param start starting with this element in the array * @param n write this number of elements * @throws IOException on read error */ public final void writeDouble(double[] pa, int start, int n) throws IOException { for (int i = 0; i < n; i++) { writeDouble(pa[start + i]); } } /** * Writes the string to the file as a sequence of bytes. Each * character in the string is written out, in sequence, by discarding * its high eight bits. * * @param s a string of bytes to be written. * @throws IOException if an I/O error occurs. */ public final void writeBytes(String s) throws IOException { int len = s.length(); for (int i = 0; i < len; i++) { write((byte) s.charAt(i)); } } /** * Writes the character array to the file as a sequence of bytes. Each * character in the string is written out, in sequence, by discarding * its high eight bits. * * @param b a character array of bytes to be written. * @param off the index of the first character to write. * @param len the number of characters to write. * @throws IOException if an I/O error occurs. */ public final void writeBytes(char b[], int off, int len) throws IOException { for (int i = off; i < len; i++) { write((byte) b[i]); } } /** * Writes a string to the file as a sequence of characters. Each * character is written to the data output stream as if by the * writeChar method. * * @param s a String value to be written. * @throws IOException if an I/O error occurs. * @see java.io.RandomAccessFile#writeChar(int) */ public final void writeChars(String s) throws IOException { int len = s.length(); for (int i = 0; i < len; i++) { int v = s.charAt(i); write((v >>> 8) & 0xFF); write((v) & 0xFF); } } /** * Writes a string to the file using UTF-8 encoding in a * machine-independent manner. *

* First, two bytes are written to the file as if by the * writeShort method giving the number of bytes to * follow. This value is the number of bytes actually written out, * not the length of the string. Following the length, each character * of the string is output, in sequence, using the UTF-8 encoding * for each character. * * @param str a string to be written. * @throws IOException if an I/O error occurs. */ public final void writeUTF(String str) throws IOException { int strlen = str.length(); int utflen = 0; for (int i = 0; i < strlen; i++) { int c = str.charAt(i); if ((c >= 0x0001) && (c <= 0x007F)) { utflen++; } else if (c > 0x07FF) { utflen += 3; } else { utflen += 2; } } if (utflen > 65535) { throw new UTFDataFormatException(); } write((utflen >>> 8) & 0xFF); write((utflen) & 0xFF); for (int i = 0; i < strlen; i++) { int c = str.charAt(i); if ((c >= 0x0001) && (c <= 0x007F)) { write(c); } else if (c > 0x07FF) { write(0xE0 | ((c >> 12) & 0x0F)); write(0x80 | ((c >> 6) & 0x3F)); write(0x80 | ((c) & 0x3F)); } else { write(0xC0 | ((c >> 6) & 0x1F)); write(0x80 | ((c) & 0x3F)); } } } /** * Create a string representation of this object. * * @return a string representation of the state of the object. */ public String toString() { return location; /* return "fp=" + filePosition + ", bs=" + bufferStart + ", de=" + dataEnd + ", ds=" + dataSize + ", bl=" + buffer.length + ", readonly=" + readonly + ", bm=" + bufferModified; */ } ///////////////////////////////////////////////// /** * Search forward from the current pos, looking for a match. * * @param match the match to look for. * @param maxBytes maximum number of bytes to search. use -1 for all * @return true if found, file position will be at the start of the match. * @throws IOException on read error */ public boolean searchForward(KMPMatch match, int maxBytes) throws IOException { long start = getFilePointer(); long last = (maxBytes < 0) ? length() : Math.min(length(), start + maxBytes); long needToScan = last - start; // check what ever is now in the buffer int bytesAvailable = (int) (dataEnd - filePosition); if (bytesAvailable < 1) { seek(filePosition); // read a new buffer bytesAvailable = (int) (dataEnd - filePosition); } int bufStart = (int) (filePosition - bufferStart); int scanBytes = (int) Math.min(bytesAvailable, needToScan); int pos = match.indexOf(buffer, bufStart, scanBytes); if (pos >= 0) { seek(bufferStart + pos); return true; } int matchLen = match.getMatchLength(); needToScan -= scanBytes - matchLen; while (needToScan > matchLen) { readBuffer(dataEnd - matchLen); // force new buffer scanBytes = (int) Math.min(buffer.length, needToScan); pos = match.indexOf(buffer, 0, scanBytes); if (pos > 0) { seek(bufferStart + pos); return true; } needToScan -= scanBytes - matchLen; } // failure seek(last); return false; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy