/* * Copyright 2006-2010 Amazon Technologies, Inc. or its affiliates. * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks * of Amazon Technologies, 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. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License 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 com.taobao.tddl.optimizer.core.datatype; import static com.taobao.tddl.optimizer.core.datatype.EncodingConstants.EMPTY_BYTE_ARRAY; import static com.taobao.tddl.optimizer.core.datatype.EncodingConstants.NULL_BYTE_HIGH; import static com.taobao.tddl.optimizer.core.datatype.EncodingConstants.NULL_BYTE_LOW; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; import java.math.BigInteger; /** * A very low-level class that decodes key components encoded by methods of * {@link DataEncoder}. * * @author Brian S O'Neill * @see KeyDecoder */ public class DataDecoder { /** * Decodes a signed integer from exactly 4 bytes. * * @param src source of encoded bytes * @param srcOffset offset into source array * @return signed integer value */ public static int decodeInt(byte[] src, int srcOffset) throws CorruptEncodingException { try { int value = (src[srcOffset] << 24) | ((src[srcOffset + 1] & 0xff) << 16) | ((src[srcOffset + 2] & 0xff) << 8) | (src[srcOffset + 3] & 0xff); return value ^ 0x80000000; } catch (IndexOutOfBoundsException e) { throw new CorruptEncodingException(null, e); } } /** * Decodes a signed Integer object from exactly 1 or 5 bytes. If null is * returned, then 1 byte was read. * * @param src source of encoded bytes * @param srcOffset offset into source array * @return signed Integer object or null */ public static Integer decodeIntegerObj(byte[] src, int srcOffset) throws CorruptEncodingException { try { int b = src[srcOffset]; if (b == NULL_BYTE_HIGH || b == NULL_BYTE_LOW) { return null; } return decodeInt(src, srcOffset + 1); } catch (IndexOutOfBoundsException e) { throw new CorruptEncodingException(null, e); } } /** * Decodes a signed long from exactly 8 bytes. * * @param src source of encoded bytes * @param srcOffset offset into source array * @return signed long value */ public static long decodeLong(byte[] src, int srcOffset) throws CorruptEncodingException { try { return (((long) (((src[srcOffset]) << 24) | ((src[srcOffset + 1] & 0xff) << 16) | ((src[srcOffset + 2] & 0xff) << 8) | ((src[srcOffset + 3] & 0xff))) ^ 0x80000000) << 32) | (((((src[srcOffset + 4]) << 24) | ((src[srcOffset + 5] & 0xff) << 16) | ((src[srcOffset + 6] & 0xff) << 8) | ((src[srcOffset + 7] & 0xff))) & 0xffffffffL)); } catch (IndexOutOfBoundsException e) { throw new CorruptEncodingException(null, e); } } /** * Decodes a signed Long object from exactly 1 or 9 bytes. If null is * returned, then 1 byte was read. * * @param src source of encoded bytes * @param srcOffset offset into source array * @return signed Long object or null */ public static Long decodeLongObj(byte[] src, int srcOffset) throws CorruptEncodingException { try { int b = src[srcOffset]; if (b == NULL_BYTE_HIGH || b == NULL_BYTE_LOW) { return null; } return decodeLong(src, srcOffset + 1); } catch (IndexOutOfBoundsException e) { throw new CorruptEncodingException(null, e); } } /** * Decodes a signed byte from exactly 1 byte. * * @param src source of encoded bytes * @param srcOffset offset into source array * @return signed byte value */ public static byte decodeByte(byte[] src, int srcOffset) throws CorruptEncodingException { try { return (byte) (src[srcOffset] ^ 0x80); } catch (IndexOutOfBoundsException e) { throw new CorruptEncodingException(null, e); } } /** * Decodes a signed Byte object from exactly 1 or 2 bytes. If null is * returned, then 1 byte was read. * * @param src source of encoded bytes * @param srcOffset offset into source array * @return signed Byte object or null */ public static Byte decodeByteObj(byte[] src, int srcOffset) throws CorruptEncodingException { try { int b = src[srcOffset]; if (b == NULL_BYTE_HIGH || b == NULL_BYTE_LOW) { return null; } return decodeByte(src, srcOffset + 1); } catch (IndexOutOfBoundsException e) { throw new CorruptEncodingException(null, e); } } /** * Decodes a signed short from exactly 2 bytes. * * @param src source of encoded bytes * @param srcOffset offset into source array * @return signed short value */ public static short decodeShort(byte[] src, int srcOffset) throws CorruptEncodingException { try { return (short) (((src[srcOffset] << 8) | (src[srcOffset + 1] & 0xff)) ^ 0x8000); } catch (IndexOutOfBoundsException e) { throw new CorruptEncodingException(null, e); } } /** * Decodes a signed Short object from exactly 1 or 3 bytes. If null is * returned, then 1 byte was read. * * @param src source of encoded bytes * @param srcOffset offset into source array * @return signed Short object or null */ public static Short decodeShortObj(byte[] src, int srcOffset) throws CorruptEncodingException { try { int b = src[srcOffset]; if (b == NULL_BYTE_HIGH || b == NULL_BYTE_LOW) { return null; } return decodeShort(src, srcOffset + 1); } catch (IndexOutOfBoundsException e) { throw new CorruptEncodingException(null, e); } } /** * Decodes a char from exactly 2 bytes. * * @param src source of encoded bytes * @param srcOffset offset into source array * @return char value */ public static char decodeChar(byte[] src, int srcOffset) throws CorruptEncodingException { try { return (char) ((src[srcOffset] << 8) | (src[srcOffset + 1] & 0xff)); } catch (IndexOutOfBoundsException e) { throw new CorruptEncodingException(null, e); } } /** * Decodes a Character object from exactly 1 or 3 bytes. If null is * returned, then 1 byte was read. * * @param src source of encoded bytes * @param srcOffset offset into source array * @return Character object or null */ public static Character decodeCharacterObj(byte[] src, int srcOffset) throws CorruptEncodingException { try { int b = src[srcOffset]; if (b == NULL_BYTE_HIGH || b == NULL_BYTE_LOW) { return null; } return decodeChar(src, srcOffset + 1); } catch (IndexOutOfBoundsException e) { throw new CorruptEncodingException(null, e); } } /** * Decodes a boolean from exactly 1 byte. * * @param src source of encoded bytes * @param srcOffset offset into source array * @return boolean value */ public static boolean decodeBoolean(byte[] src, int srcOffset) throws CorruptEncodingException { try { return src[srcOffset] == (byte) 128; } catch (IndexOutOfBoundsException e) { throw new CorruptEncodingException(null, e); } } /** * Decodes a Boolean object from exactly 1 byte. * * @param src source of encoded bytes * @param srcOffset offset into source array * @return Boolean object or null */ public static Boolean decodeBooleanObj(byte[] src, int srcOffset) throws CorruptEncodingException { try { switch (src[srcOffset]) { case NULL_BYTE_LOW: case NULL_BYTE_HIGH: return null; case (byte) 128: return Boolean.TRUE; default: return Boolean.FALSE; } } catch (IndexOutOfBoundsException e) { throw new CorruptEncodingException(null, e); } } /** * Decodes a float from exactly 4 bytes. * * @param src source of encoded bytes * @param srcOffset offset into source array * @return float value */ public static float decodeFloat(byte[] src, int srcOffset) throws CorruptEncodingException { int bits = decodeFloatBits(src, srcOffset); bits ^= (bits < 0) ? 0x80000000 : 0xffffffff; return Float.intBitsToFloat(bits); } /** * Decodes a Float object from exactly 4 bytes. * * @param src source of encoded bytes * @param srcOffset offset into source array * @return Float object or null */ public static Float decodeFloatObj(byte[] src, int srcOffset) throws CorruptEncodingException { int bits = decodeFloatBits(src, srcOffset); bits ^= (bits < 0) ? 0x80000000 : 0xffffffff; return bits == 0x7fffffff ? null : Float.intBitsToFloat(bits); } protected static int decodeFloatBits(byte[] src, int srcOffset) throws CorruptEncodingException { try { return (src[srcOffset] << 24) | ((src[srcOffset + 1] & 0xff) << 16) | ((src[srcOffset + 2] & 0xff) << 8) | (src[srcOffset + 3] & 0xff); } catch (IndexOutOfBoundsException e) { throw new CorruptEncodingException(null, e); } } /** * Decodes a double from exactly 8 bytes. * * @param src source of encoded bytes * @param srcOffset offset into source array * @return double value */ public static double decodeDouble(byte[] src, int srcOffset) throws CorruptEncodingException { long bits = decodeDoubleBits(src, srcOffset); bits ^= (bits < 0) ? 0x8000000000000000L : 0xffffffffffffffffL; return Double.longBitsToDouble(bits); } /** * Decodes a Double object from exactly 8 bytes. * * @param src source of encoded bytes * @param srcOffset offset into source array * @return Double object or null */ public static Double decodeDoubleObj(byte[] src, int srcOffset) throws CorruptEncodingException { long bits = decodeDoubleBits(src, srcOffset); bits ^= (bits < 0) ? 0x8000000000000000L : 0xffffffffffffffffL; return bits == 0x7fffffffffffffffL ? null : Double.longBitsToDouble(bits); } protected static long decodeDoubleBits(byte[] src, int srcOffset) throws CorruptEncodingException { try { return (((long) (((src[srcOffset]) << 24) | ((src[srcOffset + 1] & 0xff) << 16) | ((src[srcOffset + 2] & 0xff) << 8) | ((src[srcOffset + 3] & 0xff)))) << 32) | (((((src[srcOffset + 4]) << 24) | ((src[srcOffset + 5] & 0xff) << 16) | ((src[srcOffset + 6] & 0xff) << 8) | ((src[srcOffset + 7] & 0xff))) & 0xffffffffL)); } catch (IndexOutOfBoundsException e) { throw new CorruptEncodingException(null, e); } } /** * Decodes a BigInteger. * * @param src source of encoded data * @param srcOffset offset into encoded data * @param valueRef decoded BigInteger 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, BigInteger[] valueRef) throws CorruptEncodingException { byte[][] bytesRef = new byte[1][]; int amt = decode(src, srcOffset, bytesRef); valueRef[0] = (bytesRef[0] == null) ? null : new BigInteger(bytesRef[0]); 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. * * @param src source of encoded data * @param srcOffset offset into encoded data * @param valueRef decoded byte array is stored in element 0, which may be * null * @return amount of bytes read from source * @throws CorruptEncodingException if source data is corrupt */ public static int decode(byte[] src, int srcOffset, byte[][] valueRef) throws CorruptEncodingException { try { final int originalOffset = srcOffset; int b = src[srcOffset++] & 0xff; if (b >= 0xf8) { valueRef[0] = null; return 1; } int valueLength; if (b <= 0x7f) { valueLength = b; } else if (b <= 0xbf) { valueLength = ((b & 0x3f) << 8) | (src[srcOffset++] & 0xff); } else if (b <= 0xdf) { valueLength = ((b & 0x1f) << 16) | ((src[srcOffset++] & 0xff) << 8) | (src[srcOffset++] & 0xff); } else if (b <= 0xef) { valueLength = ((b & 0x0f) << 24) | ((src[srcOffset++] & 0xff) << 16) | ((src[srcOffset++] & 0xff) << 8) | (src[srcOffset++] & 0xff); } else { valueLength = ((src[srcOffset++] & 0xff) << 24) | ((src[srcOffset++] & 0xff) << 16) | ((src[srcOffset++] & 0xff) << 8) | (src[srcOffset++] & 0xff); } if (valueLength == 0) { valueRef[0] = EMPTY_BYTE_ARRAY; } else { byte[] value = new byte[valueLength]; System.arraycopy(src, srcOffset, value, 0, valueLength); valueRef[0] = value; } return srcOffset - originalOffset + valueLength; } catch (IndexOutOfBoundsException e) { throw new CorruptEncodingException(null, e); } } /** * Decodes an encoded string from the given byte array. * * @param src source of encoded data * @param srcOffset offset into encoded data * @param valueRef decoded string is stored in element 0, which may be null * @return amount of bytes read from source * @throws CorruptEncodingException if source data is corrupt */ public static int decodeString(byte[] src, int srcOffset, String[] valueRef) throws CorruptEncodingException { try { final int originalOffset = srcOffset; int b = src[srcOffset++] & 0xff; if (b >= 0xf8) { valueRef[0] = null; return 1; } int valueLength; if (b <= 0x7f) { valueLength = b; } else if (b <= 0xbf) { valueLength = ((b & 0x3f) << 8) | (src[srcOffset++] & 0xff); } else if (b <= 0xdf) { valueLength = ((b & 0x1f) << 16) | ((src[srcOffset++] & 0xff) << 8) | (src[srcOffset++] & 0xff); } else if (b <= 0xef) { valueLength = ((b & 0x0f) << 24) | ((src[srcOffset++] & 0xff) << 16) | ((src[srcOffset++] & 0xff) << 8) | (src[srcOffset++] & 0xff); } else { valueLength = ((src[srcOffset++] & 0xff) << 24) | ((src[srcOffset++] & 0xff) << 16) | ((src[srcOffset++] & 0xff) << 8) | (src[srcOffset++] & 0xff); } if (valueLength == 0) { valueRef[0] = ""; return srcOffset - originalOffset; } char[] value; try { value = new char[valueLength]; } catch (NegativeArraySizeException e) { throw new CorruptEncodingException("Corrupt encoded string length (negative size): " + valueLength); } catch (OutOfMemoryError e) { throw new CorruptEncodingException("Corrupt encoded string length (too large): " + valueLength, e); } int valueOffset = 0; while (valueOffset < valueLength) { int c = src[srcOffset++] & 0xff; switch (c >> 5) { case 0: case 1: case 2: case 3: // 0xxxxxxx value[valueOffset++] = (char) c; break; case 4: case 5: // 10xxxxxx xxxxxxxx value[valueOffset++] = (char) (((c & 0x3f) << 8) | (src[srcOffset++] & 0xff)); break; case 6: // 110xxxxx xxxxxxxx xxxxxxxx c = ((c & 0x1f) << 16) | ((src[srcOffset++] & 0xff) << 8) | (src[srcOffset++] & 0xff); if (c >= 0x10000) { // Split into surrogate pair. c -= 0x10000; value[valueOffset++] = (char) (0xd800 | ((c >> 10) & 0x3ff)); value[valueOffset++] = (char) (0xdc00 | (c & 0x3ff)); } else { value[valueOffset++] = (char) c; } break; default: // 111xxxxx // Illegal. throw new CorruptEncodingException("Corrupt encoded string data (source offset = " + (srcOffset - 1) + ')'); } } valueRef[0] = new String(value); return srcOffset - originalOffset; } catch (IndexOutOfBoundsException e) { throw new CorruptEncodingException(null, e); } } /** * Decodes a length value which was encoded by * {@link DataEncoder#writeLength}. * * @return length value * @since 1.2 */ public static int readLength(InputStream in) throws IOException, EOFException { int b0 = in.read(); if (b0 < 0) { throw new EOFException(); } if (b0 <= 0x7f) { return b0; } int b1 = in.read(); if (b1 < 0) { throw new EOFException(); } if (b0 <= 0xbf) { return ((b0 & 0x3f) << 8) | b1; } int b2 = in.read(); if (b2 < 0) { throw new EOFException(); } if (b0 <= 0xdf) { return ((b0 & 0x1f) << 16) | (b1 << 8) | b2; } int b3 = in.read(); if (b3 < 0) { throw new EOFException(); } if (b0 <= 0xef) { return ((b0 & 0x0f) << 24) | (b1 << 16) | (b2 << 8) | b3; } int b4 = in.read(); if (b4 < 0) { throw new EOFException(); } return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4; } /** * Reads as many bytes from the stream as is necessary to fill the given * byte array. An EOFException is thrown if the stream end is encountered. * * @since 1.2 */ public static void readFully(InputStream in, byte[] b) throws IOException, EOFException { final int length = b.length; int total = 0; while (total < length) { int amt = in.read(b, total, length - total); if (amt < 0) { throw new EOFException(); } total += amt; } } /** * Decodes the given byte array which was encoded by * {@link DataEncoder#encodeSingle}. Always returns a new byte array * instance. * * @param prefixPadding amount of extra bytes to skip from start of encoded * byte array * @param suffixPadding amount of extra bytes to skip at end of encoded byte * array */ public static byte[] decodeSingle(byte[] src, int prefixPadding, int suffixPadding) throws CorruptEncodingException { try { int length = src.length - suffixPadding - prefixPadding; if (length == 0) { return EMPTY_BYTE_ARRAY; } if (prefixPadding <= 0 && suffixPadding <= 0) { // Always return a new byte array instance return src.clone(); } byte[] dst = new byte[length]; System.arraycopy(src, prefixPadding, dst, 0, length); return dst; } catch (IndexOutOfBoundsException e) { throw new CorruptEncodingException(null, e); } } /** * Decodes the given byte array which was encoded by * {@link DataEncoder#encodeSingleNullable}. Always returns a new byte array * instance. */ public static byte[] decodeSingleNullable(byte[] src) throws CorruptEncodingException { return decodeSingleNullable(src, 0, 0); } /** * Decodes the given byte array which was encoded by * {@link DataEncoder#encodeSingleNullable}. Always returns a new byte array * instance. * * @param prefixPadding amount of extra bytes to skip from start of encoded * byte array * @param suffixPadding amount of extra bytes to skip at end of encoded byte * array */ public static byte[] decodeSingleNullable(byte[] src, int prefixPadding, int suffixPadding) throws CorruptEncodingException { try { byte b = src[prefixPadding]; if (b == NULL_BYTE_HIGH || b == NULL_BYTE_LOW) { return null; } int length = src.length - suffixPadding - 1 - prefixPadding; if (length == 0) { return EMPTY_BYTE_ARRAY; } byte[] value = new byte[length]; System.arraycopy(src, 1 + prefixPadding, value, 0, length); return value; } catch (IndexOutOfBoundsException e) { throw new CorruptEncodingException(null, e); } } }