From 6bea245169222895f879a396b1c7c1bdb73642b2 Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" Date: Sat, 19 Jul 2008 19:12:01 +0000 Subject: Added utilities for encoding and decoding BigDecimals. --- .../java/com/amazon/carbonado/raw/DataDecoder.java | 57 ++++++++ .../java/com/amazon/carbonado/raw/DataEncoder.java | 150 +++++++++++++-------- 2 files changed, 148 insertions(+), 59 deletions(-) (limited to 'src/main/java/com/amazon') diff --git a/src/main/java/com/amazon/carbonado/raw/DataDecoder.java b/src/main/java/com/amazon/carbonado/raw/DataDecoder.java index 5ea5c1d..407df0c 100644 --- a/src/main/java/com/amazon/carbonado/raw/DataDecoder.java +++ b/src/main/java/com/amazon/carbonado/raw/DataDecoder.java @@ -18,6 +18,7 @@ package com.amazon.carbonado.raw; +import java.math.BigDecimal; import java.math.BigInteger; import java.io.EOFException; @@ -390,6 +391,62 @@ public class DataDecoder { return amt; } + /** + * Decodes a BigDecimal. + * + * @param src source of encoded data + * @param srcOffset offset into encoded data + * @param valueRef decoded BigDecimal is stored in element 0, which may be null + * @return amount of bytes read from source + * @throws CorruptEncodingException if source data is corrupt + * @since 1.2 + */ + public static int decode(byte[] src, int srcOffset, BigDecimal[] valueRef) + throws CorruptEncodingException + { + try { + final int originalOffset = srcOffset; + + int b = src[srcOffset++] & 0xff; + if (b >= 0xf8) { + valueRef[0] = null; + return 1; + } + + int scale; + if (b <= 0x7f) { + scale = b; + } else if (b <= 0xbf) { + scale = ((b & 0x3f) << 8) | (src[srcOffset++] & 0xff); + } else if (b <= 0xdf) { + scale = ((b & 0x1f) << 16) | ((src[srcOffset++] & 0xff) << 8) | + (src[srcOffset++] & 0xff); + } else if (b <= 0xef) { + scale = ((b & 0x0f) << 24) | ((src[srcOffset++] & 0xff) << 16) | + ((src[srcOffset++] & 0xff) << 8) | (src[srcOffset++] & 0xff); + } else { + scale = ((src[srcOffset++] & 0xff) << 24) | + ((src[srcOffset++] & 0xff) << 16) | + ((src[srcOffset++] & 0xff) << 8) | (src[srcOffset++] & 0xff); + } + + if ((scale & 1) != 0) { + scale = (~(scale >> 1)) | (1 << 31); + } else { + scale >>>= 1; + } + + BigInteger[] unscaledRef = new BigInteger[1]; + int amt = decode(src, srcOffset, unscaledRef); + + valueRef[0] = new BigDecimal(unscaledRef[0], scale); + + return (srcOffset + amt) - originalOffset; + } catch (IndexOutOfBoundsException e) { + throw new CorruptEncodingException(null, e); + } + } + /** * Decodes the given byte array. * diff --git a/src/main/java/com/amazon/carbonado/raw/DataEncoder.java b/src/main/java/com/amazon/carbonado/raw/DataEncoder.java index 7f4f5d3..4b6cab4 100644 --- a/src/main/java/com/amazon/carbonado/raw/DataEncoder.java +++ b/src/main/java/com/amazon/carbonado/raw/DataEncoder.java @@ -18,6 +18,7 @@ package com.amazon.carbonado.raw; +import java.math.BigDecimal; import java.math.BigInteger; import java.io.IOException; @@ -328,7 +329,7 @@ public class DataEncoder { byte[] bytes = value.toByteArray(); // Write the byte array length first, in a variable amount of bytes. - int amt = encodeLength(bytes.length, dst, dstOffset); + int amt = encodeUnsignedVarInt(bytes.length, dst, dstOffset); // Now write the byte array. System.arraycopy(bytes, 0, dst, dstOffset + amt, bytes.length); @@ -347,20 +348,42 @@ public class DataEncoder { if (value == null) { return 1; } - int byteCount = (value.bitLength() >> 3) + 1; + return unsignedVarIntLength(byteCount) + byteCount; + } - if (byteCount < 128) { - return 1 + byteCount; - } else if (byteCount < 16384) { - return 2 + byteCount; - } else if (byteCount < 2097152) { - return 3 + byteCount; - } else if (byteCount < 268435456) { - return 4 + byteCount; - } else { - return 5 + byteCount; + /** + * Encodes the given optional BigDecimal into a variable amount of + * bytes. If the BigDecimal is null, exactly 1 byte is written. Otherwise, + * the amount written can be determined by calling calculateEncodedLength. + * + * @param value BigDecimal value to encode, may be null + * @param dst destination for encoded bytes + * @param dstOffset offset into destination array + * @return amount of bytes written + * @since 1.2 + */ + public static int encode(BigDecimal value, byte[] dst, int dstOffset) { + if (value == null) { + dst[dstOffset] = NULL_BYTE_HIGH; + return 1; + } + int amt = encodeSignedVarInt(value.scale(), dst, dstOffset); + return amt + encode(value.unscaledValue(), dst, dstOffset + amt); + } + + /** + * Returns the amount of bytes required to encode the given BigDecimal. + * + * @param value BigDecimal value to encode, may be null + * @return amount of bytes needed to encode + * @since 1.2 + */ + public static int calculateEncodedLength(BigDecimal value) { + if (value == null) { + return 1; } + return signedVarIntLength(value.scale()) + calculateEncodedLength(value.unscaledValue()); } /** @@ -401,7 +424,7 @@ public class DataEncoder { } // Write the value length first, in a variable amount of bytes. - int amt = encodeLength(valueLength, dst, dstOffset); + int amt = encodeUnsignedVarInt(valueLength, dst, dstOffset); // Now write the value. System.arraycopy(value, valueOffset, dst, dstOffset + amt, valueLength); @@ -428,19 +451,7 @@ public class DataEncoder { * @return amount of bytes needed to encode */ public static int calculateEncodedLength(byte[] value, int valueOffset, int valueLength) { - if (value == null) { - return 1; - } else if (valueLength < 128) { - return 1 + valueLength; - } else if (valueLength < 16384) { - return 2 + valueLength; - } else if (valueLength < 2097152) { - return 3 + valueLength; - } else if (valueLength < 268435456) { - return 4 + valueLength; - } else { - return 5 + valueLength; - } + return value == null ? 1 : (unsignedVarIntLength(valueLength) + valueLength); } /** @@ -467,7 +478,7 @@ public class DataEncoder { int valueLength = value.length(); // Write the value length first, in a variable amount of bytes. - dstOffset += encodeLength(valueLength, dst, dstOffset); + dstOffset += encodeUnsignedVarInt(valueLength, dst, dstOffset); for (int i = 0; i < valueLength; i++) { int c = value.charAt(i); @@ -508,19 +519,7 @@ public class DataEncoder { } int valueLength = value.length(); - int encodedLen; - - if (valueLength < 128) { - encodedLen = 1; - } else if (valueLength < 16384) { - encodedLen = 2; - } else if (valueLength < 2097152) { - encodedLen = 3; - } else if (valueLength < 268435456) { - encodedLen = 4; - } else { - encodedLen = 5; - } + int encodedLen = unsignedVarIntLength(valueLength); for (int i = 0; i < valueLength; i++) { int c = value.charAt(i); @@ -546,35 +545,68 @@ public class DataEncoder { return encodedLen; } - private static int encodeLength(int valueLength, byte[] dst, int dstOffset) { - if (valueLength < 128) { - dst[dstOffset] = (byte)valueLength; + private static int encodeUnsignedVarInt(int value, byte[] dst, int dstOffset) { + if (value < 128) { + dst[dstOffset] = (byte)value; return 1; - } else if (valueLength < 16384) { - dst[dstOffset++] = (byte)((valueLength >> 8) | 0x80); - dst[dstOffset] = (byte)valueLength; + } else if (value < 16384) { + dst[dstOffset++] = (byte)((value >> 8) | 0x80); + dst[dstOffset] = (byte)value; return 2; - } else if (valueLength < 2097152) { - dst[dstOffset++] = (byte)((valueLength >> 16) | 0xc0); - dst[dstOffset++] = (byte)(valueLength >> 8); - dst[dstOffset] = (byte)valueLength; + } else if (value < 2097152) { + dst[dstOffset++] = (byte)((value >> 16) | 0xc0); + dst[dstOffset++] = (byte)(value >> 8); + dst[dstOffset] = (byte)value; return 3; - } else if (valueLength < 268435456) { - dst[dstOffset++] = (byte)((valueLength >> 24) | 0xe0); - dst[dstOffset++] = (byte)(valueLength >> 16); - dst[dstOffset++] = (byte)(valueLength >> 8); - dst[dstOffset] = (byte)valueLength; + } else if (value < 268435456) { + dst[dstOffset++] = (byte)((value >> 24) | 0xe0); + dst[dstOffset++] = (byte)(value >> 16); + dst[dstOffset++] = (byte)(value >> 8); + dst[dstOffset] = (byte)value; return 4; } else { dst[dstOffset++] = (byte)0xf0; - dst[dstOffset++] = (byte)(valueLength >> 24); - dst[dstOffset++] = (byte)(valueLength >> 16); - dst[dstOffset++] = (byte)(valueLength >> 8); - dst[dstOffset] = (byte)valueLength; + dst[dstOffset++] = (byte)(value >> 24); + dst[dstOffset++] = (byte)(value >> 16); + dst[dstOffset++] = (byte)(value >> 8); + dst[dstOffset] = (byte)value; return 5; } } + private static int unsignedVarIntLength(int value) { + if (value < 128) { + return 1; + } else if (value < 16384) { + return 2; + } else if (value < 2097152) { + return 3; + } else if (value < 268435456) { + return 4; + } else { + return 5; + } + } + + private static int encodeSignedVarInt(int value, byte[] dst, int dstOffset) { + value = (value < 0 ? (((~value) << 1) | 1) : (value << 1)); + if (value < 0) { + dst[dstOffset++] = (byte)0xf0; + dst[dstOffset++] = (byte)(value >> 24); + dst[dstOffset++] = (byte)(value >> 16); + dst[dstOffset++] = (byte)(value >> 8); + dst[dstOffset] = (byte)value; + return 5; + } else { + return encodeUnsignedVarInt(value, dst, dstOffset); + } + } + + private static int signedVarIntLength(int value) { + value = (value < 0 ? ~value : value) << 1; + return value < 0 ? 5 : unsignedVarIntLength(value); + } + /** * Writes a positive length value in up to five bytes. * -- cgit v1.2.3