From 79cebfadf8703afe9bf28786bc4df1af348c876e Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" Date: Thu, 5 Oct 2006 03:54:20 +0000 Subject: Moved raw package out of spi package. --- .../amazon/carbonado/spi/StorableSerializer.java | 2 +- .../carbonado/spi/raw/CustomStorableCodec.java | 332 ---- .../spi/raw/CustomStorableCodecFactory.java | 70 - .../com/amazon/carbonado/spi/raw/DataDecoder.java | 567 ------ .../com/amazon/carbonado/spi/raw/DataEncoder.java | 595 ------ .../carbonado/spi/raw/GenericEncodingStrategy.java | 1963 -------------------- .../carbonado/spi/raw/GenericInstanceFactory.java | 36 - .../carbonado/spi/raw/GenericPropertyInfo.java | 60 - .../carbonado/spi/raw/GenericStorableCodec.java | 813 -------- .../spi/raw/GenericStorableCodecFactory.java | 76 - .../com/amazon/carbonado/spi/raw/KeyDecoder.java | 646 ------- .../com/amazon/carbonado/spi/raw/KeyEncoder.java | 741 -------- .../carbonado/spi/raw/LayoutPropertyInfo.java | 86 - .../com/amazon/carbonado/spi/raw/RawCursor.java | 743 -------- .../carbonado/spi/raw/RawStorableGenerator.java | 355 ---- .../com/amazon/carbonado/spi/raw/RawSupport.java | 97 - .../java/com/amazon/carbonado/spi/raw/RawUtil.java | 66 - .../amazon/carbonado/spi/raw/StorableCodec.java | 118 -- .../carbonado/spi/raw/StorableCodecFactory.java | 54 - .../carbonado/spi/raw/StorablePropertyInfo.java | 132 -- .../com/amazon/carbonado/spi/raw/package-info.java | 23 - 21 files changed, 1 insertion(+), 7574 deletions(-) delete mode 100644 src/main/java/com/amazon/carbonado/spi/raw/CustomStorableCodec.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/raw/CustomStorableCodecFactory.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/raw/DataDecoder.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/raw/DataEncoder.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/raw/GenericEncodingStrategy.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/raw/GenericInstanceFactory.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/raw/GenericPropertyInfo.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/raw/GenericStorableCodec.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/raw/GenericStorableCodecFactory.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/raw/KeyDecoder.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/raw/KeyEncoder.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/raw/LayoutPropertyInfo.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/raw/RawCursor.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/raw/RawStorableGenerator.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/raw/RawSupport.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/raw/RawUtil.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/raw/StorableCodec.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/raw/StorableCodecFactory.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/raw/StorablePropertyInfo.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/raw/package-info.java (limited to 'src/main/java/com/amazon/carbonado/spi') diff --git a/src/main/java/com/amazon/carbonado/spi/StorableSerializer.java b/src/main/java/com/amazon/carbonado/spi/StorableSerializer.java index 1f6ab6e..83621e9 100644 --- a/src/main/java/com/amazon/carbonado/spi/StorableSerializer.java +++ b/src/main/java/com/amazon/carbonado/spi/StorableSerializer.java @@ -52,7 +52,7 @@ import com.amazon.carbonado.info.StorableProperty; import static com.amazon.carbonado.spi.CommonMethodNames.*; -import com.amazon.carbonado.spi.raw.GenericEncodingStrategy; +import com.amazon.carbonado.raw.GenericEncodingStrategy; /** * Support for general-purpose serialization of storables. diff --git a/src/main/java/com/amazon/carbonado/spi/raw/CustomStorableCodec.java b/src/main/java/com/amazon/carbonado/spi/raw/CustomStorableCodec.java deleted file mode 100644 index f908635..0000000 --- a/src/main/java/com/amazon/carbonado/spi/raw/CustomStorableCodec.java +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Copyright 2006 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.amazon.carbonado.spi.raw; - -import java.util.Map; - -import org.cojen.classfile.ClassFile; -import org.cojen.classfile.CodeBuilder; -import org.cojen.classfile.MethodInfo; -import org.cojen.classfile.Modifiers; -import org.cojen.classfile.TypeDesc; -import org.cojen.util.ClassInjector; -import org.cojen.util.WeakIdentityMap; - -import com.amazon.carbonado.CorruptEncodingException; -import com.amazon.carbonado.FetchException; -import com.amazon.carbonado.Storable; -import com.amazon.carbonado.Storage; -import com.amazon.carbonado.SupportException; - -import com.amazon.carbonado.info.Direction; -import com.amazon.carbonado.info.StorableIndex; -import com.amazon.carbonado.info.StorableIntrospector; -import com.amazon.carbonado.info.StorableProperty; - -import com.amazon.carbonado.util.QuickConstructorGenerator; - -/** - * Allows codecs to be defined for storables that have a custom encoding. - * - * @author Brian S O'Neill - * @see CustomStorableCodecFactory - */ -public abstract class CustomStorableCodec implements StorableCodec { - // Generated storable instances maintain a reference to user-defined - // concrete subclass of this class. - private static final String CUSTOM_STORABLE_CODEC_FIELD_NAME = "customStorableCodec$"; - - @SuppressWarnings("unchecked") - private static Map> cCache = - new WeakIdentityMap(); - - /** - * Returns a storable implementation that calls into CustomStorableCodec - * implementation for encoding and decoding. - */ - @SuppressWarnings("unchecked") - static Class - getStorableClass(Class type, boolean isMaster) - throws SupportException - { - synchronized (cCache) { - Class storableClass; - - RawStorableGenerator.Flavors flavors = - (RawStorableGenerator.Flavors) cCache.get(type); - - if (flavors == null) { - flavors = new RawStorableGenerator.Flavors(); - cCache.put(type, flavors); - } else if ((storableClass = flavors.getClass(isMaster)) != null) { - return storableClass; - } - - storableClass = generateStorableClass(type, isMaster); - flavors.setClass(storableClass, isMaster); - - return storableClass; - } - } - - @SuppressWarnings("unchecked") - private static Class - generateStorableClass(Class type, boolean isMaster) - throws SupportException - { - final Class abstractClass = - RawStorableGenerator.getAbstractClass(type, isMaster); - - ClassInjector ci = ClassInjector.create - (type.getName(), abstractClass.getClassLoader()); - - ClassFile cf = new ClassFile(ci.getClassName(), abstractClass); - cf.markSynthetic(); - cf.setSourceFile(CustomStorableCodec.class.getName()); - cf.setTarget("1.5"); - - // Declare some types. - final TypeDesc rawSupportType = TypeDesc.forClass(RawSupport.class); - final TypeDesc byteArrayType = TypeDesc.forClass(byte[].class); - final TypeDesc[] byteArrayParam = {byteArrayType}; - final TypeDesc customStorableCodecType = TypeDesc.forClass(CustomStorableCodec.class); - - // Add field for saving reference to concrete CustomStorableCodec. - cf.addField(Modifiers.PRIVATE.toFinal(true), - CUSTOM_STORABLE_CODEC_FIELD_NAME, customStorableCodecType); - - // Add constructor that accepts a RawSupport and a CustomStorableCodec. - { - TypeDesc[] params = {rawSupportType, customStorableCodecType}; - MethodInfo mi = cf.addConstructor(Modifiers.PUBLIC, params); - CodeBuilder b = new CodeBuilder(mi); - - // Call super class constructor. - b.loadThis(); - b.loadLocal(b.getParameter(0)); - params = new TypeDesc[] {rawSupportType}; - b.invokeSuperConstructor(params); - - // Set private reference to customStorableCodec. - b.loadThis(); - b.loadLocal(b.getParameter(1)); - b.storeField(CUSTOM_STORABLE_CODEC_FIELD_NAME, customStorableCodecType); - - b.returnVoid(); - } - - // Add constructor that accepts a RawSupport, an encoded key, an - // encoded data, and a CustomStorableCodec. - { - TypeDesc[] params = {rawSupportType, byteArrayType, byteArrayType, - customStorableCodecType}; - MethodInfo mi = cf.addConstructor(Modifiers.PUBLIC, params); - CodeBuilder b = new CodeBuilder(mi); - - // Set private reference to customStorableCodec before calling - // super constructor. This is necessary because super class - // constructor will call our decode methods, which need the - // customStorableCodec. This trick is not allowed in Java, but the - // virtual machine verifier allows it. - b.loadThis(); - b.loadLocal(b.getParameter(3)); - b.storeField(CUSTOM_STORABLE_CODEC_FIELD_NAME, customStorableCodecType); - - // Now call super class constructor. - b.loadThis(); - b.loadLocal(b.getParameter(0)); - b.loadLocal(b.getParameter(1)); - b.loadLocal(b.getParameter(2)); - params = new TypeDesc[] {rawSupportType, byteArrayType, byteArrayType}; - b.invokeSuperConstructor(params); - - b.returnVoid(); - } - - // Implement protected abstract methods inherited from parent class. - - // byte[] encodeKey() - { - // Encode the primary key into a byte array that supports correct - // ordering. No special key comparator is needed. - MethodInfo mi = cf.addMethod(Modifiers.PROTECTED, - RawStorableGenerator.ENCODE_KEY_METHOD_NAME, - byteArrayType, null); - CodeBuilder b = new CodeBuilder(mi); - - b.loadThis(); - b.loadField(CUSTOM_STORABLE_CODEC_FIELD_NAME, customStorableCodecType); - TypeDesc[] params = {TypeDesc.forClass(Storable.class)}; - b.loadThis(); - b.invokeVirtual(customStorableCodecType, "encodePrimaryKey", byteArrayType, params); - b.returnValue(byteArrayType); - } - - // byte[] encodeData() - { - // Encoding non-primary key data properties. - MethodInfo mi = cf.addMethod(Modifiers.PROTECTED, - RawStorableGenerator.ENCODE_DATA_METHOD_NAME, - byteArrayType, null); - CodeBuilder b = new CodeBuilder(mi); - - b.loadThis(); - b.loadField(CUSTOM_STORABLE_CODEC_FIELD_NAME, customStorableCodecType); - TypeDesc[] params = {TypeDesc.forClass(Storable.class)}; - b.loadThis(); - b.invokeVirtual(customStorableCodecType, "encodeData", byteArrayType, params); - b.returnValue(byteArrayType); - } - - // void decodeKey(byte[]) - { - MethodInfo mi = cf.addMethod(Modifiers.PROTECTED, - RawStorableGenerator.DECODE_KEY_METHOD_NAME, - null, byteArrayParam); - CodeBuilder b = new CodeBuilder(mi); - - b.loadThis(); - b.loadField(CUSTOM_STORABLE_CODEC_FIELD_NAME, customStorableCodecType); - TypeDesc[] params = {TypeDesc.forClass(Storable.class), byteArrayType}; - b.loadThis(); - b.loadLocal(b.getParameter(0)); - b.invokeVirtual(customStorableCodecType, "decodePrimaryKey", null, params); - b.returnVoid(); - } - - // void decodeData(byte[]) - { - MethodInfo mi = cf.addMethod(Modifiers.PROTECTED, - RawStorableGenerator.DECODE_DATA_METHOD_NAME, - null, byteArrayParam); - CodeBuilder b = new CodeBuilder(mi); - - b.loadThis(); - b.loadField(CUSTOM_STORABLE_CODEC_FIELD_NAME, customStorableCodecType); - TypeDesc[] params = {TypeDesc.forClass(Storable.class), byteArrayType}; - b.loadThis(); - b.loadLocal(b.getParameter(0)); - b.invokeVirtual(customStorableCodecType, "decodeData", null, params); - b.returnVoid(); - } - - return ci.defineClass(cf); - } - - private final Class mType; - private final int mPkPropertyCount; - private final InstanceFactory mInstanceFactory; - - public interface InstanceFactory { - Storable instantiate(RawSupport support, CustomStorableCodec codec); - - Storable instantiate(RawSupport support, byte[] key, byte[] value, - CustomStorableCodec codec) - throws FetchException; - } - - /** - * @param isMaster when true, version properties and sequences are managed - * @throws SupportException if Storable is not supported - */ - public CustomStorableCodec(Class type, boolean isMaster) throws SupportException { - mType = type; - mPkPropertyCount = getPrimaryKeyIndex().getPropertyCount(); - Class storableClass = getStorableClass(type, isMaster); - mInstanceFactory = QuickConstructorGenerator - .getInstance(storableClass, InstanceFactory.class); - } - - public Class getStorableType() { - return mType; - } - - @SuppressWarnings("unchecked") - public S instantiate(RawSupport support) { - return (S) mInstanceFactory.instantiate(support, this); - } - - @SuppressWarnings("unchecked") - public S instantiate(RawSupport support, byte[] key, byte[] value) - throws FetchException - { - return (S) mInstanceFactory.instantiate(support, key, value, this); - } - - public byte[] encodePrimaryKey(S storable) { - return encodePrimaryKey(storable, 0, mPkPropertyCount); - } - - public byte[] encodePrimaryKey(Object[] values) { - return encodePrimaryKey(values, 0, mPkPropertyCount); - } - - /** - * Convenient access to all the storable properties. - */ - public Map> getAllProperties() { - return StorableIntrospector.examine(getStorableType()).getAllProperties(); - } - - /** - * Convenient way to define the clustered primary key index - * descriptor. Direction can be specified by prefixing the property name - * with a '+' or '-'. If unspecified, direction is assumed to be ascending. - */ - @SuppressWarnings("unchecked") - public StorableIndex buildPkIndex(String... propertyNames) { - Map> map = getAllProperties(); - int length = propertyNames.length; - StorableProperty[] properties = new StorableProperty[length]; - Direction[] directions = new Direction[length]; - for (int i=0; i(properties, directions, true, true); - } - - /** - * Decode the primary key into properties of the storable. - */ - public abstract void decodePrimaryKey(S storable, byte[] bytes) - throws CorruptEncodingException; - - /** - * Encode all properties of the storable excluding the primary key. - */ - public abstract byte[] encodeData(S storable); - - /** - * Decode the data into properties of the storable. - */ - public abstract void decodeData(S storable, byte[] bytes) - throws CorruptEncodingException; -} diff --git a/src/main/java/com/amazon/carbonado/spi/raw/CustomStorableCodecFactory.java b/src/main/java/com/amazon/carbonado/spi/raw/CustomStorableCodecFactory.java deleted file mode 100644 index 64d19e8..0000000 --- a/src/main/java/com/amazon/carbonado/spi/raw/CustomStorableCodecFactory.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2006 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.amazon.carbonado.spi.raw; - -import com.amazon.carbonado.Storable; -import com.amazon.carbonado.SupportException; - -import com.amazon.carbonado.info.StorableIndex; -import com.amazon.carbonado.layout.Layout; - -/** - * Factory for custom storable codecs. - * - * @author Brian S O'Neill - */ -public abstract class CustomStorableCodecFactory implements StorableCodecFactory { - public CustomStorableCodecFactory() { - } - - /** - * Returns null to let repository decide what the name should be. - */ - public String getStorageName(Class type) throws SupportException { - return null; - } - - /** - * @param type type of storable to create codec for - * @param pkIndex ignored - * @param isMaster when true, version properties and sequences are managed - * @param layout when non-null, attempt to encode a storable layout - * generation value in each storable - * @throws SupportException if type is not supported - */ - public CustomStorableCodec createCodec(Class type, - StorableIndex pkIndex, - boolean isMaster, - Layout layout) - throws SupportException - { - return createCodec(type, isMaster, layout); - } - - /** - * @param type type of storable to create codec for - * @param isMaster when true, version properties and sequences are managed - * @param layout when non-null, attempt to encode a storable layout - * generation value in each storable - * @throws SupportException if type is not supported - */ - protected abstract CustomStorableCodec - createCodec(Class type, boolean isMaster, Layout layout) - throws SupportException; -} diff --git a/src/main/java/com/amazon/carbonado/spi/raw/DataDecoder.java b/src/main/java/com/amazon/carbonado/spi/raw/DataDecoder.java deleted file mode 100644 index 66f2f2f..0000000 --- a/src/main/java/com/amazon/carbonado/spi/raw/DataDecoder.java +++ /dev/null @@ -1,567 +0,0 @@ -/* - * Copyright 2006 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.amazon.carbonado.spi.raw; - -import com.amazon.carbonado.CorruptEncodingException; - -import static com.amazon.carbonado.spi.raw.DataEncoder.*; - -/** - * A very low-level class that decodes key components encoded by methods of - * {@link DataEncoder}. - * - * @author Brian S O'Neill - */ -public class DataDecoder { - static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; - - /** - * 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) | - (((long)(((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) | - (((long)(((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 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 = ((b & 0x07) << 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 = new char[valueLength]; - 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 the given byte array which was encoded by {@link - * DataEncoder#encodeSingle}. - * - * @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) { - return src; - } - 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}. - */ - 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}. - * - * @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); - } - } -} diff --git a/src/main/java/com/amazon/carbonado/spi/raw/DataEncoder.java b/src/main/java/com/amazon/carbonado/spi/raw/DataEncoder.java deleted file mode 100644 index 15b8dca..0000000 --- a/src/main/java/com/amazon/carbonado/spi/raw/DataEncoder.java +++ /dev/null @@ -1,595 +0,0 @@ -/* - * Copyright 2006 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.amazon.carbonado.spi.raw; - -/** - * A very low-level class that supports encoding of primitive data. For - * encoding data into keys, see {@link KeyEncoder}. - * - * @author Brian S O'Neill - * @see DataDecoder - */ -public class DataEncoder { - // Note: Most of these methods are inherited by KeyEncoder, which is why - // they are encoded for supporting proper ordering. - - /** Byte to use for null, low ordering */ - static final byte NULL_BYTE_LOW = 0; - - /** Byte to use for null, high ordering */ - static final byte NULL_BYTE_HIGH = (byte)~NULL_BYTE_LOW; - - /** Byte to use for not-null, low ordering */ - static final byte NOT_NULL_BYTE_HIGH = (byte)128; - - /** Byte to use for not-null, high ordering */ - static final byte NOT_NULL_BYTE_LOW = (byte)~NOT_NULL_BYTE_HIGH; - - static final byte[] NULL_BYTE_ARRAY_HIGH = {NULL_BYTE_HIGH}; - static final byte[] NULL_BYTE_ARRAY_LOW = {NULL_BYTE_LOW}; - static final byte[] NOT_NULL_BYTE_ARRAY_HIGH = {NOT_NULL_BYTE_HIGH}; - static final byte[] NOT_NULL_BYTE_ARRAY_LOW = {NOT_NULL_BYTE_LOW}; - - /** - * Encodes the given signed integer into exactly 4 bytes. - * - * @param value signed integer value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - */ - public static void encode(int value, byte[] dst, int dstOffset) { - value ^= 0x80000000; - dst[dstOffset ] = (byte)(value >> 24); - dst[dstOffset + 1] = (byte)(value >> 16); - dst[dstOffset + 2] = (byte)(value >> 8); - dst[dstOffset + 3] = (byte)value; - } - - /** - * Encodes the given signed Integer object into exactly 1 or 5 bytes. If - * the Integer object is never expected to be null, consider encoding as an - * int primitive. - * - * @param value optional signed Integer value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - * @return amount of bytes written - */ - public static int encode(Integer value, byte[] dst, int dstOffset) { - if (value == null) { - dst[dstOffset] = NULL_BYTE_HIGH; - return 1; - } else { - dst[dstOffset] = NOT_NULL_BYTE_HIGH; - encode(value.intValue(), dst, dstOffset + 1); - return 5; - } - } - - /** - * Encodes the given signed long into exactly 8 bytes. - * - * @param value signed long value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - */ - public static void encode(long value, byte[] dst, int dstOffset) { - int w = ((int)(value >> 32)) ^ 0x80000000; - dst[dstOffset ] = (byte)(w >> 24); - dst[dstOffset + 1] = (byte)(w >> 16); - dst[dstOffset + 2] = (byte)(w >> 8); - dst[dstOffset + 3] = (byte)w; - w = (int)value; - dst[dstOffset + 4] = (byte)(w >> 24); - dst[dstOffset + 5] = (byte)(w >> 16); - dst[dstOffset + 6] = (byte)(w >> 8); - dst[dstOffset + 7] = (byte)w; - } - - /** - * Encodes the given signed Long object into exactly 1 or 9 bytes. If the - * Long object is never expected to be null, consider encoding as a long - * primitive. - * - * @param value optional signed Long value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - * @return amount of bytes written - */ - public static int encode(Long value, byte[] dst, int dstOffset) { - if (value == null) { - dst[dstOffset] = NULL_BYTE_HIGH; - return 1; - } else { - dst[dstOffset] = NOT_NULL_BYTE_HIGH; - encode(value.longValue(), dst, dstOffset + 1); - return 9; - } - } - - /** - * Encodes the given signed byte into exactly 1 byte. - * - * @param value signed byte value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - */ - public static void encode(byte value, byte[] dst, int dstOffset) { - dst[dstOffset] = (byte)(value ^ 0x80); - } - - /** - * Encodes the given signed Byte object into exactly 1 or 2 bytes. If the - * Byte object is never expected to be null, consider encoding as a byte - * primitive. - * - * @param value optional signed Byte value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - * @return amount of bytes written - */ - public static int encode(Byte value, byte[] dst, int dstOffset) { - if (value == null) { - dst[dstOffset] = NULL_BYTE_HIGH; - return 1; - } else { - dst[dstOffset] = NOT_NULL_BYTE_HIGH; - dst[dstOffset + 1] = (byte)(value ^ 0x80); - return 2; - } - } - - /** - * Encodes the given signed short into exactly 2 bytes. - * - * @param value signed short value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - */ - public static void encode(short value, byte[] dst, int dstOffset) { - value ^= 0x8000; - dst[dstOffset ] = (byte)(value >> 8); - dst[dstOffset + 1] = (byte)value; - } - - /** - * Encodes the given signed Short object into exactly 1 or 3 bytes. If the - * Short object is never expected to be null, consider encoding as a short - * primitive. - * - * @param value optional signed Short value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - * @return amount of bytes written - */ - public static int encode(Short value, byte[] dst, int dstOffset) { - if (value == null) { - dst[dstOffset] = NULL_BYTE_HIGH; - return 1; - } else { - dst[dstOffset] = NOT_NULL_BYTE_HIGH; - encode(value.shortValue(), dst, dstOffset + 1); - return 3; - } - } - - /** - * Encodes the given character into exactly 2 bytes. - * - * @param value character value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - */ - public static void encode(char value, byte[] dst, int dstOffset) { - dst[dstOffset ] = (byte)(value >> 8); - dst[dstOffset + 1] = (byte)value; - } - - /** - * Encodes the given Character object into exactly 1 or 3 bytes. If the - * Character object is never expected to be null, consider encoding as a - * char primitive. - * - * @param value optional Character value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - * @return amount of bytes written - */ - public static int encode(Character value, byte[] dst, int dstOffset) { - if (value == null) { - dst[dstOffset] = NULL_BYTE_HIGH; - return 1; - } else { - dst[dstOffset] = NOT_NULL_BYTE_HIGH; - encode(value.charValue(), dst, dstOffset + 1); - return 3; - } - } - - /** - * Encodes the given boolean into exactly 1 byte. - * - * @param value boolean value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - */ - public static void encode(boolean value, byte[] dst, int dstOffset) { - dst[dstOffset] = value ? (byte)128 : (byte)127; - } - - /** - * Encodes the given Boolean object into exactly 1 byte. - * - * @param value optional Boolean value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - */ - public static void encode(Boolean value, byte[] dst, int dstOffset) { - if (value == null) { - dst[dstOffset] = NULL_BYTE_HIGH; - } else { - dst[dstOffset] = value.booleanValue() ? (byte)128 : (byte)127; - } - } - - /** - * Encodes the given float into exactly 4 bytes. - * - * @param value float value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - */ - public static void encode(float value, byte[] dst, int dstOffset) { - int bits = Float.floatToIntBits(value); - bits ^= (bits < 0) ? 0xffffffff : 0x80000000; - dst[dstOffset ] = (byte)(bits >> 24); - dst[dstOffset + 1] = (byte)(bits >> 16); - dst[dstOffset + 2] = (byte)(bits >> 8); - dst[dstOffset + 3] = (byte)bits; - } - - /** - * Encodes the given Float object into exactly 4 bytes. A non-canonical NaN - * value is used to represent null. - * - * @param value optional Float value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - */ - public static void encode(Float value, byte[] dst, int dstOffset) { - if (value == null) { - encode(0x7fffffff, dst, dstOffset); - } else { - encode(value.floatValue(), dst, dstOffset); - } - } - - /** - * Encodes the given double into exactly 8 bytes. - * - * @param value double value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - */ - public static void encode(double value, byte[] dst, int dstOffset) { - long bits = Double.doubleToLongBits(value); - bits ^= (bits < 0) ? 0xffffffffffffffffL : 0x8000000000000000L; - int w = (int)(bits >> 32); - dst[dstOffset ] = (byte)(w >> 24); - dst[dstOffset + 1] = (byte)(w >> 16); - dst[dstOffset + 2] = (byte)(w >> 8); - dst[dstOffset + 3] = (byte)w; - w = (int)bits; - dst[dstOffset + 4] = (byte)(w >> 24); - dst[dstOffset + 5] = (byte)(w >> 16); - dst[dstOffset + 6] = (byte)(w >> 8); - dst[dstOffset + 7] = (byte)w; - } - - /** - * Encodes the given Double object into exactly 8 bytes. A non-canonical - * NaN value is used to represent null. - * - * @param value optional Double value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - */ - public static void encode(Double value, byte[] dst, int dstOffset) { - if (value == null) { - encode(0x7fffffffffffffffL, dst, dstOffset); - } else { - encode(value.doubleValue(), dst, dstOffset); - } - } - - /** - * Encodes the given optional byte array into a variable amount of - * bytes. If the byte array is null, exactly 1 byte is written. Otherwise, - * the amount written can be determined by calling calculateEncodedLength. - * - * @param value byte array value to encode, may be null - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - * @return amount of bytes written - */ - public static int encode(byte[] value, byte[] dst, int dstOffset) { - if (value == null) { - dst[dstOffset] = NULL_BYTE_HIGH; - return 1; - } - return encode(value, 0, value.length, dst, dstOffset); - } - - /** - * Encodes the given optional byte array into a variable amount of - * bytes. If the byte array is null, exactly 1 byte is written. Otherwise, - * the amount written can be determined by calling calculateEncodedLength. - * - * @param value byte array value to encode, may be null - * @param valueOffset offset into byte array - * @param valueLength length of data in byte array - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - * @return amount of bytes written - */ - public static int encode(byte[] value, int valueOffset, int valueLength, - byte[] dst, int dstOffset) { - if (value == null) { - dst[dstOffset] = NULL_BYTE_HIGH; - return 1; - } - - // Write the value length first, in a variable amount of bytes. - int amt = writeLength(valueLength, dst, dstOffset); - - // Now write the value. - System.arraycopy(value, valueOffset, dst, dstOffset + amt, valueLength); - - return amt + valueLength; - } - - /** - * Returns the amount of bytes required to encode the given byte array. - * - * @param value byte array value to encode, may be null - * @return amount of bytes needed to encode - */ - public static int calculateEncodedLength(byte[] value) { - return value == null ? 1 : calculateEncodedLength(value, 0, value.length); - } - - /** - * Returns the amount of bytes required to encode the given byte array. - * - * @param value byte array value to encode, may be null - * @param valueOffset offset into byte array - * @param valueLength length of data in byte array - * @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; - } - } - - /** - * Encodes the given optional String into a variable amount of bytes. The - * amount written can be determined by calling - * calculateEncodedStringLength. - *

- * Strings are encoded in a fashion similar to UTF-8, in that ASCII - * characters are written in one byte. This encoding is more efficient than - * UTF-8, but it isn't compatible with UTF-8. - * - * @param value String value to encode, may be null - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - * @return amount of bytes written - */ - public static int encode(String value, byte[] dst, int dstOffset) { - if (value == null) { - dst[dstOffset] = NULL_BYTE_HIGH; - return 1; - } - final int originalOffset = dstOffset; - - int valueLength = value.length(); - - // Write the value length first, in a variable amount of bytes. - dstOffset += writeLength(valueLength, dst, dstOffset); - - for (int i = 0; i < valueLength; i++) { - int c = value.charAt(i); - if (c <= 0x7f) { - dst[dstOffset++] = (byte)c; - } else if (c <= 0x3fff) { - dst[dstOffset++] = (byte)(0x80 | (c >> 8)); - dst[dstOffset++] = (byte)(c & 0xff); - } else { - if (c >= 0xd800 && c <= 0xdbff) { - // Found a high surrogate. Verify that surrogate pair is - // well-formed. Low surrogate must follow high surrogate. - if (i + 1 < valueLength) { - int c2 = value.charAt(i + 1); - if (c2 >= 0xdc00 && c2 <= 0xdfff) { - c = 0x10000 + (((c & 0x3ff) << 10) | (c2 & 0x3ff)); - i++; - } - } - } - dst[dstOffset++] = (byte)(0xc0 | (c >> 16)); - dst[dstOffset++] = (byte)((c >> 8) & 0xff); - dst[dstOffset++] = (byte)(c & 0xff); - } - } - - return dstOffset - originalOffset; - } - - /** - * Returns the amount of bytes required to encode the given String. - * - * @param value String to encode, may be null - */ - public static int calculateEncodedStringLength(String value) { - if (value == null) { - return 1; - } - - 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; - } - - for (int i = 0; i < valueLength; i++) { - int c = value.charAt(i); - if (c <= 0x7f) { - encodedLen++; - } else if (c <= 0x3fff) { - encodedLen += 2; - } else { - if (c >= 0xd800 && c <= 0xdbff) { - // Found a high surrogate. Verify that surrogate pair is - // well-formed. Low surrogate must follow high surrogate. - if (i + 1 < valueLength) { - int c2 = value.charAt(i + 1); - if (c2 >= 0xdc00 && c2 <= 0xdfff) { - i++; - } - } - } - encodedLen += 3; - } - } - - return encodedLen; - } - - private static int writeLength(int valueLength, byte[] dst, int dstOffset) { - if (valueLength < 128) { - dst[dstOffset] = (byte)valueLength; - return 1; - } else if (valueLength < 16384) { - dst[dstOffset++] = (byte)((valueLength >> 8) | 0x80); - dst[dstOffset] = (byte)valueLength; - return 2; - } else if (valueLength < 2097152) { - dst[dstOffset++] = (byte)((valueLength >> 16) | 0xc0); - dst[dstOffset++] = (byte)(valueLength >> 8); - dst[dstOffset] = (byte)valueLength; - 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; - 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; - return 5; - } - } - - /** - * Encodes the given byte array for use when there is only a single - * property, whose type is a byte array. The original byte array is - * returned if the padding lengths are zero. - * - * @param prefixPadding amount of extra bytes to allocate at start of encoded byte array - * @param suffixPadding amount of extra bytes to allocate at end of encoded byte array - */ - public static byte[] encodeSingle(byte[] value, int prefixPadding, int suffixPadding) { - if (prefixPadding <= 0 && suffixPadding <= 0) { - return value; - } - int length = value.length; - byte[] dst = new byte[prefixPadding + length + suffixPadding]; - System.arraycopy(value, 0, dst, prefixPadding, length); - return dst; - } - - /** - * Encodes the given byte array for use when there is only a single - * nullable property, whose type is a byte array. - */ - public static byte[] encodeSingleNullable(byte[] value) { - return encodeSingleNullable(value, 0, 0); - } - - /** - * Encodes the given byte array for use when there is only a single - * nullable property, whose type is a byte array. - * - * @param prefixPadding amount of extra bytes to allocate at start of encoded byte array - * @param suffixPadding amount of extra bytes to allocate at end of encoded byte array - */ - public static byte[] encodeSingleNullable(byte[] value, int prefixPadding, int suffixPadding) { - if (prefixPadding <= 0 && suffixPadding <= 0) { - if (value == null) { - return NULL_BYTE_ARRAY_HIGH; - } - - int length = value.length; - if (length == 0) { - return NOT_NULL_BYTE_ARRAY_HIGH; - } - - byte[] dst = new byte[1 + length]; - dst[0] = NOT_NULL_BYTE_HIGH; - System.arraycopy(value, 0, dst, 1, length); - return dst; - } - - if (value == null) { - byte[] dst = new byte[prefixPadding + 1 + suffixPadding]; - dst[prefixPadding] = NULL_BYTE_HIGH; - return dst; - } - - int length = value.length; - byte[] dst = new byte[prefixPadding + 1 + length + suffixPadding]; - dst[prefixPadding] = NOT_NULL_BYTE_HIGH; - System.arraycopy(value, 0, dst, prefixPadding + 1, length); - return dst; - } -} diff --git a/src/main/java/com/amazon/carbonado/spi/raw/GenericEncodingStrategy.java b/src/main/java/com/amazon/carbonado/spi/raw/GenericEncodingStrategy.java deleted file mode 100644 index cd408e9..0000000 --- a/src/main/java/com/amazon/carbonado/spi/raw/GenericEncodingStrategy.java +++ /dev/null @@ -1,1963 +0,0 @@ -/* - * Copyright 2006 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.amazon.carbonado.spi.raw; - -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Map; - -import org.cojen.classfile.CodeAssembler; -import org.cojen.classfile.Label; -import org.cojen.classfile.LocalVariable; -import org.cojen.classfile.Opcode; -import org.cojen.classfile.TypeDesc; -import org.cojen.util.BeanIntrospector; -import org.cojen.util.BeanProperty; - -import com.amazon.carbonado.CorruptEncodingException; -import com.amazon.carbonado.Storable; -import com.amazon.carbonado.SupportException; - -import com.amazon.carbonado.lob.Blob; -import com.amazon.carbonado.lob.Clob; -import com.amazon.carbonado.lob.Lob; - -import com.amazon.carbonado.spi.StorableGenerator; -import com.amazon.carbonado.spi.TriggerSupport; - -import com.amazon.carbonado.info.ChainedProperty; -import com.amazon.carbonado.info.Direction; -import com.amazon.carbonado.info.OrderedProperty; -import com.amazon.carbonado.info.StorableIndex; -import com.amazon.carbonado.info.StorableIntrospector; -import com.amazon.carbonado.info.StorableProperty; -import com.amazon.carbonado.info.StorablePropertyAdapter; - -/** - * Generates bytecode instructions for encoding/decoding Storable properties - * to/from raw bytes. - * - *

Note: subclasses must override and specialize the hashCode and equals - * methods. Failure to do so interferes with {@link StorableCodecFactory}'s - * generated code cache. - * - * @author Brian S O'Neill - */ -public class GenericEncodingStrategy { - private final Class mType; - private final StorableIndex mPkIndex; - - private final int mKeyPrefixPadding; - private final int mKeySuffixPadding; - private final int mDataPrefixPadding; - private final int mDataSuffixPadding; - - /** - * @param type type of Storable to generate code for - * @param pkIndex specifies sequence and ordering of key properties (optional) - */ - public GenericEncodingStrategy(Class type, StorableIndex pkIndex) { - this(type, pkIndex, 0, 0, 0, 0); - } - - /** - * @param type type of Storable to generate code for - * @param pkIndex specifies sequence and ordering of key properties (optional) - * @param keyPrefixPadding amount of padding bytes at start of keys - * @param keySuffixPadding amount of padding bytes at end of keys - * @param dataPrefixPadding amount of padding bytes at start of data values - * @param dataSuffixPadding amount of padding bytes at end of data values - */ - @SuppressWarnings("unchecked") - public GenericEncodingStrategy(Class type, StorableIndex pkIndex, - int keyPrefixPadding, int keySuffixPadding, - int dataPrefixPadding, int dataSuffixPadding) { - mType = type; - - if (keyPrefixPadding < 0 || keySuffixPadding < 0 || - dataPrefixPadding < 0 || dataSuffixPadding < 0) { - throw new IllegalArgumentException(); - } - mKeyPrefixPadding = keyPrefixPadding; - mKeySuffixPadding = keySuffixPadding; - mDataPrefixPadding = dataPrefixPadding; - mDataSuffixPadding = dataSuffixPadding; - - if (pkIndex == null) { - Map> map = - StorableIntrospector.examine(mType).getPrimaryKeyProperties(); - - StorableProperty[] properties = new StorableProperty[map.size()]; - map.values().toArray(properties); - - Direction[] directions = new Direction[map.size()]; - Arrays.fill(directions, Direction.UNSPECIFIED); - - pkIndex = new StorableIndex(properties, directions, true); - } - - mPkIndex = pkIndex; - } - - /** - * Generates bytecode instructions to encode properties. The encoding is - * suitable for "key" encoding, which means it is correctly comparable. - * - *

Note: if a partialStartVar is provided and this strategy has a key - * prefix, the prefix is allocated only if the runtime value of - * partialStartVar is zero. Likewise, if a partialEndVar is provided and - * this strategy has a key suffix, the suffix is allocated only of the - * runtime value of partialEndVar is one less than the property count. - * - * @param assembler code assembler to receive bytecode instructions - * @param properties specific properties to encode, defaults to all key - * properties if null - * @param instanceVar local variable referencing Storable instance, - * defaults to "this" if null. If variable type is an Object array, then - * property values are read from the runtime value of this array instead - * of a Storable instance. - * @param adapterInstanceClass class containing static references to - * adapter instances - defaults to instanceVar - * @param useReadMethods when true, access properties by public read - * methods instead of protected fields - should be used if class being - * generated doesn't have access to these fields - * @param partialStartVar optional variable for supporting partial key - * generation. It must be an int, whose runtime value must be less than the - * properties array length. It marks the range start of the partial - * property range. - * @param partialEndVar optional variable for supporting partial key - * generation. It must be an int, whose runtime value must be less than or - * equal to the properties array length. It marks the range end (exclusive) - * of the partial property range. - * - * @return local variable referencing a byte array with encoded key - * - * @throws SupportException if any property type is not supported - * @throws IllegalArgumentException if assembler is null, or if instanceVar - * is not the correct instance type, or if partial variable types are not - * ints - */ - public LocalVariable buildKeyEncoding(CodeAssembler assembler, - OrderedProperty[] properties, - LocalVariable instanceVar, - Class adapterInstanceClass, - boolean useReadMethods, - LocalVariable partialStartVar, - LocalVariable partialEndVar) - throws SupportException - { - properties = ensureKeyProperties(properties); - return buildEncoding(true, assembler, - extractProperties(properties), extractDirections(properties), - instanceVar, adapterInstanceClass, - useReadMethods, - -1, // no generation support - partialStartVar, partialEndVar); - } - - /** - * Generates bytecode instructions to decode properties. A - * CorruptEncodingException may be thrown from generated code. - * - * @param assembler code assembler to receive bytecode instructions - * @param properties specific properties to decode, defaults to all key - * properties if null - * @param instanceVar local variable referencing Storable instance, - * defaults to "this" if null. If variable type is an Object array, then - * property values are placed into the runtime value of this array instead - * of a Storable instance. - * @param adapterInstanceClass class containing static references to - * adapter instances - defaults to instanceVar - * @param useWriteMethods when true, set properties by public write - * methods instead of protected fields - should be used if class being - * generated doesn't have access to these fields - * @param encodedVar required variable, which must be a byte array. At - * runtime, it references an encoded key. - * - * @throws SupportException if any property type is not supported - * @throws IllegalArgumentException if assembler is null, or if instanceVar - * is not the correct instance type, or if encodedVar is not a byte array - */ - public void buildKeyDecoding(CodeAssembler assembler, - OrderedProperty[] properties, - LocalVariable instanceVar, - Class adapterInstanceClass, - boolean useWriteMethods, - LocalVariable encodedVar) - throws SupportException - { - properties = ensureKeyProperties(properties); - buildDecoding(true, assembler, - extractProperties(properties), extractDirections(properties), - instanceVar, adapterInstanceClass, useWriteMethods, - -1, null, // no generation support - encodedVar); - } - - /** - * Generates bytecode instructions to encode properties. The encoding is - * suitable for "data" encoding, which means it is not correctly - * comparable, but it is more efficient than key encoding. Partial encoding - * is not supported. - * - * @param assembler code assembler to receive bytecode instructions - * @param properties specific properties to encode, defaults to all non-key - * properties if null - * @param instanceVar local variable referencing Storable instance, - * defaults to "this" if null. If variable type is an Object array, then - * property values are read from the runtime value of this array instead - * of a Storable instance. - * @param adapterInstanceClass class containing static references to - * adapter instances - defaults to instanceVar - * @param useReadMethods when true, access properties by public read - * methods instead of protected fields - * @param generation when non-negative, write a storable layout generation - * value in one or four bytes. Generation 0..127 is encoded in one byte, and - * 128..max is encoded in four bytes, with the most significant bit set. - * - * @return local variable referencing a byte array with encoded data - * - * @throws SupportException if any property type is not supported - * @throws IllegalArgumentException if assembler is null, or if instanceVar - * is not the correct instance type - */ - public LocalVariable buildDataEncoding(CodeAssembler assembler, - StorableProperty[] properties, - LocalVariable instanceVar, - Class adapterInstanceClass, - boolean useReadMethods, - int generation) - throws SupportException - { - properties = ensureDataProperties(properties); - return buildEncoding(false, assembler, - properties, null, - instanceVar, adapterInstanceClass, - useReadMethods, generation, null, null); - } - - /** - * Generates bytecode instructions to decode properties. A - * CorruptEncodingException may be thrown from generated code. - * - * @param assembler code assembler to receive bytecode instructions - * @param properties specific properties to decode, defaults to all non-key - * properties if null - * @param instanceVar local variable referencing Storable instance, - * defaults to "this" if null. If variable type is an Object array, then - * property values are placed into the runtime value of this array instead - * of a Storable instance. - * @param adapterInstanceClass class containing static references to - * adapter instances - defaults to instanceVar - * @param useWriteMethods when true, set properties by public write - * methods instead of protected fields - should be used if class being - * generated doesn't have access to these fields - * @param generation when non-negative, decoder expects a storable layout - * generation value to match this value. Otherwise, it throws a - * CorruptEncodingException. - * @param altGenerationHandler if non-null and a generation is provided, - * this label defines an alternate generation handler. It is executed - * instead of throwing a CorruptEncodingException if the generation doesn't - * match. The actual generation is available on the top of the stack for - * the handler to consume. - * @param encodedVar required variable, which must be a byte array. At - * runtime, it references encoded data. - * - * @throws SupportException if any property type is not supported - * @throws IllegalArgumentException if assembler is null, or if instanceVar - * is not the correct instance type, or if encodedVar is not a byte array - */ - public void buildDataDecoding(CodeAssembler assembler, - StorableProperty[] properties, - LocalVariable instanceVar, - Class adapterInstanceClass, - boolean useWriteMethods, - int generation, - Label altGenerationHandler, - LocalVariable encodedVar) - throws SupportException - { - properties = ensureDataProperties(properties); - buildDecoding(false, assembler, properties, null, - instanceVar, adapterInstanceClass, useWriteMethods, - generation, altGenerationHandler, encodedVar); - } - - /** - * Returns the type of Storable that code is generated for. - */ - public final Class getType() { - return mType; - } - - /** - * Returns true if the type of the given property type is supported. The - * types currently supported are primitives, primitive wrapper objects, - * Strings, and byte arrays. - */ - public boolean isSupported(Class propertyType) { - return isSupported(TypeDesc.forClass(propertyType)); - } - - /** - * Returns true if the type of the given property type is supported. The - * types currently supported are primitives, primitive wrapper objects, - * Strings, byte arrays and Lobs. - */ - public boolean isSupported(TypeDesc propertyType) { - if (propertyType.toPrimitiveType() != null) { - return true; - } - return propertyType == TypeDesc.STRING || - propertyType == TypeDesc.forClass(byte[].class) || - propertyType.toClass() != null && Lob.class.isAssignableFrom(propertyType.toClass()); - } - - public int getKeyPrefixPadding() { - return mKeyPrefixPadding; - } - - public int getKeySuffixPadding() { - return mKeySuffixPadding; - } - - public int getDataPrefixPadding() { - return mDataPrefixPadding; - } - - public int getDataSuffixPadding() { - return mDataSuffixPadding; - } - - /** - * Returns amount of prefix key bytes that encoding strategy instance - * produces which are always the same. Default implementation returns 0. - */ - public int getConstantKeyPrefixLength() { - return 0; - } - - @Override - public int hashCode() { - return mType.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof GenericEncodingStrategy) { - GenericEncodingStrategy other = (GenericEncodingStrategy) obj; - return mType == other.mType - && mKeyPrefixPadding == other.mKeyPrefixPadding - && mKeySuffixPadding == other.mKeySuffixPadding - && mDataPrefixPadding == other.mDataPrefixPadding - && mDataSuffixPadding == other.mDataSuffixPadding; - } - return false; - } - - /** - * Returns all key properties in the form of an index. - */ - protected StorableIndex getPrimaryKeyIndex() { - return mPkIndex; - } - - /** - * Returns all key properties as ordered properties, possibly with - * unspecified directions. - */ - protected OrderedProperty[] gatherAllKeyProperties() { - return mPkIndex.getOrderedProperties(); - } - - /** - * Returns all data properties for storable. - */ - @SuppressWarnings("unchecked") - protected StorableProperty[] gatherAllDataProperties() { - Map> map = - StorableIntrospector.examine(mType).getDataProperties(); - - StorableProperty[] properties = new StorableProperty[map.size()]; - - int ordinal = 0; - for (StorableProperty property : map.values()) { - properties[ordinal++] = property; - } - - return properties; - } - - protected StorablePropertyInfo checkSupport(StorableProperty property) - throws SupportException - { - if (isSupported(property.getType())) { - return new StorablePropertyInfo(property); - } - - // Look for an adapter that will allow this property to be supported. - if (property.getAdapter() != null) { - StorablePropertyAdapter adapter = property.getAdapter(); - for (Class storageType : adapter.getStorageTypePreferences()) { - if (!isSupported(storageType)) { - continue; - } - - if (property.isNullable() && storageType.isPrimitive()) { - continue; - } - - Method fromStorage, toStorage; - fromStorage = adapter.findAdaptMethod(storageType, property.getType()); - if (fromStorage == null) { - continue; - } - toStorage = adapter.findAdaptMethod(property.getType(), storageType); - if (toStorage != null) { - return new StorablePropertyInfo(property, storageType, fromStorage, toStorage); - } - } - } - - throw notSupported(property); - } - - @SuppressWarnings("unchecked") - protected StorablePropertyInfo[] checkSupport(StorableProperty[] properties) - throws SupportException - { - int length = properties.length; - StorablePropertyInfo[] infos = new StorablePropertyInfo[length]; - for (int i=0; i property) { - return notSupported(property.getName(), property.getType().getName()); - } - - private SupportException notSupported(String propertyName, String typeName) { - return new SupportException - ("Type \"" + typeName + - "\" not supported for property \"" + propertyName + '"'); - } - - private OrderedProperty[] ensureKeyProperties(OrderedProperty[] properties) { - if (properties == null) { - properties = gatherAllKeyProperties(); - } else { - for (Object prop : properties) { - if (prop == null) { - throw new IllegalArgumentException(); - } - } - } - return properties; - } - - @SuppressWarnings("unchecked") - private StorableProperty[] extractProperties(OrderedProperty[] ordered) { - StorableProperty[] properties = new StorableProperty[ordered.length]; - for (int i=0; i 0) { - throw new IllegalArgumentException(); - } - properties[i] = chained.getPrimeProperty(); - } - return properties; - } - - private Direction[] extractDirections(OrderedProperty[] ordered) { - Direction[] directions = new Direction[ordered.length]; - for (int i=0; i[] ensureDataProperties(StorableProperty[] properties) { - if (properties == null) { - properties = gatherAllDataProperties(); - } else { - for (Object prop : properties) { - if (prop == null) { - throw new IllegalArgumentException(); - } - } - } - return properties; - } - - ///////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////// - - private LocalVariable buildEncoding(boolean forKey, - CodeAssembler a, - StorableProperty[] properties, - Direction[] directions, - LocalVariable instanceVar, - Class adapterInstanceClass, - boolean useReadMethods, - int generation, - LocalVariable partialStartVar, - LocalVariable partialEndVar) - throws SupportException - { - if (a == null) { - throw new IllegalArgumentException(); - } - if (partialStartVar != null && partialStartVar.getType() != TypeDesc.INT) { - throw new IllegalArgumentException(); - } - if (partialEndVar != null && partialEndVar.getType() != TypeDesc.INT) { - throw new IllegalArgumentException(); - } - - // Encoding order is: - // - // 1. Prefix - // 2. Generation prefix - // 3. Properties - // 4. Suffix - - final int prefix = forKey ? mKeyPrefixPadding : mDataPrefixPadding; - - final int generationPrefix; - if (generation < 0) { - generationPrefix = 0; - } else if (generation < 128) { - generationPrefix = 1; - } else { - generationPrefix = 4; - } - - final int suffix = forKey ? mKeySuffixPadding : mDataSuffixPadding; - - final TypeDesc byteArrayType = TypeDesc.forClass(byte[].class); - final LocalVariable encodedVar = a.createLocalVariable(null, byteArrayType); - - StorablePropertyInfo[] infos = checkSupport(properties); - - if (properties.length == 1) { - // Ignore partial key encoding variables, since there can't be a - // partial of one property. - partialStartVar = null; - partialEndVar = null; - - StorableProperty property = properties[0]; - StorablePropertyInfo info = infos[0]; - - if (info.getStorageType().toClass() == byte[].class) { - // Since there is only one property, and it is just a byte - // array, optimize by not doing any fancy encoding. If the - // property is optional, then a byte prefix is needed to - // identify a null reference. - - loadPropertyValue(a, info, 0, useReadMethods, - instanceVar, adapterInstanceClass, partialStartVar); - - boolean descending = - forKey && directions != null && directions[0] == Direction.DESCENDING; - - TypeDesc[] params; - if (prefix > 0 || generationPrefix > 0 || suffix > 0) { - a.loadConstant(prefix + generationPrefix); - a.loadConstant(suffix); - params = new TypeDesc[] {byteArrayType, TypeDesc.INT, TypeDesc.INT}; - } else { - params = new TypeDesc[] {byteArrayType}; - } - - if (property.isNullable()) { - if (descending) { - a.invokeStatic(KeyEncoder.class.getName(), "encodeSingleNullableDesc", - byteArrayType, params); - } else { - a.invokeStatic(DataEncoder.class.getName(), "encodeSingleNullable", - byteArrayType, params); - } - } else if (descending) { - a.invokeStatic(KeyEncoder.class.getName(), "encodeSingleDesc", - byteArrayType, params); - } else if (prefix > 0 || generationPrefix > 0 || suffix > 0) { - a.invokeStatic(DataEncoder.class.getName(), "encodeSingle", - byteArrayType, params); - } else { - // Just return raw property value - no need to cache it either. - } - - a.storeLocal(encodedVar); - - encodeGeneration(a, encodedVar, prefix, generation); - - return encodedVar; - } - } - - boolean doPartial = forKey && (partialStartVar != null || partialEndVar != null); - - // Calculate exactly how many bytes are needed to encode. The length - // is composed of a static and a variable amount. The variable amount - // is determined at runtime. - - int staticLength = 0; - if (!forKey || partialStartVar == null) { - // Only include prefix as static if no runtime check is needed - // against runtime partial start value. - staticLength += prefix + generationPrefix; - } - if (!forKey || partialEndVar == null) { - // Only include suffix as static if no runtime check is needed - // against runtime partial end value. - staticLength += suffix; - } - - boolean hasVariableLength; - if (doPartial) { - hasVariableLength = true; - } else { - hasVariableLength = false; - for (GenericPropertyInfo info : infos) { - int len = staticEncodingLength(info); - if (len >= 0) { - staticLength += len; - } else { - staticLength += ~len; - hasVariableLength = true; - } - } - } - - // Generate code that loops over all the properties that have a - // variable length. Load each property and perform the necessary - // tests to determine the exact encoding length. - - boolean hasStackVar = false; - if (hasVariableLength) { - Label[] entryPoints = null; - - if (partialStartVar != null) { - // Will jump into an arbitrary location, so always have a stack - // variable available. - a.loadConstant(0); - hasStackVar = true; - - entryPoints = jumpToPartialEntryPoints(a, partialStartVar, properties.length); - } - - Label exitPoint = a.createLabel(); - - for (int i=0; i property = properties[i]; - StorablePropertyInfo info = infos[i]; - - if (doPartial) { - if (entryPoints != null) { - entryPoints[i].setLocation(); - } - if (partialEndVar != null) { - // Add code to jump out of partial. - a.loadConstant(i); - a.loadLocal(partialEndVar); - a.ifComparisonBranch(exitPoint, ">="); - } - } else if (staticEncodingLength(info) >= 0) { - continue; - } - - TypeDesc propType = info.getStorageType(); - - if (propType.isPrimitive()) { - // This should only ever get executed if implementing - // partial support. Otherwise, the static encoding length - // would have been already calculated. - a.loadConstant(staticEncodingLength(info)); - if (hasStackVar) { - a.math(Opcode.IADD); - } else { - hasStackVar = true; - } - } else if (propType.toPrimitiveType() != null) { - int amt = 0; - switch (propType.toPrimitiveType().getTypeCode()) { - case TypeDesc.BYTE_CODE: - case TypeDesc.BOOLEAN_CODE: - amt = 1; - break; - case TypeDesc.SHORT_CODE: - case TypeDesc.CHAR_CODE: - amt = 2; - break; - case TypeDesc.INT_CODE: - case TypeDesc.FLOAT_CODE: - amt = 4; - break; - case TypeDesc.LONG_CODE: - case TypeDesc.DOUBLE_CODE: - amt = 8; - break; - } - - int extra = 0; - if (doPartial) { - // If value is null, then there may be a one byte size - // adjust for the null value. Otherwise it is the extra - // amount plus the size to encode the raw primitive - // value. If doPartial is false, then this extra amount - // was already accounted for in the static encoding - // length. - - switch (propType.toPrimitiveType().getTypeCode()) { - case TypeDesc.BYTE_CODE: - case TypeDesc.SHORT_CODE: - case TypeDesc.CHAR_CODE: - case TypeDesc.INT_CODE: - case TypeDesc.LONG_CODE: - extra = 1; - } - } - - if (!property.isNullable() || (doPartial && extra == 0)) { - a.loadConstant(amt); - if (hasStackVar) { - a.math(Opcode.IADD); - } - hasStackVar = true; - } else { - // Load property to test for null. - loadPropertyValue(a, info, i, useReadMethods, - instanceVar, adapterInstanceClass, partialStartVar); - - Label isNull = a.createLabel(); - a.ifNullBranch(isNull, true); - - a.loadConstant(amt); - - if (hasStackVar) { - a.math(Opcode.IADD); - isNull.setLocation(); - if (extra > 0) { - a.loadConstant(extra); - a.math(Opcode.IADD); - } - } else { - hasStackVar = true; - // Make sure that there is a zero (or extra) value on - // the stack if the isNull branch is taken. - Label notNull = a.createLabel(); - a.branch(notNull); - isNull.setLocation(); - a.loadConstant(extra); - notNull.setLocation(); - } - } - } else if (propType == TypeDesc.STRING) { - // Load property to test for null. - loadPropertyValue(a, info, i, useReadMethods, - instanceVar, adapterInstanceClass, partialStartVar); - - String className = - (forKey ? KeyEncoder.class : DataEncoder.class).getName(); - a.invokeStatic(className, "calculateEncodedStringLength", - TypeDesc.INT, new TypeDesc[] {TypeDesc.STRING}); - if (hasStackVar) { - a.math(Opcode.IADD); - } else { - hasStackVar = true; - } - } else if (propType.toClass() == byte[].class) { - // Load property to test for null. - loadPropertyValue(a, info, i, useReadMethods, - instanceVar, adapterInstanceClass, partialStartVar); - - String className = - (forKey ? KeyEncoder.class : DataEncoder.class).getName(); - a.invokeStatic(className, "calculateEncodedLength", - TypeDesc.INT, new TypeDesc[] {byteArrayType}); - if (hasStackVar) { - a.math(Opcode.IADD); - } else { - hasStackVar = true; - } - } else if (info.isLob()) { - // Lob locator is a long, or 8 bytes. - a.loadConstant(8); - if (hasStackVar) { - a.math(Opcode.IADD); - } else { - hasStackVar = true; - } - } else { - throw notSupported(property); - } - } - - exitPoint.setLocation(); - - if (forKey && partialStartVar != null && (prefix > 0 || generationPrefix > 0)) { - // Prefix must be allocated only if runtime value of - // partialStartVar is zero. - a.loadLocal(partialStartVar); - Label noPrefix = a.createLabel(); - a.ifZeroComparisonBranch(noPrefix, "!="); - a.loadConstant(prefix + generationPrefix); - if (hasStackVar) { - a.math(Opcode.IADD); - } else { - hasStackVar = true; - } - noPrefix.setLocation(); - } - - if (forKey && partialEndVar != null && suffix > 0) { - // Suffix must be allocated only if runtime value of - // partialEndVar is equal to property count. - a.loadLocal(partialEndVar); - Label noSuffix = a.createLabel(); - a.loadConstant(properties.length); - a.ifComparisonBranch(noSuffix, "!="); - a.loadConstant(suffix); - if (hasStackVar) { - a.math(Opcode.IADD); - } else { - hasStackVar = true; - } - noSuffix.setLocation(); - } - } - - // Allocate a byte array of the exact size. - if (hasStackVar) { - if (staticLength > 0) { - a.loadConstant(staticLength); - a.math(Opcode.IADD); - } - } else { - a.loadConstant(staticLength); - } - a.newObject(byteArrayType); - a.storeLocal(encodedVar); - - // Now encode into the byte array. - - int constantOffset = 0; - LocalVariable offset = null; - - if (!forKey || partialStartVar == null) { - // Only include prefix as constant offset if no runtime check is - // needed against runtime partial start value. - constantOffset += prefix + generationPrefix; - encodeGeneration(a, encodedVar, prefix, generation); - } - - Label[] entryPoints = null; - - if (forKey && partialStartVar != null) { - // Will jump into an arbitrary location, so put an initial value - // into offset variable. - - offset = a.createLocalVariable(null, TypeDesc.INT); - a.loadConstant(0); - if (prefix > 0) { - // Prefix is allocated only if partial start is zero. Check if - // offset should be adjusted to skip over it. - a.loadLocal(partialStartVar); - Label noPrefix = a.createLabel(); - a.ifZeroComparisonBranch(noPrefix, "!="); - a.loadConstant(prefix + generationPrefix); - a.math(Opcode.IADD); - encodeGeneration(a, encodedVar, prefix, generation); - noPrefix.setLocation(); - } - a.storeLocal(offset); - - entryPoints = jumpToPartialEntryPoints(a, partialStartVar, properties.length); - } - - Label exitPoint = a.createLabel(); - - for (int i=0; i property = properties[i]; - StorablePropertyInfo info = infos[i]; - - if (doPartial) { - if (entryPoints != null) { - entryPoints[i].setLocation(); - } - if (partialEndVar != null) { - // Add code to jump out of partial. - a.loadConstant(i); - a.loadLocal(partialEndVar); - a.ifComparisonBranch(exitPoint, ">="); - } - } - - if (info.isLob()) { - // Need RawSupport instance for getting locator from Lob. - pushRawSupport(a, instanceVar); - } - - boolean fromInstance = loadPropertyValue - (a, info, i, useReadMethods, instanceVar, adapterInstanceClass, partialStartVar); - - TypeDesc propType = info.getStorageType(); - if (!property.isNullable() && propType.toPrimitiveType() != null) { - // Since property type is a required primitive wrapper, convert - // to a primitive rather than encoding using the form that - // distinguishes null. - - // Property value that was passed in may be null, which is not - // allowed. - if (!fromInstance && !propType.isPrimitive()) { - a.dup(); - Label notNull = a.createLabel(); - a.ifNullBranch(notNull, false); - - TypeDesc errorType = TypeDesc.forClass(IllegalArgumentException.class); - a.newObject(errorType); - a.dup(); - a.loadConstant("Value for property \"" + property.getName() + - "\" cannot be null"); - a.invokeConstructor(errorType, new TypeDesc[] {TypeDesc.STRING}); - a.throwObject(); - - notNull.setLocation(); - } - - a.convert(propType, propType.toPrimitiveType()); - propType = propType.toPrimitiveType(); - } - - if (info.isLob()) { - // Extract locator from RawSupport. - getLobLocator(a, info); - - // Locator is a long, so switch the type to be encoded properly. - propType = TypeDesc.LONG; - } - - // Fill out remaining parameters before calling specific method - // to encode property value. - a.loadLocal(encodedVar); - if (offset == null) { - a.loadConstant(constantOffset); - } else { - a.loadLocal(offset); - } - - boolean descending = - forKey && directions != null && directions[i] == Direction.DESCENDING; - - int amt = encodeProperty(a, propType, forKey, descending); - - if (amt > 0) { - if (i + 1 < properties.length) { - // Only adjust offset if there are more properties. - - if (offset == null) { - constantOffset += amt; - } else { - a.loadConstant(amt); - a.loadLocal(offset); - a.math(Opcode.IADD); - a.storeLocal(offset); - } - } - } else { - if (i + 1 >= properties.length) { - // Don't need to keep track of offset anymore. - a.pop(); - } else { - // Only adjust offset if there are more properties. - if (offset == null) { - if (constantOffset > 0) { - a.loadConstant(constantOffset); - a.math(Opcode.IADD); - } - offset = a.createLocalVariable(null, TypeDesc.INT); - } else { - a.loadLocal(offset); - a.math(Opcode.IADD); - } - a.storeLocal(offset); - } - } - } - - exitPoint.setLocation(); - - return encodedVar; - } - - /** - * Generates code to load a property value onto the operand stack. - * - * @param info info for property to load - * @param ordinal zero-based property ordinal, used only if instanceVar - * refers to an object array. - * @param useReadMethod when true, access property by public read method - * instead of protected field - * @param instanceVar local variable referencing Storable instance, - * defaults to "this" if null. If variable type is an Object array, then - * property values are read from the runtime value of this array instead - * of a Storable instance. - * @param adapterInstanceClass class containing static references to - * adapter instances - defaults to instanceVar - * @param partialStartVar optional variable for supporting partial key - * generation. It must be an int, whose runtime value must be less than the - * properties array length. It marks the range start of the partial - * property range. - * @return true if property was loaded from instance, false if loaded from - * value array - */ - protected boolean loadPropertyValue(CodeAssembler a, - StorablePropertyInfo info, int ordinal, - boolean useReadMethod, - LocalVariable instanceVar, - Class adapterInstanceClass, - LocalVariable partialStartVar) - { - TypeDesc type = info.getPropertyType(); - TypeDesc storageType = info.getStorageType(); - - boolean isObjectArrayInstanceVar = instanceVar != null - && instanceVar.getType() == TypeDesc.forClass(Object[].class); - - boolean useAdapterInstance = adapterInstanceClass != null - && info.getToStorageAdapter() != null - && (useReadMethod || isObjectArrayInstanceVar); - - if (useAdapterInstance) { - // Push adapter instance to stack to be used later. - String fieldName = - info.getPropertyName() + StorableGenerator.ADAPTER_FIELD_ELEMENT + 0; - TypeDesc adapterType = TypeDesc.forClass - (info.getToStorageAdapter().getDeclaringClass()); - a.loadStaticField - (TypeDesc.forClass(adapterInstanceClass), fieldName, adapterType); - } - - if (instanceVar == null) { - a.loadThis(); - if (useReadMethod) { - info.addInvokeReadMethod(a); - } else { - // Access property value directly from protected field of "this". - if (info.getToStorageAdapter() == null) { - a.loadField(info.getPropertyName(), type); - } else { - // Invoke adapter method. - a.invokeVirtual(info.getReadMethodName() + '$', storageType, null); - } - } - } else if (!isObjectArrayInstanceVar) { - a.loadLocal(instanceVar); - if (useReadMethod) { - info.addInvokeReadMethod(a, instanceVar.getType()); - } else { - // Access property value directly from protected field of - // referenced instance. Assumes code is being defined in the - // same package or a subclass. - if (info.getToStorageAdapter() == null) { - a.loadField(instanceVar.getType(), info.getPropertyName(), type); - } else { - // Invoke adapter method. - a.invokeVirtual(instanceVar.getType(), - info.getReadMethodName() + '$', storageType, null); - } - } - } else { - // Access property value from object array. - - a.loadLocal(instanceVar); - a.loadConstant(ordinal); - if (ordinal > 0 && partialStartVar != null) { - a.loadLocal(partialStartVar); - a.math(Opcode.ISUB); - } - - a.loadFromArray(TypeDesc.OBJECT); - a.checkCast(type.toObjectType()); - if (type.isPrimitive()) { - a.convert(type.toObjectType(), type); - } - } - - if (useAdapterInstance) { - // Invoke adapter method on instance pushed earlier. - a.invoke(info.getToStorageAdapter()); - } - - return !isObjectArrayInstanceVar; - } - - /** - * Returns a negative value if encoding is variable. The minimum static - * amount is computed from the one's compliment. Of the types with variable - * encoding lengths, only for primitives is the minimum static amount - * returned more than zero. - */ - private int staticEncodingLength(GenericPropertyInfo info) { - TypeDesc type = info.getStorageType(); - TypeDesc primType = type.toPrimitiveType(); - - if (primType == null) { - if (info.isLob()) { - // Lob locator is stored as a long. - return 8; - } - } else { - if (info.isNullable()) { - // Type is a primitive wrapper. - switch (primType.getTypeCode()) { - case TypeDesc.BYTE_CODE: - return ~1; - case TypeDesc.BOOLEAN_CODE: - return 1; - case TypeDesc.SHORT_CODE: - case TypeDesc.CHAR_CODE: - return ~1; - case TypeDesc.INT_CODE: - return ~1; - case TypeDesc.FLOAT_CODE: - return 4; - case TypeDesc.LONG_CODE: - return ~1; - case TypeDesc.DOUBLE_CODE: - return 8; - } - } else { - // Type is primitive or a required primitive wrapper. - switch (type.getTypeCode()) { - case TypeDesc.BYTE_CODE: - case TypeDesc.BOOLEAN_CODE: - return 1; - case TypeDesc.SHORT_CODE: - case TypeDesc.CHAR_CODE: - return 2; - case TypeDesc.INT_CODE: - case TypeDesc.FLOAT_CODE: - return 4; - case TypeDesc.LONG_CODE: - case TypeDesc.DOUBLE_CODE: - return 8; - } - } - } - - return ~0; - } - - /** - * @param partialStartVar must not be null - */ - private Label[] jumpToPartialEntryPoints(CodeAssembler a, LocalVariable partialStartVar, - int propertyCount) { - // Create all the entry points for offset var, whose locations will be - // set later. - int[] cases = new int[propertyCount]; - Label[] entryPoints = new Label[propertyCount]; - for (int i=0; i> (8 * (3 - i)))); - a.storeToArray(TypeDesc.BYTE); - } - } - } - - /** - * Generates code to push RawSupport instance to the stack. RawSupport is - * available only in Storable instances. If instanceVar is an Object[], a - * SupportException is thrown. - */ - private void pushRawSupport(CodeAssembler a, LocalVariable instanceVar) - throws SupportException - { - boolean isObjectArrayInstanceVar = instanceVar != null - && instanceVar.getType() == TypeDesc.forClass(Object[].class); - - if (isObjectArrayInstanceVar) { - throw new SupportException("Lob properties not supported"); - } - - if (instanceVar == null) { - a.loadThis(); - } else { - a.loadLocal(instanceVar); - } - - a.loadField(StorableGenerator.SUPPORT_FIELD_NAME, - TypeDesc.forClass(TriggerSupport.class)); - a.checkCast(TypeDesc.forClass(RawSupport.class)); - } - - /** - * Generates code to get a Lob locator value from RawSupport. RawSupport - * instance and Lob instance must be on the stack. Result is a long locator - * value on the stack. - */ - private void getLobLocator(CodeAssembler a, StorablePropertyInfo info) { - if (!info.isLob()) { - throw new IllegalArgumentException(); - } - a.invokeInterface(TypeDesc.forClass(RawSupport.class), "getLocator", - TypeDesc.LONG, new TypeDesc[] {info.getStorageType()}); - } - - /** - * Generates code to get a Lob from a locator from RawSupport. RawSupport - * instance and long locator must be on the stack. Result is a Lob on the - * stack, which may be null. - */ - private void getLobFromLocator(CodeAssembler a, StorablePropertyInfo info) { - if (!info.isLob()) { - throw new IllegalArgumentException(); - } - - TypeDesc type = info.getStorageType(); - String name; - if (Blob.class.isAssignableFrom(type.toClass())) { - name = "getBlob"; - } else if (Clob.class.isAssignableFrom(type.toClass())) { - name = "getClob"; - } else { - throw new IllegalArgumentException(); - } - - a.invokeInterface(TypeDesc.forClass(RawSupport.class), name, - type, new TypeDesc[] {TypeDesc.LONG}); - } - - ///////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////// - - private void buildDecoding(boolean forKey, - CodeAssembler a, - StorableProperty[] properties, - Direction[] directions, - LocalVariable instanceVar, - Class adapterInstanceClass, - boolean useWriteMethods, - int generation, - Label altGenerationHandler, - LocalVariable encodedVar) - throws SupportException - { - if (a == null) { - throw new IllegalArgumentException(); - } - if (encodedVar == null || encodedVar.getType() != TypeDesc.forClass(byte[].class)) { - throw new IllegalArgumentException(); - } - - // Decoding order is: - // - // 1. Prefix - // 2. Generation prefix - // 3. Properties - // 4. Suffix - - final int prefix = forKey ? mKeyPrefixPadding : mDataPrefixPadding; - - final int generationPrefix; - if (generation < 0) { - generationPrefix = 0; - } else if (generation < 128) { - generationPrefix = 1; - } else { - generationPrefix = 4; - } - - final int suffix = forKey ? mKeySuffixPadding : mDataSuffixPadding; - - final TypeDesc byteArrayType = TypeDesc.forClass(byte[].class); - - StorablePropertyInfo[] infos = checkSupport(properties); - - decodeGeneration(a, encodedVar, prefix, generation, altGenerationHandler); - - if (properties.length == 1) { - StorableProperty property = properties[0]; - StorablePropertyInfo info = infos[0]; - - if (info.getStorageType().toClass() == byte[].class) { - // Since there is only one property, and it is just a byte - // array, it doesn't have any fancy encoding. - - // Push to stack in preparation for storing a property. - pushDecodingInstanceVar(a, 0, instanceVar); - - a.loadLocal(encodedVar); - - boolean descending = - forKey && directions != null && directions[0] == Direction.DESCENDING; - - TypeDesc[] params; - if (prefix > 0 || generationPrefix > 0 || suffix > 0) { - a.loadConstant(prefix + generationPrefix); - a.loadConstant(suffix); - params = new TypeDesc[] {byteArrayType, TypeDesc.INT, TypeDesc.INT}; - } else { - params = new TypeDesc[] {byteArrayType}; - } - - if (property.isNullable()) { - if (descending) { - a.invokeStatic(KeyDecoder.class.getName(), "decodeSingleNullableDesc", - byteArrayType, params); - } else { - a.invokeStatic(DataDecoder.class.getName(), "decodeSingleNullable", - byteArrayType, params); - } - } else if (descending) { - a.invokeStatic(KeyDecoder.class.getName(), "decodeSingleDesc", - byteArrayType, params); - } else if (prefix > 0 || generationPrefix > 0 || suffix > 0) { - a.invokeStatic(DataDecoder.class.getName(), "decodeSingle", - byteArrayType, params); - } else { - // Just store raw property value. - } - - storePropertyValue(a, info, useWriteMethods, instanceVar, adapterInstanceClass); - return; - } - } - - // Now decode from the byte array. - - int constantOffset = prefix + generationPrefix; - LocalVariable offset = null; - // References to local variables which will hold references. - LocalVariable[] stringRef = new LocalVariable[1]; - LocalVariable[] byteArrayRef = new LocalVariable[1]; - LocalVariable[] valueRefRef = new LocalVariable[1]; - - for (int i=0; i 0) { - if (offset == null) { - constantOffset += amt; - } else { - a.loadConstant(amt); - a.loadLocal(offset); - a.math(Opcode.IADD); - a.storeLocal(offset); - } - } else { - // Offset adjust is one if returned object is null. - a.dup(); - Label notNull = a.createLabel(); - a.ifNullBranch(notNull, false); - a.loadConstant(1 + (offset == null ? constantOffset : 0)); - Label cont = a.createLabel(); - a.branch(cont); - notNull.setLocation(); - a.loadConstant(~amt + (offset == null ? constantOffset : 0)); - cont.setLocation(); - - if (offset == null) { - offset = a.createLocalVariable(null, TypeDesc.INT); - } else { - a.loadLocal(offset); - a.math(Opcode.IADD); - } - a.storeLocal(offset); - } - } - } else { - if (i + 1 >= properties.length) { - // Don't need to keep track of offset anymore. - a.pop(); - } else { - // Only adjust offset if there are more properties. - if (offset == null) { - if (constantOffset > 0) { - a.loadConstant(constantOffset); - a.math(Opcode.IADD); - } - offset = a.createLocalVariable(null, TypeDesc.INT); - } else { - a.loadLocal(offset); - a.math(Opcode.IADD); - } - a.storeLocal(offset); - } - - // Get the value out of the ref array so that it can be stored. - a.loadLocal(valueRefRef[0]); - a.loadConstant(0); - a.loadFromArray(valueRefRef[0].getType()); - } - - storePropertyValue(a, info, useWriteMethods, instanceVar, adapterInstanceClass); - } - } - - /** - * Generates code that calls a decoding method in DataDecoder or - * KeyDecoder. Parameters must already be on the stack. - * - * @return 0 if an int amount is pushed onto the stack, or a positive value - * if offset adjust amount is constant, or a negative value if offset - * adjust is constant or one more - */ - private int decodeProperty(CodeAssembler a, - GenericPropertyInfo info, TypeDesc storageType, - boolean forKey, boolean descending, - LocalVariable[] stringRefRef, LocalVariable[] byteArrayRefRef, - LocalVariable[] valueRefRef) - throws SupportException - { - TypeDesc primType = storageType.toPrimitiveType(); - - if (primType != null) { - String methodName; - TypeDesc returnType; - int adjust; - - if (primType != storageType && info.isNullable()) { - // Property type is a nullable boxed primitive. - returnType = storageType; - - switch (primType.getTypeCode()) { - case TypeDesc.BYTE_CODE: - methodName = "decodeByteObj"; - adjust = ~2; - break; - case TypeDesc.BOOLEAN_CODE: - methodName = "decodeBooleanObj"; - adjust = 1; - break; - case TypeDesc.SHORT_CODE: - methodName = "decodeShortObj"; - adjust = ~3; - break; - case TypeDesc.CHAR_CODE: - methodName = "decodeCharacterObj"; - adjust = ~3; - break; - default: - case TypeDesc.INT_CODE: - methodName = "decodeIntegerObj"; - adjust = ~5; - break; - case TypeDesc.FLOAT_CODE: - methodName = "decodeFloatObj"; - adjust = 4; - break; - case TypeDesc.LONG_CODE: - methodName = "decodeLongObj"; - adjust = ~9; - break; - case TypeDesc.DOUBLE_CODE: - methodName = "decodeDoubleObj"; - adjust = 8; - break; - } - } else { - // Property type is a primitive or a boxed primitive. - returnType = primType; - - switch (primType.getTypeCode()) { - case TypeDesc.BYTE_CODE: - methodName = "decodeByte"; - adjust = 1; - break; - case TypeDesc.BOOLEAN_CODE: - methodName = "decodeBoolean"; - adjust = 1; - break; - case TypeDesc.SHORT_CODE: - methodName = "decodeShort"; - adjust = 2; - break; - case TypeDesc.CHAR_CODE: - methodName = "decodeChar"; - adjust = 2; - break; - default: - case TypeDesc.INT_CODE: - methodName = "decodeInt"; - adjust = 4; - break; - case TypeDesc.FLOAT_CODE: - methodName = "decodeFloat"; - adjust = 4; - break; - case TypeDesc.LONG_CODE: - methodName = "decodeLong"; - adjust = 8; - break; - case TypeDesc.DOUBLE_CODE: - methodName = "decodeDouble"; - adjust = 8; - break; - } - } - - TypeDesc[] params = {TypeDesc.forClass(byte[].class), TypeDesc.INT}; - if (forKey && descending) { - a.invokeStatic - (KeyDecoder.class.getName(), methodName + "Desc", returnType, params); - } else { - a.invokeStatic - (DataDecoder.class.getName(), methodName, returnType, params); - } - - if (returnType.isPrimitive()) { - if (!storageType.isPrimitive()) { - // Wrap it. - a.convert(returnType, storageType); - } - } - - return adjust; - } else { - String className = (forKey ? KeyDecoder.class : DataDecoder.class).getName(); - String methodName; - TypeDesc refType; - - if (storageType == TypeDesc.STRING) { - methodName = (forKey && descending) ? "decodeStringDesc" : "decodeString"; - refType = TypeDesc.forClass(String[].class); - if (stringRefRef[0] == null) { - stringRefRef[0] = a.createLocalVariable(null, refType); - a.loadConstant(1); - a.newObject(refType); - a.storeLocal(stringRefRef[0]); - } - a.loadLocal(stringRefRef[0]); - valueRefRef[0] = stringRefRef[0]; - } else if (storageType.toClass() == byte[].class) { - methodName = (forKey && descending) ? "decodeDesc" : "decode"; - refType = TypeDesc.forClass(byte[][].class); - if (byteArrayRefRef[0] == null) { - byteArrayRefRef[0] = a.createLocalVariable(null, refType); - a.loadConstant(1); - a.newObject(refType); - a.storeLocal(byteArrayRefRef[0]); - } - a.loadLocal(byteArrayRefRef[0]); - valueRefRef[0] = byteArrayRefRef[0]; - } else { - throw notSupported(info.getPropertyName(), storageType.getFullName()); - } - - TypeDesc[] params = {TypeDesc.forClass(byte[].class), TypeDesc.INT, refType}; - a.invokeStatic(className, methodName, TypeDesc.INT, params); - - return 0; - } - } - - /** - * Push decoding instanceVar to stack in preparation to calling - * storePropertyValue. - * - * @param ordinal zero-based property ordinal, used only if instanceVar - * refers to an object array. - * @param instanceVar local variable referencing Storable instance, - * defaults to "this" if null. If variable type is an Object array, then - * property values are written to the runtime value of this array instead - * of a Storable instance. - * @see #storePropertyValue storePropertyValue - */ - protected void pushDecodingInstanceVar(CodeAssembler a, int ordinal, - LocalVariable instanceVar) { - if (instanceVar == null) { - // Push this to stack in preparation for storing a property. - a.loadThis(); - } else if (instanceVar.getType() != TypeDesc.forClass(Object[].class)) { - // Push reference to stack in preparation for storing a property. - a.loadLocal(instanceVar); - } else { - // Push array and index to stack in preparation for storing a property. - a.loadLocal(instanceVar); - a.loadConstant(ordinal); - } - } - - /** - * Generates code to store a property value into an instance which is - * already on the operand stack. If instance is an Object array, index into - * array must also be on the operand stack. - * - * @param info info for property to store to - * @param useWriteMethod when true, set property by public write method - * instead of protected field - * @param instanceVar local variable referencing Storable instance, - * defaults to "this" if null. If variable type is an Object array, then - * property values are written to the runtime value of this array instead - * of a Storable instance. - * @param adapterInstanceClass class containing static references to - * adapter instances - defaults to instanceVar - * @see #pushDecodingInstanceVar pushDecodingInstanceVar - */ - protected void storePropertyValue(CodeAssembler a, StorablePropertyInfo info, - boolean useWriteMethod, - LocalVariable instanceVar, - Class adapterInstanceClass) { - TypeDesc type = info.getPropertyType(); - TypeDesc storageType = info.getStorageType(); - - boolean isObjectArrayInstanceVar = instanceVar != null - && instanceVar.getType() == TypeDesc.forClass(Object[].class); - - boolean useAdapterInstance = adapterInstanceClass != null - && info.getToStorageAdapter() != null - && (useWriteMethod || isObjectArrayInstanceVar); - - if (useAdapterInstance) { - // Push adapter instance to adapt property value. It must be on the - // stack before the property value, so swap. - - // Store unadapted property to temp var in order to be swapped. - LocalVariable temp = a.createLocalVariable(null, storageType); - a.storeLocal(temp); - - String fieldName = - info.getPropertyName() + StorableGenerator.ADAPTER_FIELD_ELEMENT + 0; - TypeDesc adapterType = TypeDesc.forClass - (info.getToStorageAdapter().getDeclaringClass()); - a.loadStaticField - (TypeDesc.forClass(adapterInstanceClass), fieldName, adapterType); - - a.loadLocal(temp); - a.invoke(info.getFromStorageAdapter()); - - // Stack now contains property adapted to its publicly declared type. - } - - if (instanceVar == null) { - if (useWriteMethod) { - info.addInvokeWriteMethod(a); - } else { - // Set property value directly to protected field of instance. - if (info.getToStorageAdapter() == null) { - a.storeField(info.getPropertyName(), type); - } else { - // Invoke adapter method. - a.invokeVirtual(info.getWriteMethodName() + '$', - null, new TypeDesc[] {storageType}); - } - } - } else if (!isObjectArrayInstanceVar) { - TypeDesc instanceVarType = instanceVar.getType(); - - // Drop properties that are missing or whose types are incompatible. - doDrop: { - Class instanceVarClass = instanceVarType.toClass(); - if (instanceVarClass != null) { - Map props = - BeanIntrospector.getAllProperties(instanceVarClass); - BeanProperty prop = props.get(info.getPropertyName()); - if (prop != null) { - if (prop.getType() == type.toClass()) { - break doDrop; - } - // Types differ, but if primitive types, perform conversion. - TypeDesc primType = type.toPrimitiveType(); - if (primType != null) { - TypeDesc propType = TypeDesc.forClass(prop.getType()); - TypeDesc primPropType = propType.toPrimitiveType(); - if (primPropType != null) { - // Apply conversion and store property. - a.convert(type, propType); - type = propType; - break doDrop; - } - } - } - } - - // Drop missing or incompatible property. - if (storageType.isDoubleWord()) { - a.pop2(); - } else { - a.pop(); - } - return; - } - - if (useWriteMethod) { - info.addInvokeWriteMethod(a, instanceVarType); - } else { - // Set property value directly to protected field of referenced - // instance. Assumes code is being defined in the same package - // or a subclass. - if (info.getToStorageAdapter() == null) { - a.storeField(instanceVarType, info.getPropertyName(), type); - } else { - // Invoke adapter method. - a.invokeVirtual(instanceVarType, info.getWriteMethodName() + '$', - null, new TypeDesc[] {storageType}); - } - } - } else { - // Set property value to object array. No need to check if we - // should call a write method because arrays don't have write - // methods. - if (type.isPrimitive()) { - a.convert(type, type.toObjectType()); - } - a.storeToArray(TypeDesc.OBJECT); - } - } - - /** - * Generates code that ensures a matching generation value exists in the - * byte array referenced by the local variable, throwing a - * CorruptEncodingException otherwise. - * - * @param generation if less than zero, no code is generated - */ - private void decodeGeneration(CodeAssembler a, LocalVariable encodedVar, - int offset, int generation, Label altGenerationHandler) - { - if (offset < 0) { - throw new IllegalArgumentException(); - } - if (generation < 0) { - return; - } - - LocalVariable actualGeneration = a.createLocalVariable(null, TypeDesc.INT); - a.loadLocal(encodedVar); - a.loadConstant(offset); - a.loadFromArray(TypeDesc.BYTE); - a.storeLocal(actualGeneration); - a.loadLocal(actualGeneration); - Label compareGeneration = a.createLabel(); - a.ifZeroComparisonBranch(compareGeneration, ">="); - - // Decode four byte generation format. - a.loadLocal(actualGeneration); - a.loadConstant(24); - a.math(Opcode.ISHL); - a.loadConstant(0x7fffffff); - a.math(Opcode.IAND); - for (int i=1; i<4; i++) { - a.loadLocal(encodedVar); - a.loadConstant(offset + i); - a.loadFromArray(TypeDesc.BYTE); - a.loadConstant(0xff); - a.math(Opcode.IAND); - int shift = 8 * (3 - i); - if (shift > 0) { - a.loadConstant(shift); - a.math(Opcode.ISHL); - } - a.math(Opcode.IOR); - } - a.storeLocal(actualGeneration); - - compareGeneration.setLocation(); - - a.loadConstant(generation); - a.loadLocal(actualGeneration); - Label generationMatches = a.createLabel(); - a.ifComparisonBranch(generationMatches, "=="); - - if (altGenerationHandler != null) { - a.loadLocal(actualGeneration); - a.branch(altGenerationHandler); - } else { - // Throw CorruptEncodingException. - - TypeDesc corruptEncodingEx = TypeDesc.forClass(CorruptEncodingException.class); - a.newObject(corruptEncodingEx); - a.dup(); - a.loadConstant(generation); // expected generation - a.loadLocal(actualGeneration); // actual generation - a.invokeConstructor(corruptEncodingEx, new TypeDesc[] {TypeDesc.INT, TypeDesc.INT}); - a.throwObject(); - } - - generationMatches.setLocation(); - } -} diff --git a/src/main/java/com/amazon/carbonado/spi/raw/GenericInstanceFactory.java b/src/main/java/com/amazon/carbonado/spi/raw/GenericInstanceFactory.java deleted file mode 100644 index 5a6a4cb..0000000 --- a/src/main/java/com/amazon/carbonado/spi/raw/GenericInstanceFactory.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2006 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.amazon.carbonado.spi.raw; - -import com.amazon.carbonado.FetchException; -import com.amazon.carbonado.Storable; -import com.amazon.carbonado.Storage; - -/** - * Can be used with {@link com.amazon.carbonado.util.QuickConstructorGenerator} - * for instantiating generic storable instances. - * - * @author Brian S O'Neill - */ -public interface GenericInstanceFactory { - Storable instantiate(RawSupport support); - - Storable instantiate(RawSupport support, byte[] key, byte[] value) - throws FetchException; -} diff --git a/src/main/java/com/amazon/carbonado/spi/raw/GenericPropertyInfo.java b/src/main/java/com/amazon/carbonado/spi/raw/GenericPropertyInfo.java deleted file mode 100644 index c734f03..0000000 --- a/src/main/java/com/amazon/carbonado/spi/raw/GenericPropertyInfo.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2006 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.amazon.carbonado.spi.raw; - -import java.lang.reflect.Method; - -import org.cojen.classfile.TypeDesc; - -/** - * Minimal information required by {@link GenericEncodingStrategy} to encode - * and decode a storable property or layout property. - * - * @author Brian S O'Neill - */ -public interface GenericPropertyInfo { - String getPropertyName(); - - /** - * Returns the user specified property type. - */ - TypeDesc getPropertyType(); - - /** - * Returns the storage supported type. If it differs from the property - * type, then adapter methods must also exist. - */ - TypeDesc getStorageType(); - - boolean isNullable(); - - boolean isLob(); - - /** - * Returns the optional method used to adapt the property from the - * storage supported type to the user visible type. - */ - Method getFromStorageAdapter(); - - /** - * Returns the optional method used to adapt the property from the user - * visible type to the storage supported type. - */ - Method getToStorageAdapter(); -} diff --git a/src/main/java/com/amazon/carbonado/spi/raw/GenericStorableCodec.java b/src/main/java/com/amazon/carbonado/spi/raw/GenericStorableCodec.java deleted file mode 100644 index 7a98540..0000000 --- a/src/main/java/com/amazon/carbonado/spi/raw/GenericStorableCodec.java +++ /dev/null @@ -1,813 +0,0 @@ -/* - * Copyright 2006 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.amazon.carbonado.spi.raw; - -import java.lang.ref.WeakReference; -import java.lang.reflect.Method; -import java.lang.reflect.UndeclaredThrowableException; -import java.util.Map; - -import org.cojen.classfile.ClassFile; -import org.cojen.classfile.CodeBuilder; -import org.cojen.classfile.Label; -import org.cojen.classfile.LocalVariable; -import org.cojen.classfile.MethodInfo; -import org.cojen.classfile.Modifiers; -import org.cojen.classfile.TypeDesc; -import org.cojen.util.ClassInjector; -import org.cojen.util.IntHashMap; -import org.cojen.util.KeyFactory; -import org.cojen.util.SoftValuedHashMap; - -import com.amazon.carbonado.CorruptEncodingException; -import com.amazon.carbonado.FetchException; -import com.amazon.carbonado.FetchNoneException; -import com.amazon.carbonado.RepositoryException; -import com.amazon.carbonado.Storable; -import com.amazon.carbonado.Storage; -import com.amazon.carbonado.SupportException; -import com.amazon.carbonado.info.Direction; -import com.amazon.carbonado.info.OrderedProperty; -import com.amazon.carbonado.info.StorableIndex; -import com.amazon.carbonado.layout.Layout; -import com.amazon.carbonado.spi.CodeBuilderUtil; -import com.amazon.carbonado.util.ThrowUnchecked; -import com.amazon.carbonado.util.QuickConstructorGenerator; - -/** - * Generic codec that supports any kind of storable by auto-generating and - * caching storable implementations. - * - * @author Brian S O'Neill - * @see GenericStorableCodecFactory - */ -public class GenericStorableCodec implements StorableCodec { - private static final String BLANK_KEY_FIELD_NAME = "blankKey$"; - private static final String CODEC_FIELD_NAME = "codec$"; - private static final String ASSIGN_CODEC_METHOD_NAME = "assignCodec$"; - - // Maps GenericEncodingStrategy instances to GenericStorableCodec instances. - private static final Map cCache = new SoftValuedHashMap(); - - /** - * Returns an instance of the codec. The Storable type itself may be an - * interface or a class. If it is a class, then it must not be final, and - * it must have a public, no-arg constructor. - * - * @param isMaster when true, version properties and sequences are managed - * @param layout when non-null, encode a storable layout generation - * value in one or four bytes. Generation 0..127 is encoded in one byte, and - * 128..max is encoded in four bytes, with the most significant bit set. - * @throws SupportException if Storable is not supported - * @throws amazon.carbonado.MalformedTypeException if Storable type is not well-formed - * @throws IllegalArgumentException if type is null - */ - @SuppressWarnings("unchecked") - static synchronized GenericStorableCodec getInstance - (GenericStorableCodecFactory factory, - GenericEncodingStrategy encodingStrategy, boolean isMaster, Layout layout) - throws SupportException - { - Object key; - if (layout == null) { - key = KeyFactory.createKey(new Object[] {encodingStrategy, isMaster}); - } else { - key = KeyFactory.createKey - (new Object[] {encodingStrategy, isMaster, factory, layout.getGeneration()}); - } - - GenericStorableCodec codec = (GenericStorableCodec) cCache.get(key); - if (codec == null) { - codec = new GenericStorableCodec - (factory, - encodingStrategy.getType(), - generateStorable(encodingStrategy, isMaster, layout), - encodingStrategy, - layout); - cCache.put(key, codec); - } - - return codec; - } - - @SuppressWarnings("unchecked") - private static Class generateStorable - (GenericEncodingStrategy encodingStrategy, boolean isMaster, Layout layout) - throws SupportException - { - final Class storableClass = encodingStrategy.getType(); - final Class abstractClass = - RawStorableGenerator.getAbstractClass(storableClass, isMaster); - final int generation = layout == null ? -1 : layout.getGeneration(); - - ClassInjector ci = ClassInjector.create - (storableClass.getName(), abstractClass.getClassLoader()); - - ClassFile cf = new ClassFile(ci.getClassName(), abstractClass); - cf.markSynthetic(); - cf.setSourceFile(GenericStorableCodec.class.getName()); - cf.setTarget("1.5"); - - // Declare some types. - final TypeDesc storageType = TypeDesc.forClass(Storage.class); - final TypeDesc rawSupportType = TypeDesc.forClass(RawSupport.class); - final TypeDesc byteArrayType = TypeDesc.forClass(byte[].class); - final TypeDesc[] byteArrayParam = {byteArrayType}; - final TypeDesc codecType = TypeDesc.forClass(GenericStorableCodec.class); - final TypeDesc decoderType = TypeDesc.forClass(Decoder.class); - final TypeDesc weakRefType = TypeDesc.forClass(WeakReference.class); - - // If Layout provided, then keep a (weak) static reference to this - // GenericStorableCodec in order to get decoders for different - // generations. It is assigned a value after the class is loaded via a - // public static method. It can only be assigned once. - if (layout != null) { - cf.addField(Modifiers.PRIVATE.toStatic(true), CODEC_FIELD_NAME, weakRefType); - MethodInfo mi = cf.addMethod(Modifiers.PUBLIC.toStatic(true), ASSIGN_CODEC_METHOD_NAME, - null, new TypeDesc[] {weakRefType}); - CodeBuilder b = new CodeBuilder(mi); - b.loadStaticField(CODEC_FIELD_NAME, weakRefType); - Label done = b.createLabel(); - b.ifNullBranch(done, false); - b.loadLocal(b.getParameter(0)); - b.storeStaticField(CODEC_FIELD_NAME, weakRefType); - done.setLocation(); - b.returnVoid(); - } - - // Add constructor that accepts a RawSupport. - { - TypeDesc[] params = {rawSupportType}; - MethodInfo mi = cf.addConstructor(Modifiers.PUBLIC, params); - CodeBuilder b = new CodeBuilder(mi); - b.loadThis(); - b.loadLocal(b.getParameter(0)); - b.invokeSuperConstructor(params); - b.returnVoid(); - } - - // Add constructor that accepts a RawSupport, an encoded key, and an - // encoded data. - { - TypeDesc[] params = {rawSupportType, byteArrayType, byteArrayType}; - MethodInfo mi = cf.addConstructor(Modifiers.PUBLIC, params); - CodeBuilder b = new CodeBuilder(mi); - b.loadThis(); - b.loadLocal(b.getParameter(0)); - b.loadLocal(b.getParameter(1)); - b.loadLocal(b.getParameter(2)); - b.invokeSuperConstructor(params); - b.returnVoid(); - } - - // Implement protected abstract methods inherited from parent class. - - // byte[] encodeKey() - { - // Encode the primary key into a byte array that supports correct - // ordering. No special key comparator is needed. - MethodInfo mi = cf.addMethod(Modifiers.PROTECTED, - RawStorableGenerator.ENCODE_KEY_METHOD_NAME, - byteArrayType, null); - CodeBuilder b = new CodeBuilder(mi); - - // TODO: Consider caching generated key. Rebuild if null or if pk is dirty. - - // assembler = b - // properties = null (defaults to all key properties) - // instanceVar = null (null means "this") - // adapterInstanceClass = null (null means use instanceVar, in this case is "this") - // useReadMethods = false (will read fields directly) - // partialStartVar = null (only support encoding all properties) - // partialEndVar = null (only support encoding all properties) - LocalVariable encodedVar = - encodingStrategy.buildKeyEncoding(b, null, null, null, false, null, null); - - b.loadLocal(encodedVar); - b.returnValue(byteArrayType); - } - - // byte[] encodeData() - { - // Encoding non-primary key data properties. - MethodInfo mi = cf.addMethod(Modifiers.PROTECTED, - RawStorableGenerator.ENCODE_DATA_METHOD_NAME, - byteArrayType, null); - CodeBuilder b = new CodeBuilder(mi); - - // assembler = b - // properties = null (defaults to all non-key properties) - // instanceVar = null (null means "this") - // adapterInstanceClass = null (null means use instanceVar, in this case is "this") - // useReadMethods = false (will read fields directly) - // generation = generation - LocalVariable encodedVar = - encodingStrategy.buildDataEncoding(b, null, null, null, false, generation); - - b.loadLocal(encodedVar); - b.returnValue(byteArrayType); - } - - // void decodeKey(byte[]) - { - MethodInfo mi = cf.addMethod(Modifiers.PROTECTED, - RawStorableGenerator.DECODE_KEY_METHOD_NAME, - null, byteArrayParam); - CodeBuilder b = new CodeBuilder(mi); - - // assembler = b - // properties = null (defaults to all key properties) - // instanceVar = null (null means "this") - // adapterInstanceClass = null (null means use instanceVar, in this case is "this") - // useWriteMethods = false (will set fields directly) - // encodedVar = references byte array with encoded key - encodingStrategy.buildKeyDecoding(b, null, null, null, false, b.getParameter(0)); - - b.returnVoid(); - } - - // void decodeData(byte[]) - { - MethodInfo mi = cf.addMethod(Modifiers.PROTECTED, - RawStorableGenerator.DECODE_DATA_METHOD_NAME, - null, byteArrayParam); - CodeBuilder b = new CodeBuilder(mi); - Label altGenerationHandler = b.createLabel(); - - // assembler = b - // properties = null (defaults to all non-key properties) - // instanceVar = null (null means "this") - // adapterInstanceClass = null (null means use instanceVar, in this case is "this") - // useWriteMethods = false (will set fields directly) - // generation = generation - // altGenerationHandler = altGenerationHandler - // encodedVar = references byte array with encoded data - encodingStrategy.buildDataDecoding - (b, null, null, null, false, generation, altGenerationHandler, b.getParameter(0)); - - b.returnVoid(); - - // Support decoding alternate generation. - - altGenerationHandler.setLocation(); - LocalVariable actualGeneration = b.createLocalVariable(null, TypeDesc.INT); - b.storeLocal(actualGeneration); - - b.loadStaticField(CODEC_FIELD_NAME, weakRefType); - b.invokeVirtual(weakRefType, "get", TypeDesc.OBJECT, null); - b.dup(); - Label haveCodec = b.createLabel(); - b.ifNullBranch(haveCodec, false); - - // Codec got reclaimed, which is unlikely to happen during normal - // use since it must be referenced by the storage object. - b.pop(); // Don't need the duped codec instance. - CodeBuilderUtil.throwException(b, IllegalStateException.class, "Codec missing"); - - haveCodec.setLocation(); - b.checkCast(codecType); - b.loadLocal(actualGeneration); - b.invokeVirtual(codecType, "getDecoder", decoderType, new TypeDesc[] {TypeDesc.INT}); - b.loadThis(); - b.loadLocal(b.getParameter(0)); - b.invokeInterface(decoderType, "decode", null, - new TypeDesc[] {TypeDesc.forClass(Storable.class), byteArrayType}); - - b.returnVoid(); - } - - return ci.defineClass(cf); - } - - private final GenericStorableCodecFactory mFactory; - - private final Class mType; - - private final Class mStorableClass; - - // Weakly reference the encoding strategy because of the way - // GenericStorableCodec instances are cached in a SoftValuedHashMap. - // GenericStorableCodec can still be reclaimed by the garbage collector. - private final WeakReference> mEncodingStrategy; - - private final GenericInstanceFactory mInstanceFactory; - - private final SearchKeyFactory mPrimaryKeyFactory; - - // Maps OrderedProperty[] keys to SearchKeyFactory instances. - private final Map mSearchKeyFactories = new SoftValuedHashMap(); - - private final Layout mLayout; - - // Maps layout generations to Decoders. - private IntHashMap mDecoders; - - private GenericStorableCodec(GenericStorableCodecFactory factory, - Class type, Class storableClass, - GenericEncodingStrategy encodingStrategy, - Layout layout) { - mFactory = factory; - mType = type; - mStorableClass = storableClass; - mEncodingStrategy = new WeakReference>(encodingStrategy); - mInstanceFactory = QuickConstructorGenerator - .getInstance(storableClass, GenericInstanceFactory.class); - mPrimaryKeyFactory = getSearchKeyFactory(encodingStrategy.gatherAllKeyProperties()); - mLayout = layout; - - if (layout != null) { - try { - // Assign static reference back to this codec. - Method m = storableClass.getMethod - (ASSIGN_CODEC_METHOD_NAME, WeakReference.class); - m.invoke(null, new WeakReference(this)); - } catch (Exception e) { - ThrowUnchecked.fireFirstDeclaredCause(e); - } - } - } - - /** - * Returns the type of Storable that code is generated for. - */ - public final Class getStorableType() { - return mType; - } - - /** - * Instantiate a Storable with no key or value defined yet. - * - * @param support binds generated storable with a storage layer - */ - @SuppressWarnings("unchecked") - public S instantiate(RawSupport support) { - return (S) mInstanceFactory.instantiate(support); - } - - /** - * Instantiate a Storable with a specific key and value. - * - * @param support binds generated storable with a storage layer - */ - @SuppressWarnings("unchecked") - public S instantiate(RawSupport support, byte[] key, byte[] value) - throws FetchException - { - return (S) mInstanceFactory.instantiate(support, key, value); - } - - public StorableIndex getPrimaryKeyIndex() { - return getEncodingStrategy().getPrimaryKeyIndex(); - } - - public int getPrimaryKeyPrefixLength() { - return getEncodingStrategy().getConstantKeyPrefixLength(); - } - - public byte[] encodePrimaryKey(S storable) { - return mPrimaryKeyFactory.encodeSearchKey(storable); - } - - public byte[] encodePrimaryKey(S storable, int rangeStart, int rangeEnd) { - return mPrimaryKeyFactory.encodeSearchKey(storable, rangeStart, rangeEnd); - } - - public byte[] encodePrimaryKey(Object[] values) { - return mPrimaryKeyFactory.encodeSearchKey(values); - } - - public byte[] encodePrimaryKey(Object[] values, int rangeStart, int rangeEnd) { - return mPrimaryKeyFactory.encodeSearchKey(values, rangeStart, rangeEnd); - } - - public byte[] encodePrimaryKeyPrefix() { - return mPrimaryKeyFactory.encodeSearchKeyPrefix(); - } - - /** - * Returns a concrete Storable implementation, which is fully - * thread-safe. It has two constructors defined: - * - *

-     * public <init>(Storage, RawSupport);
-     *
-     * public <init>(Storage, RawSupport, byte[] key, byte[] value);
-     * 
- * - * Convenience methods are provided in this class to instantiate the - * generated Storable. - */ - public Class getStorableClass() { - return mStorableClass; - } - - /** - * Returns a search key factory, which is useful for implementing indexes - * and queries. - * - * @param properties properties to build the search key from - */ - @SuppressWarnings("unchecked") - public SearchKeyFactory getSearchKeyFactory(OrderedProperty[] properties) { - // This KeyFactory makes arrays work as hashtable keys. - Object key = org.cojen.util.KeyFactory.createKey(properties); - - synchronized (mSearchKeyFactories) { - SearchKeyFactory factory = (SearchKeyFactory) mSearchKeyFactories.get(key); - if (factory == null) { - factory = generateSearchKeyFactory(properties); - mSearchKeyFactories.put(key, factory); - } - return factory; - } - } - - /** - * Returns a data decoder for the given generation. - * - * @throws FetchNoneException if generation is unknown - */ - public Decoder getDecoder(int generation) throws FetchNoneException, FetchException { - try { - synchronized (mLayout) { - IntHashMap decoders = mDecoders; - if (decoders == null) { - mDecoders = decoders = new IntHashMap(); - } - Decoder decoder = (Decoder) decoders.get(generation); - if (decoder == null) { - decoder = generateDecoder(generation); - mDecoders.put(generation, decoder); - } - return decoder; - } - } catch (NullPointerException e) { - if (mLayout == null) { - throw new FetchNoneException("Layout evolution not supported"); - } - throw e; - } - } - - private GenericEncodingStrategy getEncodingStrategy() { - // Should never be null, even though it is weakly referenced. As long - // as this class can be reached by the cache, the encoding strategy - // object exists since it is the cache key. - return mEncodingStrategy.get(); - } - - @SuppressWarnings("unchecked") - private SearchKeyFactory generateSearchKeyFactory(OrderedProperty[] properties) { - GenericEncodingStrategy encodingStrategy = getEncodingStrategy(); - - ClassInjector ci; - { - StringBuilder b = new StringBuilder(); - b.append(mType.getName()); - b.append('$'); - for (OrderedProperty property : properties) { - if (property.getDirection() == Direction.UNSPECIFIED) { - property = property.direction(Direction.ASCENDING); - } - try { - property.appendTo(b); - } catch (java.io.IOException e) { - // Not gonna happen - } - } - String prefix = b.toString(); - ci = ClassInjector.create(prefix, mStorableClass.getClassLoader()); - } - - ClassFile cf = new ClassFile(ci.getClassName()); - cf.addInterface(SearchKeyFactory.class); - cf.markSynthetic(); - cf.setSourceFile(GenericStorableCodec.class.getName()); - cf.setTarget("1.5"); - - // Add public no-arg constructor. - cf.addDefaultConstructor(); - - // Declare some types. - final TypeDesc storableType = TypeDesc.forClass(Storable.class); - final TypeDesc byteArrayType = TypeDesc.forClass(byte[].class); - final TypeDesc objectArrayType = TypeDesc.forClass(Object[].class); - final TypeDesc instanceType = TypeDesc.forClass(mStorableClass); - - // Define encodeSearchKey(Storable). - try { - MethodInfo mi = cf.addMethod - (Modifiers.PUBLIC, "encodeSearchKey", byteArrayType, - new TypeDesc[] {storableType}); - CodeBuilder b = new CodeBuilder(mi); - b.loadLocal(b.getParameter(0)); - b.checkCast(instanceType); - LocalVariable instanceVar = b.createLocalVariable(null, instanceType); - b.storeLocal(instanceVar); - - // assembler = b - // properties = properties to encode - // instanceVar = instanceVar which references storable instance - // adapterInstanceClass = null (null means use instanceVar) - // useReadMethods = false (will read fields directly) - // partialStartVar = null (only support encoding all properties) - // partialEndVar = null (only support encoding all properties) - LocalVariable encodedVar = encodingStrategy.buildKeyEncoding - (b, properties, instanceVar, null, false, null, null); - - b.loadLocal(encodedVar); - b.returnValue(byteArrayType); - } catch (SupportException e) { - // Shouldn't happen since all properties were checked in order - // to create this StorableCodec. - throw new UndeclaredThrowableException(e); - } - - // Define encodeSearchKey(Storable, int, int). - try { - MethodInfo mi = cf.addMethod - (Modifiers.PUBLIC, "encodeSearchKey", byteArrayType, - new TypeDesc[] {storableType, TypeDesc.INT, TypeDesc.INT}); - CodeBuilder b = new CodeBuilder(mi); - b.loadLocal(b.getParameter(0)); - b.checkCast(instanceType); - LocalVariable instanceVar = b.createLocalVariable(null, instanceType); - b.storeLocal(instanceVar); - - // assembler = b - // properties = properties to encode - // instanceVar = instanceVar which references storable instance - // adapterInstanceClass = null (null means use instanceVar) - // useReadMethods = false (will read fields directly) - // partialStartVar = int parameter 1, references start property index - // partialEndVar = int parameter 2, references end property index - LocalVariable encodedVar = encodingStrategy.buildKeyEncoding - (b, properties, instanceVar, null, false, b.getParameter(1), b.getParameter(2)); - - b.loadLocal(encodedVar); - b.returnValue(byteArrayType); - } catch (SupportException e) { - // Shouldn't happen since all properties were checked in order - // to create this StorableCodec. - throw new UndeclaredThrowableException(e); - } - - // The Storable class that we generated earlier is a subclass of the - // abstract class defined by StorableGenerator. StorableGenerator - // creates static final adapter instances, with protected - // access. Calling getSuperclass results in the exact class that - // StorableGenerator made, which is where the fields are. - final Class adapterInstanceClass = getStorableClass().getSuperclass(); - - // Define encodeSearchKey(Object[] values). - try { - MethodInfo mi = cf.addMethod - (Modifiers.PUBLIC, "encodeSearchKey", byteArrayType, - new TypeDesc[] {objectArrayType}); - CodeBuilder b = new CodeBuilder(mi); - - // assembler = b - // properties = properties to encode - // instanceVar = parameter 0, an object array - // adapterInstanceClass = adapterInstanceClass - see comment above - // useReadMethods = false (will read fields directly) - // partialStartVar = null (only support encoding all properties) - // partialEndVar = null (only support encoding all properties) - LocalVariable encodedVar = encodingStrategy.buildKeyEncoding - (b, properties, b.getParameter(0), adapterInstanceClass, false, null, null); - - b.loadLocal(encodedVar); - b.returnValue(byteArrayType); - } catch (SupportException e) { - // Shouldn't happen since all properties were checked in order - // to create this StorableCodec. - throw new UndeclaredThrowableException(e); - } - - // Define encodeSearchKey(Object[] values, int, int). - try { - MethodInfo mi = cf.addMethod - (Modifiers.PUBLIC, "encodeSearchKey", byteArrayType, - new TypeDesc[] {objectArrayType, TypeDesc.INT, TypeDesc.INT}); - CodeBuilder b = new CodeBuilder(mi); - - // assembler = b - // properties = properties to encode - // instanceVar = parameter 0, an object array - // adapterInstanceClass = adapterInstanceClass - see comment above - // useReadMethods = false (will read fields directly) - // partialStartVar = int parameter 1, references start property index - // partialEndVar = int parameter 2, references end property index - LocalVariable encodedVar = encodingStrategy.buildKeyEncoding - (b, properties, b.getParameter(0), adapterInstanceClass, - false, b.getParameter(1), b.getParameter(2)); - - b.loadLocal(encodedVar); - b.returnValue(byteArrayType); - } catch (SupportException e) { - // Shouldn't happen since all properties were checked in order - // to create this StorableCodec. - throw new UndeclaredThrowableException(e); - } - - // Define encodeSearchKeyPrefix(). - try { - MethodInfo mi = cf.addMethod - (Modifiers.PUBLIC, "encodeSearchKeyPrefix", byteArrayType, null); - CodeBuilder b = new CodeBuilder(mi); - - if (encodingStrategy.getKeyPrefixPadding() == 0 && - encodingStrategy.getKeySuffixPadding() == 0) { - // Return null instead of a zero-length array. - b.loadNull(); - b.returnValue(byteArrayType); - } else { - // Build array once and re-use. Trust that no one modifies it. - cf.addField(Modifiers.PRIVATE.toStatic(true).toFinal(true), - BLANK_KEY_FIELD_NAME, byteArrayType); - b.loadStaticField(BLANK_KEY_FIELD_NAME, byteArrayType); - b.returnValue(byteArrayType); - - // Create static initializer to set field. - mi = cf.addInitializer(); - b = new CodeBuilder(mi); - - // assembler = b - // properties = no parameters - we just want the key prefix - // instanceVar = null (no parameters means we don't need this) - // adapterInstanceClass = null (no parameters means we don't need this) - // useReadMethods = false (no parameters means we don't need this) - // partialStartVar = null (no parameters means we don't need this) - // partialEndVar = null (no parameters means we don't need this) - LocalVariable encodedVar = encodingStrategy.buildKeyEncoding - (b, new OrderedProperty[0], null, null, false, null, null); - - b.loadLocal(encodedVar); - b.storeStaticField(BLANK_KEY_FIELD_NAME, byteArrayType); - b.returnVoid(); - } - } catch (SupportException e) { - // Shouldn't happen since all properties were checked in order - // to create this StorableCodec. - throw new UndeclaredThrowableException(e); - } - - Class clazz = ci.defineClass(cf); - try { - return clazz.newInstance(); - } catch (InstantiationException e) { - throw new UndeclaredThrowableException(e); - } catch (IllegalAccessException e) { - throw new UndeclaredThrowableException(e); - } - } - - private Decoder generateDecoder(int generation) throws FetchException { - // Create an encoding strategy against the reconstructed storable. - GenericEncodingStrategy altStrategy; - try { - Class altStorable = mLayout.getGeneration(generation) - .reconstruct(mStorableClass.getClassLoader()); - altStrategy = mFactory.createStrategy(altStorable, null); - } catch (RepositoryException e) { - throw new CorruptEncodingException(e); - } - - ClassInjector ci = ClassInjector.create(mType.getName(), mStorableClass.getClassLoader()); - ClassFile cf = new ClassFile(ci.getClassName()); - cf.addInterface(Decoder.class); - cf.markSynthetic(); - cf.setSourceFile(GenericStorableCodec.class.getName()); - cf.setTarget("1.5"); - - // Add public no-arg constructor. - cf.addDefaultConstructor(); - - // Declare some types. - final TypeDesc storableType = TypeDesc.forClass(Storable.class); - final TypeDesc byteArrayType = TypeDesc.forClass(byte[].class); - - // Define the required decode method. - MethodInfo mi = cf.addMethod - (Modifiers.PUBLIC, "decode", null, new TypeDesc[] {storableType, byteArrayType}); - CodeBuilder b = new CodeBuilder(mi); - - LocalVariable uncastDestVar = b.getParameter(0); - b.loadLocal(uncastDestVar); - LocalVariable destVar = b.createLocalVariable(null, TypeDesc.forClass(mStorableClass)); - b.checkCast(destVar.getType()); - b.storeLocal(destVar); - LocalVariable dataVar = b.getParameter(1); - - // assembler = b - // properties = null (defaults to all non-key properties) - // instanceVar = "dest" storable - // adapterInstanceClass = null (null means use instanceVar, in this case is "dest") - // useWriteMethods = false (will set fields directly) - // generation = generation - // altGenerationHandler = null (generation should match) - // encodedVar = "data" byte array - try { - altStrategy.buildDataDecoding - (b, null, destVar, null, false, generation, null, dataVar); - } catch (SupportException e) { - throw new CorruptEncodingException(e); - } - - b.returnVoid(); - - Class clazz = ci.defineClass(cf); - try { - return clazz.newInstance(); - } catch (InstantiationException e) { - throw new UndeclaredThrowableException(e); - } catch (IllegalAccessException e) { - throw new UndeclaredThrowableException(e); - } - } - - /** - * Creates custom raw search keys for {@link Storable} types. It is - * intended for supporting queries and indexes. - */ - public interface SearchKeyFactory { - /** - * Build a search key by extracting all the desired properties from the - * given storable. - * - * @param storable extract a subset of properties from this instance - * @return raw search key - */ - byte[] encodeSearchKey(S storable); - - /** - * Build a search key by extracting all the desired properties from the - * given storable. - * - * @param storable extract a subset of properties from this instance - * @param rangeStart index of first property to use. Its value must be less - * than the count of properties used by this factory. - * @param rangeEnd index of last property to use, exlusive. Its value must - * be less than or equal to the count of properties used by this factory. - * @return raw search key - */ - byte[] encodeSearchKey(S storable, int rangeStart, int rangeEnd); - - /** - * Build a search key by supplying property values without a storable. - * - * @param values values to build into a key. It must be long enough to - * accommodate all of properties used by this factory. - * @return raw search key - */ - byte[] encodeSearchKey(Object[] values); - - /** - * Build a search key by supplying property values without a storable. - * - * @param values values to build into a key. The length may be less than - * the amount of properties used by this factory. It must not be less than the - * difference between rangeStart and rangeEnd. - * @param rangeStart index of first property to use. Its value must be less - * than the count of properties used by this factory. - * @param rangeEnd index of last property to use, exlusive. Its value must - * be less than or equal to the count of properties used by this factory. - * @return raw search key - */ - byte[] encodeSearchKey(Object[] values, int rangeStart, int rangeEnd); - - /** - * Returns the search key for when there are no values. Returned value - * may be null. - */ - byte[] encodeSearchKeyPrefix(); - } - - /** - * Used for decoding different generations of Storable. - */ - public interface Decoder { - /** - * @param dest storable to receive decoded properties - * @param data decoded into properties, some of which may be dropped if - * destination storable doesn't have it - */ - void decode(S dest, byte[] data); - } -} diff --git a/src/main/java/com/amazon/carbonado/spi/raw/GenericStorableCodecFactory.java b/src/main/java/com/amazon/carbonado/spi/raw/GenericStorableCodecFactory.java deleted file mode 100644 index fefa880..0000000 --- a/src/main/java/com/amazon/carbonado/spi/raw/GenericStorableCodecFactory.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2006 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.amazon.carbonado.spi.raw; - -import com.amazon.carbonado.Storable; -import com.amazon.carbonado.SupportException; - -import com.amazon.carbonado.info.StorableIndex; -import com.amazon.carbonado.layout.Layout; - -/** - * Factory for generic codec that supports any kind of storable by - * auto-generating and caching storable implementations. - * - * @author Brian S O'Neill - */ -public class GenericStorableCodecFactory implements StorableCodecFactory { - public GenericStorableCodecFactory() { - } - - /** - * Returns null to let repository decide what the name should be. - */ - public String getStorageName(Class type) throws SupportException { - return null; - } - - /** - * @param type type of storable to create codec for - * @param pkIndex suggested index for primary key (optional) - * @param isMaster when true, version properties and sequences are managed - * @param layout when non-null, encode a storable layout generation - * value in one or four bytes. Generation 0..127 is encoded in one byte, and - * 128..max is encoded in four bytes, with the most significant bit set. - * @throws SupportException if type is not supported - */ - @SuppressWarnings("unchecked") - public GenericStorableCodec createCodec(Class type, - StorableIndex pkIndex, - boolean isMaster, - Layout layout) - throws SupportException - { - return GenericStorableCodec.getInstance - (this, createStrategy(type, pkIndex), isMaster, layout); - } - - /** - * Override to return a different EncodingStrategy. - * - * @param type type of Storable to generate code for - * @param pkIndex specifies sequence and ordering of key properties (optional) - */ - protected GenericEncodingStrategy createStrategy - (Class type, StorableIndex pkIndex) - throws SupportException - { - return new GenericEncodingStrategy(type, pkIndex); - } -} diff --git a/src/main/java/com/amazon/carbonado/spi/raw/KeyDecoder.java b/src/main/java/com/amazon/carbonado/spi/raw/KeyDecoder.java deleted file mode 100644 index 127216f..0000000 --- a/src/main/java/com/amazon/carbonado/spi/raw/KeyDecoder.java +++ /dev/null @@ -1,646 +0,0 @@ -/* - * Copyright 2006 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.amazon.carbonado.spi.raw; - -import com.amazon.carbonado.CorruptEncodingException; - -import static com.amazon.carbonado.spi.raw.KeyEncoder.*; - -/** - * A very low-level class that decodes key components encoded by methods of - * {@link KeyEncoder}. - * - * @author Brian S O'Neill - */ -public class KeyDecoder extends DataDecoder { - - /** - * Decodes a signed integer from exactly 4 bytes, as encoded for descending - * order. - * - * @param src source of encoded bytes - * @param srcOffset offset into source array - * @return signed integer value - */ - public static int decodeIntDesc(byte[] src, int srcOffset) - throws CorruptEncodingException - { - return ~decodeInt(src, srcOffset); - } - - /** - * Decodes a signed Integer object from exactly 1 or 5 bytes, as encoded - * for descending order. 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 decodeIntegerObjDesc(byte[] src, int srcOffset) - throws CorruptEncodingException - { - try { - int b = src[srcOffset]; - if (b == NULL_BYTE_HIGH || b == NULL_BYTE_LOW) { - return null; - } - return decodeIntDesc(src, srcOffset + 1); - } catch (IndexOutOfBoundsException e) { - throw new CorruptEncodingException(null, e); - } - } - - /** - * Decodes a signed long from exactly 8 bytes, as encoded for descending - * order. - * - * @param src source of encoded bytes - * @param srcOffset offset into source array - * @return signed long value - */ - public static long decodeLongDesc(byte[] src, int srcOffset) - throws CorruptEncodingException - { - return ~decodeLong(src, srcOffset); - } - - /** - * Decodes a signed Long object from exactly 1 or 9 bytes, as encoded for - * descending order. 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 decodeLongObjDesc(byte[] src, int srcOffset) - throws CorruptEncodingException - { - try { - int b = src[srcOffset]; - if (b == NULL_BYTE_HIGH || b == NULL_BYTE_LOW) { - return null; - } - return decodeLongDesc(src, srcOffset + 1); - } catch (IndexOutOfBoundsException e) { - throw new CorruptEncodingException(null, e); - } - } - - /** - * Decodes a signed byte from exactly 1 byte, as encoded for descending - * order. - * - * @param src source of encoded bytes - * @param srcOffset offset into source array - * @return signed byte value - */ - public static byte decodeByteDesc(byte[] src, int srcOffset) - throws CorruptEncodingException - { - try { - return (byte)(src[srcOffset] ^ 0x7f); - } catch (IndexOutOfBoundsException e) { - throw new CorruptEncodingException(null, e); - } - } - - /** - * Decodes a signed Byte object from exactly 1 or 2 bytes, as encoded for - * descending order. 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 decodeByteObjDesc(byte[] src, int srcOffset) - throws CorruptEncodingException - { - try { - int b = src[srcOffset]; - if (b == NULL_BYTE_HIGH || b == NULL_BYTE_LOW) { - return null; - } - return decodeByteDesc(src, srcOffset + 1); - } catch (IndexOutOfBoundsException e) { - throw new CorruptEncodingException(null, e); - } - } - - /** - * Decodes a signed short from exactly 2 bytes, as encoded for descending - * order. - * - * @param src source of encoded bytes - * @param srcOffset offset into source array - * @return signed short value - */ - public static short decodeShortDesc(byte[] src, int srcOffset) - throws CorruptEncodingException - { - try { - return (short)(((src[srcOffset] << 8) | (src[srcOffset + 1] & 0xff)) ^ 0x7fff); - } catch (IndexOutOfBoundsException e) { - throw new CorruptEncodingException(null, e); - } - } - - /** - * Decodes a signed Short object from exactly 1 or 3 bytes, as encoded for - * descending order. 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 decodeShortObjDesc(byte[] src, int srcOffset) - throws CorruptEncodingException - { - try { - int b = src[srcOffset]; - if (b == NULL_BYTE_HIGH || b == NULL_BYTE_LOW) { - return null; - } - return decodeShortDesc(src, srcOffset + 1); - } catch (IndexOutOfBoundsException e) { - throw new CorruptEncodingException(null, e); - } - } - - /** - * Decodes a char from exactly 2 bytes, as encoded for descending order. - * - * @param src source of encoded bytes - * @param srcOffset offset into source array - * @return char value - */ - public static char decodeCharDesc(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, as encoded for - * descending order. 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 decodeCharacterObjDesc(byte[] src, int srcOffset) - throws CorruptEncodingException - { - try { - int b = src[srcOffset]; - if (b == NULL_BYTE_HIGH || b == NULL_BYTE_LOW) { - return null; - } - return decodeCharDesc(src, srcOffset + 1); - } catch (IndexOutOfBoundsException e) { - throw new CorruptEncodingException(null, e); - } - } - - /** - * Decodes a boolean from exactly 1 byte, as encoded for descending order. - * - * @param src source of encoded bytes - * @param srcOffset offset into source array - * @return boolean value - */ - public static boolean decodeBooleanDesc(byte[] src, int srcOffset) - throws CorruptEncodingException - { - try { - return src[srcOffset] == 127; - } catch (IndexOutOfBoundsException e) { - throw new CorruptEncodingException(null, e); - } - } - - /** - * Decodes a Boolean object from exactly 1 byte, as encoded for descending - * order. - * - * @param src source of encoded bytes - * @param srcOffset offset into source array - * @return Boolean object or null - */ - public static Boolean decodeBooleanObjDesc(byte[] src, int srcOffset) - throws CorruptEncodingException - { - try { - switch (src[srcOffset]) { - case NULL_BYTE_LOW: case NULL_BYTE_HIGH: - return null; - case (byte)127: - return Boolean.TRUE; - default: - return Boolean.FALSE; - } - } catch (IndexOutOfBoundsException e) { - throw new CorruptEncodingException(null, e); - } - } - - /** - * Decodes a float from exactly 4 bytes, as encoded for descending order. - * - * @param src source of encoded bytes - * @param srcOffset offset into source array - * @return float value - */ - public static float decodeFloatDesc(byte[] src, int srcOffset) - throws CorruptEncodingException - { - int bits = decodeFloatBits(src, srcOffset); - if (bits >= 0) { - bits ^= 0x7fffffff; - } - 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 decodeFloatObjDesc(byte[] src, int srcOffset) - throws CorruptEncodingException - { - int bits = decodeFloatBits(src, srcOffset); - if (bits >= 0) { - bits ^= 0x7fffffff; - } - return bits == 0x7fffffff ? null : Float.intBitsToFloat(bits); - } - - /** - * Decodes a double from exactly 8 bytes, as encoded for descending order. - * - * @param src source of encoded bytes - * @param srcOffset offset into source array - * @return double value - */ - public static double decodeDoubleDesc(byte[] src, int srcOffset) - throws CorruptEncodingException - { - long bits = decodeDoubleBits(src, srcOffset); - if (bits >= 0) { - bits ^= 0x7fffffffffffffffL; - } - 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 decodeDoubleObjDesc(byte[] src, int srcOffset) - throws CorruptEncodingException - { - long bits = decodeDoubleBits(src, srcOffset); - if (bits >= 0) { - bits ^= 0x7fffffffffffffffL; - } - return bits == 0x7fffffffffffffffL ? null : Double.longBitsToDouble(bits); - } - - /** - * Decodes the given byte array as originally encoded for ascending order. - * The decoding stops when any kind of terminator or illegal byte has been - * read. The decoded bytes are stored in valueRef. - * - * @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 { - return decode(src, srcOffset, valueRef, 0); - } catch (IndexOutOfBoundsException e) { - throw new CorruptEncodingException(null, e); - } - } - - /** - * Decodes the given byte array as originally encoded for descending order. - * The decoding stops when any kind of terminator or illegal byte has been - * read. The decoded bytes are stored in valueRef. - * - * @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 decodeDesc(byte[] src, int srcOffset, byte[][] valueRef) - throws CorruptEncodingException - { - try { - return decode(src, srcOffset, valueRef, -1); - } catch (IndexOutOfBoundsException e) { - throw new CorruptEncodingException(null, e); - } - } - - /** - * @param xorMask 0 for normal decoding, -1 for descending decoding - */ - private static int decode(byte[] src, int srcOffset, byte[][] valueRef, int xorMask) { - // Scan ahead, looking for terminator. - int srcEnd = srcOffset; - while (true) { - byte b = src[srcEnd++]; - if (-32 <= b && b < 32) { - if (b == NULL_BYTE_HIGH || b == NULL_BYTE_LOW) { - if ((srcEnd - 1) <= srcOffset) { - valueRef[0] = null; - return 1; - } - } - break; - } - } - - if (srcEnd - srcOffset == 1) { - valueRef[0] = EMPTY_BYTE_ARRAY; - return 1; - } - - // Value is decoded from base-32768. - - int valueLength = ((srcEnd - srcOffset - 1) * 120) >> 7; - byte[] value = new byte[valueLength]; - int valueOffset = 0; - - final int originalOffset = srcOffset; - - int accumBits = 0; - int accum = 0; - - while (true) { - int d = (src[srcOffset++] ^ xorMask) & 0xff; - int b; - if (srcOffset == srcEnd || - (b = (src[srcOffset++] ^ xorMask) & 0xff) < 32 || b > 223) { - // Handle special case where one byte was emitted for digit. - d -= 32; - // To produce digit, multiply d by 192 and add 191 to adjust - // for missing remainder. The lower bits are discarded anyhow. - d = (d << 7) + (d << 6) + 191; - - // Shift decoded digit into accumulator. - accumBits += 15; - accum = (accum << 15) | d; - - break; - } - - d -= 32; - // To produce digit, multiply d by 192 and add in remainder. - d = ((d << 7) + (d << 6)) + b - 32; - - // Shift decoded digit into accumulator. - accumBits += 15; - accum = (accum << 15) | d; - - if (accumBits == 15) { - value[valueOffset++] = (byte)(accum >> 7); - } else { - value[valueOffset++] = (byte)(accum >> (accumBits - 8)); - accumBits -= 8; - value[valueOffset++] = (byte)(accum >> (accumBits - 8)); - } - accumBits -= 8; - } - - if (accumBits >= 8 && valueOffset < valueLength) { - value[valueOffset] = (byte)(accum >> (accumBits - 8)); - } - - valueRef[0] = value; - - return srcOffset - originalOffset; - } - - /** - * 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 { - return decodeString(src, srcOffset, valueRef, 0); - } catch (IndexOutOfBoundsException e) { - throw new CorruptEncodingException(null, e); - } - } - - /** - * Decodes an encoded string from the given byte array as originally - * encoded for descending order. - * - * @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 decodeStringDesc(byte[] src, int srcOffset, String[] valueRef) - throws CorruptEncodingException - { - try { - return decodeString(src, srcOffset, valueRef, -1); - } catch (IndexOutOfBoundsException e) { - throw new CorruptEncodingException(null, e); - } - } - - /** - * @param xorMask 0 for normal decoding, -1 for descending decoding - */ - private static int decodeString(byte[] src, int srcOffset, String[] valueRef, int xorMask) - throws CorruptEncodingException - { - // Scan ahead, looking for terminator. - int srcEnd = srcOffset; - while (true) { - byte b = src[srcEnd++]; - if (-2 <= b && b < 2) { - if (b == NULL_BYTE_HIGH || b == NULL_BYTE_LOW) { - if ((srcEnd - 1) <= srcOffset) { - valueRef[0] = null; - return 1; - } - } - break; - } - } - - if (srcEnd - srcOffset == 1) { - valueRef[0] = ""; - return 1; - } - - // Allocate a character array which may be longer than needed once - // bytes are decoded into characters. - char[] value = new char[srcEnd - srcOffset]; - int valueOffset = 0; - - final int originalOffset = srcOffset; - - while (srcOffset < srcEnd) { - int c = (src[srcOffset++] ^ xorMask) & 0xff; - switch (c >> 5) { - case 0: case 1: case 2: case 3: - // 0xxxxxxx - value[valueOffset++] = (char)(c - 2); - break; - case 4: case 5: - // 10xxxxxx xxxxxxxx - - c = c & 0x3f; - // Multiply by 192, add in remainder, remove offset of 2, and de-normalize. - value[valueOffset++] = - (char)((c << 7) + (c << 6) + ((src[srcOffset++] ^ xorMask) & 0xff) + 94); - - break; - case 6: - // 110xxxxx xxxxxxxx xxxxxxxx - - c = c & 0x1f; - // Multiply by 192, add in remainder... - c = (c << 7) + (c << 6) + ((src[srcOffset++] ^ xorMask) & 0xff) - 32; - // ...multiply by 192, add in remainder, remove offset of 2, and de-normalize. - c = (c << 7) + (c << 6) + ((src[srcOffset++] ^ xorMask) & 0xff) + 12382; - - 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, 0, valueOffset - 1); - - return srcEnd - originalOffset; - } - - /** - * Decodes the given byte array which was encoded by {@link - * KeyEncoder#encodeSingleDesc}. - */ - public static byte[] decodeSingleDesc(byte[] src) throws CorruptEncodingException { - return decodeSingleDesc(src, 0, 0); - } - - /** - * Decodes the given byte array which was encoded by {@link - * KeyEncoder#encodeSingleDesc}. - * - * @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[] decodeSingleDesc(byte[] src, int prefixPadding, int suffixPadding) - throws CorruptEncodingException - { - try { - int length = src.length - suffixPadding - prefixPadding; - if (length == 0) { - return EMPTY_BYTE_ARRAY; - } - byte[] dst = new byte[length]; - while (--length >= 0) { - dst[length] = (byte) (~src[prefixPadding + length]); - } - return dst; - } catch (IndexOutOfBoundsException e) { - throw new CorruptEncodingException(null, e); - } - } - - /** - * Decodes the given byte array which was encoded by {@link - * KeyEncoder#encodeSingleNullableDesc}. - */ - public static byte[] decodeSingleNullableDesc(byte[] src) throws CorruptEncodingException { - return decodeSingleNullableDesc(src, 0, 0); - } - - /** - * Decodes the given byte array which was encoded by {@link - * KeyEncoder#encodeSingleNullableDesc}. - * - * @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[] decodeSingleNullableDesc(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[] dst = new byte[length]; - while (--length >= 0) { - dst[length] = (byte) (~src[1 + prefixPadding + length]); - } - return dst; - } catch (IndexOutOfBoundsException e) { - throw new CorruptEncodingException(null, e); - } - } -} diff --git a/src/main/java/com/amazon/carbonado/spi/raw/KeyEncoder.java b/src/main/java/com/amazon/carbonado/spi/raw/KeyEncoder.java deleted file mode 100644 index dd0faf9..0000000 --- a/src/main/java/com/amazon/carbonado/spi/raw/KeyEncoder.java +++ /dev/null @@ -1,741 +0,0 @@ -/* - * Copyright 2006 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.amazon.carbonado.spi.raw; - -/** - * A very low-level class that supports encoding of primitive data into unique, - * sortable byte array keys. If the data to encode is of a variable size, then - * it is written in base-32768, using only byte values 32..223. This allows - * special values such as nulls and terminators to be unambiguously - * encoded. Terminators for variable data can be encoded using 1 for ascending - * order and 254 for descending order. Nulls can be encoded as 255 for high - * ordering and 0 for low ordering. - * - * @author Brian S O'Neill - * @see KeyDecoder - */ -public class KeyEncoder extends DataEncoder { - - /** Byte to terminate variable data encoded for ascending order */ - static final byte TERMINATOR = (byte)1; - - /** - * Encodes the given signed integer into exactly 4 bytes for descending - * order. - * - * @param value signed integer value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - */ - public static void encodeDesc(int value, byte[] dst, int dstOffset) { - encode(~value, dst, dstOffset); - } - - /** - * Encodes the given signed Integer object into exactly 1 or 5 bytes for - * descending order. If the Integer object is never expected to be null, - * consider encoding as an int primitive. - * - * @param value optional signed Integer value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - * @return amount of bytes written - */ - public static int encodeDesc(Integer value, byte[] dst, int dstOffset) { - if (value == null) { - dst[dstOffset] = NULL_BYTE_LOW; - return 1; - } else { - dst[dstOffset] = NOT_NULL_BYTE_LOW; - encode(~value.intValue(), dst, dstOffset + 1); - return 5; - } - } - - /** - * Encodes the given signed long into exactly 8 bytes for descending order. - * - * @param value signed long value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - */ - public static void encodeDesc(long value, byte[] dst, int dstOffset) { - encode(~value, dst, dstOffset); - } - - /** - * Encodes the given signed Long object into exactly 1 or 9 bytes for - * descending order. If the Long object is never expected to be null, - * consider encoding as a long primitive. - * - * @param value optional signed Long value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - * @return amount of bytes written - */ - public static int encodeDesc(Long value, byte[] dst, int dstOffset) { - if (value == null) { - dst[dstOffset] = NULL_BYTE_LOW; - return 1; - } else { - dst[dstOffset] = NOT_NULL_BYTE_LOW; - encode(~value.longValue(), dst, dstOffset + 1); - return 9; - } - } - - /** - * Encodes the given signed byte into exactly 1 byte for descending order. - * - * @param value signed byte value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - */ - public static void encodeDesc(byte value, byte[] dst, int dstOffset) { - dst[dstOffset] = (byte)(value ^ 0x7f); - } - - /** - * Encodes the given signed Byte object into exactly 1 or 2 bytes for - * descending order. If the Byte object is never expected to be null, - * consider encoding as a byte primitive. - * - * @param value optional signed Byte value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - * @return amount of bytes written - */ - public static int encodeDesc(Byte value, byte[] dst, int dstOffset) { - if (value == null) { - dst[dstOffset] = NULL_BYTE_LOW; - return 1; - } else { - dst[dstOffset] = NOT_NULL_BYTE_LOW; - dst[dstOffset + 1] = (byte)(value ^ 0x7f); - return 2; - } - } - - /** - * Encodes the given signed short into exactly 2 bytes for descending - * order. - * - * @param value signed short value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - */ - public static void encodeDesc(short value, byte[] dst, int dstOffset) { - encode((short) ~value, dst, dstOffset); - } - - /** - * Encodes the given signed Short object into exactly 1 or 3 bytes for - * descending order. If the Short object is never expected to be null, - * consider encoding as a short primitive. - * - * @param value optional signed Short value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - * @return amount of bytes written - */ - public static int encodeDesc(Short value, byte[] dst, int dstOffset) { - if (value == null) { - dst[dstOffset] = NULL_BYTE_LOW; - return 1; - } else { - dst[dstOffset] = NOT_NULL_BYTE_LOW; - encode((short) ~value.shortValue(), dst, dstOffset + 1); - return 3; - } - } - - /** - * Encodes the given character into exactly 2 bytes for descending order. - * - * @param value character value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - */ - public static void encodeDesc(char value, byte[] dst, int dstOffset) { - encode((char) ~value, dst, dstOffset); - } - - /** - * Encodes the given Character object into exactly 1 or 3 bytes for - * descending order. If the Character object is never expected to be null, - * consider encoding as a char primitive. - * - * @param value optional Character value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - * @return amount of bytes written - */ - public static int encodeDesc(Character value, byte[] dst, int dstOffset) { - if (value == null) { - dst[dstOffset] = NULL_BYTE_LOW; - return 1; - } else { - dst[dstOffset] = NOT_NULL_BYTE_LOW; - encode((char) ~value.charValue(), dst, dstOffset + 1); - return 3; - } - } - - /** - * Encodes the given boolean into exactly 1 byte for descending order. - * - * @param value boolean value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - */ - public static void encodeDesc(boolean value, byte[] dst, int dstOffset) { - dst[dstOffset] = value ? (byte)127 : (byte)128; - } - - /** - * Encodes the given Boolean object into exactly 1 byte for descending - * order. - * - * @param value optional Boolean value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - */ - public static void encodeDesc(Boolean value, byte[] dst, int dstOffset) { - if (value == null) { - dst[dstOffset] = NULL_BYTE_LOW; - } else { - dst[dstOffset] = value.booleanValue() ? (byte)127 : (byte)128; - } - } - - /** - * Encodes the given float into exactly 4 bytes for descending order. - * - * @param value float value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - */ - public static void encodeDesc(float value, byte[] dst, int dstOffset) { - int bits = Float.floatToIntBits(value); - if (bits >= 0) { - bits ^= 0x7fffffff; - } - dst[dstOffset ] = (byte)(bits >> 24); - dst[dstOffset + 1] = (byte)(bits >> 16); - dst[dstOffset + 2] = (byte)(bits >> 8); - dst[dstOffset + 3] = (byte)bits; - } - - /** - * Encodes the given Float object into exactly 4 bytes for descending - * order. A non-canonical NaN value is used to represent null. - * - * @param value optional Float value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - */ - public static void encodeDesc(Float value, byte[] dst, int dstOffset) { - if (value == null) { - encode(~0x7fffffff, dst, dstOffset); - } else { - encodeDesc(value.floatValue(), dst, dstOffset); - } - } - - /** - * Encodes the given double into exactly 8 bytes for descending order. - * - * @param value double value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - */ - public static void encodeDesc(double value, byte[] dst, int dstOffset) { - long bits = Double.doubleToLongBits(value); - if (bits >= 0) { - bits ^= 0x7fffffffffffffffL; - } - int w = (int)(bits >> 32); - dst[dstOffset ] = (byte)(w >> 24); - dst[dstOffset + 1] = (byte)(w >> 16); - dst[dstOffset + 2] = (byte)(w >> 8); - dst[dstOffset + 3] = (byte)w; - w = (int)bits; - dst[dstOffset + 4] = (byte)(w >> 24); - dst[dstOffset + 5] = (byte)(w >> 16); - dst[dstOffset + 6] = (byte)(w >> 8); - dst[dstOffset + 7] = (byte)w; - } - - /** - * Encodes the given Double object into exactly 8 bytes for descending - * order. A non-canonical NaN value is used to represent null. - * - * @param value optional Double value to encode - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - */ - public static void encodeDesc(Double value, byte[] dst, int dstOffset) { - if (value == null) { - encode(~0x7fffffffffffffffL, dst, dstOffset); - } else { - encodeDesc(value.doubleValue(), dst, dstOffset); - } - } - - /** - * Encodes the given optional unsigned byte array into a variable amount of - * bytes. If the byte array is null, exactly 1 byte is written. Otherwise, - * the amount written can be determined by calling calculateEncodedLength. - * - * @param value byte array value to encode, may be null - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - * @return amount of bytes written - */ - public static int encode(byte[] value, byte[] dst, int dstOffset) { - if (value == null) { - dst[dstOffset] = NULL_BYTE_HIGH; - return 1; - } - return encode(value, 0, value.length, dst, dstOffset, 0); - } - - /** - * Encodes the given optional unsigned byte array into a variable amount of - * bytes. If the byte array is null, exactly 1 byte is written. Otherwise, - * the amount written can be determined by calling calculateEncodedLength. - * - * @param value byte array value to encode, may be null - * @param valueOffset offset into byte array - * @param valueLength length of data in byte array - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - * @return amount of bytes written - */ - public static int encode(byte[] value, int valueOffset, int valueLength, - byte[] dst, int dstOffset) { - return encode(value, valueOffset, valueLength, dst, dstOffset, 0); - } - - /** - * Encodes the given optional unsigned byte array into a variable amount of - * bytes for descending order. If the byte array is null, exactly 1 byte is - * written. Otherwise, the amount written is determined by calling - * calculateEncodedLength. - * - * @param value byte array value to encode, may be null - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - * @return amount of bytes written - */ - public static int encodeDesc(byte[] value, byte[] dst, int dstOffset) { - if (value == null) { - dst[dstOffset] = NULL_BYTE_LOW; - return 1; - } - return encode(value, 0, value.length, dst, dstOffset, -1); - } - - /** - * Encodes the given optional unsigned byte array into a variable amount of - * bytes for descending order. If the byte array is null, exactly 1 byte is - * written. Otherwise, the amount written is determined by calling - * calculateEncodedLength. - * - * @param value byte array value to encode, may be null - * @param valueOffset offset into byte array - * @param valueLength length of data in byte array - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - * @return amount of bytes written - */ - public static int encodeDesc(byte[] value, int valueOffset, int valueLength, - byte[] dst, int dstOffset) { - return encode(value, valueOffset, valueLength, dst, dstOffset, -1); - } - - /** - * @param xorMask 0 for normal encoding, -1 for descending encoding - */ - private static int encode(byte[] value, int valueOffset, int valueLength, - byte[] dst, int dstOffset, int xorMask) { - if (value == null) { - dst[dstOffset] = (byte)(NULL_BYTE_HIGH ^ xorMask); - return 1; - } - - final int originalOffset = dstOffset; - - // Value is encoded in base-32768. - - int accumBits = 0; - int accum = 0; - - final int end = valueOffset + valueLength; - for (int i=valueOffset; i> (8 - supply)); - emitDigit(accum, dst, dstOffset, xorMask); - dstOffset += 2; - accumBits = 8 - supply; - accum = value[i] & ((1 << accumBits) - 1); - } - } - - if (accumBits > 0) { - // Pad with zeros. - accum <<= (15 - accumBits); - if (accumBits <= 7) { - // Since amount of significant bits is small, emit only the - // upper half of the digit. The following code is modified from - // emitDigit. - - int a = (accum * 21845) >> 22; - if (accum - ((a << 7) + (a << 6)) == 192) { - a++; - } - dst[dstOffset++] = (byte)((a + 32) ^ xorMask); - } else { - emitDigit(accum, dst, dstOffset, xorMask); - dstOffset += 2; - } - } - - // Append terminator. - dst[dstOffset++] = (byte)(TERMINATOR ^ xorMask); - - return dstOffset - originalOffset; - } - - /** - * Emits a base-32768 digit using exactly two bytes. The first byte is in the range - * 32..202 and the second byte is in the range 32..223. - * - * @param value digit value in the range 0..32767 - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - * @param xorMask 0 for normal encoding, -1 for descending encoding - */ - private static void emitDigit(int value, byte[] dst, int dstOffset, int xorMask) { - // The first byte is computed as ((value / 192) + 32) and the second - // byte is computed as ((value % 192) + 32). To speed things up a bit, - // the integer division and remainder operations are replaced with a - // scaled multiplication. - - // approximate value / 192 - int a = (value * 21845) >> 22; - - // approximate value % 192 - // Note: the value 192 was chosen as a divisor because a multiply by - // 192 can be replaced with two summed shifts. - int b = value - ((a << 7) + (a << 6)); - if (b == 192) { - // Fix error. - a++; - b = 0; - } - - dst[dstOffset++] = (byte)((a + 32) ^ xorMask); - dst[dstOffset] = (byte)((b + 32) ^ xorMask); - } - - /** - * Returns the amount of bytes required to encode a byte array of the given - * length. - * - * @param value byte array value to encode, may be null - * @return amount of bytes needed to encode - */ - public static int calculateEncodedLength(byte[] value) { - return value == null ? 1 : calculateEncodedLength(value, 0, value.length); - } - - /** - * Returns the amount of bytes required to encode the given byte array. - * - * @param value byte array value to encode, may be null - * @param valueOffset offset into byte array - * @param valueLength length of data in byte array - * @return amount of bytes needed to encode - */ - public static int calculateEncodedLength(byte[] value, int valueOffset, int valueLength) { - // The add of 119 is used to force ceiling rounding. - return value == null ? 1 : (((valueLength << 7) + 119) / 120 + 1); - } - - /** - * Encodes the given optional String into a variable amount of bytes. The - * amount written can be determined by calling - * calculateEncodedStringLength. - *

- * Strings are encoded in a fashion similar to UTF-8, in that ASCII - * characters are usually written in one byte. This encoding is more - * efficient than UTF-8, but it isn't compatible with UTF-8. - * - * @param value String value to encode, may be null - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - * @return amount of bytes written - */ - public static int encode(String value, byte[] dst, int dstOffset) { - return encode(value, dst, dstOffset, 0); - } - - /** - * Encodes the given optional String into a variable amount of bytes for - * descending order. The amount written can be determined by calling - * calculateEncodedStringLength. - *

- * Strings are encoded in a fashion similar to UTF-8, in that ASCII - * characters are usually written in one byte. This encoding is more - * efficient than UTF-8, but it isn't compatible with UTF-8. - * - * @param value String value to encode, may be null - * @param dst destination for encoded bytes - * @param dstOffset offset into destination array - * @return amount of bytes written - */ - public static int encodeDesc(String value, byte[] dst, int dstOffset) { - return encode(value, dst, dstOffset, -1); - } - - /** - * @param xorMask 0 for normal encoding, -1 for descending encoding - */ - private static int encode(String value, byte[] dst, int dstOffset, int xorMask) { - if (value == null) { - dst[dstOffset] = (byte)(NULL_BYTE_HIGH ^ xorMask); - return 1; - } - - final int originalOffset = dstOffset; - - // All characters have an offset of 2 added, in order to reserve bytes - // 0 and 1 for encoding nulls and terminators. This means the ASCII - // string "HelloWorld" is actually encoded as "JgnnqYqtnf". This also - // means that the ASCII '~' and del characters are encoded in two bytes. - - int length = value.length(); - for (int i = 0; i < length; i++) { - int c = value.charAt(i) + 2; - if (c <= 0x7f) { - // 0xxxxxxx - dst[dstOffset++] = (byte)(c ^ xorMask); - } else if (c <= 12415) { - // 10xxxxxx xxxxxxxx - - // Second byte cannot have the values 0, 1, 254, or 255 because - // they clash with null and terminator bytes. Divide by 192 and - // store in first 6 bits. The remainder, with 32 added, goes - // into the second byte. Note that (192 * 63 + 191) + 128 == 12415. - // 63 is the maximum value that can be represented in 6 bits. - - c -= 128; // c will always be at least 128, so normalize. - - // approximate value / 192 - int a = (c * 21845) >> 22; - - // approximate value % 192 - // Note: the value 192 was chosen as a divisor because a multiply by - // 192 can be replaced with two summed shifts. - c = c - ((a << 7) + (a << 6)); - if (c == 192) { - // Fix error. - a++; - c = 0; - } - - dst[dstOffset++] = (byte)((0x80 | a) ^ xorMask); - dst[dstOffset++] = (byte)((c + 32) ^ xorMask); - } else { - // 110xxxxx xxxxxxxx xxxxxxxx - - if ((c - 2) >= 0xd800 && (c - 2) <= 0xdbff) { - // Found a high surrogate. Verify that surrogate pair is - // well-formed. Low surrogate must follow high surrogate. - if (i + 1 < length) { - int c2 = value.charAt(i + 1); - if (c2 >= 0xdc00 && c2 <= 0xdfff) { - c = ((((c - 2) & 0x3ff) << 10) | (c2 & 0x3ff)) + 0x10002; - i++; - } - } - } - - // Second and third bytes cannot have the values 0, 1, 254, or - // 255 because they clash with null and terminator - // bytes. Divide by 192 twice, storing the first and second - // remainders in the third and second bytes, respectively. - // Note that largest unicode value supported is 2^20 + 65535 == - // 1114111. When divided by 192 twice, the value is 30, which - // just barely fits in the 5 available bits of the first byte. - - c -= 12416; // c will always be at least 12416, so normalize. - - int a = (int)((c * 21845L) >> 22); - c = c - ((a << 7) + (a << 6)); - if (c == 192) { - a++; - c = 0; - } - - dst[dstOffset + 2] = (byte)((c + 32) ^ xorMask); - - c = (a * 21845) >> 22; - a = a - ((c << 7) + (c << 6)); - if (a == 192) { - c++; - a = 0; - } - - dst[dstOffset++] = (byte)((0xc0 | c) ^ xorMask); - dst[dstOffset++] = (byte)((a + 32) ^ xorMask); - dstOffset++; - } - } - - // Append terminator. - dst[dstOffset++] = (byte)(TERMINATOR ^ xorMask); - - return dstOffset - originalOffset; - } - - /** - * Returns the amount of bytes required to encode the given String. - * - * @param value String to encode, may be null - */ - public static int calculateEncodedStringLength(String value) { - int encodedLen = 1; - if (value != null) { - int valueLength = value.length(); - for (int i = 0; i < valueLength; i++) { - int c = value.charAt(i); - if (c <= (0x7f - 2)) { - encodedLen++; - } else if (c <= (12415 - 2)) { - encodedLen += 2; - } else { - if (c >= 0xd800 && c <= 0xdbff) { - // Found a high surrogate. Verify that surrogate pair is - // well-formed. Low surrogate must follow high surrogate. - if (i + 1 < valueLength) { - int c2 = value.charAt(i + 1); - if (c2 >= 0xdc00 && c2 <= 0xdfff) { - i++; - } - } - } - encodedLen += 3; - } - } - } - return encodedLen; - } - - /** - * Encodes the given byte array for use when there is only a single - * required property, descending order, whose type is a byte array. The - * original byte array is returned if the length is zero. - */ - public static byte[] encodeSingleDesc(byte[] value) { - return encodeSingleDesc(value, 0, 0); - } - - /** - * Encodes the given byte array for use when there is only a single - * required property, descending order, whose type is a byte array. The - * original byte array is returned if the length and padding lengths are - * zero. - * - * @param prefixPadding amount of extra bytes to allocate at start of encoded byte array - * @param suffixPadding amount of extra bytes to allocate at end of encoded byte array - */ - public static byte[] encodeSingleDesc(byte[] value, int prefixPadding, int suffixPadding) { - int length = value.length; - if (prefixPadding <= 0 && suffixPadding <= 0 && length == 0) { - return value; - } - byte[] dst = new byte[prefixPadding + length + suffixPadding]; - while (--length >= 0) { - dst[prefixPadding + length] = (byte) (~value[length]); - } - return dst; - } - - /** - * Encodes the given byte array for use when there is only a single - * nullable property, descending order, whose type is a byte array. - */ - public static byte[] encodeSingleNullableDesc(byte[] value) { - return encodeSingleNullableDesc(value, 0, 0); - } - - /** - * Encodes the given byte array for use when there is only a single - * nullable property, descending order, whose type is a byte array. - * - * @param prefixPadding amount of extra bytes to allocate at start of encoded byte array - * @param suffixPadding amount of extra bytes to allocate at end of encoded byte array - */ - public static byte[] encodeSingleNullableDesc(byte[] value, - int prefixPadding, int suffixPadding) { - if (prefixPadding <= 0 && suffixPadding <= 0) { - if (value == null) { - return NULL_BYTE_ARRAY_LOW; - } - - int length = value.length; - if (length == 0) { - return NOT_NULL_BYTE_ARRAY_LOW; - } - - byte[] dst = new byte[1 + length]; - dst[0] = NOT_NULL_BYTE_LOW; - while (--length >= 0) { - dst[1 + length] = (byte) (~value[length]); - } - return dst; - } - - if (value == null) { - byte[] dst = new byte[prefixPadding + 1 + suffixPadding]; - dst[prefixPadding] = NULL_BYTE_LOW; - return dst; - } - - int length = value.length; - byte[] dst = new byte[prefixPadding + 1 + length + suffixPadding]; - dst[prefixPadding] = NOT_NULL_BYTE_LOW; - while (--length >= 0) { - dst[prefixPadding + 1 + length] = (byte) (~value[length]); - } - return dst; - } -} diff --git a/src/main/java/com/amazon/carbonado/spi/raw/LayoutPropertyInfo.java b/src/main/java/com/amazon/carbonado/spi/raw/LayoutPropertyInfo.java deleted file mode 100644 index 362c17c..0000000 --- a/src/main/java/com/amazon/carbonado/spi/raw/LayoutPropertyInfo.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2006 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.amazon.carbonado.spi.raw; - -import java.lang.reflect.Method; - -import org.cojen.classfile.TypeDesc; - -import com.amazon.carbonado.layout.LayoutProperty; -import com.amazon.carbonado.lob.Lob; - -/** - * - * - * @author Brian S O'Neill - */ -public class LayoutPropertyInfo implements GenericPropertyInfo { - private final LayoutProperty mProp; - private final TypeDesc mPropertyType; - private final TypeDesc mStorageType; - private final Method mFromStorage; - private final Method mToStorage; - - LayoutPropertyInfo(LayoutProperty property) { - this(property, null, null, null); - } - - LayoutPropertyInfo(LayoutProperty property, - Class storageType, Method fromStorage, Method toStorage) - { - mProp = property; - mPropertyType = TypeDesc.forDescriptor(property.getPropertyTypeDescriptor()); - if (storageType == null) { - mStorageType = mPropertyType; - } else { - mStorageType = TypeDesc.forClass(storageType); - } - mFromStorage = fromStorage; - mToStorage = toStorage; - } - - public String getPropertyName() { - return mProp.getPropertyName(); - } - - public TypeDesc getPropertyType() { - return mPropertyType; - } - - public TypeDesc getStorageType() { - return mStorageType; - } - - public boolean isNullable() { - return mProp.isNullable(); - } - - public boolean isLob() { - Class clazz = mPropertyType.toClass(); - return clazz != null && Lob.class.isAssignableFrom(clazz); - } - - public Method getFromStorageAdapter() { - return mFromStorage; - } - - public Method getToStorageAdapter() { - return mToStorage; - } -} diff --git a/src/main/java/com/amazon/carbonado/spi/raw/RawCursor.java b/src/main/java/com/amazon/carbonado/spi/raw/RawCursor.java deleted file mode 100644 index b665191..0000000 --- a/src/main/java/com/amazon/carbonado/spi/raw/RawCursor.java +++ /dev/null @@ -1,743 +0,0 @@ -/* - * Copyright 2006 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.amazon.carbonado.spi.raw; - -import java.util.NoSuchElementException; - -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -import com.amazon.carbonado.FetchException; -import com.amazon.carbonado.cursor.AbstractCursor; - -/** - * Abstract Cursor implementation for a repository that manipulates raw bytes. - * - * @author Brian S O'Neill - */ -public abstract class RawCursor extends AbstractCursor { - // States for mState. - private static final byte - UNINITIALIZED = 0, - CLOSED = 1, - TRY_NEXT = 2, - HAS_NEXT = 3; - - /** Lock object, as passed into the constructor */ - protected final Lock mLock; - - private final byte[] mStartBound; - private final boolean mInclusiveStart; - private final byte[] mEndBound; - private final boolean mInclusiveEnd; - private final int mPrefixLength; - private final boolean mReverse; - - private byte mState; - - /** - * @param lock operations lock on this object - * @param startBound specify the starting key for the cursor, or null if first - * @param inclusiveStart true if start bound is inclusive - * @param endBound specify the ending key for the cursor, or null if last - * @param inclusiveEnd true if end bound is inclusive - * @param maxPrefix maximum expected common initial bytes in start and end bound - * @param reverse when true, iteration is reversed - * @throws IllegalArgumentException if any bound is null but is not inclusive - */ - protected RawCursor(Lock lock, - byte[] startBound, boolean inclusiveStart, - byte[] endBound, boolean inclusiveEnd, - int maxPrefix, - boolean reverse) { - mLock = lock == null ? new ReentrantLock() : lock; - - if ((startBound == null && !inclusiveStart) || (endBound == null && !inclusiveEnd)) { - throw new IllegalArgumentException(); - } - - mStartBound = startBound; - mInclusiveStart = inclusiveStart; - mEndBound = endBound; - mInclusiveEnd = inclusiveEnd; - mReverse = reverse; - - // Determine common prefix for start and end bound. - if (maxPrefix <= 0 || startBound == null && endBound == null) { - mPrefixLength = 0; - } else { - int len = Math.min(maxPrefix, Math.min(startBound.length, endBound.length)); - int i; - for (i=0; i= amount) { - return actual; - } - mState = TRY_NEXT; - // Since state was HAS_NEXT and is forced into TRY_NEXT, actual - // amount skipped is effectively one more. - actual++; - } - - // Reached the end naturally, so close. - close(); - - return actual; - } finally { - mLock.unlock(); - } - } - - /** - * Release any internal resources, called when closed. - */ - protected abstract void release() throws FetchException; - - /** - * Returns the contents of the current key being referenced, or null - * otherwise. Caller is responsible for making a copy of the key. The array - * must not be modified concurrently. - * - *

If cursor is not opened, null must be returned. - * - * @return currently referenced key bytes or null if no current - * @throws IllegalStateException if key is disabled - */ - protected abstract byte[] getCurrentKey() throws FetchException; - - /** - * Returns the contents of the current value being referenced, or null - * otherwise. Caller is responsible for making a copy of the value. The - * array must not be modified concurrently. - * - *

If cursor is not opened, null must be returned. - * - * @return currently referenced value bytes or null if no current - * @throws IllegalStateException if value is disabled - */ - protected abstract byte[] getCurrentValue() throws FetchException; - - /** - * An optimization hint which disables key and value acquisition. The - * default implementation of this method does nothing. - */ - protected void disableKeyAndValue() { - } - - /** - * An optimization hint which disables just value acquisition. The default - * implementation of this method does nothing. - */ - protected void disableValue() { - } - - /** - * Enable key and value acquisition again, after they have been - * disabled. Calling this method forces the key and value to be - * re-acquired, if they had been disabled. Key and value acquisition must - * be enabled by default. The default implementation of this method does - * nothing. - */ - protected void enableKeyAndValue() throws FetchException { - } - - /** - * Returns a new Storable instance for the currently referenced entry. - * - * @return new Storable instance, never null - * @throws IllegalStateException if no current entry to instantiate - */ - protected abstract S instantiateCurrent() throws FetchException; - - /** - * Move the cursor to the first available entry. If false is returned, the - * cursor must be positioned before the first available entry. - * - * @return true if first entry exists and is now current - * @throws IllegalStateException if cursor is not opened - */ - protected abstract boolean toFirst() throws FetchException; - - /** - * Move the cursor to the first available entry at or after the given - * key. If false is returned, the cursor must be positioned before the - * first available entry. Caller is responsible for preserving contents of - * array. - * - * @param key key to search for - * @return true if first entry exists and is now current - * @throws IllegalStateException if cursor is not opened - */ - protected abstract boolean toFirst(byte[] key) throws FetchException; - - /** - * Move the cursor to the last available entry. If false is returned, the - * cursor must be positioned after the last available entry. - * - * @return true if last entry exists and is now current - * @throws IllegalStateException if cursor is not opened - */ - protected abstract boolean toLast() throws FetchException; - - /** - * Move the cursor to the last available entry at or before the given - * key. If false is returned, the cursor must be positioned after the last - * available entry. Caller is responsible for preserving contents of array. - * - * @param key key to search for - * @return true if last entry exists and is now current - * @throws IllegalStateException if cursor is not opened - */ - protected abstract boolean toLast(byte[] key) throws FetchException; - - /** - * Move the cursor to the next available entry, returning false if none. If - * false is returned, the cursor must be positioned after the last - * available entry. - * - * @return true if moved to next entry - * @throws IllegalStateException if cursor is not opened - */ - protected abstract boolean toNext() throws FetchException; - - /** - * Move the cursor to the next available entry, incrementing by the amount - * given. The actual amount incremented is returned. If the amount is less - * then requested, the cursor must be positioned after the last available - * entry. Subclasses may wish to override this method with a faster - * implementation. - * - *

Calling to toNext(1) is equivalent to calling toNext(). - * - * @param amount positive amount to advance - * @return actual amount advanced - * @throws IllegalStateException if cursor is not opened - */ - protected int toNext(int amount) throws FetchException { - if (amount <= 1) { - return (amount <= 0) ? 0 : (toNext() ? 1 : 0); - } - - int count = 0; - - disableKeyAndValue(); - try { - while (amount > 0) { - if (toNext()) { - count++; - amount--; - } else { - break; - } - } - } finally { - enableKeyAndValue(); - } - - return count; - } - - /** - * Move the cursor to the next unique key, returning false if none. If - * false is returned, the cursor must be positioned after the last - * available entry. Subclasses may wish to override this method with a - * faster implementation. - * - * @return true if moved to next unique key - * @throws IllegalStateException if cursor is not opened - */ - protected boolean toNextKey() throws FetchException { - byte[] initialKey = getCurrentKey(); - if (initialKey == null) { - return false; - } - - disableValue(); - try { - while (true) { - if (toNext()) { - byte[] currentKey = getCurrentKey(); - if (currentKey == null) { - return false; - } - if (compareKeysPartially(currentKey, initialKey) > 0) { - break; - } - } else { - return false; - } - } - } finally { - enableKeyAndValue(); - } - - return true; - } - - /** - * Move the cursor to the previous available entry, returning false if - * none. If false is returned, the cursor must be positioned before the - * first available entry. - * - * @return true if moved to previous entry - * @throws IllegalStateException if cursor is not opened - */ - protected abstract boolean toPrevious() throws FetchException; - - /** - * Move the cursor to the previous available entry, decrementing by the - * amount given. The actual amount decremented is returned. If the amount - * is less then requested, the cursor must be positioned before the first - * available entry. Subclasses may wish to override this method with a - * faster implementation. - * - *

Calling to toPrevious(1) is equivalent to calling toPrevious(). - * - * @param amount positive amount to retreat - * @return actual amount retreated - * @throws IllegalStateException if cursor is not opened - */ - protected int toPrevious(int amount) throws FetchException { - if (amount <= 1) { - return (amount <= 0) ? 0 : (toPrevious() ? 1 : 0); - } - - int count = 0; - - disableKeyAndValue(); - try { - while (amount > 0) { - if (toPrevious()) { - count++; - amount--; - } else { - break; - } - } - } finally { - enableKeyAndValue(); - } - - return count; - } - - /** - * Move the cursor to the previous unique key, returning false if none. If - * false is returned, the cursor must be positioned before the first - * available entry. Subclasses may wish to override this method with a - * faster implementation. - * - * @return true if moved to previous unique key - * @throws IllegalStateException if cursor is not opened - */ - protected boolean toPreviousKey() throws FetchException { - byte[] initialKey = getCurrentKey(); - if (initialKey == null) { - return false; - } - - disableValue(); - try { - while (true) { - if (toPrevious()) { - byte[] currentKey = getCurrentKey(); - if (currentKey == null) { - return false; - } - if (compareKeysPartially(getCurrentKey(), initialKey) < 0) { - break; - } - } else { - return false; - } - } - } finally { - enableKeyAndValue(); - } - - return true; - } - - /** - * Returns <0 if key1 is less, 0 if equal (at least partially), >0 - * if key1 is greater. - */ - protected int compareKeysPartially(byte[] key1, byte[] key2) { - int length = Math.min(key1.length, key2.length); - for (int i=0; i 0) { - byte[] prefix = mStartBound; - byte[] key = getCurrentKey(); - if (key == null) { - return false; - } - for (int i=0; i= 0) { - if (result > 0 || !mInclusiveEnd) { - return false; - } - } - } - - return prefixMatches(); - } - - // Calls toLast, but considers start and end bounds. Caller is responsible - // for preserving key. - private boolean toBoundedLast() throws FetchException { - if (mEndBound == null) { - if (!toLast()) { - return false; - } - } else { - if (!toLast(mEndBound.clone())) { - return false; - } - if (!mInclusiveEnd) { - byte[] currentKey = getCurrentKey(); - if (currentKey == null) { - return false; - } - if (compareKeysPartially(mEndBound, currentKey) == 0) { - if (!toPreviousKey()) { - return false; - } - } - } - } - - if (mStartBound != null) { - byte[] currentKey = getCurrentKey(); - if (currentKey == null) { - return false; - } - int result = compareKeysPartially(currentKey, mStartBound); - if (result <= 0) { - if (result < 0 || !mInclusiveStart) { - return false; - } - } - } - - return prefixMatches(); - } - - // Calls toNext, but considers end bound. - private boolean toBoundedNext() throws FetchException { - if (!toNext()) { - return false; - } - - if (mEndBound != null) { - byte[] currentKey = getCurrentKey(); - if (currentKey == null) { - return false; - } - int result = compareKeysPartially(currentKey, mEndBound); - if (result >= 0) { - if (result > 0 || !mInclusiveEnd) { - return false; - } - } - } - - return prefixMatches(); - } - - // Calls toNext, but considers end bound. - private int toBoundedNext(int amount) throws FetchException { - if (mEndBound == null) { - return toNext(amount); - } - - int count = 0; - - disableValue(); - try { - while (amount > 0) { - if (!toNext()) { - break; - } - - byte[] currentKey = getCurrentKey(); - if (currentKey == null) { - break; - } - - int result = compareKeysPartially(currentKey, mEndBound); - if (result >= 0) { - if (result > 0 || !mInclusiveEnd) { - break; - } - } - - if (!prefixMatches()) { - break; - } - - count++; - amount--; - } - } finally { - enableKeyAndValue(); - } - - return count; - } - - // Calls toPrevious, but considers start bound. - private boolean toBoundedPrevious() throws FetchException { - if (!toPrevious()) { - return false; - } - - if (mStartBound != null) { - byte[] currentKey = getCurrentKey(); - if (currentKey == null) { - return false; - } - int result = compareKeysPartially(currentKey, mStartBound); - if (result <= 0) { - if (result < 0 || !mInclusiveStart) { - // Too far now, reset to first. - toBoundedFirst(); - return false; - } - } - } - - return prefixMatches(); - } - - // Calls toPrevious, but considers start bound. - private int toBoundedPrevious(int amount) throws FetchException { - if (mStartBound == null) { - return toPrevious(amount); - } - - int count = 0; - - disableValue(); - try { - while (amount > 0) { - if (!toPrevious()) { - break; - } - - byte[] currentKey = getCurrentKey(); - if (currentKey == null) { - break; - } - - int result = compareKeysPartially(currentKey, mStartBound); - if (result <= 0) { - if (result < 0 || !mInclusiveStart) { - // Too far now, reset to first. - toBoundedFirst(); - break; - } - } - - if (!prefixMatches()) { - break; - } - - count++; - amount--; - } - } finally { - enableKeyAndValue(); - } - - return count; - } -} diff --git a/src/main/java/com/amazon/carbonado/spi/raw/RawStorableGenerator.java b/src/main/java/com/amazon/carbonado/spi/raw/RawStorableGenerator.java deleted file mode 100644 index 6d6fbe5..0000000 --- a/src/main/java/com/amazon/carbonado/spi/raw/RawStorableGenerator.java +++ /dev/null @@ -1,355 +0,0 @@ -/* - * Copyright 2006 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.amazon.carbonado.spi.raw; - -import java.lang.ref.Reference; -import java.lang.ref.SoftReference; -import java.util.EnumSet; -import java.util.Map; - -import org.cojen.classfile.ClassFile; -import org.cojen.classfile.CodeBuilder; -import org.cojen.classfile.Label; -import org.cojen.classfile.LocalVariable; -import org.cojen.classfile.MethodInfo; -import org.cojen.classfile.Modifiers; -import org.cojen.classfile.TypeDesc; -import org.cojen.util.ClassInjector; -import org.cojen.util.WeakIdentityMap; - -import com.amazon.carbonado.FetchException; -import com.amazon.carbonado.PersistException; -import com.amazon.carbonado.Storable; -import com.amazon.carbonado.Storage; -import com.amazon.carbonado.SupportException; - -import com.amazon.carbonado.spi.MasterFeature; -import com.amazon.carbonado.spi.MasterStorableGenerator; -import com.amazon.carbonado.spi.MasterSupport; -import com.amazon.carbonado.spi.StorableGenerator; -import com.amazon.carbonado.spi.TriggerSupport; - -import static com.amazon.carbonado.spi.CommonMethodNames.*; - -/** - * Generates and caches abstract implementations of {@link Storable} types - * which are encoded and decoded in a raw format. The generated abstract - * classes extend those created by {@link MasterStorableGenerator}. - * - * @author Brian S O'Neill - * @see GenericStorableCodec - * @see RawSupport - */ -public class RawStorableGenerator { - // Note: All generated fields/methods have a "$" character in them to - // prevent name collisions with any inherited fields/methods. User storable - // properties are defined as fields which exactly match the property - // name. We don't want collisions with those either. Legal bean properties - // cannot have "$" in them, so there's nothing to worry about. - - /** Name of protected abstract method in generated storable */ - public static final String - ENCODE_KEY_METHOD_NAME = "encodeKey$", - DECODE_KEY_METHOD_NAME = "decodeKey$", - ENCODE_DATA_METHOD_NAME = "encodeData$", - DECODE_DATA_METHOD_NAME = "decodeData$"; - - @SuppressWarnings("unchecked") - private static Map> cCache = new WeakIdentityMap(); - - /** - * Collection of different abstract class flavors. - */ - static class Flavors { - private Reference> mMasterFlavor; - - private Reference> mNonMasterFlavor; - - /** - * May return null. - */ - Class getClass(boolean isMaster) { - Reference> ref; - if (isMaster) { - ref = mMasterFlavor; - } else { - ref = mNonMasterFlavor; - } - return (ref != null) ? ref.get() : null; - } - - @SuppressWarnings("unchecked") - void setClass(Class clazz, boolean isMaster) { - Reference> ref = new SoftReference(clazz); - if (isMaster) { - mMasterFlavor = ref; - } else { - mNonMasterFlavor = ref; - } - } - } - - // Can't be instantiated or extended - private RawStorableGenerator() { - } - - /** - * Returns an abstract implementation of the given Storable type, which is - * fully thread-safe. The Storable type itself may be an interface or a - * class. If it is a class, then it must not be final, and it must have a - * public, no-arg constructor. Two constructors are defined for the - * abstract implementation: - * - *

-     * public <init>(RawSupport);
-
-     * public <init>(RawSupport, byte[] key, byte[] value);
-     * 
- * - *

Subclasses must implement the following abstract protected methods, - * whose exact names are defined by constants in this class: - * - *

-     * // Encode the primary key of this storable.
-     * protected abstract byte[] encodeKey();
-     *
-     * // Encode all properties of this storable excluding the primary key.
-     * protected abstract byte[] encodeData();
-     *
-     * // Decode the primary key into properties of this storable.
-     * // Note: this method is also invoked by the four argument constructor.
-     * protected abstract void decodeKey(byte[]);
-     *
-     * // Decode the data into properties of this storable.
-     * // Note: this method is also invoked by the four argument constructor.
-     * protected abstract void decodeData(byte[]);
-     * 
- * - * @param isMaster when true, version properties, sequences, and triggers are managed - * @throws IllegalArgumentException if type is null - */ - @SuppressWarnings("unchecked") - public static Class - getAbstractClass(Class type, boolean isMaster) - throws SupportException, IllegalArgumentException - { - synchronized (cCache) { - Class abstractClass; - - Flavors flavors = (Flavors) cCache.get(type); - - if (flavors == null) { - flavors = new Flavors(); - cCache.put(type, flavors); - } else if ((abstractClass = flavors.getClass(isMaster)) != null) { - return abstractClass; - } - - abstractClass = generateAbstractClass(type, isMaster); - flavors.setClass(abstractClass, isMaster); - - return abstractClass; - } - } - - @SuppressWarnings("unchecked") - private static Class - generateAbstractClass(Class storableClass, boolean isMaster) - throws SupportException - { - EnumSet features; - if (isMaster) { - features = EnumSet.of(MasterFeature.VERSIONING, - MasterFeature.UPDATE_FULL, - MasterFeature.INSERT_SEQUENCES, - MasterFeature.INSERT_CHECK_REQUIRED); - } else { - features = EnumSet.of(MasterFeature.UPDATE_FULL); - } - - final Class abstractClass = - MasterStorableGenerator.getAbstractClass(storableClass, features); - - ClassInjector ci = ClassInjector.create - (storableClass.getName(), abstractClass.getClassLoader()); - - ClassFile cf = new ClassFile(ci.getClassName(), abstractClass); - cf.setModifiers(cf.getModifiers().toAbstract(true)); - cf.markSynthetic(); - cf.setSourceFile(RawStorableGenerator.class.getName()); - cf.setTarget("1.5"); - - // Declare some types. - final TypeDesc storableType = TypeDesc.forClass(Storable.class); - final TypeDesc storageType = TypeDesc.forClass(Storage.class); - final TypeDesc triggerSupportType = TypeDesc.forClass(TriggerSupport.class); - final TypeDesc masterSupportType = TypeDesc.forClass(MasterSupport.class); - final TypeDesc rawSupportType = TypeDesc.forClass(RawSupport.class); - final TypeDesc byteArrayType = TypeDesc.forClass(byte[].class); - - // Add constructor that accepts a RawSupport. - { - TypeDesc[] params = {rawSupportType}; - MethodInfo mi = cf.addConstructor(Modifiers.PUBLIC, params); - CodeBuilder b = new CodeBuilder(mi); - b.loadThis(); - b.loadLocal(b.getParameter(0)); - b.invokeSuperConstructor(new TypeDesc[] {masterSupportType}); - b.returnVoid(); - } - - // Add constructor that accepts a RawSupport, an encoded key, and an - // encoded data. - { - TypeDesc[] params = {rawSupportType, byteArrayType, byteArrayType}; - MethodInfo mi = cf.addConstructor(Modifiers.PUBLIC, params); - CodeBuilder b = new CodeBuilder(mi); - b.loadThis(); - b.loadLocal(b.getParameter(0)); - b.invokeSuperConstructor(new TypeDesc[] {masterSupportType}); - - params = new TypeDesc[] {byteArrayType}; - - b.loadThis(); - b.loadLocal(b.getParameter(1)); - b.invokeVirtual(DECODE_KEY_METHOD_NAME, null, params); - - b.loadThis(); - b.loadLocal(b.getParameter(2)); - b.invokeVirtual(DECODE_DATA_METHOD_NAME, null, params); - - // Indicate that object is clean by calling markAllPropertiesClean. - b.loadThis(); - b.invokeVirtual(MARK_ALL_PROPERTIES_CLEAN, null, null); - - b.returnVoid(); - } - - // Declare protected abstract methods. - { - cf.addMethod(Modifiers.PROTECTED.toAbstract(true), - ENCODE_KEY_METHOD_NAME, byteArrayType, null); - cf.addMethod(Modifiers.PROTECTED.toAbstract(true), - DECODE_KEY_METHOD_NAME, null, new TypeDesc[]{byteArrayType}); - cf.addMethod(Modifiers.PROTECTED.toAbstract(true), - ENCODE_DATA_METHOD_NAME, byteArrayType, null); - cf.addMethod(Modifiers.PROTECTED.toAbstract(true), - DECODE_DATA_METHOD_NAME, null, new TypeDesc[]{byteArrayType}); - } - - // Add required protected doTryLoad_master method, which delegates to RawSupport. - { - MethodInfo mi = cf.addMethod - (Modifiers.PROTECTED.toFinal(true), - MasterStorableGenerator.DO_TRY_LOAD_MASTER_METHOD_NAME, TypeDesc.BOOLEAN, null); - mi.addException(TypeDesc.forClass(FetchException.class)); - CodeBuilder b = new CodeBuilder(mi); - - b.loadThis(); - b.loadField(StorableGenerator.SUPPORT_FIELD_NAME, triggerSupportType); - b.checkCast(rawSupportType); - b.loadThis(); - b.invokeVirtual(ENCODE_KEY_METHOD_NAME, byteArrayType, null); - TypeDesc[] params = {byteArrayType}; - b.invokeInterface(rawSupportType, "tryLoad", byteArrayType, params); - LocalVariable encodedDataVar = b.createLocalVariable(null, byteArrayType); - b.storeLocal(encodedDataVar); - b.loadLocal(encodedDataVar); - Label notNull = b.createLabel(); - b.ifNullBranch(notNull, false); - b.loadConstant(false); - b.returnValue(TypeDesc.BOOLEAN); - notNull.setLocation(); - b.loadThis(); - b.loadLocal(encodedDataVar); - params = new TypeDesc[] {byteArrayType}; - b.invokeVirtual(DECODE_DATA_METHOD_NAME, null, params); - b.loadConstant(true); - b.returnValue(TypeDesc.BOOLEAN); - } - - // Add required protected doTryInsert_master method, which delegates to RawSupport. - { - MethodInfo mi = cf.addMethod - (Modifiers.PROTECTED.toFinal(true), - MasterStorableGenerator.DO_TRY_INSERT_MASTER_METHOD_NAME, TypeDesc.BOOLEAN, null); - mi.addException(TypeDesc.forClass(PersistException.class)); - CodeBuilder b = new CodeBuilder(mi); - - // return rawSupport.tryInsert(this, this.encodeKey$(), this.encodeData$()); - b.loadThis(); - b.loadField(StorableGenerator.SUPPORT_FIELD_NAME, triggerSupportType); - b.checkCast(rawSupportType); - b.loadThis(); // pass this to tryInsert method - b.loadThis(); - b.invokeVirtual(ENCODE_KEY_METHOD_NAME, byteArrayType, null); - b.loadThis(); - b.invokeVirtual(ENCODE_DATA_METHOD_NAME, byteArrayType, null); - TypeDesc[] params = {storableType, byteArrayType, byteArrayType}; - b.invokeInterface(rawSupportType, "tryInsert", TypeDesc.BOOLEAN, params); - b.returnValue(TypeDesc.BOOLEAN); - } - - // Add required protected doTryUpdate_master method, which delegates to RawSupport. - { - MethodInfo mi = cf.addMethod - (Modifiers.PROTECTED.toFinal(true), - MasterStorableGenerator.DO_TRY_UPDATE_MASTER_METHOD_NAME, TypeDesc.BOOLEAN, null); - mi.addException(TypeDesc.forClass(PersistException.class)); - CodeBuilder b = new CodeBuilder(mi); - - // rawSupport.store(this, this.encodeKey$(), this.encodeData$()); - // return true; - b.loadThis(); - b.loadField(StorableGenerator.SUPPORT_FIELD_NAME, triggerSupportType); - b.checkCast(rawSupportType); - b.loadThis(); // pass this to store method - b.loadThis(); - b.invokeVirtual(ENCODE_KEY_METHOD_NAME, byteArrayType, null); - b.loadThis(); - b.invokeVirtual(ENCODE_DATA_METHOD_NAME, byteArrayType, null); - TypeDesc[] params = {storableType, byteArrayType, byteArrayType}; - b.invokeInterface(rawSupportType, "store", null, params); - b.loadConstant(true); - b.returnValue(TypeDesc.BOOLEAN); - } - - // Add required protected doTryDelete_master method, which delegates to RawSupport. - { - MethodInfo mi = cf.addMethod - (Modifiers.PROTECTED.toFinal(true), - MasterStorableGenerator.DO_TRY_DELETE_MASTER_METHOD_NAME, TypeDesc.BOOLEAN, null); - mi.addException(TypeDesc.forClass(PersistException.class)); - CodeBuilder b = new CodeBuilder(mi); - - // return rawSupport.tryDelete(this.encodeKey$()); - b.loadThis(); - b.loadField(StorableGenerator.SUPPORT_FIELD_NAME, triggerSupportType); - b.checkCast(rawSupportType); - b.loadThis(); - b.invokeVirtual(ENCODE_KEY_METHOD_NAME, byteArrayType, null); - - TypeDesc[] params = {byteArrayType}; - b.invokeInterface(rawSupportType, "tryDelete", TypeDesc.BOOLEAN, params); - b.returnValue(TypeDesc.BOOLEAN); - } - - return ci.defineClass(cf); - } -} diff --git a/src/main/java/com/amazon/carbonado/spi/raw/RawSupport.java b/src/main/java/com/amazon/carbonado/spi/raw/RawSupport.java deleted file mode 100644 index 895089d..0000000 --- a/src/main/java/com/amazon/carbonado/spi/raw/RawSupport.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2006 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.amazon.carbonado.spi.raw; - -import com.amazon.carbonado.FetchException; -import com.amazon.carbonado.PersistException; -import com.amazon.carbonado.Storable; - -import com.amazon.carbonado.lob.Blob; -import com.amazon.carbonado.lob.Clob; - -import com.amazon.carbonado.spi.MasterSupport; - -/** - * Provides runtime support for Storable classes generated by {@link RawStorableGenerator}. - * - * @author Brian S O'Neill - */ -public interface RawSupport extends MasterSupport { - /** - * Try to load the entry referenced by the given key, but return null - * if not found. - * - * @param key non-null key to search for - * @return non-null value that was found, or null if not found - */ - byte[] tryLoad(byte[] key) throws FetchException; - - /** - * Try to insert the entry referenced by the given key with the given - * value. - * - * @param storable storable object that key and value were derived from - * @param key non-null key to insert - * @param value non-null value to insert - * @return false if unique constraint prevents insert - */ - boolean tryInsert(S storable, byte[] key, byte[] value) throws PersistException; - - /** - * Try to store the entry referenced by the given key with the given - * value. If the entry does not exist, insert it. Otherwise, update it. - * - * @param storable storable object that key and value were derived from - * @param key non-null key to store - * @param value non-null value to store - */ - void store(S storable, byte[] key, byte[] value) throws PersistException; - - /** - * Try to delete the entry referenced by the given key. - * - * @param key non-null key to delete - * @return true if entry existed and is now deleted - */ - boolean tryDelete(byte[] key) throws PersistException; - - /** - * Returns the Blob for the given locator, returning null if not found. - */ - Blob getBlob(long locator) throws FetchException; - - /** - * Returns the locator for the given Blob, returning zero if null. - * - * @throws PersistException if blob is unrecognized - */ - long getLocator(Blob blob) throws PersistException; - - /** - * Returns the Clob for the given locator, returning null if not found. - */ - Clob getClob(long locator) throws FetchException; - - /** - * Returns the locator for the given Clob, returning zero if null. - * - * @throws PersistException if blob is unrecognized - */ - long getLocator(Clob clob) throws PersistException; -} diff --git a/src/main/java/com/amazon/carbonado/spi/raw/RawUtil.java b/src/main/java/com/amazon/carbonado/spi/raw/RawUtil.java deleted file mode 100644 index 5224b6c..0000000 --- a/src/main/java/com/amazon/carbonado/spi/raw/RawUtil.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2006 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.amazon.carbonado.spi.raw; - -/** - * Utilities for manipulating binary data. - * - * @author Brian S O'Neill - */ -public class RawUtil { - /** - * Adds one to an unsigned integer, represented as a byte array. If - * overflowed, value in byte array is 0x00, 0x00, 0x00... - * - * @param value unsigned integer to increment - * @return false if overflowed - */ - public static boolean increment(byte[] value) { - for (int i=value.length; --i>=0; ) { - byte newValue = (byte) ((value[i] & 0xff) + 1); - value[i] = newValue; - if (newValue != 0) { - // No carry bit, so done adding. - return true; - } - } - // This point is reached upon overflow. - return false; - } - - /** - * Subtracts one from an unsigned integer, represented as a byte array. If - * overflowed, value in byte array is 0xff, 0xff, 0xff... - * - * @param value unsigned integer to decrement - * @return false if overflowed - */ - public static boolean decrement(byte[] value) { - for (int i=value.length; --i>=0; ) { - byte newValue = (byte) ((value[i] & 0xff) + -1); - value[i] = newValue; - if (newValue != -1) { - // No borrow bit, so done subtracting. - return true; - } - } - // This point is reached upon overflow. - return false; - } -} diff --git a/src/main/java/com/amazon/carbonado/spi/raw/StorableCodec.java b/src/main/java/com/amazon/carbonado/spi/raw/StorableCodec.java deleted file mode 100644 index 307fe7e..0000000 --- a/src/main/java/com/amazon/carbonado/spi/raw/StorableCodec.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2006 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.amazon.carbonado.spi.raw; - -import com.amazon.carbonado.FetchException; -import com.amazon.carbonado.Storable; -import com.amazon.carbonado.Storage; - -import com.amazon.carbonado.info.StorableIndex; - -/** - * Supports encoding and decoding of storables. - * - * @author Brian S O'Neill - * @see StorableCodecFactory - */ -public interface StorableCodec { - /** - * Returns the type of Storable produced by this codec. - */ - Class getStorableType(); - - /** - * Instantiate a Storable with no key or value defined yet. - * - * @param support binds generated storable with a storage layer - */ - S instantiate(RawSupport support); - - /** - * Instantiate a Storable with a specific key and value. - * - * @param support binds generated storable with a storage layer - */ - S instantiate(RawSupport support, byte[] key, byte[] value) - throws FetchException; - - /** - * Returns the sequence and directions of properties that make up the - * primary key. - */ - StorableIndex getPrimaryKeyIndex(); - - /** - * Returns the number of prefix bytes in the primary key, which may be - * zero. - */ - int getPrimaryKeyPrefixLength(); - - /** - * Encode a key by extracting all the primary key properties from the given - * storable. - * - * @param storable extract primary key properties from this instance - * @return raw search key - */ - byte[] encodePrimaryKey(S storable); - - /** - * Encode a key by extracting all the primary key properties from the given - * storable. - * - * @param storable extract primary key properties from this instance - * @param rangeStart index of first property to use. Its value must be less - * than the count of primary key properties. - * @param rangeEnd index of last property to use, exlusive. Its value must - * be less than or equal to the count of primary key properties. - * @return raw search key - */ - byte[] encodePrimaryKey(S storable, int rangeStart, int rangeEnd); - - /** - * Encode a key by extracting all the primary key properties from the given - * storable. - * - * @param values values to build into a key. It must be long enough to - * accommodate all primary key properties. - * @return raw search key - */ - byte[] encodePrimaryKey(Object[] values); - - /** - * Encode a key by extracting all the primary key properties from the given - * storable. - * - * @param values values to build into a key. The length may be less than - * the amount of primary key properties used by this factory. It must not - * be less than the difference between rangeStart and rangeEnd. - * @param rangeStart index of first property to use. Its value must be less - * than the count of primary key properties. - * @param rangeEnd index of last property to use, exlusive. Its value must - * be less than or equal to the count of primary key properties. - * @return raw search key - */ - byte[] encodePrimaryKey(Object[] values, int rangeStart, int rangeEnd); - - /** - * Encode the primary key for when there are no values, but there may be a - * prefix. Returned value may be null if no prefix is defined. - */ - byte[] encodePrimaryKeyPrefix(); -} diff --git a/src/main/java/com/amazon/carbonado/spi/raw/StorableCodecFactory.java b/src/main/java/com/amazon/carbonado/spi/raw/StorableCodecFactory.java deleted file mode 100644 index 26e3858..0000000 --- a/src/main/java/com/amazon/carbonado/spi/raw/StorableCodecFactory.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2006 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.amazon.carbonado.spi.raw; - -import com.amazon.carbonado.Storable; -import com.amazon.carbonado.SupportException; - -import com.amazon.carbonado.info.StorableIndex; -import com.amazon.carbonado.layout.Layout; - -/** - * Factory for creating instances of {@link StorableCodec}. - * - * @author Brian S O'Neill - */ -public interface StorableCodecFactory { - /** - * Returns the preferred storage/database name for the given type. Return - * null to let repository decide. - * - * @throws SupportException if type is not supported - */ - String getStorageName(Class type) throws SupportException; - - /** - * @param type type of storable to create codec for - * @param pkIndex suggested index for primary key (optional) - * @param isMaster when true, version properties and sequences are managed - * @param layout when non-null, attempt to encode a storable layout - * generation value in each storable - * @throws SupportException if type is not supported - */ - StorableCodec createCodec(Class type, - StorableIndex pkIndex, - boolean isMaster, - Layout layout) - throws SupportException; -} diff --git a/src/main/java/com/amazon/carbonado/spi/raw/StorablePropertyInfo.java b/src/main/java/com/amazon/carbonado/spi/raw/StorablePropertyInfo.java deleted file mode 100644 index f13a56c..0000000 --- a/src/main/java/com/amazon/carbonado/spi/raw/StorablePropertyInfo.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2006 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.amazon.carbonado.spi.raw; - -import java.lang.reflect.Method; - -import org.cojen.classfile.CodeAssembler; -import org.cojen.classfile.TypeDesc; - -import com.amazon.carbonado.info.StorableProperty; -import com.amazon.carbonado.lob.Lob; - -/** - * - * - * @author Brian S O'Neill - */ -public class StorablePropertyInfo implements GenericPropertyInfo { - private final StorableProperty mProp; - private final TypeDesc mPropertyType; - private final TypeDesc mStorageType; - private final Method mFromStorage; - private final Method mToStorage; - - StorablePropertyInfo(StorableProperty property) { - this(property, null, null, null); - } - - StorablePropertyInfo(StorableProperty property, - Class storageType, Method fromStorage, Method toStorage) { - mProp = property; - mPropertyType = TypeDesc.forClass(property.getType()); - if (storageType == null) { - mStorageType = mPropertyType; - } else { - mStorageType = TypeDesc.forClass(storageType); - } - mFromStorage = fromStorage; - mToStorage = toStorage; - } - - public String getPropertyName() { - return mProp.getName(); - } - - public TypeDesc getPropertyType() { - return mPropertyType; - } - - public TypeDesc getStorageType() { - return mStorageType; - } - - public boolean isNullable() { - return mProp.isNullable(); - } - - public boolean isLob() { - Class clazz = mPropertyType.toClass(); - return clazz != null && Lob.class.isAssignableFrom(clazz); - } - - public Method getFromStorageAdapter() { - return mFromStorage; - } - - public Method getToStorageAdapter() { - return mToStorage; - } - - public String getReadMethodName() { - return mProp.getReadMethodName(); - } - - public void addInvokeReadMethod(CodeAssembler a) { - a.invoke(mProp.getReadMethod()); - } - - public void addInvokeReadMethod(CodeAssembler a, TypeDesc instanceType) { - Class clazz = instanceType.toClass(); - if (clazz == null) { - // Can't know if instance should be invoked as an interface or as a - // virtual method. - throw new IllegalArgumentException("Instance type has no known class"); - } - if (clazz.isInterface()) { - a.invokeInterface(instanceType, getReadMethodName(), getPropertyType(), null); - } else { - a.invokeVirtual(instanceType, getReadMethodName(), getPropertyType(), null); - } - } - - public String getWriteMethodName() { - return mProp.getWriteMethodName(); - } - - public void addInvokeWriteMethod(CodeAssembler a) { - a.invoke(mProp.getWriteMethod()); - } - - public void addInvokeWriteMethod(CodeAssembler a, TypeDesc instanceType) { - Class clazz = instanceType.toClass(); - if (clazz == null) { - // Can't know if instance should be invoked as an interface or as a - // virtual method. - throw new IllegalArgumentException("Instance type has no known class"); - } - if (clazz.isInterface()) { - a.invokeInterface(instanceType, - getWriteMethodName(), null, new TypeDesc[] {getPropertyType()}); - } else { - a.invokeVirtual(instanceType, - getWriteMethodName(), null, new TypeDesc[] {getPropertyType()}); - } - } -} diff --git a/src/main/java/com/amazon/carbonado/spi/raw/package-info.java b/src/main/java/com/amazon/carbonado/spi/raw/package-info.java deleted file mode 100644 index 8d47419..0000000 --- a/src/main/java/com/amazon/carbonado/spi/raw/package-info.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2006 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. - */ - -/** - * Provides support for repositories that encode/decode storables in a raw - * binary format. - */ -package com.amazon.carbonado.spi.raw; -- cgit v1.2.3