/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2000-2006
* Oracle Corporation. All rights reserved.
*
* $Id: TupleInput.java,v 12.4 2006/08/31 18:14:06 bostic Exp $
*/
package com.sleepycat.bind.tuple;
import com.sleepycat.util.FastInputStream;
import com.sleepycat.util.PackedInteger;
import com.sleepycat.util.UtfOps;
/**
* An InputStream with DataInput-like methods for
* reading tuple fields. It is used by TupleBinding.
*
*
This class has many methods that have the same signatures as methods in * the {@link java.io.DataInput} interface. The reason this class does not * implement {@link java.io.DataInput} is because it would break the interface * contract for those methods because of data format differences.
* *Signed numbers are stored in the buffer in MSB (most significant byte * first) order with their sign bit (high-order bit) inverted to cause negative * numbers to be sorted first when comparing values as unsigned byte arrays, * as done in a database. Unsigned numbers, including characters, are stored * in MSB order with no change to their sign bit.
* *Strings and character arrays are stored either as a fixed length array of * unicode characters, where the length must be known by the application, or as * a null-terminated UTF byte array.
*Floats and doubles are stored using two different representations: sorted * representation and integer-bit (IEEE 754) representation. If you use * negative floating point numbers in a key, you should use sorted * representation; alternatively you may use integer-bit representation but you * will need to implement and configure a custom comparator to get correct * numeric ordering for negative numbers.
* *To use sorted representation use this set of methods:
*To use integer-bit representation use this set of methods:
*Float.intBitsToFloat is used to convert the signed int
* value.
*
* Note: This method operations on byte array values that by * default (without a custom comparator) do not sort correctly for * negative values. Only non-negative values are sorted correctly by * default. To sort all values correctly by default, use {@link * #readSortedFloat}.
* * @return the value read from the buffer. * * @throws IndexOutOfBoundsException if not enough bytes are available in * the buffer. */ public final float readFloat() throws IndexOutOfBoundsException { return Float.intBitsToFloat((int) readUnsignedInt()); } /** * Reads a signed double (eight byte) value from the buffer. * Reads values that were written using {@link TupleOutput#writeDouble}. *Double.longBitsToDouble is used to convert the signed long
* value.
*
* Note: This method operations on byte array values that by * default (without a custom comparator) do not sort correctly for * negative values. Only non-negative values are sorted correctly by * default. To sort all values correctly by default, use {@link * #readSortedDouble}.
* * @return the value read from the buffer. * * @throws IndexOutOfBoundsException if not enough bytes are available in * the buffer. */ public final double readDouble() throws IndexOutOfBoundsException { return Double.longBitsToDouble(readUnsignedLong()); } /** * Reads a signed float (four byte) value from the buffer, with support * for correct default sorting of all values. * Reads values that were written using {@link * TupleOutput#writeSortedFloat}. * *Float.intBitsToFloat and the following bit
* manipulations are used to convert the stored representation to a signed
* float value.
* int val = ... // get stored bits
* val ^= (val < 0) ? 0x80000000 : 0xffffffff;
* return Float.intBitsToFloat(val);
*
*
* @return the value read from the buffer.
*
* @throws IndexOutOfBoundsException if not enough bytes are available in
* the buffer.
*/
public final float readSortedFloat()
throws IndexOutOfBoundsException {
int val = (int) readUnsignedInt();
val ^= (val < 0) ? 0x80000000 : 0xffffffff;
return Float.intBitsToFloat(val);
}
/**
* Reads a signed double (eight byte) value from the buffer, with support
* for correct default sorting of all values.
* Reads values that were written using {@link
* TupleOutput#writeSortedDouble}.
*
* Float.longBitsToDouble and the following bit
* manipulations are used to convert the stored representation to a signed
* double value.
* int val = ... // get stored bits
val ^= (val < 0) ? 0x8000000000000000L : 0xffffffffffffffffL;
return Double.longBitsToDouble(val);
*
*
* @return the value read from the buffer.
*
* @throws IndexOutOfBoundsException if not enough bytes are available in
* the buffer.
*/
public final double readSortedDouble()
throws IndexOutOfBoundsException {
long val = readUnsignedLong();
val ^= (val < 0) ? 0x8000000000000000L : 0xffffffffffffffffL;
return Double.longBitsToDouble(val);
}
/**
* Reads an unsigned byte (one byte) value from the buffer.
* Reads values that were written using {@link
* TupleOutput#writeUnsignedByte}.
*
* @return the value read from the buffer.
*
* @throws IndexOutOfBoundsException if not enough bytes are available in
* the buffer.
*/
public final int readUnsignedByte()
throws IndexOutOfBoundsException {
int c = readFast();
if (c < 0) {
throw new IndexOutOfBoundsException();
}
return c;
}
/**
* Reads an unsigned short (two byte) value from the buffer.
* Reads values that were written using {@link
* TupleOutput#writeUnsignedShort}.
*
* @return the value read from the buffer.
*
* @throws IndexOutOfBoundsException if not enough bytes are available in
* the buffer.
*/
public final int readUnsignedShort()
throws IndexOutOfBoundsException {
int c1 = readFast();
int c2 = readFast();
if ((c1 | c2) < 0) {
throw new IndexOutOfBoundsException();
}
return ((c1 << 8) | c2);
}
// --- end DataInput compatible methods ---
/**
* Reads an unsigned int (four byte) value from the buffer.
* Reads values that were written using {@link
* TupleOutput#writeUnsignedInt}.
*
* @return the value read from the buffer.
*
* @throws IndexOutOfBoundsException if not enough bytes are available in
* the buffer.
*/
public final long readUnsignedInt()
throws IndexOutOfBoundsException {
long c1 = readFast();
long c2 = readFast();
long c3 = readFast();
long c4 = readFast();
if ((c1 | c2 | c3 | c4) < 0) {
throw new IndexOutOfBoundsException();
}
return ((c1 << 24) | (c2 << 16) | (c3 << 8) | c4);
}
/**
* This method is private since an unsigned long cannot be treated as
* such in Java, nor converted to a BigInteger of the same value.
*/
private final long readUnsignedLong()
throws IndexOutOfBoundsException {
long c1 = readFast();
long c2 = readFast();
long c3 = readFast();
long c4 = readFast();
long c5 = readFast();
long c6 = readFast();
long c7 = readFast();
long c8 = readFast();
if ((c1 | c2 | c3 | c4 | c5 | c6 | c7 | c8) < 0) {
throw new IndexOutOfBoundsException();
}
return ((c1 << 56) | (c2 << 48) | (c3 << 40) | (c4 << 32) |
(c5 << 24) | (c6 << 16) | (c7 << 8) | c8);
}
/**
* Reads the specified number of bytes from the buffer, converting each
* unsigned byte value to a character of the resulting string.
* Reads values that were written using {@link TupleOutput#writeBytes}.
* Only characters with values below 0x100 may be read using this method.
*
* @param length is the number of bytes to be read.
*
* @return the value read from the buffer.
*
* @throws IndexOutOfBoundsException if not enough bytes are available in
* the buffer.
*/
public final String readBytes(int length)
throws IndexOutOfBoundsException {
StringBuffer buf = new StringBuffer(length);
for (int i = 0; i < length; i++) {
int c = readFast();
if (c < 0) {
throw new IndexOutOfBoundsException();
}
buf.append((char) c);
}
return buf.toString();
}
/**
* Reads the specified number of characters from the buffer, converting
* each two byte unsigned value to a character of the resulting string.
* Reads values that were written using {@link TupleOutput#writeChars}.
*
* @param length is the number of characters to be read.
*
* @return the value read from the buffer.
*
* @throws IndexOutOfBoundsException if not enough bytes are available in
* the buffer.
*/
public final String readChars(int length)
throws IndexOutOfBoundsException {
StringBuffer buf = new StringBuffer(length);
for (int i = 0; i < length; i++) {
buf.append(readChar());
}
return buf.toString();
}
/**
* Reads the specified number of bytes from the buffer, converting each
* unsigned byte value to a character of the resulting array.
* Reads values that were written using {@link TupleOutput#writeBytes}.
* Only characters with values below 0x100 may be read using this method.
*
* @param chars is the array to receive the data and whose length is used
* to determine the number of bytes to be read.
*
* @throws IndexOutOfBoundsException if not enough bytes are available in
* the buffer.
*/
public final void readBytes(char[] chars)
throws IndexOutOfBoundsException {
for (int i = 0; i < chars.length; i++) {
int c = readFast();
if (c < 0) {
throw new IndexOutOfBoundsException();
}
chars[i] = (char) c;
}
}
/**
* Reads the specified number of characters from the buffer, converting
* each two byte unsigned value to a character of the resulting array.
* Reads values that were written using {@link TupleOutput#writeChars}.
*
* @param chars is the array to receive the data and whose length is used
* to determine the number of characters to be read.
*
* @throws IndexOutOfBoundsException if not enough bytes are available in
* the buffer.
*/
public final void readChars(char[] chars)
throws IndexOutOfBoundsException {
for (int i = 0; i < chars.length; i++) {
chars[i] = readChar();
}
}
/**
* Reads the specified number of UTF characters string from the data
* buffer and converts the data from UTF to Unicode.
* Reads values that were written using {@link
* TupleOutput#writeString(char[])}.
*
* @param length is the number of characters to be read.
*
* @return the converted string.
*
* @throws IndexOutOfBoundsException if no null terminating byte is found
* in the buffer.
*
* @throws IllegalArgumentException malformed UTF data is encountered.
*/
public final String readString(int length)
throws IndexOutOfBoundsException, IllegalArgumentException {
char[] chars = new char[length];
readString(chars);
return new String(chars);
}
/**
* Reads the specified number of UTF characters string from the data
* buffer and converts the data from UTF to Unicode.
* Reads values that were written using {@link
* TupleOutput#writeString(char[])}.
*
* @param chars is the array to receive the data and whose length is used
* to determine the number of characters to be read.
*
* @throws IndexOutOfBoundsException if no null terminating byte is found
* in the buffer.
*
* @throws IllegalArgumentException malformed UTF data is encountered.
*/
public final void readString(char[] chars)
throws IndexOutOfBoundsException, IllegalArgumentException {
off = UtfOps.bytesToChars(buf, off, chars, 0, chars.length, false);
}
/**
* Returns the byte length of a null-terminated UTF string in the data
* buffer, including the terminator. Used with string values that were
* written using {@link TupleOutput#writeString(String)}.
*
* @throws IndexOutOfBoundsException if no null terminating byte is found
* in the buffer.
*
* @throws IllegalArgumentException malformed UTF data is encountered.
*/
public final int getStringByteLength()
throws IndexOutOfBoundsException, IllegalArgumentException {
if (available() >= 2 &&
buf[off] == TupleOutput.NULL_STRING_UTF_VALUE &&
buf[off + 1] == 0) {
return 2;
} else {
return UtfOps.getZeroTerminatedByteLength(buf, off) + 1;
}
}
/**
* Reads a packed integer. Note that packed integers are not appropriate
* for sorted values (keys) unless a custom comparator is used.
*
* @see PackedInteger
*/
public int readPackedInt() {
int len = PackedInteger.getReadIntLength(buf, off);
int val = PackedInteger.readInt(buf, off);
off += len;
return val;
}
/**
* Returns the byte length of a packed integer.
*
* @see PackedInteger
*/
public int getPackedIntByteLength() {
return PackedInteger.getReadIntLength(buf, off);
}
}