2013-02-19 18:40:27 +11:00
|
|
|
/*-
|
2015-01-04 14:07:56 -05:00
|
|
|
* Public Domain 2014-2015 MongoDB, Inc.
|
2014-01-07 10:30:12 -05:00
|
|
|
* Public Domain 2008-2014 WiredTiger, Inc.
|
2013-02-19 18:40:27 +11:00
|
|
|
*
|
2014-01-07 10:30:12 -05:00
|
|
|
* This is free and unencumbered software released into the public domain.
|
|
|
|
|
*
|
|
|
|
|
* Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
|
|
|
* distribute this software, either in source code form or as a compiled
|
|
|
|
|
* binary, for any purpose, commercial or non-commercial, and by any
|
|
|
|
|
* means.
|
|
|
|
|
*
|
|
|
|
|
* In jurisdictions that recognize copyright laws, the author or authors
|
|
|
|
|
* of this software dedicate any and all copyright interest in the
|
|
|
|
|
* software to the public domain. We make this dedication for the benefit
|
|
|
|
|
* of the public at large and to the detriment of our heirs and
|
|
|
|
|
* successors. We intend this dedication to be an overt act of
|
|
|
|
|
* relinquishment in perpetuity of all present and future rights to this
|
|
|
|
|
* software under copyright law.
|
|
|
|
|
*
|
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
|
|
|
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
|
|
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
|
|
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
|
|
|
* OTHER DEALINGS IN THE SOFTWARE.
|
2013-02-19 18:40:27 +11:00
|
|
|
*/
|
2013-02-08 14:15:19 +11:00
|
|
|
package com.wiredtiger.db;
|
|
|
|
|
|
2013-02-12 21:00:49 +11:00
|
|
|
import java.io.ByteArrayInputStream;
|
2013-02-08 14:15:19 +11:00
|
|
|
import java.lang.StringBuffer;
|
2013-02-12 21:00:49 +11:00
|
|
|
import com.wiredtiger.db.PackUtil;
|
2013-02-08 14:15:19 +11:00
|
|
|
import com.wiredtiger.db.WiredTigerPackingException;
|
|
|
|
|
|
2013-02-19 17:06:41 +11:00
|
|
|
/**
|
|
|
|
|
* An internal helper class for decoding WiredTiger packed values.
|
|
|
|
|
*
|
|
|
|
|
* Applications should not need to use this class.
|
|
|
|
|
*/
|
2013-02-08 14:15:19 +11:00
|
|
|
public class PackInputStream {
|
|
|
|
|
|
2013-02-12 21:00:49 +11:00
|
|
|
protected PackFormatInputStream format;
|
|
|
|
|
protected byte[] value;
|
|
|
|
|
protected int valueOff;
|
|
|
|
|
protected int valueLen;
|
2013-02-08 14:15:19 +11:00
|
|
|
|
2013-02-19 17:06:41 +11:00
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
2013-02-12 21:00:49 +11:00
|
|
|
public PackInputStream(String format, byte[] value) {
|
|
|
|
|
this(format, value, 0, value.length);
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-19 17:06:41 +11:00
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
2013-02-12 21:00:49 +11:00
|
|
|
public PackInputStream(String format, byte[] value, int off, int len) {
|
|
|
|
|
this.format = new PackFormatInputStream(format);
|
|
|
|
|
this.value = value;
|
|
|
|
|
this.valueOff = off;
|
|
|
|
|
this.valueLen = len;
|
2013-02-08 14:15:19 +11:00
|
|
|
}
|
|
|
|
|
|
2013-02-19 17:06:41 +11:00
|
|
|
/**
|
|
|
|
|
* Returns the raw packing format string.
|
|
|
|
|
*/
|
2013-02-08 14:15:19 +11:00
|
|
|
public String getFormat() {
|
|
|
|
|
return format.toString();
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-19 17:06:41 +11:00
|
|
|
/**
|
|
|
|
|
* Returns the raw value byte array.
|
|
|
|
|
*/
|
2013-02-08 14:15:19 +11:00
|
|
|
public byte[] getValue() {
|
2013-02-12 21:00:49 +11:00
|
|
|
return value;
|
2013-02-08 14:15:19 +11:00
|
|
|
}
|
|
|
|
|
|
2013-02-19 17:06:41 +11:00
|
|
|
/**
|
|
|
|
|
* Retrieves a byte field from the stream.
|
|
|
|
|
*/
|
|
|
|
|
public byte getByte()
|
2013-02-12 21:00:49 +11:00
|
|
|
throws WiredTigerPackingException {
|
2013-02-19 17:06:41 +11:00
|
|
|
format.checkType('b', false);
|
|
|
|
|
format.consume();
|
2013-02-12 21:00:49 +11:00
|
|
|
return (byte)(value[valueOff++] - 0x80);
|
2013-02-08 14:15:19 +11:00
|
|
|
}
|
|
|
|
|
|
2013-02-19 17:06:41 +11:00
|
|
|
/**
|
|
|
|
|
* 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)
|
2013-02-12 21:00:49 +11:00
|
|
|
throws WiredTigerPackingException {
|
2013-02-19 17:06:41 +11:00
|
|
|
this.getByteArray(dest, 0, dest.length);
|
2013-02-08 14:15:19 +11:00
|
|
|
}
|
|
|
|
|
|
2013-02-19 17:06:41 +11:00
|
|
|
/**
|
|
|
|
|
* 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)
|
2013-02-12 21:00:49 +11:00
|
|
|
throws WiredTigerPackingException {
|
2013-02-19 17:06:41 +11:00
|
|
|
format.checkType('U', false);
|
|
|
|
|
getByteArrayInternal(getByteArrayLength(), dest, off, len);
|
2013-02-12 21:00:49 +11:00
|
|
|
|
2013-02-08 14:15:19 +11:00
|
|
|
}
|
|
|
|
|
|
2013-02-19 17:06:41 +11:00
|
|
|
/**
|
|
|
|
|
* 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()
|
2013-02-12 21:00:49 +11:00
|
|
|
throws WiredTigerPackingException {
|
2013-02-19 17:06:41 +11:00
|
|
|
int itemLen = getByteArrayLength();
|
2013-02-12 21:00:49 +11:00
|
|
|
byte[] unpacked = new byte[itemLen];
|
2013-02-19 17:06:41 +11:00
|
|
|
getByteArrayInternal(itemLen, unpacked, 0, itemLen);
|
2013-02-12 21:00:49 +11:00
|
|
|
return unpacked;
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-19 17:06:41 +11:00
|
|
|
/**
|
|
|
|
|
* 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()
|
2013-02-12 21:00:49 +11:00
|
|
|
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;
|
2013-02-08 14:15:19 +11:00
|
|
|
}
|
|
|
|
|
|
2013-02-19 17:06:41 +11:00
|
|
|
/**
|
|
|
|
|
* Do the work of retrieving a byte array.
|
|
|
|
|
*/
|
|
|
|
|
private void getByteArrayInternal(
|
2013-02-12 21:00:49 +11:00
|
|
|
int itemLen, byte[] dest, int off, int destLen)
|
|
|
|
|
throws WiredTigerPackingException {
|
2013-02-08 14:15:19 +11:00
|
|
|
/* TODO: padding. */
|
2013-02-12 21:00:49 +11:00
|
|
|
int copyLen = itemLen;
|
2013-02-19 18:40:27 +11:00
|
|
|
if (itemLen > destLen) {
|
2013-02-12 21:00:49 +11:00
|
|
|
copyLen = destLen;
|
2013-02-19 18:40:27 +11:00
|
|
|
}
|
2013-02-19 17:06:41 +11:00
|
|
|
format.consume();
|
2013-02-12 21:00:49 +11:00
|
|
|
System.arraycopy(value, valueOff, dest, off, copyLen);
|
|
|
|
|
valueOff += itemLen;
|
2013-02-08 14:15:19 +11:00
|
|
|
}
|
|
|
|
|
|
2013-02-19 17:06:41 +11:00
|
|
|
/**
|
|
|
|
|
* Retrieves an integer field from the stream.
|
|
|
|
|
*/
|
|
|
|
|
public int getInt()
|
2013-02-12 21:00:49 +11:00
|
|
|
throws WiredTigerPackingException {
|
|
|
|
|
boolean signed = false;
|
2013-02-19 17:06:41 +11:00
|
|
|
format.checkType('i', false);
|
|
|
|
|
if (format.getType() == 'I' ||
|
2013-02-19 18:40:27 +11:00
|
|
|
format.getType() == 'L') {
|
2013-02-12 21:00:49 +11:00
|
|
|
signed = true;
|
2013-02-19 18:40:27 +11:00
|
|
|
}
|
2013-02-19 17:06:41 +11:00
|
|
|
format.consume();
|
2013-02-12 21:00:49 +11:00
|
|
|
return unpackInt(signed);
|
2013-02-08 14:15:19 +11:00
|
|
|
}
|
|
|
|
|
|
2013-02-19 17:06:41 +11:00
|
|
|
/**
|
|
|
|
|
* Retrieves a long field from the stream.
|
|
|
|
|
*/
|
|
|
|
|
public long getLong()
|
2013-02-12 21:00:49 +11:00
|
|
|
throws WiredTigerPackingException {
|
|
|
|
|
boolean signed = false;
|
2013-02-19 17:06:41 +11:00
|
|
|
format.checkType('q', false);
|
2013-02-19 18:40:27 +11:00
|
|
|
if (format.getType() == 'Q') {
|
2013-02-12 21:00:49 +11:00
|
|
|
signed = true;
|
2013-02-19 18:40:27 +11:00
|
|
|
}
|
2013-02-19 17:06:41 +11:00
|
|
|
format.consume();
|
2013-02-12 21:00:49 +11:00
|
|
|
return unpackLong(signed);
|
2013-02-08 14:15:19 +11:00
|
|
|
}
|
|
|
|
|
|
2013-02-19 17:06:41 +11:00
|
|
|
/**
|
|
|
|
|
* Retrieves a record field from the stream.
|
|
|
|
|
*/
|
|
|
|
|
public long getRecord()
|
2013-02-12 21:00:49 +11:00
|
|
|
throws WiredTigerPackingException {
|
2013-02-19 17:06:41 +11:00
|
|
|
format.checkType('r', false);
|
|
|
|
|
format.consume();
|
2013-02-12 21:00:49 +11:00
|
|
|
return unpackLong(false);
|
2013-02-08 14:15:19 +11:00
|
|
|
}
|
|
|
|
|
|
2013-02-19 17:06:41 +11:00
|
|
|
/**
|
|
|
|
|
* Retrieves a short field from the stream.
|
|
|
|
|
*/
|
|
|
|
|
public short getShort()
|
2013-02-12 21:00:49 +11:00
|
|
|
throws WiredTigerPackingException {
|
|
|
|
|
boolean signed = false;
|
2013-02-19 17:06:41 +11:00
|
|
|
format.checkType('h', false);
|
2013-02-19 18:40:27 +11:00
|
|
|
if (format.getType() == 'H') {
|
2013-02-12 21:00:49 +11:00
|
|
|
signed = true;
|
2013-02-19 18:40:27 +11:00
|
|
|
}
|
2013-02-19 17:06:41 +11:00
|
|
|
format.consume();
|
2013-02-12 21:00:49 +11:00
|
|
|
return unpackShort(signed);
|
2013-02-08 14:15:19 +11:00
|
|
|
}
|
|
|
|
|
|
2013-02-19 17:06:41 +11:00
|
|
|
/**
|
|
|
|
|
* Retrieves a string field from the stream.
|
|
|
|
|
*/
|
|
|
|
|
public String getString()
|
2013-02-12 21:00:49 +11:00
|
|
|
throws WiredTigerPackingException {
|
|
|
|
|
int stringLength = 0;
|
2015-01-09 09:54:21 -05:00
|
|
|
int skipnull = 0;
|
2013-02-19 17:06:41 +11:00
|
|
|
format.checkType('S', false);
|
2013-02-12 21:00:49 +11:00
|
|
|
// Get the length for a fixed length string
|
2013-02-19 17:06:41 +11:00
|
|
|
if (format.getType() != 'S') {
|
2013-02-12 21:00:49 +11:00
|
|
|
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++) {}
|
2015-01-09 09:54:21 -05:00
|
|
|
skipnull = 1;
|
2013-02-08 14:15:19 +11:00
|
|
|
}
|
2013-02-19 17:06:41 +11:00
|
|
|
format.consume();
|
2013-02-12 21:00:49 +11:00
|
|
|
String result = new String(value, valueOff, stringLength);
|
2015-01-09 09:54:21 -05:00
|
|
|
valueOff += stringLength + skipnull;
|
2013-02-12 21:00:49 +11:00
|
|
|
return result;
|
2013-02-08 14:15:19 +11:00
|
|
|
}
|
|
|
|
|
|
2013-02-19 17:06:41 +11:00
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
2013-02-12 21:00:49 +11:00
|
|
|
private short unpackShort(boolean signed)
|
|
|
|
|
throws WiredTigerPackingException {
|
|
|
|
|
long ret = unpackLong(true);
|
2015-01-09 09:54:21 -05:00
|
|
|
if ((signed && (ret > Short.MAX_VALUE || ret < Short.MIN_VALUE)) ||
|
2013-02-19 18:40:27 +11:00
|
|
|
(!signed && (short)ret < 0)) {
|
2013-02-12 21:00:49 +11:00
|
|
|
throw new WiredTigerPackingException("Overflow unpacking short.");
|
2013-02-19 18:40:27 +11:00
|
|
|
}
|
2013-02-12 21:00:49 +11:00
|
|
|
return (short)ret;
|
|
|
|
|
}
|
2013-02-08 14:15:19 +11:00
|
|
|
|
2013-02-19 17:06:41 +11:00
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
2013-02-12 21:00:49 +11:00
|
|
|
private int unpackInt(boolean signed)
|
|
|
|
|
throws WiredTigerPackingException {
|
|
|
|
|
long ret = unpackLong(true);
|
2015-01-09 09:54:21 -05:00
|
|
|
if ((signed && (ret > Integer.MAX_VALUE || ret < Integer.MIN_VALUE)) ||
|
2013-02-19 18:40:27 +11:00
|
|
|
(!signed && (int)ret < 0)) {
|
2013-02-12 21:00:49 +11:00
|
|
|
throw new WiredTigerPackingException("Overflow unpacking integer.");
|
2013-02-19 18:40:27 +11:00
|
|
|
}
|
2013-02-12 21:00:49 +11:00
|
|
|
return (int)ret;
|
|
|
|
|
}
|
2013-02-08 14:15:19 +11:00
|
|
|
|
2013-02-19 17:06:41 +11:00
|
|
|
/**
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
2013-02-12 21:00:49 +11:00
|
|
|
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);
|
|
|
|
|
|
2013-02-19 18:40:27 +11:00
|
|
|
for (unpacked = 0xffffffff; len != 0; --len) {
|
2013-02-12 21:00:49 +11:00
|
|
|
unpacked = (unpacked << 8) | value[valueOff++] & 0xff;
|
2013-02-19 18:40:27 +11:00
|
|
|
}
|
2013-02-12 21:00:49 +11:00
|
|
|
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);
|
|
|
|
|
|
2013-02-19 18:40:27 +11:00
|
|
|
for (unpacked = 0; len != 0; --len) {
|
2013-02-12 21:00:49 +11:00
|
|
|
unpacked = (unpacked << 8) | value[valueOff++] & 0xff;
|
2013-02-19 18:40:27 +11:00
|
|
|
}
|
2013-02-12 21:00:49 +11:00
|
|
|
unpacked += PackUtil.POS_2BYTE_MAX + 1;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
throw new WiredTigerPackingException(
|
|
|
|
|
"Error decoding packed value.");
|
2013-02-08 14:15:19 +11:00
|
|
|
}
|
2013-02-12 21:00:49 +11:00
|
|
|
// Check for overflow if decoding an unsigned value - since Java only
|
|
|
|
|
// supports signed values.
|
2013-02-19 18:40:27 +11:00
|
|
|
if (!signed && unpacked < 0) {
|
2013-02-12 21:00:49 +11:00
|
|
|
throw new WiredTigerPackingException("Overflow unpacking long.");
|
2013-02-19 18:40:27 +11:00
|
|
|
}
|
2013-02-12 21:00:49 +11:00
|
|
|
|
|
|
|
|
return (unpacked);
|
2013-02-08 14:15:19 +11:00
|
|
|
}
|
|
|
|
|
}
|
2013-02-12 21:00:49 +11:00
|
|
|
|