software.amazon.ion.impl.IonReaderBinaryRawX Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ion-java Show documentation
Show all versions of ion-java Show documentation
A Java implementation of the Amazon Ion data notation.
/*
* Copyright 2009-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at:
*
* http://aws.amazon.com/apache2.0/
*
* or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package software.amazon.ion.impl;
import static software.amazon.ion.SystemSymbols.ION_1_0_SID;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import software.amazon.ion.Decimal;
import software.amazon.ion.IonException;
import software.amazon.ion.IonReader;
import software.amazon.ion.IonType;
import software.amazon.ion.SymbolTable;
import software.amazon.ion.Timestamp;
import software.amazon.ion.Timestamp.Precision;
import software.amazon.ion.impl.PrivateScalarConversions.AS_TYPE;
import software.amazon.ion.impl.PrivateScalarConversions.ValueVariant;
import software.amazon.ion.impl.UnifiedSavePointManagerX.SavePoint;
/**
* low level reader, base class, for reading Ion binary
* input sources. This using the UnifiedInputStream just
* as the updated (july 2009) text reader does. The
* routines in this impl only include those needed to handle
* field id's, annotation ids', and access to the value
* headers. In particular hasNext, next, stepIn and stepOut
* are handled here.
*
* scalar values are handled by IonReaderBinarySystem and
* symbol tables (as well as field names and annotations as
* strings) are handled by IonBinaryReaderUser.
*/
abstract class IonReaderBinaryRawX
implements IonReader
{
static final int DEFAULT_CONTAINER_STACK_SIZE = 12; // a multiple of 3
static final int DEFAULT_ANNOTATION_SIZE = 10;
static final int NO_LIMIT = Integer.MIN_VALUE;
protected enum State {
S_INVALID,
S_BEFORE_FIELD, // only true in structs
S_BEFORE_TID,
S_BEFORE_VALUE,
S_AFTER_VALUE,
S_EOF
}
State _state;
UnifiedInputStreamX _input;
int _local_remaining;
boolean _eof;
boolean _has_next_needed;
ValueVariant _v;
IonType _value_type;
boolean _value_is_null;
boolean _value_is_true; // cached boolean value (since we step on the length)
/**
* {@link SymbolTable#UNKNOWN_SYMBOL_ID} means "not on a struct field"
* since otherwise we always know the SID.
*/
int _value_field_id;
int _value_tid;
int _value_len;
int _value_lob_remaining;
boolean _value_lob_is_ready;
long _position_start;
long _position_len;
SavePoint _annotations;
int[] _annotation_ids;
int _annotation_count;
// local stack for stepInto() and stepOut()
boolean _is_in_struct;
boolean _struct_is_ordered;
int _parent_tid;
int _container_top;
long[] _container_stack; // triples of: position, type, local_end
protected IonReaderBinaryRawX() {
}
/**
* @return This implementation always returns null.
*/
public T asFacet(Class facetType)
{
return null;
}
protected final void init_raw(UnifiedInputStreamX uis) {
_input = uis;
_container_stack = new long[DEFAULT_CONTAINER_STACK_SIZE];
_annotations = uis.savePointAllocate();
_v = new ValueVariant();
_annotation_ids = new int[DEFAULT_ANNOTATION_SIZE];
re_init_raw();
_position_start = -1;
}
final void re_init_raw() {
_local_remaining = NO_LIMIT;
_parent_tid = PrivateIonConstants.tidDATAGRAM;
_value_field_id = SymbolTable.UNKNOWN_SYMBOL_ID;
_state = State.S_BEFORE_TID; // this is where we always start
_has_next_needed = true;
_eof = false;
_value_type = null;
_value_is_null = false;
_value_is_true = false;
_value_len = 0;
_value_lob_remaining = 0;
_value_lob_is_ready = false;
_annotation_count = 0;
_is_in_struct = false;
_struct_is_ordered = false;
_parent_tid = 0;
_container_top = 0;
}
public void close()
throws IOException
{
_input.close();
}
static private final int POS_OFFSET = 0;
static private final int TYPE_LIMIT_OFFSET = 1;
static private final long TYPE_MASK = 0xffffffff;
static private final int LIMIT_SHIFT = 32;
static private final int POS_STACK_STEP = 2;
private final void push(int type, long position, int local_remaining)
{
int oldlen = _container_stack.length;
if ((_container_top + POS_STACK_STEP) >= oldlen) {
int newlen = oldlen * 2;
long[] temp = new long[newlen];
System.arraycopy(_container_stack, 0, temp, 0, oldlen);
_container_stack = temp;
}
_container_stack[_container_top + POS_OFFSET] = position;
long type_limit = local_remaining;
type_limit <<= LIMIT_SHIFT;
type_limit |= (type & TYPE_MASK);
_container_stack[_container_top + TYPE_LIMIT_OFFSET] = type_limit;
_container_top += POS_STACK_STEP;
}
private final long get_top_position() {
assert(_container_top > 0);
long pos = _container_stack[(_container_top - POS_STACK_STEP) + POS_OFFSET];
return pos;
}
private final int get_top_type() {
assert(_container_top > 0);
long type_limit = _container_stack[(_container_top - POS_STACK_STEP) + TYPE_LIMIT_OFFSET];
int type = (int)(type_limit & TYPE_MASK);
if (type < 0 || type > PrivateIonConstants.tidDATAGRAM) {
throwErrorAt("invalid type id in parent stack");
}
return type;
}
private final int get_top_local_remaining() {
assert(_container_top > 0);
long type_limit = _container_stack[_container_top - POS_STACK_STEP + TYPE_LIMIT_OFFSET];
int local_remaining = (int)((type_limit >> LIMIT_SHIFT) & TYPE_MASK);
return local_remaining;
}
private final void pop() {
assert(_container_top > 0);
_container_top -= POS_STACK_STEP;
}
boolean hasNext()
{
if (!_eof && _has_next_needed) {
try {
has_next_helper_raw();
}
catch (IOException e) {
error(e);
}
}
return !_eof;
}
public IonType next()
{
if (_eof) {
return null;
}
if (_has_next_needed) {
try {
has_next_helper_raw();
}
catch (IOException e) {
error(e);
}
}
_has_next_needed = true;
// this should only be null here if we're at eof
assert( _value_type != null || _eof == true);
return _value_type;
}
// from IonConstants
// public static final byte[] BINARY_VERSION_MARKER_1_0 =
// { (byte) 0xE0,
// (byte) 0x01,
// (byte) 0x00,
// (byte) 0xEA };
private static final int BINARY_VERSION_MARKER_TID = PrivateIonConstants.getTypeCode(PrivateIonConstants.BINARY_VERSION_MARKER_1_0[0] & 0xff);
private static final int BINARY_VERSION_MARKER_LEN = PrivateIonConstants.getLowNibble(PrivateIonConstants.BINARY_VERSION_MARKER_1_0[0] & 0xff);
private final void has_next_helper_raw() throws IOException
{
clear_value();
while (_value_tid == -1 && !_eof) {
switch (_state) {
case S_BEFORE_FIELD:
assert _value_field_id == SymbolTable.UNKNOWN_SYMBOL_ID;
_value_field_id = read_field_id();
if (_value_field_id == UnifiedInputStreamX.EOF) {
// FIXME why is EOF ever okay in the middle of a struct?
assert UnifiedInputStreamX.EOF == SymbolTable.UNKNOWN_SYMBOL_ID;
_eof = true;
break;
}
// fall through to try to read the type id right now
case S_BEFORE_TID:
_state = State.S_BEFORE_VALUE; // read_type_id may change this for null and bool values
_value_tid = read_type_id();
if (_value_tid == UnifiedInputStreamX.EOF) {
_state = State.S_EOF;
_eof = true;
break;
}
if (_value_tid == PrivateIonConstants.tidNopPad) {
// skips size of pad and resets State machine
skip(_value_len);
clear_value();
break;
}
else if (_value_tid == PrivateIonConstants.tidTypedecl) {
assert (_value_tid == (BINARY_VERSION_MARKER_TID & 0xff)); // the bvm tid happens to be type decl
if (_value_len == BINARY_VERSION_MARKER_LEN ) {
// this isn't valid for any type descriptor except the first byte
// of a 4 byte version marker - so lets read the rest
load_version_marker();
_value_type = IonType.SYMBOL;
}
else {
// if it's not a bvm then it's an ordinary annotated value
// The next call changes our positions to that of the
// wrapped value, but we need to remember the overall
// wrapper position.
long wrapperStart = _position_start;
long wrapperLen = _position_len;
_value_type = load_annotation_start_with_value_type();
// Wrapper and wrapped value should finish together!
long wrapperFinish = wrapperStart + wrapperLen;
long wrappedValueFinish = _position_start + _position_len;
if (wrapperFinish != wrappedValueFinish) {
throw newErrorAt(String.format("Wrapper length mismatch: wrapper %s wrapped value %s", wrapperFinish, wrappedValueFinish));
}
_position_start = wrapperStart;
_position_len = wrapperLen;
}
}
else {
// if it's not a typedesc then we just get the IonType and we're done
_value_type = get_iontype_from_tid(_value_tid);
}
break;
case S_BEFORE_VALUE:
skip(_value_len);
// fall through to "after value"
case S_AFTER_VALUE:
if (isInStruct()) {
_state = State.S_BEFORE_FIELD;
}
else {
_state = State.S_BEFORE_TID;
}
break;
case S_EOF:
break;
default:
error("internal error: raw binary reader in invalid state!");
}
}
// we always want to exit here
_has_next_needed = false;
return;
}
private final void load_version_marker() throws IOException
{
for (int ii=1; ii= oldlen) {
int newlen = oldlen * 2;
int[] temp = new int[newlen];
System.arraycopy(_annotation_ids, 0, temp, 0, oldlen);
_annotation_ids = temp;
}
_annotation_ids[_annotation_count++] = a;
}
private final void clear_value()
{
_value_type = null;
_value_tid = -1;
_value_is_null = false;
_value_lob_is_ready = false;
_annotations.clear();
_v.clear();
_annotation_count = 0;
_value_field_id = SymbolTable.UNKNOWN_SYMBOL_ID;
}
/**
* @return the field SID, or -1 if at EOF.
*/
private final int read_field_id() throws IOException
{
int field_id = readVarUIntOrEOF();
return field_id;
}
private final int read_type_id() throws IOException
{
long start_of_tid = _input.getPosition();
long start_of_value = start_of_tid + 1;
int td = read();
if (td < 0) {
return UnifiedInputStreamX.EOF;
}
int tid = PrivateIonConstants.getTypeCode(td);
int len = PrivateIonConstants.getLowNibble(td);
// NOP Padding
if (tid == PrivateIonConstants.tidNull && len != PrivateIonConstants.lnIsNull) {
if (len == PrivateIonConstants.lnIsVarLen) {
len = readVarUInt();
}
_state = _is_in_struct ? State.S_BEFORE_FIELD : State.S_BEFORE_TID;
tid = PrivateIonConstants.tidNopPad; // override typeId to use Pad marker
}
else if (len == PrivateIonConstants.lnIsVarLen) {
len = readVarUInt();
start_of_value = _input.getPosition();
}
else if (tid == PrivateIonConstants.tidNull) {
_value_is_null = true;
len = 0;
_state = State.S_AFTER_VALUE;
}
else if (len == PrivateIonConstants.lnIsNull) {
_value_is_null = true;
len = 0;
_state = State.S_AFTER_VALUE;
}
else if (tid == PrivateIonConstants.tidBoolean) {
switch (len) {
case PrivateIonConstants.lnBooleanFalse:
_value_is_true = false;
break;
case PrivateIonConstants.lnBooleanTrue:
_value_is_true = true;
break;
default:
throwErrorAt("invalid length nibble in boolean value: "+len);
break;
}
len = 0;
_state = State.S_AFTER_VALUE;
}
else if (tid == PrivateIonConstants.tidStruct) {
if ((_struct_is_ordered = (len == 1))) {
// special case of an ordered struct, it gets the
// otherwise impossible to have length of 1
len = readVarUInt();
start_of_value = _input.getPosition();
}
}
_value_tid = tid;
_value_len = len;
_position_len = len + (start_of_value - start_of_tid);
_position_start = start_of_tid;
return tid;
}
private final IonType get_iontype_from_tid(int tid)
{
IonType t = null;
switch (tid) {
case PrivateIonConstants.tidNull: // 0
t = IonType.NULL;
break;
case PrivateIonConstants.tidBoolean: // 1
t = IonType.BOOL;
break;
case PrivateIonConstants.tidPosInt: // 2
case PrivateIonConstants.tidNegInt: // 3
t = IonType.INT;
break;
case PrivateIonConstants.tidFloat: // 4
t = IonType.FLOAT;
break;
case PrivateIonConstants.tidDecimal: // 5
t = IonType.DECIMAL;
break;
case PrivateIonConstants.tidTimestamp: // 6
t = IonType.TIMESTAMP;
break;
case PrivateIonConstants.tidSymbol: // 7
t = IonType.SYMBOL;
break;
case PrivateIonConstants.tidString: // 8
t = IonType.STRING;
break;
case PrivateIonConstants.tidClob: // 9
t = IonType.CLOB;
break;
case PrivateIonConstants.tidBlob: // 10 A
t = IonType.BLOB;
break;
case PrivateIonConstants.tidList: // 11 B
t = IonType.LIST;
break;
case PrivateIonConstants.tidSexp: // 12 C
t = IonType.SEXP;
break;
case PrivateIonConstants.tidStruct: // 13 D
t = IonType.STRUCT;
break;
case PrivateIonConstants.tidTypedecl: // 14 E
t = null; // we don't know yet
break;
default:
throw newErrorAt("unrecognized value type encountered: "+tid);
}
return t;
}
public void stepIn()
{
if (_value_type == null || _eof) {
throw new IllegalStateException();
}
switch (_value_type) {
case STRUCT:
case LIST:
case SEXP:
break;
default:
throw new IllegalStateException();
}
if (_value_is_null) {
if (_state != State.S_AFTER_VALUE) {
assert( _state == State.S_AFTER_VALUE );
}
}
else {
if (_state != State.S_BEFORE_VALUE) {
assert( _state == State.S_BEFORE_VALUE );
}
}
// first push place where we'll take up our next
// value processing when we step out
long curr_position = getPosition();
long next_position = curr_position + _value_len;
int next_remaining = _local_remaining;
if (next_remaining != NO_LIMIT) {
next_remaining -= _value_len;
if (next_remaining < 0) {
next_remaining = 0; // we'll see and EOF down the road TODO: should we error now?
}
}
push(_parent_tid, next_position, next_remaining);
_is_in_struct = (_value_tid == PrivateIonConstants.tidStruct);
_local_remaining = _value_len;
_state = _is_in_struct ? State.S_BEFORE_FIELD : State.S_BEFORE_TID;
_parent_tid = _value_tid;
clear_value();
_has_next_needed = true;
}
public void stepOut()
{
if (getDepth() < 1) {
throw new IllegalStateException(IonMessages.CANNOT_STEP_OUT);
}
// first we get the top values, then we
// pop them all off in one fell swoop.
long next_position = get_top_position();
int local_remaining = get_top_local_remaining();
int parent_tid = get_top_type();
pop();
_eof = false;
_parent_tid = parent_tid;
// later, only after we've skipped to our new location: _local_remaining = local_remaining;
if (_parent_tid == PrivateIonConstants.tidStruct) {
_is_in_struct = true;
_state = State.S_BEFORE_FIELD;
}
else {
_is_in_struct = false;
_state = State.S_BEFORE_TID;
}
_has_next_needed = true;
clear_value();
long curr_position = getPosition();
if (next_position > curr_position) {
try {
long distance = next_position - curr_position;
int max_skip = Integer.MAX_VALUE - 1; // -1 just in case
while (distance > max_skip) {
skip(max_skip);
distance -= max_skip;
}
if (distance > 0) {
assert( distance < Integer.MAX_VALUE );
skip((int)distance);
}
}
catch (IOException e) {
error(e);
}
}
else if (next_position < curr_position) {
String message = "invalid position during stepOut, current position "
+ curr_position
+ " next value at "
+ next_position;
error(message);
}
assert(next_position == getPosition());
_local_remaining = local_remaining;
}
public int byteSize()
{
int len;
switch (_value_type) {
case BLOB:
case CLOB:
break;
default:
throw new IllegalStateException("only valid for LOB values");
}
if (!_value_lob_is_ready) {
if (_value_is_null) {
len = 0;
}
else {
len = _value_len;
}
_value_lob_remaining = len;
_value_lob_is_ready = true;
}
return _value_lob_remaining;
}
public byte[] newBytes()
{
int len = byteSize(); // does out validation for us
byte[] bytes;
if (_value_is_null) {
bytes = null;
}
else {
bytes = new byte[len];
getBytes(bytes, 0, len);
}
return bytes;
}
public int getBytes(byte[] buffer, int offset, int len)
{
int value_len = byteSize(); // again validation
if (value_len > len) {
value_len = len;
}
int read_len = readBytes(buffer, offset, value_len);
return read_len;
}
public int readBytes(byte[] buffer, int offset, int len)
{
if (offset < 0 || len < 0) {
throw new IllegalArgumentException();
}
int value_len = byteSize(); // again validation
if (_value_lob_remaining > len) {
len = _value_lob_remaining;
}
if (len < 1) {
return 0;
}
int read_len;
try {
read_len = read(buffer, offset, value_len);
_value_lob_remaining -= read_len;
}
catch (IOException e) {
read_len = -1;
error(e);
}
if (_value_lob_remaining == 0) {
_state = State.S_AFTER_VALUE;
}
else {
_value_len = _value_lob_remaining;
}
return read_len;
}
public int getDepth()
{
return (_container_top / POS_STACK_STEP);
}
public IonType getType()
{
//if (_has_next_needed) {
// throw new IllegalStateException("getType() isn't valid until you have called next()");
//}
return _value_type;
}
public boolean isInStruct()
{
return _is_in_struct;
}
public boolean isNullValue()
{
return _value_is_null;
}
//
// helper read routines - these were lifted
// from SimpleByteBuffer.SimpleByteReader
//
private final int read() throws IOException
{
if (_local_remaining != NO_LIMIT) {
if (_local_remaining < 1) {
return UnifiedInputStreamX.EOF;
}
_local_remaining--;
}
return _input.read();
}
private final int read(byte[] dst, int start, int len) throws IOException
{
if (dst == null || start < 0 || len < 0 || start + len > dst.length) {
// no need to test this start >= dst.length ||
// since we test start+len > dst.length which is the correct test
throw new IllegalArgumentException();
}
int read;
if (_local_remaining == NO_LIMIT) {
read = _input.read(dst, start, len);
}
else {
if (len > _local_remaining) {
if (_local_remaining < 1) {
throwUnexpectedEOFException();
}
len = _local_remaining;
}
read = _input.read(dst, start, len);
_local_remaining -= read;
}
return read;
}
/**
* Uses {@link #read(byte[], int, int)} until the entire length is read.
* This method will block until the request is satisfied.
*
* @param buf The buffer to read to.
* @param offset The offset of the buffer to read from.
* @param len The length of the data to read.
*/
public void readAll(byte[] buf, int offset, int len) throws IOException
{
int rem = len;
while (rem > 0)
{
int amount = read(buf, offset, rem);
if (amount <= 0)
{
throwUnexpectedEOFException();
}
rem -= amount;
offset += amount;
}
}
private final boolean isEOF() {
if (_local_remaining > 0) return false;
if (_local_remaining == NO_LIMIT) {
return _input.isEOF();
}
return true;
}
private final long getPosition() {
long pos = _input.getPosition();
return pos;
}
private final void skip(int len) throws IOException
{
if (len < 0) {
// no need to test this start >= dst.length ||
// since we test start+len > dst.length which is the correct test
throw new IllegalArgumentException();
}
if (_local_remaining == NO_LIMIT) {
_input.skip(len);
}
else {
if (len > _local_remaining) {
if (_local_remaining < 1) {
throwUnexpectedEOFException();
}
len = _local_remaining;
}
_input.skip(len);
_local_remaining -= len;
}
return;
}
protected final long readULong(int len) throws IOException
{
long retvalue = 0;
int b;
switch (len) {
default:
throw new IonException("value too large for Java long");
case 8:
if ((b = read()) < 0) throwUnexpectedEOFException();
retvalue = (retvalue << 8) | b;
case 7:
if ((b = read()) < 0) throwUnexpectedEOFException();
retvalue = (retvalue << 8) | b;
case 6:
if ((b = read()) < 0) throwUnexpectedEOFException();
retvalue = (retvalue << 8) | b;
case 5:
if ((b = read()) < 0) throwUnexpectedEOFException();
retvalue = (retvalue << 8) | b;
case 4:
if ((b = read()) < 0) throwUnexpectedEOFException();
retvalue = (retvalue << 8) | b;
case 3:
if ((b = read()) < 0) throwUnexpectedEOFException();
retvalue = (retvalue << 8) | b;
case 2:
if ((b = read()) < 0) throwUnexpectedEOFException();
retvalue = (retvalue << 8) | b;
case 1:
if ((b = read()) < 0) throwUnexpectedEOFException();
retvalue = (retvalue << 8) | b;
case 0:
// do nothing, it's just a 0 length is a 0 value
}
return retvalue;
}
protected final BigInteger readBigInteger(int len, boolean is_negative) throws IOException
{
BigInteger value;
if (len > 0) {
byte[] bits = new byte[len];
readAll(bits, 0, len);
int signum = is_negative ? -1 : 1;
value = new BigInteger(signum, bits);
}
else {
value = BigInteger.ZERO;
}
return value;
}
protected final int readVarInt() throws IOException
{
int retvalue = 0;
boolean is_negative = false;
int b;
// synthetic label "done" (yuck)
done: for (;;) {
// read the first byte - it has the sign bit
if ((b = read()) < 0) throwUnexpectedEOFException();
if ((b & 0x40) != 0) {
is_negative = true;
}
retvalue = (b & 0x3F);
if ((b & 0x80) != 0) break done;
// for the second byte we shift our eariler bits just as much,
// but there are fewer of them there to shift
if ((b = read()) < 0) throwUnexpectedEOFException();
retvalue = (retvalue << 7) | (b & 0x7F);
if ((b & 0x80) != 0) break done;
// for the rest, they're all the same
if ((b = read()) < 0) throwUnexpectedEOFException();
retvalue = (retvalue << 7) | (b & 0x7F);
if ((b & 0x80) != 0) break done;
// for the rest, they're all the same
if ((b = read()) < 0) throwUnexpectedEOFException();
retvalue = (retvalue << 7) | (b & 0x7F);
if ((b & 0x80) != 0) break done;
// for the rest, they're all the same
if ((b = read()) < 0) throwUnexpectedEOFException();
retvalue = (retvalue << 7) | (b & 0x7F);
if ((b & 0x80) != 0) break done;
// if we get here we have more bits than we have room for :(
throwIntOverflowExeption();
}
if (is_negative) {
retvalue = -retvalue;
}
return retvalue;
}
protected final long readVarLong() throws IOException
{
long retvalue = 0;
boolean is_negative = false;
int b;
// synthetic label "done" (yuck)
done: for (;;) {
// read the first byte - it has the sign bit
if ((b = read()) < 0) throwUnexpectedEOFException();
if ((b & 0x40) != 0) {
is_negative = true;
}
retvalue = (b & 0x3F);
if ((b & 0x80) != 0) break done;
// for the second byte we shift our eariler bits just as much,
// but there are fewer of them there to shift
if ((b = read()) < 0) throwUnexpectedEOFException();
retvalue = (retvalue << 7) | (b & 0x7F);
if ((b & 0x80) != 0) break done;
// for the rest, they're all the same
for (;;) {
if ((b = read()) < 0) throwUnexpectedEOFException();
if ((retvalue & 0xFE00000000000000L) != 0) throwIntOverflowExeption();
retvalue = (retvalue << 7) | (b & 0x7F);
if ((b & 0x80) != 0) break done;
}
}
if (is_negative) {
retvalue = -retvalue;
}
return retvalue;
}
/**
* Reads an integer value, returning null to mean -0.
* @throws IOException
*/
protected final Integer readVarInteger() throws IOException
{
int retvalue = 0;
boolean is_negative = false;
int b;
// Synthetic label "done" (yuck)
done: for (;;) {
// read the first byte - it has the sign bit
if ((b = read()) < 0) throwUnexpectedEOFException();
if ((b & 0x40) != 0) {
is_negative = true;
}
retvalue = (b & 0x3F);
if ((b & 0x80) != 0) break done;
// for the second byte we shift our eariler bits just as much,
// but there are fewer of them there to shift
if ((b = read()) < 0) throwUnexpectedEOFException();
retvalue = (retvalue << 7) | (b & 0x7F);
if ((b & 0x80) != 0) break done;
// for the rest, they're all the same
if ((b = read()) < 0) throwUnexpectedEOFException();
retvalue = (retvalue << 7) | (b & 0x7F);
if ((b & 0x80) != 0) break done;
// for the rest, they're all the same
if ((b = read()) < 0) throwUnexpectedEOFException();
retvalue = (retvalue << 7) | (b & 0x7F);
if ((b & 0x80) != 0) break done;
// for the rest, they're all the same
if ((b = read()) < 0) throwUnexpectedEOFException();
retvalue = (retvalue << 7) | (b & 0x7F);
if ((b & 0x80) != 0) break done;
// if we get here we have more bits than we have room for :(
throwIntOverflowExeption();
}
Integer retInteger = null;
if (is_negative) {
if (retvalue != 0) {
retInteger = new Integer(-retvalue);
}
}
else {
retInteger = new Integer(retvalue);
}
return retInteger;
}
protected final int readVarUIntOrEOF() throws IOException
{
int retvalue = 0;
int b;
for (;;) { // fake loop to create a "goto done"
if ((b = read()) < 0) {
return UnifiedInputStreamX.EOF;
}
retvalue = (retvalue << 7) | (b & 0x7F);
if ((b & 0x80) != 0) break;
if ((b = read()) < 0) throwUnexpectedEOFException();
retvalue = (retvalue << 7) | (b & 0x7F);
if ((b & 0x80) != 0) break;
if ((b = read()) < 0) throwUnexpectedEOFException();
retvalue = (retvalue << 7) | (b & 0x7F);
if ((b & 0x80) != 0) break;
if ((b = read()) < 0) throwUnexpectedEOFException();
retvalue = (retvalue << 7) | (b & 0x7F);
if ((b & 0x80) != 0) break;
if ((b = read()) < 0) throwUnexpectedEOFException();
retvalue = (retvalue << 7) | (b & 0x7F);
if ((b & 0x80) != 0) break;
// if we get here we have more bits than we have room for :(
throwIntOverflowExeption();
}
return retvalue;
}
protected final int readVarUInt() throws IOException
{
int retvalue = 0;
int b;
for (;;) { // fake loop to create a "goto done"
if ((b = read()) < 0) throwUnexpectedEOFException();
retvalue = (retvalue << 7) | (b & 0x7F);
if ((b & 0x80) != 0) break;
if ((b = read()) < 0) throwUnexpectedEOFException();
retvalue = (retvalue << 7) | (b & 0x7F);
if ((b & 0x80) != 0) break;
if ((b = read()) < 0) throwUnexpectedEOFException();
retvalue = (retvalue << 7) | (b & 0x7F);
if ((b & 0x80) != 0) break;
if ((b = read()) < 0) throwUnexpectedEOFException();
retvalue = (retvalue << 7) | (b & 0x7F);
if ((b & 0x80) != 0) break;
if ((b = read()) < 0) throwUnexpectedEOFException();
retvalue = (retvalue << 7) | (b & 0x7F);
if ((b & 0x80) != 0) break;
// if we get here we have more bits than we have room for :(
throwIntOverflowExeption();
}
return retvalue;
}
protected final double readFloat(int len) throws IOException
{
if (len == 0)
{
// special case, return pos zero
return 0.0d;
}
if (len != 4 && len != 8)
{
throw new IOException("Length of float read must be 0, 4, or 8");
}
long dBits = this.readULong(len);
return len == 4
? (double) Float.intBitsToFloat((int) (dBits & 0xffffffffL))
: Double.longBitsToDouble(dBits);
}
protected final long readVarULong() throws IOException
{
long retvalue = 0;
int b;
for (;;) {
if ((b = read()) < 0) throwUnexpectedEOFException();
if ((retvalue & 0xFE00000000000000L) != 0) throwIntOverflowExeption();
retvalue = (retvalue << 7) | (b & 0x7F);
if ((b & 0x80) != 0) break;
}
return retvalue;
}
protected final Decimal readDecimal(int len) throws IOException
{
MathContext mathContext = MathContext.UNLIMITED;
Decimal bd;
// we only write out the '0' value as the nibble 0
if (len == 0) {
bd = Decimal.valueOf(0, mathContext);
}
else {
// otherwise we to it the hard way ....
int save_limit = _local_remaining - len;
_local_remaining = len;
int exponent = readVarInt();
BigInteger value;
int signum;
if (_local_remaining > 0)
{
byte[] bits = new byte[_local_remaining];
readAll(bits, 0, _local_remaining);
signum = 1;
if (bits[0] < 0)
{
// value is negative, clear the sign
bits[0] &= 0x7F;
signum = -1;
}
value = new BigInteger(signum, bits);
}
else {
signum = 0;
value = BigInteger.ZERO;
}
// Ion stores exponent, BigDecimal uses the negation "scale"
int scale = -exponent;
if (value.signum() == 0 && signum == -1)
{
assert value.equals(BigInteger.ZERO);
bd = Decimal.negativeZero(scale, mathContext);
}
else
{
bd = Decimal.valueOf(value, scale, mathContext);
}
_local_remaining = save_limit;
}
return bd;
}
protected final Timestamp readTimestamp(int len) throws IOException
{
if (len < 1) {
// nothing to do here - and the timestamp will be NULL
return null;
}
int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
BigDecimal frac = null;
int save_limit = _local_remaining - len;
_local_remaining = len; // > 0
// first up is the offset, which requires a special int reader
// to return the -0 as a null Integer
Integer offset = readVarInteger();
// now we'll read the struct values from the input stream
// year is from 0001 to 9999
// or 0x1 to 0x270F or 14 bits - 1 or 2 bytes
year = readVarUInt();
Precision p = Precision.YEAR; // our lowest significant option
// now we look for hours and minutes
if (_local_remaining > 0) {
month = readVarUInt();
p = Precision.MONTH;
// now we look for hours and minutes
if (_local_remaining > 0) {
day = readVarUInt();
p = Precision.DAY; // our lowest significant option
// now we look for hours and minutes
if (_local_remaining > 0) {
hour = readVarUInt();
minute = readVarUInt();
p = Precision.MINUTE;
if (_local_remaining > 0) {
second = readVarUInt();
p = Precision.SECOND;
if (_local_remaining > 0) {
// now we read in our actual "milliseconds since the epoch"
frac = readDecimal(_local_remaining);
}
}
}
}
}
// restore out outer limit(s)
_local_remaining = save_limit;
// now we let timestamp put it all together
try {
@SuppressWarnings("deprecation")
Timestamp val =
Timestamp.createFromUtcFields(p, year, month, day, hour,
minute, second, frac, offset);
return val;
}
catch (IllegalArgumentException e)
{
// Rewrap to the expected type.
throw newErrorAt("Invalid timestamp encoding: " + e.getMessage());
}
}
protected final String readString(int len) throws IOException
{
// len is bytes, which is greater than or equal to java
// chars even after utf8 to utf16 decoding nonsense
// the char array is way faster than using string buffer
char[] chars = new char[len];
int c, ii = 0;
int save_limit = _local_remaining - len;
_local_remaining = len;
while (!isEOF()) {
c = readUnicodeScalar();
if (c < 0) throwUnexpectedEOFException();
if (c < 0x10000) {
chars[ii++] = (char)c;
}
else { // when c is >= 0x10000 we need surrogate encoding
chars[ii++] = (char)PrivateIonConstants.makeHighSurrogate(c);
chars[ii++] = (char)PrivateIonConstants.makeLowSurrogate(c);
}
}
_local_remaining = save_limit;
return new String(chars, 0, ii);
}
private final int readUnicodeScalar() throws IOException
{
int c = -1, b;
b = read();
// ascii is all good, even -1 (eof)
if (IonUTF8.isOneByteUTF8(b)) {
return b;
}
switch(IonUTF8.getUTF8LengthFromFirstByte(b)) {
case 2:
// now we start gluing the multi-byte value together
assert((b & 0xe0) == 0xc0);
// for values from 0x80 to 0x7FF (all legal)
int b2 = read();
if (!IonUTF8.isContinueByteUTF8(b2)) throwUTF8Exception();
c = IonUTF8.twoByteScalar(b, b2);
break;
case 3:
assert((b & 0xf0) == 0xe0);
// for values from 0x800 to 0xFFFFF (NOT all legal)
b2 = read();
if (!IonUTF8.isContinueByteUTF8(b2)) throwUTF8Exception();
int b3 = read();
if (!IonUTF8.isContinueByteUTF8(b3)) throwUTF8Exception();
c = IonUTF8.threeByteScalar(b, b2, b3);
break;
case 4:
assert((b & 0xf8) == 0xf0);
// for values from 0x010000 to 0x1FFFFF (NOT all legal)
b2 = read();
if (!IonUTF8.isContinueByteUTF8(b2)) throwUTF8Exception();
b3 = read();
if (!IonUTF8.isContinueByteUTF8(b3)) throwUTF8Exception();
int b4 = read();
if (!IonUTF8.isContinueByteUTF8(b4)) throwUTF8Exception();
c = IonUTF8.fourByteScalar(b, b2, b3, b4);
if (c > 0x10FFFF) {
throw new IonException("illegal utf value encountered in input utf-8 stream");
}
break;
default:
throwUTF8Exception();
}
return c;
}
private final void throwUTF8Exception() throws IOException
{
throwErrorAt("Invalid UTF-8 character encounter in a string at position ");
}
private final void throwUnexpectedEOFException() throws IOException {
throwErrorAt("unexpected EOF in value");
}
private final void throwIntOverflowExeption() throws IOException {
throwErrorAt("int in stream is too long for a Java int 32 use readLong()");
}
protected IonException newErrorAt(String msg) {
String msg2 = msg + " at position " + getPosition();
return new IonException(msg2);
}
protected void throwErrorAt(String msg) {
throw newErrorAt(msg);
}
protected void error(String msg) {
throw new IonException(msg);
}
protected void error(Exception e) {
throw new IonException(e);
}
}