Files
mongo/lang/java/src/com/wiredtiger/db/PackInputStream.java

306 lines
10 KiB
Java
Raw Normal View History

package com.wiredtiger.db;
import java.io.ByteArrayInputStream;
import java.lang.StringBuffer;
import com.wiredtiger.db.PackUtil;
import com.wiredtiger.db.WiredTigerPackingException;
/**
* An internal helper class for decoding WiredTiger packed values.
*
* Applications should not need to use this class.
*/
public class PackInputStream {
protected PackFormatInputStream format;
protected byte[] value;
protected int valueOff;
protected int valueLen;
/**
* Constructor.
*
* \param format A String that contains the WiredTiger format that
* defines the layout of this packed value.
* \param value The raw bytes that back the stream.
*/
public PackInputStream(String format, byte[] value) {
this(format, value, 0, value.length);
}
/**
* Constructor.
*
* \param format A String that contains the WiredTiger format that
* defines the layout of this packed value.
* \param value The raw bytes that back the stream.
* \param off Offset into the value array at which the stream begins.
* \param len Length of the value array that forms the stream.
*/
public PackInputStream(String format, byte[] value, int off, int len) {
this.format = new PackFormatInputStream(format);
this.value = value;
this.valueOff = off;
this.valueLen = len;
}
/**
* Returns the raw packing format string.
*/
public String getFormat() {
return format.toString();
}
/**
* Returns the raw value byte array.
*/
public byte[] getValue() {
return value;
}
/**
* Retrieves a byte field from the stream.
*/
public byte getByte()
throws WiredTigerPackingException {
format.checkType('b', false);
format.consume();
return (byte)(value[valueOff++] - 0x80);
}
/**
* Retrieves a byte array field from the stream.
*
* \param dest The byte array where the returned value will be stored. The
* array should be large enough to store the entire data item,
* if it is not, a truncated value will be returned.
*/
public void getByteArray(byte[] dest)
throws WiredTigerPackingException {
this.getByteArray(dest, 0, dest.length);
}
/**
* Retrieves a byte array field from the stream.
*
* \param dest The byte array where the returned value will be stored.
* \param off Offset into the destination buffer to start copying into.
* \param len The length should be large enough to store the entire data
* item, if it is not, a truncated value will be returned.
*/
public void getByteArray(byte[] dest, int off, int len)
throws WiredTigerPackingException {
format.checkType('U', false);
getByteArrayInternal(getByteArrayLength(), dest, off, len);
}
/**
* Retrieves a byte array field from the stream. Creates a new byte array
* that is the size of the object being retrieved.
*/
public byte[] getByteArray()
throws WiredTigerPackingException {
int itemLen = getByteArrayLength();
byte[] unpacked = new byte[itemLen];
getByteArrayInternal(itemLen, unpacked, 0, itemLen);
return unpacked;
}
/**
* Finds the length of a byte array. Either by decoding the length from
* the format or using the remaining size of the stream.
*/
private int getByteArrayLength()
throws WiredTigerPackingException {
int itemLen = 0;
/* The rest of the buffer is a byte array. */
if (format.available() == 1) {
itemLen = valueLen - valueOff;
} else {
itemLen = unpackInt(false);
}
return itemLen;
}
/**
* Do the work of retrieving a byte array.
*/
private void getByteArrayInternal(
int itemLen, byte[] dest, int off, int destLen)
throws WiredTigerPackingException {
/* TODO: padding. */
int copyLen = itemLen;
if (itemLen > destLen)
copyLen = destLen;
format.consume();
System.arraycopy(value, valueOff, dest, off, copyLen);
valueOff += itemLen;
}
/**
* Retrieves an integer field from the stream.
*/
public int getInt()
throws WiredTigerPackingException {
boolean signed = false;
format.checkType('i', false);
if (format.getType() == 'I' ||
format.getType() == 'L')
signed = true;
format.consume();
return unpackInt(signed);
}
/**
* Retrieves a long field from the stream.
*/
public long getLong()
throws WiredTigerPackingException {
boolean signed = false;
format.checkType('q', false);
if (format.getType() == 'Q')
signed = true;
format.consume();
return unpackLong(signed);
}
/**
* Retrieves a record field from the stream.
*/
public long getRecord()
throws WiredTigerPackingException {
format.checkType('r', false);
format.consume();
return unpackLong(false);
}
/**
* Retrieves a short field from the stream.
*/
public short getShort()
throws WiredTigerPackingException {
boolean signed = false;
format.checkType('h', false);
if (format.getType() == 'H')
signed = true;
format.consume();
return unpackShort(signed);
}
/**
* Retrieves a string field from the stream.
*/
public String getString()
throws WiredTigerPackingException {
int stringLength = 0;
format.checkType('S', false);
// Get the length for a fixed length string
if (format.getType() != 'S') {
stringLength = format.getLengthFromFormat(true);
} else {
// The string is null terminated, but we need to know how many
// bytes are consumed - which won't necessarily match up to the
// string length.
for (; valueOff + stringLength < value.length &&
value[valueOff + stringLength] != 0; stringLength++) {}
}
format.consume();
String result = new String(value, valueOff, stringLength);
valueOff += stringLength + 1;
return result;
}
/**
* Decodes an encoded short from the stream. This method does bounds
* checking, to ensure values fit, since some values may be encoded as
* unsigned values, and Java types are all signed.
*/
private short unpackShort(boolean signed)
throws WiredTigerPackingException {
long ret = unpackLong(true);
if ((signed && (ret > Short.MAX_VALUE || ret > Short.MIN_VALUE)) ||
(!signed && (short)ret < 0))
throw new WiredTigerPackingException("Overflow unpacking short.");
return (short)ret;
}
/**
* Decodes an encoded integer from the stream. This method does bounds
* checking, to ensure values fit, since some values may be encoded as
* unsigned values, and Java types are all signed.
*/
private int unpackInt(boolean signed)
throws WiredTigerPackingException {
long ret = unpackLong(true);
if ((signed && (ret > Integer.MAX_VALUE || ret > Integer.MIN_VALUE)) ||
(!signed && (int)ret < 0))
throw new WiredTigerPackingException("Overflow unpacking integer.");
return (int)ret;
}
/**
* Decodes an encoded long from the stream. This method does bounds
* checking, to ensure values fit, since some values may be encoded as
* unsigned values, and Java types are all signed.
* The packing format is defined in the WiredTiger C integer packing
* implementation, which is at src/include/intpack.i
*/
private long unpackLong(boolean signed)
throws WiredTigerPackingException {
int len;
long unpacked = 0;
switch (value[valueOff] & 0xf0) {
case PackUtil.NEG_MULTI_MARKER & 0xff:
len = (int)PackUtil.SIZEOF_LONG - (value[valueOff++] & 0xf);
for (unpacked = 0xffffffff; len != 0; --len)
unpacked = (unpacked << 8) | value[valueOff++] & 0xff;
break;
case PackUtil.NEG_2BYTE_MARKER & 0xff:
case (PackUtil.NEG_2BYTE_MARKER | 0x10) & 0xff:
unpacked = PackUtil.GET_BITS(value[valueOff++], 5, 0) << 8;
unpacked |= value[valueOff++] & 0xff;
unpacked += PackUtil.NEG_2BYTE_MIN;
break;
case PackUtil.NEG_1BYTE_MARKER & 0xff:
case (PackUtil.NEG_1BYTE_MARKER | 0x10) & 0xff:
case (PackUtil.NEG_1BYTE_MARKER | 0x20) & 0xff:
case (PackUtil.NEG_1BYTE_MARKER | 0x30) & 0xff:
unpacked = PackUtil.NEG_1BYTE_MIN +
PackUtil.GET_BITS(value[valueOff++], 6, 0);
break;
case PackUtil.POS_1BYTE_MARKER & 0xff:
case (PackUtil.POS_1BYTE_MARKER | 0x10) & 0xff:
case (PackUtil.POS_1BYTE_MARKER | 0x20) & 0xff:
case (PackUtil.POS_1BYTE_MARKER | 0x30) & 0xff:
unpacked = PackUtil.GET_BITS(value[valueOff++], 6, 0);
break;
case PackUtil.POS_2BYTE_MARKER & 0xff:
case (PackUtil.POS_2BYTE_MARKER | 0x10) & 0xff:
unpacked = PackUtil.GET_BITS(value[valueOff++], 5, 0) << 8;
unpacked |= value[valueOff++] & 0xff;
unpacked += PackUtil.POS_1BYTE_MAX + 1;
break;
case PackUtil.POS_MULTI_MARKER & 0xff:
// There are four length bits in the first byte.
len = (value[valueOff++] & 0xf);
for (unpacked = 0; len != 0; --len)
unpacked = (unpacked << 8) | value[valueOff++] & 0xff;
unpacked += PackUtil.POS_2BYTE_MAX + 1;
break;
default:
throw new WiredTigerPackingException(
"Error decoding packed value.");
}
// Check for overflow if decoding an unsigned value - since Java only
// supports signed values.
if (!signed && unpacked < 0)
throw new WiredTigerPackingException("Overflow unpacking long.");
return (unpacked);
}
}