package com.sleepycat.bind.tuple;
import com.sleepycat.util.FastInputStream;
import com.sleepycat.util.UtfOps;
import de.ovgu.cide.jakutil.*;
/**
* An <code>InputStream</code> with <code>DataInput</code>-like methods for
* reading tuple fields. It is used by <code>TupleBinding</code>.
* <p>
* 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.
* </p>
* <p>
* 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.
* </p>
* <p>
* 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.
* </p>
* <ul>
* <li>Null strings are UTF encoded as { 0xFF }, which is not allowed in a
* standard UTF encoding. This allows null strings, as distinct from empty or
* zero length strings, to be represented in a tuple. Using the default
* comparator, null strings will be ordered last.</li>
* <li>Zero (0x0000) character values are UTF encoded as non-zero values, and
* therefore embedded zeros in the string are supported. The sequence { 0xC0,
* 0x80 } is used to encode a zero character. This UTF encoding is the same one
* used by native Java UTF libraries. However, this encoding of zero does impact
* the lexicographical ordering, and zeros will not be sorted first (the natural
* order) or last. For all character values other than zero, the default UTF
* byte ordering is the same as the Unicode lexicographical character ordering.</li>
* </ul>
* <p>
* Floats and doubles are stored in standard Java integer-bit representation
* (IEEE 754). Non-negative numbers are correctly ordered by numeric value.
* However, negative numbers are not correctly ordered; therefore, if you use
* negative floating point numbers in a key, you'll need to implement and
* configure a custom comparator to get correct numeric ordering.
* </p>
* @author Mark Hayes
*/
public class TupleInput extends FastInputStream {
/**
* Creates a tuple input object for reading a byte array of tuple data. A
* reference to the byte array will be kept by this object (it will not be
* copied) and therefore the byte array should not be modified while this
* object is in use.
* @param bufferis the byte array to be read and should contain data in tuple
* format.
*/
public TupleInput( byte[] buffer){
super(buffer);
}
/**
* Creates a tuple input object for reading a byte array of tuple data at a
* given offset for a given length. A reference to the byte array will be
* kept by this object (it will not be copied) and therefore the byte array
* should not be modified while this object is in use.
* @param bufferis the byte array to be read and should contain data in tuple
* format.
* @param offsetis the byte offset at which to begin reading.
* @param lengthis the number of bytes to be read.
*/
public TupleInput( byte[] buffer, int offset, int length){
super(buffer,offset,length);
}
/**
* Creates a tuple input object from the data contained in a tuple output
* object. A reference to the tuple output's byte array will be kept by this
* object (it will not be copied) and therefore the tuple output object
* should not be modified while this object is in use.
* @param outputis the tuple output object containing the data to be read.
*/
public TupleInput( TupleOutput output){
super(output.getBufferBytes(),output.getBufferOffset(),output.getBufferLength());
}
/**
* Reads a null-terminated UTF string from the data buffer and converts the
* data from UTF to Unicode. Reads values that were written using {@link TupleOutput#writeString(String)}.
* @return the converted string.
* @throws IndexOutOfBoundsExceptionif no null terminating byte is found in the buffer.
* @throws IllegalArgumentExceptionmalformed UTF data is encountered.
*/
public final String readString() throws IndexOutOfBoundsException, IllegalArgumentException {
byte[] buf=getBufferBytes();
int off=getBufferOffset();
if (available() >= 2 && buf[off] == TupleOutput.NULL_STRING_UTF_VALUE && buf[off + 1] == 0) {
skip(2);
return null;
}
else {
int byteLen=UtfOps.getZeroTerminatedByteLength(buf,off);
skip(byteLen + 1);
return UtfOps.bytesToString(buf,off,byteLen);
}
}
/**
* Reads a char (two byte) unsigned value from the buffer. Reads values that
* were written using {@link TupleOutput#writeChar}.
* @return the value read from the buffer.
* @throws IndexOutOfBoundsExceptionif not enough bytes are available in the buffer.
*/
public final char readChar() throws IndexOutOfBoundsException {
return (char)readUnsignedShort();
}
/**
* Reads a boolean (one byte) unsigned value from the buffer and returns
* true if it is non-zero and false if it is zero. Reads values that were
* written using {@link TupleOutput#writeBoolean}.
* @return the value read from the buffer.
* @throws IndexOutOfBoundsExceptionif not enough bytes are available in the buffer.
*/
public final boolean readBoolean() throws IndexOutOfBoundsException {
int c=readFast();
if (c < 0) {
throw new IndexOutOfBoundsException();
}
return (c != 0);
}
/**
* Reads a signed byte (one byte) value from the buffer. Reads values that
* were written using {@link TupleOutput#writeByte}.
* @return the value read from the buffer.
* @throws IndexOutOfBoundsExceptionif not enough bytes are available in the buffer.
*/
public final byte readByte() throws IndexOutOfBoundsException {
return (byte)(readUnsignedByte() ^ 0x80);
}
/**
* Reads a signed short (two byte) value from the buffer. Reads values that
* were written using {@link TupleOutput#writeShort}.
* @return the value read from the buffer.
* @throws IndexOutOfBoundsExceptionif not enough bytes are available in the buffer.
*/
public final short readShort() throws IndexOutOfBoundsException {
return (short)(readUnsignedShort() ^ 0x8000);
}
/**
* Reads a signed int (four byte) value from the buffer. Reads values that
* were written using {@link TupleOutput#writeInt}.
* @return the value read from the buffer.
* @throws IndexOutOfBoundsExceptionif not enough bytes are available in the buffer.
*/
public final int readInt() throws IndexOutOfBoundsException {
return (int)(readUnsignedInt() ^ 0x80000000);
}
/**
* Reads a signed long (eight byte) value from the buffer. Reads values that
* were written using {@link TupleOutput#writeLong}.
* @return the value read from the buffer.
* @throws IndexOutOfBoundsExceptionif not enough bytes are available in the buffer.
*/
public final long readLong() throws IndexOutOfBoundsException {
return readUnsignedLong() ^ 0x8000000000000000L;
}
/**
* Reads a signed float (four byte) value from the buffer. Reads values that
* were written using {@link TupleOutput#writeFloat}.
* <code>Float.intBitsToFloat</code> is used to convert the signed int
* value.
* @return the value read from the buffer.
* @throws IndexOutOfBoundsExceptionif 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}.
* <code>Double.longBitsToDouble</code> is used to convert the signed long
* value.
* @return the value read from the buffer.
* @throws IndexOutOfBoundsExceptionif not enough bytes are available in the buffer.
*/
public final double readDouble() throws IndexOutOfBoundsException {
return Double.longBitsToDouble(readUnsignedLong());
}
/**
* 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 IndexOutOfBoundsExceptionif 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 IndexOutOfBoundsExceptionif 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);
}
/**
* 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 IndexOutOfBoundsExceptionif 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 lengthis the number of bytes to be read.
* @return the value read from the buffer.
* @throws IndexOutOfBoundsExceptionif 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 lengthis the number of characters to be read.
* @return the value read from the buffer.
* @throws IndexOutOfBoundsExceptionif 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 charsis the array to receive the data and whose length is used to
* determine the number of bytes to be read.
* @throws IndexOutOfBoundsExceptionif 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 charsis the array to receive the data and whose length is used to
* determine the number of characters to be read.
* @throws IndexOutOfBoundsExceptionif 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 lengthis the number of characters to be read.
* @return the converted string.
* @throws IndexOutOfBoundsExceptionif no null terminating byte is found in the buffer.
* @throws IllegalArgumentExceptionmalformed 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 charsis the array to receive the data and whose length is used to
* determine the number of characters to be read.
* @throws IndexOutOfBoundsExceptionif no null terminating byte is found in the buffer.
* @throws IllegalArgumentExceptionmalformed UTF data is encountered.
*/
public final void readString( char[] chars) throws IndexOutOfBoundsException, IllegalArgumentException {
byte[] buf=getBufferBytes();
off=UtfOps.bytesToChars(buf,off,chars,0,chars.length,false);
}
}