Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 161 additions & 0 deletions src/com/amazon/ion/impl/bin/IonEncoder_1_1.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package com.amazon.ion.impl.bin;

import com.amazon.ion.Decimal;
import com.amazon.ion.IonType;
import com.amazon.ion.Timestamp;
import com.amazon.ion.impl.bin.utf8.Utf8StringEncoder;

import java.math.BigDecimal;
import java.math.BigInteger;

import static java.lang.Double.doubleToRawLongBits;
import static java.lang.Float.floatToIntBits;

/**
* Provides functions for writing various Ion values to a WriteBuffer.
*
* This class can be subsumed by IonRawBinaryWriter_1_1, when it is created.
*/
public class IonEncoder_1_1 {

/**
* Writes an Ion Null value to the given WriteBuffer.
* @return the number of bytes written
*/
public static int writeNullValue(WriteBuffer buffer, final IonType ionType) {
if (ionType == IonType.NULL) {
buffer.writeByte(OpCodes.NULL_UNTYPED);
return 1;
}

buffer.writeByte(OpCodes.NULL_TYPED);
switch (ionType) {
case BOOL:
buffer.writeByte((byte) 0x00);
break;
case INT:
buffer.writeByte((byte) 0x01);
break;
case FLOAT:
buffer.writeByte((byte) 0x02);
break;
case DECIMAL:
buffer.writeByte((byte) 0x03);
break;
case TIMESTAMP:
buffer.writeByte((byte) 0x04);
break;
case STRING:
buffer.writeByte((byte) 0x05);
break;
case SYMBOL:
buffer.writeByte((byte) 0x06);
break;
case BLOB:
buffer.writeByte((byte) 0x07);
break;
case CLOB:
buffer.writeByte((byte) 0x08);
break;
case LIST:
buffer.writeByte((byte) 0x09);
break;
case SEXP:
buffer.writeByte((byte) 0x0A);
break;
case STRUCT:
buffer.writeByte((byte) 0x0B);
break;
case DATAGRAM:
throw new IllegalArgumentException("Cannot write a null datagram");
}
return 2;
}

/**
* Writes an Ion Bool value to the given WriteBuffer.
* @return the number of bytes written
*/
public static int writeBoolValue(WriteBuffer buffer, final boolean value) {
if (value) {
buffer.writeByte(OpCodes.BOOLEAN_TRUE);
} else {
buffer.writeByte(OpCodes.BOOLEAN_FALSE);
}
return 1;
}

/**
* Writes an Ion Integer value to the given WriteBuffer.
* @return the number of bytes written
*/
public static int writeIntValue(WriteBuffer buffer, final long value) {
if (value == 0) {
buffer.writeByte(OpCodes.INTEGER_ZERO_LENGTH);
return 1;
}
int length = WriteBuffer.fixedIntLength(value);
buffer.writeByte((byte) (OpCodes.INTEGER_ZERO_LENGTH + length));
buffer.writeFixedInt(value);
return 1 + length;
}

private static final BigInteger BIG_INT_LONG_MAX_VALUE = BigInteger.valueOf(Long.MAX_VALUE);
private static final BigInteger BIG_INT_LONG_MIN_VALUE = BigInteger.valueOf(Long.MIN_VALUE);

/**
* Writes an Ion Integer value to the given WriteBuffer.
* @return the number of bytes written
*/
public static int writeIntValue(WriteBuffer buffer, final BigInteger value) {
if (value == null) {
return writeNullValue(buffer, IonType.INT);
}
if (value.compareTo(BIG_INT_LONG_MIN_VALUE) >= 0 && value.compareTo(BIG_INT_LONG_MAX_VALUE) <= 0) {
return writeIntValue(buffer, value.longValue());
}
buffer.writeByte(OpCodes.VARIABLE_LENGTH_INTEGER);
byte[] intBytes = value.toByteArray();
int totalBytes = 1 + intBytes.length + buffer.writeFlexUInt(intBytes.length);
for (int i = intBytes.length; i > 0; i--) {
buffer.writeByte(intBytes[i-1]);
}
return totalBytes;
}

/**
* Writes a float to the given WriteBuffer using the Ion 1.1 encoding for Ion Floats.
* @return the number of bytes written
*/
public static int writeFloat(WriteBuffer buffer, final float value) {
// TODO: Optimization to write a 16 bit float for non-finite and possibly other values
if (value == 0.0) {
buffer.writeByte(OpCodes.FLOAT_ZERO_LENGTH);
return 1;
} else {
buffer.writeByte(OpCodes.FLOAT_32);
buffer.writeUInt32(floatToIntBits(value));
return 5;
}
}

/**
* Writes a double to the given WriteBuffer using the Ion 1.1 encoding for Ion Floats.
* @return the number of bytes written
*/
public static int writeFloat(WriteBuffer buffer, final double value) {
// TODO: Optimization to write a 16 bit float for non-finite and possibly other values
if (value == 0.0) {
buffer.writeByte(OpCodes.FLOAT_ZERO_LENGTH);
return 1;
} else if (!Double.isFinite(value) || value == (float) value) {
buffer.writeByte(OpCodes.FLOAT_32);
buffer.writeUInt32(floatToIntBits((float) value));
return 5;
} else {
buffer.writeByte(OpCodes.FLOAT_64);
buffer.writeUInt64(doubleToRawLongBits(value));
return 9;
}
}
}
28 changes: 28 additions & 0 deletions src/com/amazon/ion/impl/bin/OpCodes.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.amazon.ion.impl.bin;

/**
* Utility class holding Ion 1.1 Op Codes.
*/
public class OpCodes {
private OpCodes() {}

public static final byte INTEGER_ZERO_LENGTH = 0x50;
// 0x51-0x58 are additional lengths of integers.
// 0x59 Reserved
public static final byte FLOAT_ZERO_LENGTH = 0x5A;
public static final byte FLOAT_16 = 0x5B;
public static final byte FLOAT_32 = 0x5C;
public static final byte FLOAT_64 = 0x5D;
public static final byte BOOLEAN_TRUE = 0x5E;
public static final byte BOOLEAN_FALSE = 0x5F;

public static final byte DECIMAL_ZERO_LENGTH = 0x60;
// 0x61-0x6E are additional lengths of decimals.
public static final byte NEGATIVE_ZERO_DECIMAL = 0x6F;


public static final byte NULL_UNTYPED = (byte) 0xEA;
public static final byte NULL_TYPED = (byte) 0xEB;

public static final byte VARIABLE_LENGTH_INTEGER = (byte) 0xF5;
}
59 changes: 59 additions & 0 deletions src/com/amazon/ion/impl/bin/WriteBuffer.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.io.Closeable;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

Expand Down Expand Up @@ -1359,6 +1360,64 @@ private int writeFlexIntOrUInt(final long value, final int numBytes) {
return numBytes;
}

public static int flexIntLength(final BigInteger value) {
return value.bitLength() / 7 + 1;
}

public static int flexUIntLength(final BigInteger value) {
return (value.bitLength() - 1) / 7 + 1;
}

public int writeFlexInt(final BigInteger value) {
int numBytes = flexIntLength(value);
return writeFlexIntOrUIntForBigInteger(value, numBytes);
}

public int writeFlexUInt(final BigInteger value) {
if (value.signum() < 0) {
throw new IllegalArgumentException("Attempted to write a FlexUInt for " + value);
}
int numBytes = flexUIntLength(value);
return writeFlexIntOrUIntForBigInteger(value, numBytes);
}

private int writeFlexIntOrUIntForBigInteger(final BigInteger value, final int numBytes) {
// TODO: Should we branch to the implementation for long if the number is small enough?
// https://github.com/amazon-ion/ion-java/issues/614
byte[] valueBytes = value.toByteArray();

int i = 0; // `i` gets incremented for every byte written.

// Start with leading zero bytes.
// If there's 1-8 total bytes, we need no leading zero-bytes.
// If there's 9-16 total bytes, we need one zero-byte
// If there's 17-24 total bytes, we need two zero-bytes, etc.
for (; i < (numBytes - 1)/8; i++) {
writeByte((byte) 0);
}

// Write the last length bits, possibly also containing some value bits.
int remainingLengthBits = (numBytes - 1) % 8;
byte lengthPart = (byte) (0x01 << remainingLengthBits);
int valueBitOffset = remainingLengthBits + 1;
byte valuePart = (byte) (valueBytes[valueBytes.length - 1] << valueBitOffset);
writeByte((byte) (valuePart | lengthPart));
i++;

for (int valueByteOffset = valueBytes.length - 1; valueByteOffset > 0; valueByteOffset--) {
// Technically it's only a nibble if the bitOffset is 4, so we call it nibble-ish
byte highNibbleIsh = (byte) (valueBytes[valueByteOffset - 1] << (valueBitOffset));
byte lowNibbleIsh = (byte) ((valueBytes[valueByteOffset] & 0xFF) >> (8 - valueBitOffset));
writeByte((byte) (highNibbleIsh | lowNibbleIsh));
i++;
}
if (i < numBytes) {
writeByte((byte) ((valueBytes[0]) >> (8 - valueBitOffset)));
}

return numBytes;
}

/** Get the length of FixedInt for the provided value. */
public static int fixedIntLength(final long value) {
int numMagnitudeBitsRequired;
Expand Down
Loading