package com.peterhi.runtime;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.EOFException;
import java.io.IOException;
import java.io.UTFDataFormatException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.charset.Charset;
import com.peterhi.obsolete.RT;
/**
* {@link BufferHelper} is a wrapper class designed around
* {@link Buffer} objects to simplify the reading and writing
* of common data types. It internally keeps an instance of
* {@link Buffer} object and reads / writes data using this
* object. Please note that since {@link Buffer} object itself
* is not synchronized, and {@link BufferHelper} can accept
* an external {@link Buffer} object to be passed into this
* helper class, synchronization is never reinforced in this
* helper class. You need to sync them manually.
*
* @see Buffer
*
* @author hytparadisee
*
*/
public class BufferHelper implements DataInput, DataOutput {
public static final BigInteger UNSIGNED_LONG_MASK;
static {
UNSIGNED_LONG_MASK = new BigInteger("0xffffffffffffffff");
}
private final Buffer buffer;
/**
* Creates an instance of {@link BufferHelper} using an
* existing {@link Buffer} object. You can later use this
* {@link BufferHelper} object to write to or read from
* the underlying {@link Buffer}. You can get the reference
* to the underlying {@link Buffer} later at any time using
* {@link #getBuffer()} method.
*
* @param existingBuffer The existing {@link Buffer} to use as the
* underlying data object.
*
* @throws NullPointerException if {@code existingBuffer} is {@code null}.
*/
public BufferHelper(Buffer existingBuffer)
throws NullPointerException {
if (existingBuffer == null) {
throw new NullPointerException();
}
this.buffer = existingBuffer;
}
/**
* Creates an instance of {@link BufferHelper} with a freshly
* created underlying {@link Buffer} object. This is a shorthand method.
* The parameter {@code capacityInBytes} is used for the creation of
* the underlying {@link Buffer}. Please refer to {@link Buffer#Buffer(int)}
* for more information.
*
* @param capacityInBytes This parameter is passed in to
* {@link Buffer#Buffer(int)}.
*
* @throws IllegalArgumentException thrown when the creation of the underlying {@link Buffer}
* fails from the {@link Buffer#Buffer(int)} constructor.
*
* @see Buffer#Buffer(int)
*/
public BufferHelper(int capacityInBytes)
throws IllegalArgumentException {
this(new Buffer(capacityInBytes));
}
/**
* Creates an instance of {@link BufferHelper} with a freshly
* created underlying {@link Buffer} object using its default
* constructor. This is a shorthand method. Please refer to
* {@link Buffer#Buffer()} for more information.
*
* @see Buffer#Buffer()
*/
public BufferHelper() {
this(new Buffer());
}
/**
* Creates an instance of {@link BufferHelper} with a freshly
* created underlying {@link Buffer} object. This is a shorthand method.
* The parameters {@code existingData} is used for the creation of the
* underlying {@link Buffer}. Please refer to {@link Buffer#Buffer(byte[])}
* for more information.
*
* @param existingData This parameter is passed in to {@link Buffer#Buffer(byte[])}.
*
* @throws NullPointerException thrown when the creation of the underlying {@link Buffer}
* fails from the {@link Buffer#Buffer(byte[])} constructor.
* @throws IllegalArgumentException thrown when the creation of the underlying {@link Buffer}
* fails from the {@link Buffer#Buffer(byte[])} constructor.
*
* @see Buffer#Buffer(byte[])
* @see #BufferHelper(byte[], int, int)
*/
public BufferHelper(byte[] existingData)
throws NullPointerException,
IllegalArgumentException {
this(new Buffer(existingData));
}
/**
* Creates an instance of {@link BufferHelper} with a freshly
* created underlying {@link Buffer} object. This is a shorthand method.
* The parameters {@code existingData}, {@code byteOffset} and
* {@code numOfBytes} are used for the creation of the underlying
* {@link Buffer}. Please refer to {@link Buffer#Buffer(byte[], int, int)}
* for more information.
*
* @param existingData This parameter is passed in to {@link Buffer#Buffer(byte[], int, int)}.
* @param byteOffset This parameter is passed in to {@link Buffer#Buffer(byte[], int, int)}.
* @param numOfBytes This parameter is passed in to {@link Buffer#Buffer(byte[], int, int)}.
*
* @throws NullPointerException thrown when the creation of the underlying {@link Buffer}
* fails from the {@link Buffer#Buffer(byte[], int, int)} constructor.
* @throws IllegalArgumentException thrown when the creation of the underlying {@link Buffer}
* fails from the {@link Buffer#Buffer(byte[], int, int)} constructor.
*
* @see Buffer#Buffer(byte[], int, int)
*/
public BufferHelper(byte[] existingData, int byteOffset, int numOfBytes)
throws NullPointerException,
IllegalArgumentException {
this(new Buffer(existingData, byteOffset, numOfBytes));
}
/**
* Creates an instance of {@link BufferHelper} with a freshly
* created underlying {@link Buffer} object. This is a shorthand method.
* The parameters {@code existingData}, {@code bitOffset}, {@code numOfBits}
* and {@code alignBits} are used for the creation of the underlying
* {@link Buffer}. Please refer to {@link Buffer#Buffer(byte[], int, int)}
* for more information.
*
* @param existingData This parameter is passed in to {@link Buffer#Buffer(byte[], long, long, boolean)}.
* @param bitOffset This parameter is passed in to {@link Buffer#Buffer(byte[], long, long, boolean)}.
* @param numOfBits This parameter is passed in to {@link Buffer#Buffer(byte[], long, long, boolean)}.
* @param alignBits This parameter is passed in to {@link Buffer#Buffer(byte[], long, long, boolean)}.
*
* @throws NullPointerException thrown when the creation of the underlying {@link Buffer}
* fails from the {@link Buffer#Buffer(byte[], long, long, boolean)} constructor.
* @throws IllegalArgumentException thrown when the creation of the underlying {@link Buffer}
* fails from the {@link Buffer#Buffer(byte[], long, long, boolean)} constructor.
* @throws IOException thrown when the creation of the underlying {@link Buffer}
* fails from the {@link Buffer#Buffer(byte[], long, long, boolean)} constructor.
*
* @see Buffer#Buffer(byte[], long, long, boolean)
*/
public BufferHelper(byte[] existingData, long bitOffset, long numOfBits, boolean alignBits)
throws NullPointerException,
IllegalArgumentException,
IOException {
this(new Buffer(existingData, bitOffset, numOfBits, alignBits));
}
/**
* Gets the reference to the underlying {@link Buffer} object.
* The underlying {@link Buffer} may have been an existing object
* passed in via the constructor, or a newly created object at
* the constructor.
*
* @return The underlying {@link Buffer} instance.
*/
public Buffer getBuffer() {
return buffer;
}
/**
* Gets the data of the underlying {@link Buffer} as a byte array.
* The parameter {@code copyMethod} defines how data is to be
* copied. Refer to {@code Buffer.COPY_METHOD_*} in the class
* {@link Buffer} and the method {@link Buffer#toByteArray(int)}
* for more details.
*
* @param copyMethod This parameter is passed in to
* {@link Buffer#toByteArray(int)}.
*
* @return The {@code byte} array containing the data.
*
* @see Buffer#toByteArray(int)
* @see Buffer#COPY_METHOD_DEFAULT
* @see Buffer#COPY_METHOD_ALIGNED
* @see Buffer#COPY_METHOD_ALL_DATA
*/
public byte[] toByteArray(int copyMethod) {
return buffer.toByteArray(copyMethod);
}
/**
* Reads the next {@code byte} of data from the underlying
* {@link Buffer} object. Returning an {@code int} value
* which is actually an {@code unsigned byte} representation
* of the data (value range between {@code zero} and {@code 255}).
* This method will return {@link Buffer#EOF} if the
* underlying {@link Buffer} has insufficient data to return
* a {@code byte}. To be compliant, this method will not
* throw any exception regarding data availability. But if
* the underlying {@link Buffer} has an erroneous I/O operation
* that caused the throw, this method will relay exactly that
* exception.
*
* @return An {@code unsigned byte} representing data between
* {@code zero} and {@code 255}. Or {@link Buffer#EOF} if the
* underlying {@link Buffer} doesn't have enough data
* available for a {@code byte} to be read.
*
* @throws IOException thrown when the underlying {@link Buffer}
* encounters an error reading the required amount of data.
*
* @see #read(byte[], int, int)
*/
public int read() throws IOException {
int numOfBytesReadable = (int )(buffer.readable() / Byte.SIZE);
if (numOfBytesReadable > 0) {
BigInteger bitsValue = buffer.read(Byte.SIZE, false, Buffer.BIG_ENDIAN);
byte byteValue = bitsValue.byteValue();
int intValue = byteValue & 0xff;
return intValue;
}
return Buffer.EOF;
}
/**
* Attempts to read as many {@code bytes} as the {@code byte}
* array, {@code copyDataTo}, can hold. This operation may
* not always read the full {@code byte}s as requested, and
* the number of {@code byte}s actually read is indicated by
* the return value. If there's currently no (or insufficient)
* data available, the return value is {@link Buffer#EOF}. To be compliant,
* this method will not throw any exception regarding data availability.
* But if the underlying {@link Buffer} has an erroneous I/O operation
* that caused the throw, this method will relay exactly that exception.
*
* @param copyDataTo A {@code byte} array used to hold data.
*
* @return The number of {@code byte}s actually read after the method completes.
* Or {@link Buffer#EOF} if there is no (or insufficient) data available.
*
* @throws NullPointerException thrown if {@code copyDataTo} is {@code null}.
* @throws IllegalArgumentException thrown if {@code copyDataTo} is an empty
* ({@code zero}-length) {@code byte} array.
* @throws IOException thrown when the underlying {@link Buffer}
* encounters an error reading the required amount of data.
*
* @see {@link #read(byte[], int, int)}
*/
public int read(byte[] copyDataTo)
throws NullPointerException,
IOException {
if (copyDataTo == null) {
throw new NullPointerException();
}
return read(copyDataTo, 0, copyDataTo.length);
}
/**
* Reads the subsequent {@code n byte}s of data, specified by the
* parameter {@code numOfBytes}, into a {@code byte} array named
* as the parameter {@code copyDataTo}, starting at index known
* as the parameter {@code byteOffset}. This operation may not always read
* the full {@code byte}s as requested, and the number of
* {@code byte}s actually read is indicated by the return value.
* If there's currently no (or insufficient) data available, the
* return value is {@link Buffer#EOF}. To be compliant, this method will not
* throw any exception regarding data availability. But if
* the underlying {@link Buffer} has an erroneous I/O operation
* that caused the throw, this method will relay exactly that
* exception.
*
*
* @param copyDataTo A {@code byte} array used to hold data.
* @param byteOffset The starting index at which the {@code byte}s are copied to.
* @param numOfBytes The number of {@code byte}s to read, actual number may be smaller.
*
* @return The number of {@code byte}s actually read after the method completes. Or
* {@link Buffer#EOF} if there is no (or insufficient) data available.
*
* @throws NullPointerException if {@code copyDataTo} is {@code null}.
* @throws IllegalArgumentException thrown when one of the following occurs:
* <ul>
* <li>if {@code copyDataTo} is an empty ({@code zero}-length) {@code byte} array.</li>
* <li>if {@code byteOffset} is negative.</li>
* <li>if {@code numOfBytes} is negative.</li>
* <li>if {@code numOfBytes} is zero.</li>
* <li>if {@code byteOffset} and {@code numOfBytes} went out of bounds
* so that {@code byteOffset + numOfBytes} is greater than the
* number of {@code byte}s the array {@code copyDataTo} can hold.</li>
* </ul>
* @throws IOException thrown when the underlying {@link Buffer}
* encounters an error reading the required amount of data.
*
* @see {@link #read()}
*
*/
public int read(byte[] copyDataTo, int byteOffset, int numOfBytes)
throws NullPointerException,
IllegalArgumentException,
IOException {
if (copyDataTo == null) {
throw new NullPointerException();
}
if (copyDataTo.length == 0) {
throw new IllegalArgumentException();
}
if (byteOffset < 0) {
throw new IllegalArgumentException();
}
if (numOfBytes < 0) {
throw new IllegalArgumentException();
}
if (numOfBytes == 0) {
throw new IllegalArgumentException();
}
if (byteOffset + numOfBytes > copyDataTo.length) {
throw new IllegalArgumentException();
}
int numOfBytesReadable = (int )(buffer.readable() / Byte.SIZE);
int numOfBytesToRead = Math.min(numOfBytes, numOfBytesReadable);
int numOfBytesActuallyRead = 0;
for (int nthByteToRead = 0; nthByteToRead < numOfBytesToRead; nthByteToRead = nthByteToRead + 1) {
int byteIndex = byteOffset + nthByteToRead;
int dataValue = read();
if (dataValue == Buffer.EOF) {
break;
}
byte byteValue = (byte )dataValue;
copyDataTo[byteIndex] = byteValue;
numOfBytesActuallyRead = numOfBytesActuallyRead + 1;
}
return numOfBytesActuallyRead;
}
/**
* Skips a number of bytes, specified by the parameter {@code numOfBytes}
* to fast-forward to the desired position. It is possible that the actual
* number of {@code byte}s skipped may be smaller than the designated
* {@code numOfBytes} parameter. Therefore, the actual number of {@code byte}s
* skipped is shown in the return value.
*
* @param numOfBytes The number of {@code byte}s planned to skip. If
* {@code numOfBytes} is {@code zero}. This method does nothing and
* returns {@code zero}.
*
* @return The actual number of bytes skipped. The position of the
* {@link Buffer} is modified as a result of the skip operation,
* if the return value is {@code zero}, the backing {@link Buffer}
* is not modified.
*
* @throws IllegalArgumentException thrown when one of the following occurs:
* <ul>
* <li>{@code numOfBytes} is negative.</li>
* <li>{@code numOfBytes} is zero.</li>
* </ul>
* @throws IOException if the underlying {@link Buffer} operation throws
* an error.
*/
public long skip(long numOfBytes)
throws IllegalArgumentException,
IOException {
if (numOfBytes < 0) {
throw new IllegalArgumentException();
}
if (numOfBytes == 0) {
throw new IllegalArgumentException();
}
long numOfBytesReadable = buffer.readable() / (long )Byte.SIZE;
long numOfBytesToSkip = Math.min(numOfBytes, numOfBytesReadable);
long numOfBitsToSkip = Byte.SIZE * numOfBytesToSkip;
long numOfBitsActuallySkipped = buffer.skip(numOfBitsToSkip);
long numOfBytesActuallySkipped = numOfBitsActuallySkipped / Byte.SIZE;
return numOfBytesActuallySkipped;
}
/**
* Returns the number of {@code byte}s that the underlying {@link Buffer}
* can be read without blocking or waiting. Due to the fact that a
* {@link Buffer} may have data that is a fraction of a {@code byte},
* this method doesn't mean that the underlying {@link Buffer} will have
* no data if this method returns {@code zero}. Say if the underlying
* {@link Buffer} has 11 {@code bit}s. The available {@code bit}s will
* be 11, but the available {@code byte}s will only be 1, because the
* remaining 3 {@code bit}s are not considered a full {@code byte}.
*
* @return The number of {@code byte}s that can be read without blocking
* or waiting.
*
* @throws IOException The underlying {@link Buffer} operation fails
* with an error.
*/
public int available()
throws IOException {
long numOfBitsReadable = buffer.readable();
int numOfBytesReadable = (int )(numOfBitsReadable / Byte.SIZE);
return numOfBytesReadable;
}
/**
* Closes the I/O stream. This method is merely a stub and does not
* do anything.
*
* @throws IOException the underlying stream I/O error occurs.
*/
public void close()
throws IOException {
}
/**
* Marks the current read position of the underlying
* {@link Buffer} so that it can be reverted later
* after a corresponding call to {@link #reset()}.
* The parameter {@code readLimit} is ignored because
* the underlying {@link Buffer} no longer needs this
* limit, but in order to keep the methods compliant,
* this parameter is kept. This method will simply call
* {@link Buffer#mark()}.
*
* @param readLimit The maximum number of bytes that
* can be read without the mark becoming valid.
* This parameter is never used so it is ignored.
* This parameter is kept for compliance reasons.
*
* @throws IOException the underlying {@link Buffer}
* fails with an error.
*
* @see Buffer#mark()
*/
public synchronized void mark(int readLimit)
throws IOException {
buffer.mark();
}
/**
* Resets from the previously marked position so that
* the {@link Buffer} reverts to a previous location.
*
* @throws IOException the underlying {@link Buffer}
* fails with an error.
*
* @see Buffer#reset();
*/
public synchronized void reset()
throws IOException {
buffer.reset();
}
/**
* Returns whether the mark / reset operation is supported
* by the underlying stream. This method is a stub because
* mark / reset operation is always supported by the underlying
* {@link Buffer} object.
*
* @return This method always returns {@code true} as the
* underlying {@link Buffer} always supports marking /
* resetting.
*
* @throws IOException the underlying {@link Buffer} fails
* with an error.
*
* @see Buffer#mark()
* @see Buffer#reset()
*/
public boolean markSupported()
throws IOException {
return true;
}
/**
* Reads the next {@code byte} of data to the underlying
* {@link Buffer} object. The parameter {@code dataValue}
* is an {@code unsigned byte} representation of the data
* (value range between {@code zero} and {@code 255}).
* The underlying {@link Buffer} will append the data
* and forward the write cursor. Please note that
* re-alloc-expansion may occur if the current buffer
* is not big enough.
*
* @param dataVaue The value of the data between
* {@code zero} and {@code 255}.
*
* @throws IllegalArgumentExcecption the value of he data
* is either negative or greater than {@code 255}.
* @throws IOException thrown when the underlying
* {@link Buffer} encounters an error writing
* the required amount of data.
*/
public void write(int dataValue)
throws IllegalArgumentException,
IOException {
if (dataValue < 0) {
throw new IllegalArgumentException();
}
if (dataValue > 0xff) {
throw new IllegalArgumentException();
}
BigInteger bitsValue = BigInteger.valueOf(dataValue);
buffer.write(Byte.SIZE, bitsValue, Buffer.BIG_ENDIAN);
}
/**
* Writes all the data from {@code byteArray} into the underlying
* {@link Buffer}.
*
* @param byteArray A {@code byte} array that contains the data
* to be written into the {@link Buffer}.
*
* @throws NullPointerException thrown of {@code byteArray} is
* {@code null}.
* @throws IOException thrown when the underlying {@link Buffer}
* encounters an error while writing the dat.
*
* @see {@link #write(int)}
*/
public void write(byte[] byteArray)
throws IOException {
if (byteArray == null) {
throw new NullPointerException();
}
write(byteArray, 0, byteArray.length);
}
/**
* Writes the subsequent {@code n byte}s of data of {@code byteArray},
* specified by the parameter {@code numOfBytes}, into the underlying
* {@link Buffer}, starting at index known as the parameter
* {@code byteOffset}.
*
* @param byteArray The source {@code byte} array containing the data
* to write.
* @param byteOffset The starting index at which the {@code byte}s are copied from.
* @param numOfBytes The number of {@code byte}s to read, re-alloc-expansion may
* occur if the underlying {@link Buffer} is not big enough.
*
* @throws NullPointerException if {@code byteArray} is {@code null}.
* @throws IllegalArgumentException thrown when one of the following occurs:
* <ul>
* <li>{@code byteArray} is an empty ({@code zero}-length) array.</li>
* <li>{@code byteOffset} is negative.</li>
* <li>{@code numOfBytes} is negative.</li>
* <li>{@code numofBytes} is zero.</li>
* <li>{#code byteOffset} and {@code numOfBytes} went out of bounds
* so that {@code byteOffset + numOfBytes} is greater than the
* number of {@code byte}s the array {@code copyDataTo} can hold.</li>
* </ul>
* @throws IOException thrown when the underlying {@link Buffer}
* encounters an error writing the required amount of data.
*
* @see #write(int)
*/
public void write(byte[] byteArray, int byteOffset, int numOfBytes)
throws IOException {
if (byteArray == null) {
throw new NullPointerException();
}
if (byteArray.length == 0) {
throw new IllegalArgumentException();
}
if (byteOffset < 0) {
throw new IllegalArgumentException();
}
if (numOfBytes < 0) {
throw new IllegalArgumentException();
}
if (numOfBytes == 0) {
throw new IllegalArgumentException();
}
if (byteOffset + numOfBytes > byteArray.length) {
throw new IllegalArgumentException();
}
for (int nthByteToWrite = byteOffset;
nthByteToWrite < byteArray.length;
nthByteToWrite = nthByteToWrite + 1) {
write(byteArray[nthByteToWrite] & 0xff);
}
}
/**
* Flushes the underlying {@link Buffer} so that all
* pending data updates are executed before this method returns.
* To be compliant, this method exists, but it doesn't actually
* do anything.
*
* @throws IOException The underlying {@link Buffer} object
* generates an error while flushing.
*/
public void flush()
throws IOException {
}
/**
* Read from the underlying {@link Buffer} and copy the data
* to the {@code byte} array provided as the parameter
* {@code copyDataTo}. This method forces that the underlying
* {@link Buffer} must have enough amount of data to be read
* to completely fill the {@code copyDataTo}. Please note that
* the behavior of {@link #readFully(byte[])} is slightly different
* from that defined in {@link DataInput#readFully(byte[])}.
* The difference is that in {@link DataInput#readFully(byte[])},
* if the underlying data source doesn't have the required amount
* of data to read, it will block until enough data has arrived.
* In {@link #readFully(byte[])}, however, it will not block
* if the underlying {@link Buffer} has insufficient data. Instead,
* it will throw an {@link InsufficientBufferException} or an
* {@link EOFException} to reflect this situation. The caller
* should always catch this exception in the event that there
* is insufficient data to be read completely. The implementation,
* though, guarantees that if an {@link InsufficientBufferException}
* or an {@link EOFException} is thrown, data won't be corrupted,
* so that the state of the underlying {@link Buffer} remains intact.
*
* @param copyDataTo A {@code byte} array that will hold the data
* read from the underlying {@link Buffer}. This method
* guarantees that {@code copyDataTo} will be completely
* filled with the data. If not, an
* {@link InsufficientBufferException} or an
* {@link EOFException} will be thrown.
*
* @throws NullPointerException if {@code copyDataTo} is {@code null}.
* @throws IllegalArgumentException thrown when one of the following occurs:
* <ul>
* <li>{@code copyDataTo} is an empty ({@code zero}-length) {@code byte} array.</li>
* <li>{@code byteOffset} is negative.</li>
* <li>{@code numOfBytes} is negative.</li>
* <li>{@code numOfBytes} is zero.</li>
* <li>{@code byteOffset} and {@code numOfBytes} went out of bounds
* so that {@code byteOffset + numOfBytes} is greater than the
* number of {@code byte}s the array {@code copyDataTo} can hold.</li>
* </ul>
* @throws InsufficientBufferException the underlying {@link Buffer}
* does not have enough amount of data to be read.
* @throws EOFException the underlying {@link Buffer}
* has no data left to read.
* @throws IOException the underlying {@link Buffer} throws an
* error during the I/O operation.
*
* @see DataInput#readFully(byte[])
*
*/
public void readFully(byte[] copyDataTo)
throws IOException {
if (copyDataTo == null) {
throw new NullPointerException();
}
readFully(copyDataTo, 0, copyDataTo.length);
}
/**
* Read from the underlying {@link Buffer} and copy the
* data to the {@code byte} array provided as the parameter
* {@code copyDataTo}, starting from the offset parameter
* {@code byteOffset}. The number of {@code byte}s to read
* is defined via the parameter {@code numOfBytes}. This
* method forces that the underlying {@link Buffer} must
* have the amount of data no less than the amount defined
* by the parameter {@code numOfBytes}. Please note that
* the behavior of {@link #readFully(byte[], int, int)} is
* slightly different from that defined in
* {@link DataInput#readFully(byte[], int, int)}. The
* difference is that in
* {@link DataInput#readFully(byte[], int, int)}, if the
* underlying data source doesn't have the required amount
* of data to read, it will block until enough data has
* arrived. In {@link #readFully(byte[], int, int)}, however,
* it will not block if the underlying {@link Buffer} has
* insufficient data. Instead, it will throw an
* {@link InsufficientBufferException} or an {@link EOFException}
* to reflect this situation. The caller should always catch
* this exception in the event that there is insufficient data
* to be read completely. The implementation, though,
* guarantees that if an {@link InsufficientBufferException}
* or an {@link EOFException} is thrown, data won't be corrupted, so
* that the state of the underlying {@link Buffer} remains intact.
*
* @param copyDataTo A {@code byte} array that will hold the data read
* from the underlying {@link Buffer}. This method guarantees
* that {@code copyDataTo} will be filled completely with data
* starting at the index specified by {@code byteOffset},
* spanning a number of {@code bytes} specified by
* {@code numOfBytes}. If not, an
* {@link InsufficientBufferException} or an
* {@link EOFException} will be thrown.
* @param byteOffset The starting index at which to read the data.
* @param numOfBytes the number of {@code byte}s to be read.
*
* @throws NullPointerException if {@code copyDataTo} is {@code null}.
* @throws IllegalArgumentException thrown when one of the following occurs:
* <ul>
* <li>{@code copyDataTo} is an empty ({@code zero}-length) {@code byte} array.</li>
* <li>{@code byteOffset} is negative.</li>
* <li>{@code numOfBytes} is negative.</li>
* <li>{@code numOfBytes} is zero.</li>
* <li>{@code byteOffset} and {@code numOfBytes} went out of bounds
* so that {@code byteOffset + numOfBytes} is greater than the
* number of {@code byte}s the array {@code copyDataTo} can hold.</li>
* </ul>
* @throws InsuffientBufferException the underlying {@link Buffer}
* does not have enough amount of data (specified by
* {@code numOfBytes}, to read).
* @throws EOFException the underlying {@link Buffer} has no data
* left to read.
* @throws IOException the underlying {@link Buffer} throws an
* error during the I/O operation.
*
* @see DataInput#readFully(byte[], int, int)
*/
public void readFully(byte[] copyDataTo, int byteOffset, int numOfBytes)
throws NullPointerException,
IllegalArgumentException,
IOException {
if (copyDataTo == null) {
throw new NullPointerException();
}
if (copyDataTo.length == 0) {
throw new IllegalArgumentException();
}
if (byteOffset < 0) {
throw new IllegalArgumentException();
}
if (numOfBytes < 0) {
throw new IllegalArgumentException();
}
if (numOfBytes == 0) {
throw new IllegalArgumentException();
}
if (byteOffset + numOfBytes > copyDataTo.length) {
throw new IllegalArgumentException();
}
long readableBits = buffer.readable();
if (readableBits == 0) {
throw new EOFException();
}
int readableBytes = (int )(readableBits / Byte.SIZE);
if (readableBytes < numOfBytes) {
throw new InsufficientBufferException();
}
read(copyDataTo, byteOffset, numOfBytes);
}
/**
* <p>Skips a specified number of bytes, specified by the
* parameter {@code numOfBytesToSkip} to fast-forward
* the read cursor of the underlying {@link Buffer}. It
* is possible that the actual number of {@code byte}s
* skipped may be smaller than the designated {@code numOfBytes}
* parameter. Therefore, the actual number of {@code byte}s
* skipped is shown in the return value.</p>
*
* <p>This method is just a delegate to {@link #skip(long)}. This
* method exists because we need it to be compliant with
* {@link DataInput}.</p>
*
* @param numOfBytes The number of {@code byte}s planned to skip. If
* {@code numOfBytes} is {@code zero}. This method does nothing and
* returns {@code zero}.
*
* @return The actual number of bytes skipped. The position of the
* {@link Buffer} is modified as a result of the skip operation,
* if the return value is {@code zero}, the backing {@link Buffer}
* is not modified.
*
* @throws IllegalArgumentException thrown when one of the following occurs:
* <ul>
* <li>{@code numOfBytes} is negative.</li>
* <li>{@code numOfBytes} is zero.</li>
* </ul>
* @throws IOException if the underlying {@link Buffer} operation throws
* an error.
*
* @see #skip(long)
* @see DataInput#skipBytes(int)
*/
public int skipBytes(int numOfBytes)
throws IOException {
return (int )skip(numOfBytes);
}
/**
* Reads the next {@code bit} from the underlying
* {@link Buffer} and return the value as an {@code int}.
* Possible values for the return value are {@code 0 zero}
* or {@code 1 one}. Unlike it's counterpart method
* {@link Buffer#read()}, if there is an {@link Buffer#EOF}
* situation this method will not return {@link Buffer#EOF},
* but instead throw an {@link EOFException}. This is because
* we want to keep the method behavior compliant with other
* {@link DataInput} methods.
*
* @return {@code 1 one} if the underlying {@link Buffer} returns
* a {@code bit} value of {@code 1 one}, otherwise, {@code 0 zero}
* if the underlying {@link Buffer} returns a {@code bit}
* value of {@code 0 zero}
*
* @throws EOFException Buffer{@link #read()} method returns
* {@link Buffer#EOF}.
* @throws IOException other error occurs while the underlying
* {@link Buffer} is carrying out the I/O operation.
*/
public int readBit()
throws IOException {
int data = buffer.read();
if (data == Buffer.EOF) {
throw new EOFException();
}
return data;
}
/**
* Reads a {@code byte} of data and returns the data
* as a representation of a {@code boolean}. The
* returned value is a {@code boolean}, where {@code true}
* means that the data is non-{@code zero}, and
* {@code false} means that the data is {@code zero}.
*
* @return A {@code boolean} value where {@code true}
* means the {@code byte} data is non-{@code zero},
* and {@code false} means the {@code byte} data
* is zero.
*
* @throws EOFException the underlying {@link Buffer} has
* no data to read.
* @throws InsufficientBufferException the underlying {@link Buffer}
* has data, but it is not enough to form a complete
* {@code byte} value to return.
* @throws IOException the underlying {@link Buffer} has
* an error during the I/O operation.
*
* @see DataInput#readBoolean()
*/
public boolean readBoolean()
throws IOException {
byte[] data = new byte[Byte.SIZE / 8];
readFully(data);
boolean isNonZero = data[0] != 0;
return isNonZero;
}
/**
* <p>Reads a {@code bit} instead of {@code byte} of data and
* returns the data as a representation of a {@code boolean}.
* The return value is such that {@code true} means the {@code bit}
* value is {@code 1 one}, and {@code false} means the {@code bit}
* value is {@code 0 zero}.</p>
*
* <p>This method is merely a delegate to {@link Buffer#readAsBoolean()}.</p>
*
* @return {@code true} if the next {@code bit} being read
* is {@code 1 one}, and otherwise, {@code false} if
* the next {@code bit} being read is {@code 0 zero}.
*
* @throws EOFException the underlying {@link Buffer} has
* no data to read.
* @throws InsufficientBufferException the underlying {@link Buffer}
* has data, but it is not enough to form a complete
* {@code byte} value to return.
* @throws IOException the underlying {@link Buffer} has
* an error during the I/O operation.
*
* @see Buffer#readAsBoolean()
*/
public boolean readBooleanAsBit()
throws IOException {
return buffer.readAsBoolean();
}
/**
* Reads a {@code byte} of data and returns the data.
*
* @return A {@code byte} representing the data.
*
* @throws EOFException the underlying {@link Buffer} has
* no data to read.
* @throws InsufficientBufferException the underlying
* {@link Buffer} has data, but it is not enough
* to form a complete {@code byte} value to return.
* @throws IOException the underlying {@link Buffer} has
* an error during the I/O operation.
*
* @see DataInput#readByte()
*
*/
public byte readByte()
throws IOException {
byte[] data = new byte[Byte.SIZE / 8];
readFully(data);
return data[0];
}
/**
* Reads a {@code byte} of data from the underlying {@link Buffer}
* but returns the value as a representation of an {@code unsigned byte}.
* Java does not have the {@code unsigned} keyword. The return values
* of such will all default to {@code int} numbers, bearing no
* negative values.
*
* @return An {@code unsigned byte} value between {@code 0x00}
* and {@code 0xff}. No negatives.
*
* @throws EOFException the underlying {@link Buffer} has
* no data to read.
* @throws InsufficientBufferException the underlying
* {@link Buffer} has data, but it is not enough
* to form a complete {@code byte} value to return.
* @throws IOException the underlying {@link Buffer} has
* an error during the I/O operation.
*
* @see DataInput#readUnsignedByte()
*/
public int readUnsignedByte()
throws IOException {
byte byteValue = readByte();
int unsignedValue = ((int )byteValue) & 0xff;
return unsignedValue;
}
/**
* Reads a {@code 16 bit integer} of data from the underlying {@link Buffer}.
*
* @return A {@code short} representing the data.
*
* @throws EOFException the underlying {@link Buffer} has
* no data to read.
* @throws InsufficientBufferException the underlying
* {@link Buffer} has data, but it is not enough
* to form a complete {@code short} value to return.
* @throws IOException the underlying {@link Buffer} has
* an error during the I/O operation.
*
* @see DataInput#readShort()
*
*/
public short readShort()
throws IOException {
return buffer.read(Short.SIZE, false, Buffer.BIG_ENDIAN).shortValue();
}
/**
* Reads a {@code 16 bit integer} of data from the underlying {@link Buffer}
* but returns the value as a representation of an {@code unsigned short}.
* Java does not have the {@code unsigned} keyword. The return values
* of such will all default to {@code int} numbers, bearing no
* negative values.
*
* @return An {@code unsigned short} value between {@code 0x0000}
* and {@code 0xffff}. No negatives.
*
* @throws EOFException the underlying {@link Buffer} has
* no data to read.
* @throws InsufficientBufferException the underlying
* {@link Buffer} has data, but it is not enough
* to form a complete {@code short} value to return.
* @throws IOException the underlying {@link Buffer} has
* an error during the I/O operation.
*
* @see DataInput#readUnsignedShort()
*
*/
public int readUnsignedShort()
throws IOException {
short shortValue = readShort();
int unsignedValue = ((int )shortValue) & 0xffff;
return unsignedValue;
}
/**
* Returns a {@code unicode char} of data from the underlying {@link Buffer}.
*
* @return A {@code unicode char} representing the data.
*
* @throws EOFException the underlying {@link Buffer} has
* no data to read.
* @throws InsufficientBufferException the underlying
* {@link Buffer} has data, but it is not enough
* to form a complete {@code char} value to return.
* @throws IOException the underlying {@link Buffer} has
* an error during the I/O operation.
*
* @see DataInput#readChar()
*
*/
public char readChar()
throws IOException {
int unsignedValue = readUnsignedShort();
return (char )unsignedValue;
}
/**
* Reads a {@code 32 bit integer} of data from the underlying {@link Buffer}.
*
* @return An {@code int} representing the data.
*
* @throws EOFException the underlying {@link Buffer} has
* no data to read.
* @throws InsufficientBufferException the underlying
* {@link Buffer} has data, but it is not enough
* to form a complete {@code int} value to return.
* @throws IOException the underlying {@link Buffer} has
* an error during the I/O operation.
*
* @see DataInput#readInt()
*
*/
public int readInt()
throws IOException {
return buffer.read(Integer.SIZE, false, Buffer.BIG_ENDIAN).intValue();
}
/**
* Reads an arbitrary {@code int} from the underlying {@link Buffer}.
* Java has a hard limit on the number of {@code bit}s an {@code int}
* can hold, so the parameter {@code numOfBits} should be
* {@code > 0 zero} and {@code <=} {@link Integer#SIZE}. The return
* value is a {@code signed integer}.
*
* @param numOfBits The number of {@code bits} to read. Must be
* {@code > 0 zero} and {@code <=} {@link Integer#SIZE}.
*
* @return An {@code signed int} value that represents the data
* spanning {@code numOfBits} bits.
*
* @throws IllegalArgumentException one of the following occurs:
* <ul>
* <li>{@code numOfBits} is negative.</li>
* <li>{@code numOfBits} is zero.</li>
* <li>{@code numOfBits} is greater than {@link Integer#SIZE}</li>
* </ul>
* @throws EOFException the underlying {@link Buffer} has
* no data to read.
* @throws InsufficientBufferException the underlying
* {@link Buffer} has data, but its size is less than
* the number of bits specified by the parameter
* {@code numOfBits}.
* @throws IOException the underlying {@link Buffer} has
* an error during the I/O operation.
*
*/
public int readInt(int numOfBits)
throws IllegalArgumentException,
IOException {
if (numOfBits > Integer.SIZE) {
throw new IllegalArgumentException();
}
return (int )readLong(numOfBits);
}
/**
* Reads a {@code 32 bit integer} of data from the underlying {@link Buffer}
* but returns the value as a representation of an {@code unsigned int}.
* Java does not have the {@code unsigned} keyword, and because all Java's
* primitive types are signed (of course except {@code char}), we can only
* use a {@code long} to hold the values returned. The returned value will
* bear no negative values.
*
* @return An {@code unsigned int} value between {@code 0x00000000}
* and {@code 0xffffffffL}.
*
* @throws EOFException the underlying {@link Buffer} has
* no data to read.
* @throws InsufficientBufferException the underlying
* {@link Buffer} has data, but it is not enough
* to form a complete {@code int} value to return.
* @throws IOException the underlying {@link Buffer} has
* an error during the I/O operation.
*/
public long readUnsignedInt()
throws IOException {
int intValue = readInt();
long unsignedValue = ((long )intValue) & 0xffffffffL;
return unsignedValue;
}
/**
* Read an arbitrary {@code int} from the underlying {@link Buffer}
* but returns the value as a representation of an {@code unsigned int}.
* Java does not have the {@code unsigned} keyword, and because all Java's
* primitive types are signed (of course except {@code char}), we can only
* use a {@code long} to hold the values returned. Java also has a hard
* limit on the number of {@code bit}s an {@code int} can hold, so the
* parameter {@code numOfBits} should be {@code > 0 zero} and {@code <=}
* {@link Integer#SIZE}. The return value is a {@code unsigned integer},
* bearing no negative values.
*
* @param numOfBits The number of {@code bits} to read. Must be
* {@code > 0 zero} and {@code <=} {@link Integer#SIZE}.
*
* @return An {@code unsigned int} value that represents the data
* spanning {@code numOfBits} bits.
*
* @throws IllegalArgumentException one of the following occurs:
* <ul>
* <li>{@code numOfBits} is negative.</li>
* <li>{@code numOfBits} is zero.</li>
* <li>{@code numOfBits} is greater than {@link Integer#SIZE}</li>
* </ul>
* @throws EOFException the underlying {@link Buffer} has
* no data to read.
* @throws InsufficientBufferException the underlying
* {@link Buffer} has data, but its size is less than
* the number of bits specified by the parameter
* {@code numOfBits}.
* @throws IOException the underlying {@link Buffer} has
* an error during the I/O operation.
*/
public long readUnsignedInt(int numOfBits)
throws IllegalArgumentException,
IOException {
if (numOfBits > Integer.SIZE) {
throw new IllegalArgumentException();
}
BigInteger unsignedValue = readUnsignedLong(numOfBits);
return unsignedValue.longValue();
}
public long readVarInt()
throws IOException {
int varIntSerialId = (int )readUnsignedInt(RT.VAR_INT_SHIFT);
int numOfDataBits = RT.INT62_DATA_LENGTH;
if (varIntSerialId == RT.INT14_SERIAL_ID) {
numOfDataBits = RT.INT14_DATA_LENGTH;
} else if (varIntSerialId == RT.INT30_SERIAL_ID) {
numOfDataBits = RT.INT30_DATA_LENGTH;
} else if (varIntSerialId == RT.INT62_SERIAL_ID) {
numOfDataBits = RT.INT62_DATA_LENGTH;
}
BigInteger varInt = buffer.read(numOfDataBits, false, Buffer.BIG_ENDIAN);
return varInt.longValue();
}
public long readUnsignedVarInt()
throws IOException {
int varIntSerialId = (int )readUnsignedInt(RT.VAR_INT_SHIFT);
int numOfDataBits = RT.INT6_DATA_LENGTH;
if (varIntSerialId == RT.INT14_SERIAL_ID) {
numOfDataBits = RT.INT14_DATA_LENGTH;
} else if (varIntSerialId == RT.INT30_SERIAL_ID) {
numOfDataBits = RT.INT30_DATA_LENGTH;
} else if (varIntSerialId == RT.INT62_SERIAL_ID) {
numOfDataBits = RT.INT62_DATA_LENGTH;
}
BigInteger varInt = buffer.read(numOfDataBits, true, Buffer.BIG_ENDIAN);
return varInt.longValue();
}
/**
* Reads a {@code 64 bit integer} of data from the underlying {@link Buffer}.
*
* @return A {@code long} representing the data.
*
* @throws EOFException the underlying {@link Buffer} has
* no data to read.
* @throws InsufficientBufferException the underlying
* {@link Buffer} has data, but it is not enough
* to form a complete {@code long} value to return.
* @throws IOException the underlying {@link Buffer} has
* an error during the I/O operation.
*
* @see DataInput#readLong()
*
*/
public long readLong()
throws IOException {
return buffer.read(Long.SIZE, false, Buffer.BIG_ENDIAN).longValue();
}
/**
* Reads an arbitrary {@code long} from the underlying {@link Buffer}.
* Java has a hard limit on the number of {@code bit}s a {@code long}
* can hold, so the parameter {@code numOfBits} should be
* {@code > 0 zero} and {@code <=} {@link Long#SIZE}. The return value
* is a {@code signed long}.
*
* @param numOfBits The number of {@code bit}s to read. Must be >
* {@code > 0 zero} and {@code <=} {@link Long#SIZE}.
*
* @return A {@code signed long} value that represents the data
* spanning {@code numOfBits} bits.
*
* @throws IllegalArgumentException one of the following occurs:
* <ul>
* <li>{@code numOfBits} is negative.</li>
* <li>{@code numOfBits} is zero.</li>
* <li>{@code numOfBits} is greater than {@link Long#SIZE}</li>
* </ul>
* @throws EOFException the underlying {@link Buffer} has
* no data to read.
* @throws InsufficientBufferException the underlying
* {@link Buffer} has data, but its size is less than
* the number of bits specified by the parameter
* {@code numOfBits}.
* @throws IOException the underlying {@link Buffer} has
* an error during the I/O operation.
*
*/
public long readLong(int numOfBits)
throws IllegalArgumentException,
IOException {
if (numOfBits > Long.SIZE) {
throw new IllegalArgumentException();
}
return buffer.read(numOfBits, false, Buffer.BIG_ENDIAN).longValue();
}
/**
* Reads a {@code 64 bit integer} of data from the underlying {@link Buffer}
* but returns the value as a representation of an {@code unsigned long}.
* Java does not have the {@code unsigned} keyword, and because all Java's
* primitive types are signed (exception {@code char}), we can only use
* a {@link BigInteger} to hold the values returned. The return value will
* bear no negative values.
*
* @return An {@code unsigned long} value between {@code 0x0000000000000000L}
* and {@code 0xffffffffffffffffLL}
*
* @throws EOFException the underlying {@link Buffer} has
* no data to read.
* @throws InsufficientBufferException the underlying
* {@link Buffer} has data, but it is not enough
* to form a complete {@code long} value to return.
* @throws IOException the underlying {@link Buffer} has
* an error during the I/O operation.
*/
public BigInteger readUnsignedLong()
throws IOException {
long longValue = readLong();
BigInteger signedValue = BigInteger.valueOf(longValue);
BigInteger unsignedValue = signedValue.and(UNSIGNED_LONG_MASK);
return unsignedValue;
}
/**
* Reads an arbitrary {@code long} from the underlying {@link Buffer}
* but returns the value as a representation of an {@code unsigned long}.
* Java does not have the {@code unsigned} keyword, and because all Java's
* primitive types are signed (except {@code char}), we can only use a
* {@code long} to hold the values returned. Java also has a hard limit
* on the number of {@code bit}s a {@code long} can hold, so the
* parameter {@code numOfBits} would be {@code > 0 zero} and {@code <=}
* {@link Long#SIZE}. The return value is a {@link BigInteger}, bearing
* no negative values.
*
* @param numOfBits The number of {@code bits} to read. Must be
* {@code > 0 zero} and {@code <=} {@link Long#SIZE}.
*
* @return An {@code unsigned long} value that represents the data
* spanning {@code numOfBits} bits.
*
* @throws IllegalArgumentException one of the following occurs:
* <ul>
* <li>{@code numOfBits} is negative.</li>
* <li>{@code numOfBits} is zero.</li>
* <li>{@code numOfBits} is greater than {@link Long#SIZE}</li>
* </ul>
* @throws EOFException the underlying {@link Buffer} has
* no data to read.
* @throws InsufficientBufferException the underlying
* {@link Buffer} has data, but its size is less than
* the number of bits specified by the parameter
* {@code numOfBits}.
* @throws IOException the underlying {@link Buffer} has
* an error during the I/O operation.
*/
public BigInteger readUnsignedLong(int numOfBits)
throws IllegalArgumentException,
IOException {
if (numOfBits > Long.SIZE) {
throw new IllegalArgumentException();
}
return buffer.read(numOfBits, false, Buffer.BIG_ENDIAN);
}
/**
* Reads an arbitrary {@code signed} {@link BigInteger} from
* the underlying {@link Buffer}. There is no limit on the
* magnitude of the number.
*
* @param numOfBits The number of {@code bit}s to read. Must be
* a positive value.
*
* @return A {@link BigInteger} value that represents the data
* spanning {@code numOfBits} bits.
*
* @throws IllegalArgumentException one of the following occurs:
* <ul>
* <li>{@code numOfBits} is negative.</li>
* <li>{@code numOfBits} is zero.</li>
* </ul>
* @throws EOFException the underlying {@link Buffer} has
* no data to read.
* @throws InsufficientBufferException the underlying
* {@link Buffer} has data, but its size is less than
* the number of bits specified by the parameter
* {@code numOfBits}.
* @throws IOException the underlying {@link Buffer} has
* an error during the I/O operation.
*/
public BigInteger readBigInteger(int numOfBits)
throws IllegalArgumentException,
IOException {
return buffer.read(numOfBits, true, Buffer.BIG_ENDIAN);
}
public BigInteger readBigInteger()
throws IOException {
int numOfBits = readInt();
boolean isNegative = readBooleanAsBit();
int numOfBitsWithoutSignBit = numOfBits - 1;
BigInteger finalValue = buffer.read(numOfBitsWithoutSignBit, false, Buffer.BIG_ENDIAN);
if (isNegative) {
finalValue = finalValue.negate();
}
return finalValue;
}
/**
* Reads an arbitrary {@code bit}s from the underlying {@link Buffer}
* but returns the value as a representation of an {@code unsigned} {@link BigInteger}.
* There is no limit on the magnitude of the number, but the
* parameter {@code numOfBits} must be positive. The return value is a
* {@link BigInteger}, bearing no negative values.
*
* @param numOfBits The number of {@code bits} to read. Must be positive.
*
* @return An {@code unsigned} {@link BigInteger} value that represents
* the data spanning {@code numOfBits} bits.
*
* @throws IllegalArgumentException one of the following occurs:
* <ul>
* <li>{@code numOfBits} is negative.</li>
* <li>{@code numOfBits} is zero.</li>
* </ul>
* @throws EOFException the underlying {@link Buffer} has
* no data to read.
* @throws InsufficientBufferException the underlying
* {@link Buffer} has data, but its size is less than
* the number of bits specified by the parameter
* {@code numOfBits}.
* @throws IOException the underlying {@link Buffer} has
* an error during the I/O operation.
*/
public BigInteger readUnsignedBigInteger(int numOfBits)
throws IllegalArgumentException,
IOException {
return buffer.read(numOfBits, false, Buffer.BIG_ENDIAN);
}
/**
* Reads a 32 {@code bit} number as a floating number from
* the underlying {@link Buffer}.
*
* @returns A {@code float} representing the 32 bit number.
*
* @see DataInput#readFloat()
*/
public float readFloat()
throws IOException {
return Float.intBitsToFloat(readInt());
}
/**
* Reads a 64 {@code bit} number as a double precision number
* from the underlying {@link Buffer}.
*
* @returns A {@code double} representing the 64 bit number.
*
* @see DataInput#readDouble()
*/
public double readDouble()
throws IOException {
return Double.longBitsToDouble(readLong());
}
/**
* <p>Reads a double-precision number of arbitrary {@code bit}s
* in size, and returns the number value as a {@link BigDecimal}.
* This parameter {@code numOfBitsExcludingScale} is the
* number of integral (unscaled) {@code bit}s that the number needs.
* There's a 32-{@code bit} {@code integer} value that will
* be read first. This value indicates the decimal point
* position (i.e. the scale). The parameter {@code numOfBitsExcludingScale}
* doesn't count this 32 {@code bit} integer. Therefore,
* the actual amount of data read will by four {@code byte}s greater
* than that specified in {@code numOfBitsExcludingScale}.</p>
*
* <p>This method will first read the {@code int} scale value, then
* read the data chunk specified by {@code numOfBitsExcludingScale},
* and finally use these two pieces of data to generate the {@link BigDecimal}.</p>
*
* <p>This method is atomic, if read operation fails halfway, the
* {@link Buffer} will revert.</p>
*
* @param numOfBitsExcludingScale The number of {@code bit}s to read
* to form the integral (unscaled) version of the number. This does
* not consider the size of the {@code int} that is needed to contain
* the decimal point (scale) information. The actual data read will
* be four {@code byte}s bigger as this extra four {@code byte}s contains
* the decimal point (scale) info.
*
* @return The {@link BigDecimal} containing the data.
*
* @throws IllegalArgumentException thrown when one of the following occurs:
* <ul>
* <li>{@code numOfBitsExcludingScale} is negative.</li>
* <li>{@code numOfBitsExcludingScale} is zero.</li>
* </ul>
* @throws EOFException the underlying {@link Buffer} has
* no data to read.
* @throws InsufficientBufferException the underlying
* {@link Buffer} has data, but its size is less than
* the number of bits specified by the parameter
* {@code numOfBitsExcludingScale} plus the four {@code byte}s
* needed to hold the decimal point (scale) info.
* @throws IOException the underlying {@link Buffer} has
* an error during the I/O operation.
*/
public BigDecimal readBigDecimal(int numOfBitsExcludingScale)
throws IllegalArgumentException,
IOException {
if (numOfBitsExcludingScale <= 0) {
throw new IllegalArgumentException();
}
int scaleValue = readInt();
BigInteger unscaledValue = readBigInteger(numOfBitsExcludingScale);
return new BigDecimal(unscaledValue, scaleValue);
}
public BigDecimal readBigDecimal()
throws IOException {
int scaleValue = readInt();
int numOfBits = readInt();
boolean isNegative = readBooleanAsBit();
int numOfBitsWithoutSign = numOfBits - 1;
BigInteger unscaledValue = buffer.read(numOfBitsWithoutSign, false, Buffer.BIG_ENDIAN);
if (isNegative) {
unscaledValue = unscaledValue.negate();
}
return new BigDecimal(unscaledValue, scaleValue);
}
/**
* @see DataInput#readLine()
*/
public String readLine()
throws IOException {
buffer.mark();
int numOfBytesToRead = 0;
while (true) {
int currentChar = read();
if (currentChar == -1) {
break;
}
numOfBytesToRead = numOfBytesToRead + 1;
if (currentChar == '\n') {
break;
} else if (currentChar == '\r') {
int nextChar = read();
if (nextChar == -1) {
throw new InsufficientBufferException();
} else if (nextChar == '\n') {
numOfBytesToRead = numOfBytesToRead + 1;
} else {
break;
}
}
}
buffer.reset();
buffer.mark();
byte[] charArray = new byte[numOfBytesToRead];
readFully(charArray);
String line = new String(charArray, Charset.forName("utf-8"));
return line.trim();
}
public String readRawUTF(int numOfBytes)
throws IOException {
byte[] byteBuffer = new byte[numOfBytes];
readFully(byteBuffer);
StringBuilder builder = new StringBuilder();
int byteCursor = 0;
while (byteCursor < numOfBytes) {
int firstByte = byteBuffer[byteCursor] & 0xff;
int flag = firstByte >> 4;
if (flag == 0 || flag == 1 || flag == 2 || flag == 3 ||
flag == 4 || flag == 5 || flag == 6 || flag == 7) {
} else if (flag == 12 || flag == 13) {
if (byteCursor + 1 > numOfBytes) {
throw new UTFDataFormatException();
}
int ch = firstByte;
builder.append((char )ch);
byteCursor = byteCursor + 1;
} else if (flag == 14) {
if (byteCursor + 2 > numOfBytes) {
throw new UTFDataFormatException();
}
int secondByte = byteBuffer[byteCursor + 1] & 0xff;
if ((secondByte & 0xc0) != 0x80) {
throw new UTFDataFormatException();
}
int ch = (firstByte & 0x1f) << 6;
ch = ch | (secondByte & 0x3f);
builder.append((char )ch);
byteCursor = byteCursor + 2;
} else {
if (byteCursor + 3 > numOfBytes) {
throw new UTFDataFormatException();
}
int secondByte = byteBuffer[byteCursor + 1] & 0xff;
int thirdByte = byteBuffer[byteCursor + 2] & 0xff;
if ((secondByte & 0xc0) != 0x80) {
throw new UTFDataFormatException();
}
if ((thirdByte & 0xc0) != 0x80) {
throw new UTFDataFormatException();
}
int ch = (firstByte & 0x0f) << 12;
ch = ch | ((secondByte & 0x3f) << 6);
ch = ch | (thirdByte & 0x3f);
builder.append((char )ch);
byteCursor = byteCursor + 3;
}
}
return builder.toString();
}
/**
* @see DataInput#readUTF()
*/
public String readUTF()
throws IOException {
int numOfBytes = readUnsignedShort();
String utfString = readRawUTF(numOfBytes);
return utfString;
}
public void writeBoolean(boolean booleanValue)
throws IOException {
if (booleanValue) {
write(1);
} else {
write(0);
}
}
public void writeBooleanAsBit(boolean booleanValue)
throws IOException {
buffer.writeAsBoolean(booleanValue);
}
@Override
public void writeByte(int byteValue)
throws IllegalArgumentException,
IOException {
if (byteValue < Byte.MIN_VALUE) {
throw new IllegalArgumentException();
}
if (byteValue > Byte.MAX_VALUE) {
throw new IllegalArgumentException();
}
int data = byteValue & 0xff;
write(data);
}
@Override
public void writeShort(int shortValue)
throws IllegalArgumentException,
IOException {
if (shortValue < Short.MIN_VALUE) {
throw new IllegalArgumentException();
}
if (shortValue > Short.MAX_VALUE) {
throw new IllegalArgumentException();
}
buffer.write(Short.SIZE, BigInteger.valueOf(shortValue), Buffer.BIG_ENDIAN);
}
@Override
public void writeChar(int charValue)
throws IllegalArgumentException,
IOException {
if (charValue < Character.MIN_VALUE) {
throw new IllegalArgumentException();
}
if (charValue > Character.MAX_VALUE) {
throw new IllegalArgumentException();
}
buffer.write(Character.SIZE, BigInteger.valueOf(charValue), Buffer.BIG_ENDIAN);
}
@Override
public void writeInt(int intValue)
throws IOException {
buffer.write(Integer.SIZE, BigInteger.valueOf(intValue), Buffer.BIG_ENDIAN);
}
public void writeInt(int intValue, int numOfBits)
throws IllegalArgumentException,
IOException {
if (numOfBits > Integer.SIZE) {
throw new IllegalArgumentException();
}
writeLong(intValue, numOfBits);
}
public int writeVarInt(long value)
throws IllegalArgumentException,
IOException {
if (value < RT.INT62_MIN || value > RT.INT62_MAX_SIGNED) {
throw new IllegalArgumentException();
}
int numOfDataBits = RT.INT6_DATA_LENGTH;
if (value < RT.INT30_MIN || value > RT.INT30_MAX_SIGNED) {
numOfDataBits = RT.INT62_DATA_LENGTH;
} else if (value < RT.INT14_MIN || value > RT.INT14_MAX_SIGNED) {
numOfDataBits = RT.INT30_DATA_LENGTH;
} else if (value < RT.INT6_MIN || value > RT.INT62_MAX_SIGNED) {
numOfDataBits = RT.INT14_DATA_LENGTH;
}
buffer.write(numOfDataBits, BigInteger.valueOf(value), Buffer.BIG_ENDIAN);
return numOfDataBits;
}
public int writeUnsignedVarInt(long value)
throws IllegalArgumentException,
IOException {
if (value < 0) {
throw new IllegalArgumentException();
}
if (value > RT.INT62_MAX_UNSIGNED) {
throw new IllegalArgumentException();
}
int numOfDataBits = RT.INT6_DATA_LENGTH;
if (value > RT.INT30_MAX_UNSIGNED) {
numOfDataBits = RT.INT62_DATA_LENGTH;
} else if (value > RT.INT14_MAX_UNSIGNED) {
numOfDataBits = RT.INT30_DATA_LENGTH;
} else if (value > RT.INT6_MAX_UNSIGNED) {
numOfDataBits = RT.INT14_DATA_LENGTH;
}
buffer.write(numOfDataBits, BigInteger.valueOf(value), Buffer.BIG_ENDIAN);
return numOfDataBits;
}
@Override
public void writeLong(long longValue)
throws IOException {
buffer.write(Long.SIZE, BigInteger.valueOf(longValue), Buffer.BIG_ENDIAN);
}
public void writeLong(long longValue, int numOfBits)
throws IllegalArgumentException,
IOException {
if (numOfBits > Long.SIZE) {
throw new IllegalArgumentException();
}
buffer.write(numOfBits, BigInteger.valueOf(longValue), Buffer.BIG_ENDIAN);
}
public void writeBigInteger(BigInteger bigIntValue, int numOfBits)
throws IllegalArgumentException,
IOException {
buffer.write(numOfBits, bigIntValue, Buffer.BIG_ENDIAN);
}
public long writeBigInteger(BigInteger bigIntValue)
throws NullPointerException,
IOException {
if (bigIntValue == null) {
throw new NullPointerException();
}
int numOfBitsWithoutSignBit = bigIntValue.bitLength();
int numOfBits = numOfBitsWithoutSignBit + 1;
writeInt(numOfBits);
if (bigIntValue.signum() == -1) {
writeBooleanAsBit(true);
} else {
writeBooleanAsBit(false);
}
buffer.write(numOfBitsWithoutSignBit, bigIntValue, Buffer.BIG_ENDIAN);
return Integer.SIZE + numOfBits;
}
@Override
public void writeFloat(float floatValue) throws IOException {
writeInt(Float.floatToIntBits(floatValue));
}
@Override
public void writeDouble(double doubleValue) throws IOException {
writeLong(Double.doubleToLongBits(doubleValue));
}
public long writeBigDecimal(BigDecimal bigDecValue, int numOfBitsExcludingScale)
throws NullPointerException,
IllegalArgumentException,
IOException {
if (bigDecValue == null) {
throw new NullPointerException();
}
if (numOfBitsExcludingScale <= 0) {
throw new IllegalArgumentException();
}
int scale = bigDecValue.scale();
writeInt(scale);
BigInteger unscaledValue = bigDecValue.unscaledValue();
long written = writeBigInteger(unscaledValue);
return written + Integer.SIZE;
}
public long writeBigDecimal(BigDecimal bigDecValue)
throws NullPointerException,
IOException {
if (bigDecValue == null) {
throw new NullPointerException();
}
int scale = bigDecValue.scale();
writeInt(scale);
BigInteger unscaledValue = bigDecValue.unscaledValue();
int numOfBitsWithoutSign = unscaledValue.bitLength();
int numOfBits = numOfBitsWithoutSign + 1;
writeInt(numOfBits);
if (unscaledValue.signum() == -1) {
writeBooleanAsBit(true);
} else {
writeBooleanAsBit(false);
}
buffer.write(numOfBitsWithoutSign, unscaledValue, Buffer.BIG_ENDIAN);
return Integer.SIZE + Integer.SIZE + numOfBits;
}
@Override
public void writeBytes(String stringValue)
throws NullPointerException,
IllegalArgumentException,
IOException {
if (stringValue == null) {
throw new NullPointerException();
}
if (stringValue.isEmpty()) {
throw new IllegalArgumentException();
}
for (int charIndex = 0; charIndex < stringValue.length(); charIndex = charIndex + 1) {
int charValue = stringValue.charAt(charIndex);
write((byte )charValue);
}
}
@Override
public void writeChars(String stringValue)
throws IOException {
if (stringValue == null) {
throw new NullPointerException();
}
if (stringValue.isEmpty()) {
throw new IllegalArgumentException();
}
for (int charIndex = 0; charIndex < stringValue.length(); charIndex = charIndex + 1) {
int charValue = stringValue.charAt(charIndex);
writeChar(charValue);
}
}
@Override
public void writeUTF(String stringValue)
throws NullPointerException,
IllegalArgumentException,
IOException {
if (stringValue == null) {
throw new NullPointerException();
}
if (stringValue.isEmpty()) {
throw new IllegalArgumentException();
}
int numOfBytes = 0;
for (int charIndex = 0; charIndex < stringValue.length(); charIndex = charIndex + 1) {
int charValue = stringValue.charAt(charIndex);
if ((charValue >= 0x0001) && (charValue <= 0x007f)) {
numOfBytes = numOfBytes + 1;
} else if (charValue > 0x07ff) {
numOfBytes = numOfBytes + 3;
} else {
numOfBytes = numOfBytes + 2;
}
}
if (numOfBytes > 0xffff) {
throw new UTFDataFormatException();
}
writeShort((short )numOfBytes);
writeRawUTF(stringValue, numOfBytes);
}
public void writeRawUTF(String stringValue, int numOfBytes)
throws NullPointerException,
IllegalArgumentException,
IOException {
if (stringValue == null) {
throw new NullPointerException();
}
if (stringValue.isEmpty()) {
throw new IllegalArgumentException();
}
byte[] byteArray = new byte[numOfBytes];
int byteCursor = 0;
for (int charIndex = 0; charIndex < stringValue.length(); charIndex = charIndex + 1) {
int charValue = stringValue.charAt(charIndex);
if ((charValue >= 0x0001) && (charValue <= 0x007f)) {
byteArray[byteCursor++] = (byte )charValue;
} else if ((charValue > 0x07ff)) {
byteArray[byteCursor++] = (byte )(0xe0 | ((charValue >> 12) & 0x0f));
byteArray[byteCursor++] = (byte )(0x80 | ((charValue >> 6) & 0x3f));
byteArray[byteCursor++] = (byte )(0x80 | ((charValue >> 0) & 0x3f));
} else {
byteArray[byteCursor++] = (byte )(0xc0 | ((charValue >> 6) & 0x1f));
byteArray[byteCursor++] = (byte )(0x80 | ((charValue >> 0) & 0x1f));
}
}
write(byteArray);
}
}