blob: 428dedcf3a0e56fc6166b51c9d38e0eb0ff6da72 [file] [log] [blame]
/*-
* Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
*
* This file was distributed by Oracle as part of a version of Oracle Berkeley
* DB Java Edition made available at:
*
* http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
*
* Please see the LICENSE file included in the top-level directory of the
* appropriate version of Oracle Berkeley DB Java Edition for a copy of the
* license and additional information.
*/
package com.sleepycat.bind.tuple;
import java.math.BigDecimal;
import java.math.BigInteger;
import com.sleepycat.util.FastInputStream;
import com.sleepycat.util.PackedInteger;
import com.sleepycat.util.UtfOps;
/**
* 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>
*
* @see <a href="package-summary.html#formats">Tuple Formats</a>
*
* @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 buffer is 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 buffer is the byte array to be read and should contain data in
* tuple format.
*
* @param offset is the byte offset at which to begin reading.
*
* @param length is 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 output is the tuple output object containing the data to be read.
*/
public TupleInput(TupleOutput output) {
super(output.getBufferBytes(), output.getBufferOffset(),
output.getBufferLength());
}
// --- begin DataInput compatible methods ---
/**
* 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 IndexOutOfBoundsException if no null terminating byte is found
* in the buffer.
*
* @throws IllegalArgumentException malformed UTF data is encountered.
*
* @see <a href="package-summary.html#stringFormats">String Formats</a>
*/
public final String readString()
throws IndexOutOfBoundsException, IllegalArgumentException {
byte[] myBuf = buf;
int myOff = off;
if (available() >= 2 &&
myBuf[myOff] == TupleOutput.NULL_STRING_UTF_VALUE &&
myBuf[myOff + 1] == 0) {
skip(2);
return null;
} else {
int byteLen = UtfOps.getZeroTerminatedByteLength(myBuf, myOff);
skip(byteLen + 1);
return UtfOps.bytesToString(myBuf, myOff, 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 IndexOutOfBoundsException if not enough bytes are available in
* the buffer.
*
* @see <a href="package-summary.html#integerFormats">Integer Formats</a>
*/
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 IndexOutOfBoundsException if not enough bytes are available in
* the buffer.
*
* @see <a href="package-summary.html#integerFormats">Integer Formats</a>
*/
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 IndexOutOfBoundsException if not enough bytes are available in
* the buffer.
*
* @see <a href="package-summary.html#integerFormats">Integer Formats</a>
*/
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 IndexOutOfBoundsException if not enough bytes are available in
* the buffer.
*
* @see <a href="package-summary.html#integerFormats">Integer Formats</a>
*/
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 IndexOutOfBoundsException if not enough bytes are available in
* the buffer.
*
* @see <a href="package-summary.html#integerFormats">Integer Formats</a>
*/
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 IndexOutOfBoundsException if not enough bytes are available in
* the buffer.
*
* @see <a href="package-summary.html#integerFormats">Integer Formats</a>
*/
public final long readLong()
throws IndexOutOfBoundsException {
return readUnsignedLong() ^ 0x8000000000000000L;
}
/**
* Reads an unsorted float (four byte) value from the buffer.
* Reads values that were written using {@link TupleOutput#writeFloat}.
*
* @return the value read from the buffer.
*
* @throws IndexOutOfBoundsException if not enough bytes are available in
* the buffer.
*
* @see <a href="package-summary.html#floatFormats">Floating Point
* Formats</a>
*/
public final float readFloat()
throws IndexOutOfBoundsException {
return Float.intBitsToFloat((int) readUnsignedInt());
}
/**
* Reads an unsorted double (eight byte) value from the buffer.
* Reads values that were written using {@link TupleOutput#writeDouble}.
*
* @return the value read from the buffer.
*
* @throws IndexOutOfBoundsException if not enough bytes are available in
* the buffer.
*
* @see <a href="package-summary.html#floatFormats">Floating Point
* Formats</a>
*/
public final double readDouble()
throws IndexOutOfBoundsException {
return Double.longBitsToDouble(readUnsignedLong());
}
/**
* Reads a sorted float (four byte) value from the buffer.
* Reads values that were written using {@link
* TupleOutput#writeSortedFloat}.
*
* @return the value read from the buffer.
*
* @throws IndexOutOfBoundsException if not enough bytes are available in
* the buffer.
*
* @see <a href="package-summary.html#floatFormats">Floating Point
* Formats</a>
*/
public final float readSortedFloat()
throws IndexOutOfBoundsException {
int val = (int) readUnsignedInt();
val ^= (val < 0) ? 0x80000000 : 0xffffffff;
return Float.intBitsToFloat(val);
}
/**
* Reads a sorted double (eight byte) value from the buffer.
* Reads values that were written using {@link
* TupleOutput#writeSortedDouble}.
*
* @return the value read from the buffer.
*
* @throws IndexOutOfBoundsException if not enough bytes are available in
* the buffer.
*
* @see <a href="package-summary.html#floatFormats">Floating Point
* Formats</a>
*/
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.
*
* @see <a href="package-summary.html#integerFormats">Integer Formats</a>
*/
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.
*
* @see <a href="package-summary.html#integerFormats">Integer Formats</a>
*/
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.
*
* @see <a href="package-summary.html#integerFormats">Integer Formats</a>
*/
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}.
*
* @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.
*
* @see <a href="package-summary.html#integerFormats">Integer Formats</a>
*/
public final String readBytes(int length)
throws IndexOutOfBoundsException {
StringBuilder buf = new StringBuilder(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.
*
* @see <a href="package-summary.html#integerFormats">Integer Formats</a>
*/
public final String readChars(int length)
throws IndexOutOfBoundsException {
StringBuilder buf = new StringBuilder(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}.
*
* @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.
*
* @see <a href="package-summary.html#integerFormats">Integer Formats</a>
*/
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.
*
* @see <a href="package-summary.html#integerFormats">Integer Formats</a>
*/
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.
*
* @see <a href="package-summary.html#stringFormats">String Formats</a>
*/
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.
*
* @see <a href="package-summary.html#stringFormats">String Formats</a>
*/
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)}.
*
* @return the byte length.
*
* @throws IndexOutOfBoundsException if no null terminating byte is found
* in the buffer.
*
* @throws IllegalArgumentException malformed UTF data is encountered.
*
* @see <a href="package-summary.html#stringFormats">String Formats</a>
*/
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 an unsorted packed integer.
*
* @return the int value.
*
* @see <a href="package-summary.html#integerFormats">Integer Formats</a>
*/
public final 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.
*
* @return the byte length.
*
* @see <a href="package-summary.html#integerFormats">Integer Formats</a>
*/
public final int getPackedIntByteLength() {
return PackedInteger.getReadIntLength(buf, off);
}
/**
* Reads an unsorted packed long integer.
*
* @return the long value.
*
* @see <a href="package-summary.html#integerFormats">Integer Formats</a>
*/
public final long readPackedLong() {
int len = PackedInteger.getReadLongLength(buf, off);
long val = PackedInteger.readLong(buf, off);
off += len;
return val;
}
/**
* Returns the byte length of a packed long integer.
*
* @return the byte length.
*
* @see <a href="package-summary.html#integerFormats">Integer Formats</a>
*/
public final int getPackedLongByteLength() {
return PackedInteger.getReadLongLength(buf, off);
}
/**
* Reads a sorted packed integer.
*
* @return the int value.
*
* @see <a href="package-summary.html#integerFormats">Integer Formats</a>
*/
public final int readSortedPackedInt() {
int len = PackedInteger.getReadSortedIntLength(buf, off);
int val = PackedInteger.readSortedInt(buf, off);
off += len;
return val;
}
/**
* Returns the byte length of a sorted packed integer.
*
* @return the byte length.
*
* @see <a href="package-summary.html#integerFormats">Integer Formats</a>
*/
public final int getSortedPackedIntByteLength() {
return PackedInteger.getReadSortedIntLength(buf, off);
}
/**
* Reads a sorted packed long integer.
*
* @return the long value.
*
* @see <a href="package-summary.html#integerFormats">Integer Formats</a>
*/
public final long readSortedPackedLong() {
int len = PackedInteger.getReadSortedLongLength(buf, off);
long val = PackedInteger.readSortedLong(buf, off);
off += len;
return val;
}
/**
* Returns the byte length of a sorted packed long integer.
*
* @return the byte length.
*
* @see <a href="package-summary.html#integerFormats">Integer Formats</a>
*/
public final int getSortedPackedLongByteLength() {
return PackedInteger.getReadSortedLongLength(buf, off);
}
/**
* Reads a {@code BigInteger}.
*
* @return the non-null BigInteger value.
*
* @see <a href="package-summary.html#integerFormats">Integer Formats</a>
*/
public final BigInteger readBigInteger() {
int len = readShort();
if (len < 0) {
len = (- len);
}
byte[] a = new byte[len];
a[0] = readByte();
readFast(a, 1, a.length - 1);
return new BigInteger(a);
}
/**
* Returns the byte length of a {@code BigInteger}.
*
* @return the byte length.
*
* @see <a href="package-summary.html#integerFormats">Integer Formats</a>
*/
public final int getBigIntegerByteLength() {
int saveOff = off;
int len = readShort();
off = saveOff;
if (len < 0) {
len = (- len);
}
return len + 2;
}
/**
* Reads an unsorted {@code BigDecimal}.
*
* @return the non-null BigDecimal value.
*
* @see <a href="package-summary.html#bigDecimalFormats">BigDecimal
* Formats</a>
*/
public final BigDecimal readBigDecimal() {
int scale = readPackedInt();
int len = readPackedInt();
byte[] a = new byte[len];
readFast(a, 0, len);
BigInteger unscaledVal = new BigInteger(a);
return new BigDecimal(unscaledVal, scale);
}
/**
* Returns the byte length of an unsorted {@code BigDecimal}.
*
* @return the byte length.
*
* @see <a href="package-summary.html#bigDecimalFormats">BigDecimal
* Formats</a>
*/
public final int getBigDecimalByteLength() {
/* First get the length of the scale. */
int scaleLen = getPackedIntByteLength();
int saveOff = off;
off += scaleLen;
/*
* Then get the length of the value which store the length of the
* following bytes.
*/
int lenOfUnscaleValLen = getPackedIntByteLength();
/* Finally get the length of the following bytes. */
int unscaledValLen = readPackedInt();
off = saveOff;
return scaleLen + lenOfUnscaleValLen + unscaledValLen;
}
/**
* Reads a sorted {@code BigDecimal}, with support for correct default
* sorting.
*
* @return the non-null BigDecimal value.
*
* @see <a href="package-summary.html#bigDecimalFormats">BigDecimal
* Formats</a>
*/
public final BigDecimal readSortedBigDecimal() {
/* Get the sign of the BigDecimal. */
int sign = readByte();
/* Get the exponent of the BigDecimal. */
int exponent = readSortedPackedInt();
/*Get the normalized BigDecimal. */
BigDecimal normalizedVal = readSortedNormalizedBigDecimal();
/*
* After getting the normalized BigDecimal, we need to scale the value
* with the exponent.
*/
return normalizedVal.scaleByPowerOfTen(exponent * sign);
}
/**
* Reads a sorted {@code BigDecimal} in normalized format with a single
* digit to the left of the decimal point.
*/
private BigDecimal readSortedNormalizedBigDecimal() {
StringBuilder valStr = new StringBuilder(32);
int subVal = readSortedPackedInt();
int sign = subVal < 0 ? -1 : 1;
/* Read through the buf, until we meet the terminator byte. */
while (subVal != -1) {
/* Adjust the sub-value back to the original. */
subVal = subVal < 0 ? subVal + 1 : subVal;
String groupDigits = Integer.toString(Math.abs(subVal));
/*
* subVal < 100000000 means some leading zeros have been removed,
* we have to add them back.
*/
if (groupDigits.length() < 9) {
final int insertLen = 9 - groupDigits.length();
for (int i = 0; i < insertLen; i++) {
valStr.append("0");
}
}
valStr.append(groupDigits);
subVal = readSortedPackedInt();
}
BigInteger digitsVal = new BigInteger(valStr.toString());
if (sign < 0) {
digitsVal = digitsVal.negate();
}
/* The normalized decimal has 1 digits in the int part. */
int scale = valStr.length() - 1;
/*
* Since we may pad trailing zeros for serialization, when doing
* de-serialization, we need to delete the trailing zeros.
*/
return new BigDecimal(digitsVal, scale).stripTrailingZeros();
}
/**
* Returns the byte length of a sorted {@code BigDecimal}.
*
* @return the byte length.
*
* @see <a href="package-summary.html#bigDecimalFormats">BigDecimal
* Formats</a>
*/
public final int getSortedBigDecimalByteLength() {
/* Save the original position, and read past the sigh byte. */
int saveOff = off++;
/* Get the length of the exponent. */
int len = getSortedPackedIntByteLength(); /* the exponent */
/* Skip to the digit part. */
off += len;
/*
* Travel through the following SortedPackedIntegers, until we meet the
* terminator byte.
*/
int subVal = readSortedPackedInt();
while (subVal != -1) {
subVal = readSortedPackedInt();
}
/*
* off is the value of end offset, while saveOff is the beginning
* offset.
*/
len = off - saveOff;
off = saveOff;
return len;
}
}