From 79cebfadf8703afe9bf28786bc4df1af348c876e Mon Sep 17 00:00:00 2001
From: "Brian S. O'Neill" <bronee@gmail.com>
Date: Thu, 5 Oct 2006 03:54:20 +0000
Subject: Moved raw package out of spi package.

---
 .../com/amazon/carbonado/raw/TestDataEncoding.java | 515 +++++++++++++
 .../amazon/carbonado/raw/TestEncodingStrategy.java | 857 +++++++++++++++++++++
 .../com/amazon/carbonado/raw/TestKeyEncoding.java  | 571 ++++++++++++++
 .../amazon/carbonado/spi/raw/TestDataEncoding.java | 515 -------------
 .../carbonado/spi/raw/TestEncodingStrategy.java    | 857 ---------------------
 .../amazon/carbonado/spi/raw/TestKeyEncoding.java  | 571 --------------
 6 files changed, 1943 insertions(+), 1943 deletions(-)
 create mode 100644 src/test/java/com/amazon/carbonado/raw/TestDataEncoding.java
 create mode 100644 src/test/java/com/amazon/carbonado/raw/TestEncodingStrategy.java
 create mode 100644 src/test/java/com/amazon/carbonado/raw/TestKeyEncoding.java
 delete mode 100644 src/test/java/com/amazon/carbonado/spi/raw/TestDataEncoding.java
 delete mode 100644 src/test/java/com/amazon/carbonado/spi/raw/TestEncodingStrategy.java
 delete mode 100644 src/test/java/com/amazon/carbonado/spi/raw/TestKeyEncoding.java

(limited to 'src/test')

diff --git a/src/test/java/com/amazon/carbonado/raw/TestDataEncoding.java b/src/test/java/com/amazon/carbonado/raw/TestDataEncoding.java
new file mode 100644
index 0000000..bff5820
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/raw/TestDataEncoding.java
@@ -0,0 +1,515 @@
+/*
+ * 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.raw;
+
+import java.util.Random;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Test case for {@link DataEncoder} and {@link DataDecoder}. 
+ * <p>
+ * It generates random data values, checks that the decoding produces the
+ * original results, and it checks that the order of the encoded bytes matches
+ * the order of the original data values.
+ *
+ * @author Brian S O'Neill
+ */
+public class TestDataEncoding extends TestCase {
+    private static final int SHORT_TEST = 100;
+    private static final int MEDIUM_TEST = 500;
+    private static final int LONG_TEST = 1000;
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(suite());
+    }
+
+    public static TestSuite suite() {
+        return new TestSuite(TestDataEncoding.class);
+    }
+
+    /**
+     * @return -1, 0, or 1
+     */
+    static int byteArrayCompare(byte[] aa, byte[] ab) {
+        int len = Math.min(aa.length, ab.length);
+        int result = byteArrayCompare(aa, ab, len);
+        if (result == 0 && aa.length != ab.length) {
+            if (aa.length == len) {
+                return -1;
+            }
+            if (ab.length == len) {
+                return 1;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @return -1, 0, or 1
+     */
+    static int byteArrayCompare(byte[] aa, byte[] ab, int len) {
+        for (int i=0; i<len; i++) {
+            int a = aa[i] & 0xff;
+            int b = ab[i] & 0xff;
+            if (a < b) {
+                return -1;
+            }
+            if (a > b) {
+                return 1;
+            }
+        }
+        return 0;
+    }
+
+    /**
+     * @return -1, 0, or 1
+     */
+    static int byteArrayOrNullCompare(byte[] aa, byte[] ab) {
+        if (aa == null) {
+            if (ab == null) {
+                return 0;
+            } else {
+                return 1;
+            }
+        } else if (ab == null) {
+            return -1;
+        } else {
+            return byteArrayCompare(aa, ab);
+        }
+    }
+
+    /**
+     * @return -1, 0, or 1
+     */
+    static int byteArrayOrNullCompare(byte[] aa, byte[] ab, int len) {
+        if (aa == null) {
+            if (ab == null) {
+                return 0;
+            } else {
+                return 1;
+            }
+        } else if (ab == null) {
+            return -1;
+        } else {
+            return byteArrayCompare(aa, ab, len);
+        }
+    }
+
+    /**
+     * @return -1, 0, or 1
+     */
+    static <C extends Comparable> int compare(C a, C b) {
+        if (a == null) {
+            if (b == null) {
+                return 0;
+            } else {
+                return 1;
+            }
+        } else if (b == null) {
+            return -1;
+        } else {
+            return Integer.signum(a.compareTo(b));
+        }
+    }
+
+    private final long mSeed;
+
+    private Random mRandom;
+
+    public TestDataEncoding(String name) {
+        super(name);
+        mSeed = 5399777425345431L;
+    }
+
+    protected void setUp() {
+        mRandom = new Random(mSeed);
+    }
+
+    protected void tearDown() {
+    }
+
+    public void test_boolean() throws Exception {
+        byte[] bytes = new byte[1];
+        boolean lastValue = false;
+        byte[] lastBytes = null;
+        for (int i=0; i<SHORT_TEST; i++) {
+            boolean value = mRandom.nextBoolean();
+            DataEncoder.encode(value, bytes, 0);
+            assertEquals(value, DataDecoder.decodeBoolean(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = compare(value, lastValue);
+                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_Boolean() throws Exception {
+        byte[] bytes = new byte[1];
+        Boolean lastValue = false;
+        byte[] lastBytes = null;
+        for (int i=0; i<SHORT_TEST; i++) {
+            Boolean value;
+            if (mRandom.nextInt(10) == 1) {
+                value = null;
+            } else {
+                value = mRandom.nextBoolean();
+            }
+            DataEncoder.encode(value, bytes, 0);
+            assertEquals(value, DataDecoder.decodeBooleanObj(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = compare(value, lastValue);
+                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_byte() throws Exception {
+        byte[] bytes = new byte[1];
+        byte lastValue = 0;
+        byte[] lastBytes = null;
+        for (int i=0; i<SHORT_TEST; i++) {
+            byte value = (byte) mRandom.nextInt();
+            DataEncoder.encode(value, bytes, 0);
+            assertEquals(value, DataDecoder.decodeByte(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = compare(value, lastValue);
+                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_Byte() throws Exception {
+        byte[] bytes = new byte[2];
+        Byte lastValue = 0;
+        byte[] lastBytes = null;
+        for (int i=0; i<SHORT_TEST; i++) {
+            Byte value;
+            if (mRandom.nextInt(10) == 1) {
+                value = null;
+                assertEquals(1, DataEncoder.encode(value, bytes, 0));
+            } else {
+                value = (byte) mRandom.nextInt();
+                assertEquals(2, DataEncoder.encode(value, bytes, 0));
+            }
+            assertEquals(value, DataDecoder.decodeByteObj(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = compare(value, lastValue);
+                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_short() throws Exception {
+        byte[] bytes = new byte[2];
+        short lastValue = 0;
+        byte[] lastBytes = null;
+        for (int i=0; i<SHORT_TEST; i++) {
+            short value = (short) mRandom.nextInt();
+            DataEncoder.encode(value, bytes, 0);
+            assertEquals(value, DataDecoder.decodeShort(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = compare(value, lastValue);
+                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_Short() throws Exception {
+        byte[] bytes = new byte[3];
+        Short lastValue = 0;
+        byte[] lastBytes = null;
+        for (int i=0; i<SHORT_TEST; i++) {
+            Short value;
+            if (mRandom.nextInt(10) == 1) {
+                value = null;
+                assertEquals(1, DataEncoder.encode(value, bytes, 0));
+            } else {
+                value = (short) mRandom.nextInt();
+                assertEquals(3, DataEncoder.encode(value, bytes, 0));
+            }
+            assertEquals(value, DataDecoder.decodeShortObj(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = compare(value, lastValue);
+                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_char() throws Exception {
+        byte[] bytes = new byte[2];
+        char lastValue = 0;
+        byte[] lastBytes = null;
+        for (int i=0; i<SHORT_TEST; i++) {
+            char value = (char) mRandom.nextInt();
+            DataEncoder.encode(value, bytes, 0);
+            assertEquals(value, DataDecoder.decodeChar(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = compare(value, lastValue);
+                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_Character() throws Exception {
+        byte[] bytes = new byte[3];
+        Character lastValue = 0;
+        byte[] lastBytes = null;
+        for (int i=0; i<SHORT_TEST; i++) {
+            Character value;
+            if (mRandom.nextInt(10) == 1) {
+                value = null;
+                assertEquals(1, DataEncoder.encode(value, bytes, 0));
+            } else {
+                value = (char) mRandom.nextInt();
+                assertEquals(3, DataEncoder.encode(value, bytes, 0));
+            }
+            assertEquals(value, DataDecoder.decodeCharacterObj(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = compare(value, lastValue);
+                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_int() throws Exception {
+        byte[] bytes = new byte[4];
+        int lastValue = 0;
+        byte[] lastBytes = null;
+        for (int i=0; i<SHORT_TEST; i++) {
+            int value = mRandom.nextInt();
+            DataEncoder.encode(value, bytes, 0);
+            assertEquals(value, DataDecoder.decodeInt(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = compare(value, lastValue);
+                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_Integer() throws Exception {
+        byte[] bytes = new byte[5];
+        Integer lastValue = 0;
+        byte[] lastBytes = null;
+        for (int i=0; i<SHORT_TEST; i++) {
+            Integer value;
+            if (mRandom.nextInt(10) == 1) {
+                value = null;
+                assertEquals(1, DataEncoder.encode(value, bytes, 0));
+            } else {
+                value = mRandom.nextInt();
+                assertEquals(5, DataEncoder.encode(value, bytes, 0));
+            }
+            assertEquals(value, DataDecoder.decodeIntegerObj(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = compare(value, lastValue);
+                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_long() throws Exception {
+        byte[] bytes = new byte[8];
+        long lastValue = 0;
+        byte[] lastBytes = null;
+        for (int i=0; i<SHORT_TEST; i++) {
+            long value = mRandom.nextLong();
+            DataEncoder.encode(value, bytes, 0);
+            assertEquals(value, DataDecoder.decodeLong(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = compare(value, lastValue);
+                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_Long() throws Exception {
+        byte[] bytes = new byte[9];
+        Long lastValue = 0L;
+        byte[] lastBytes = null;
+        for (int i=0; i<SHORT_TEST; i++) {
+            Long value;
+            if (mRandom.nextInt(10) == 1) {
+                value = null;
+                assertEquals(1, DataEncoder.encode(value, bytes, 0));
+            } else {
+                value = mRandom.nextLong();
+                assertEquals(9, DataEncoder.encode(value, bytes, 0));
+            }
+            assertEquals(value, DataDecoder.decodeLongObj(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = compare(value, lastValue);
+                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_float() throws Exception {
+        byte[] bytes = new byte[4];
+        float lastValue = 0;
+        byte[] lastBytes = null;
+        for (int i=0; i<LONG_TEST; i++) {
+            float value = Float.intBitsToFloat(mRandom.nextInt());
+            DataEncoder.encode(value, bytes, 0);
+            assertEquals(value, DataDecoder.decodeFloat(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = compare(value, lastValue);
+                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_Float() throws Exception {
+        byte[] bytes = new byte[4];
+        Float lastValue = 0f;
+        byte[] lastBytes = null;
+        for (int i=0; i<LONG_TEST; i++) {
+            Float value;
+            if (mRandom.nextInt(10) == 1) {
+                value = null;
+            } else {
+                value = Float.intBitsToFloat(mRandom.nextInt());
+            }
+            DataEncoder.encode(value, bytes, 0);
+            assertEquals(value, DataDecoder.decodeFloatObj(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = compare(value, lastValue);
+                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_double() throws Exception {
+        byte[] bytes = new byte[8];
+        double lastValue = 0;
+        byte[] lastBytes = null;
+        for (int i=0; i<LONG_TEST; i++) {
+            double value = Double.longBitsToDouble(mRandom.nextLong());
+            DataEncoder.encode(value, bytes, 0);
+            assertEquals(value, DataDecoder.decodeDouble(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = compare(value, lastValue);
+                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_Double() throws Exception {
+        byte[] bytes = new byte[8];
+        Double lastValue = 0d;
+        byte[] lastBytes = null;
+        for (int i=0; i<LONG_TEST; i++) {
+            Double value;
+            if (mRandom.nextInt(10) == 1) {
+                value = null;
+            } else {
+                value = Double.longBitsToDouble(mRandom.nextLong());
+            }
+            DataEncoder.encode(value, bytes, 0);
+            assertEquals(value, DataDecoder.decodeDoubleObj(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = compare(value, lastValue);
+                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_String() throws Exception {
+        String[] ref = new String[1];
+        for (int i=0; i<SHORT_TEST; i++) {
+            String value;
+            if (mRandom.nextInt(10) == 1) {
+                value = null;
+            } else {
+                int length;
+                switch (mRandom.nextInt(15)) {
+                default:
+                case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+                    length = mRandom.nextInt(100);
+                    break;
+                case 8: case 9: case 10: case 11:
+                    length = mRandom.nextInt(200);
+                    break;
+                case 12: case 13:
+                    length = mRandom.nextInt(20000);
+                    break;
+                case 14:
+                    length = mRandom.nextInt(3000000);
+                    break;
+                }
+                char[] chars = new char[length];
+                for (int j=0; j<length; j++) {
+                    char c;
+                    switch (mRandom.nextInt(7)) {
+                    default:
+                    case 0: case 1: case 2: case 3:
+                        c = (char) mRandom.nextInt(128);
+                        break;
+                    case 4: case 5:
+                        c = (char) mRandom.nextInt(4000);
+                        break;
+                    case 6:
+                        c = (char) mRandom.nextInt();
+                        break;
+                    }
+                    chars[j] = c;
+                }
+                value = new String(chars);
+            }
+
+            byte[] bytes = new byte[DataEncoder.calculateEncodedStringLength(value)];
+            assertEquals(bytes.length, DataEncoder.encode(value, bytes, 0));
+            assertEquals(bytes.length, DataDecoder.decodeString(bytes, 0, ref));
+            assertEquals(value, ref[0]);
+        }
+    }
+}
diff --git a/src/test/java/com/amazon/carbonado/raw/TestEncodingStrategy.java b/src/test/java/com/amazon/carbonado/raw/TestEncodingStrategy.java
new file mode 100644
index 0000000..11a4eca
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/raw/TestEncodingStrategy.java
@@ -0,0 +1,857 @@
+/*
+ * 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.raw;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.cojen.classfile.*;
+import org.cojen.util.*;
+
+import com.amazon.carbonado.*;
+import com.amazon.carbonado.info.*;
+import com.amazon.carbonado.spi.*;
+
+/**
+ * Test case for {@link GenericEncodingStrategy}.
+ * <p>
+ * It generates random selections of properties, encodes with random values,
+ * and checks that the decoding produces the original results. In addition, the
+ * proper ordering of encoded keys is checked.
+ *
+ * @author Brian S O'Neill
+ */
+public class TestEncodingStrategy extends TestCase {
+    private static final int SHORT_TEST = 100;
+    private static final int MEDIUM_TEST = 500;
+    private static final int LONG_TEST = 1000;
+
+    private static final int ENCODE_OBJECT_ARRAY = 0;
+    private static final int DECODE_OBJECT_ARRAY = 1;
+    private static final int ENCODE_OBJECT_ARRAY_PARTIAL = 2;
+
+    private static final int BOGUS_GENERATION = 99;
+
+    // Make sure BOGUS_GENERATION is not included.
+    private static final int[] GENERATIONS = {-1, 0, 1, 2, 127, 128, 129, Integer.MAX_VALUE};
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(suite());
+    }
+
+    public static TestSuite suite() {
+        return new TestSuite(TestEncodingStrategy.class);
+    }
+
+    private final long mSeed;
+    private final StorableProperty<TestStorable>[] mProperties;
+
+    private Random mRandom;
+
+    public TestEncodingStrategy(String name) {
+        super(name);
+        mSeed = 986184829029842L;
+        Collection<? extends StorableProperty<TestStorable>> properties =
+            StorableIntrospector.examine(TestStorable.class).getAllProperties().values();
+        mProperties = properties.toArray(new StorableProperty[0]);
+    }
+
+    protected void setUp() {
+        mRandom = new Random(mSeed);
+    }
+
+    protected void tearDown() {
+    }
+
+    public void test_dataEncoding_noProperties() throws Exception {
+        for (int generation : GENERATIONS) {
+            test_dataEncoding_noProperties(0, 0, generation);
+        }
+    }
+
+    public void test_dataEncoding_noProperties_prefix() throws Exception {
+        for (int generation : GENERATIONS) {
+            test_dataEncoding_noProperties(5, 0, generation);
+        }
+    }
+
+    public void test_dataEncoding_noProperties_suffix() throws Exception {
+        for (int generation : GENERATIONS) {
+            test_dataEncoding_noProperties(0, 7, generation);
+        }
+    }
+
+    public void test_dataEncoding_noProperties_prefixAndSuffix() throws Exception {
+        for (int generation : GENERATIONS) {
+            test_dataEncoding_noProperties(5, 7, generation);
+        }
+    }
+
+    private void test_dataEncoding_noProperties(int prefix, int suffix, int generation)
+        throws Exception
+    {
+        GenericEncodingStrategy strategy = new GenericEncodingStrategy
+            (TestStorable.class, null, 0, 0, prefix, suffix);
+
+        Method[] methods = generateCodecMethods
+            (strategy, new StorableProperty[0], null, generation);
+
+        byte[] encoded = (byte[]) methods[ENCODE_OBJECT_ARRAY]
+            .invoke(null, new Object[] {new Object[0]});
+
+        int generationPrefix;
+        if (generation < 0) {
+            generationPrefix = 0;
+        } else if (generation < 128) {
+            generationPrefix = 1;
+        } else {
+            generationPrefix = 4;
+        }
+
+        assertEquals(encoded.length, prefix + generationPrefix + suffix);
+
+        if (generation >= 0) {
+            if (generationPrefix == 1) {
+                assertEquals(generation, encoded[prefix]);
+            } else {
+                int actualGeneration = DataDecoder.decodeInt(encoded, prefix);
+                assertEquals(generation, actualGeneration);
+            }
+        }
+
+        // Decode should not throw an exception.
+        methods[DECODE_OBJECT_ARRAY].invoke(null, new Object[0], encoded);
+
+        // Generation mismatch should throw an exception.
+        if (generation >= 0) {
+            encoded[prefix] = BOGUS_GENERATION;
+            try {
+                methods[DECODE_OBJECT_ARRAY].invoke(null, new Object[0], encoded);
+                fail();
+            } catch (InvocationTargetException e) {
+                Throwable cause = e.getCause();
+                if (cause instanceof CorruptEncodingException) {
+                    CorruptEncodingException cee = (CorruptEncodingException) cause;
+                    // Make sure error message includes actual generation.
+                    assertTrue(cee.getMessage().indexOf(String.valueOf(BOGUS_GENERATION)) >= 0);
+                } else {
+                    throw e;
+                }
+            }
+        }
+    }
+
+    public void test_dataEncoding_multipleProperties() throws Exception {
+        for (int generation : GENERATIONS) {
+            test_dataEncoding_multipleProperties(0, 0, generation);
+        }
+    }
+
+    public void test_dataEncoding_multipleProperties_prefix() throws Exception {
+        for (int generation : GENERATIONS) {
+            test_dataEncoding_multipleProperties(5, 0, generation);
+        }
+    }
+
+    public void test_dataEncoding_multipleProperties_suffix() throws Exception {
+        for (int generation : GENERATIONS) {
+            test_dataEncoding_multipleProperties(0, 7, generation);
+        }
+    }
+
+    public void test_dataEncoding_multipleProperties_prefixAndSuffix() throws Exception {
+        for (int generation : GENERATIONS) {
+            test_dataEncoding_multipleProperties(5, 7, generation);
+        }
+    }
+
+    /**
+     * @param generation when non-negative, encode a storable layout generation
+     * value in one or four bytes.
+     */
+    private void test_dataEncoding_multipleProperties(int prefix, int suffix, int generation)
+        throws Exception
+    {
+        GenericEncodingStrategy strategy = new GenericEncodingStrategy
+            (TestStorable.class, null, 0, 0, prefix, suffix);
+
+        for (int i=0; i<SHORT_TEST; i++) {
+            StorableProperty<TestStorable>[] properties = selectProperties(1, 50);
+
+            Method[] methods = generateCodecMethods(strategy, properties, null, generation);
+            Object[] values = selectPropertyValues(properties);
+
+            byte[] encoded = (byte[]) methods[ENCODE_OBJECT_ARRAY]
+                .invoke(null, new Object[] {values});
+
+            int generationPrefix;
+            if (generation < 0) {
+                generationPrefix = 0;
+            } else if (generation < 128) {
+                generationPrefix = 1;
+            } else {
+                generationPrefix = 4;
+            }
+
+            assertTrue(encoded.length > (prefix + generationPrefix + suffix));
+
+            if (generation >= 0) {
+                if (generationPrefix == 1) {
+                    assertEquals(generation, encoded[prefix]);
+                } else {
+                    int actualGeneration = DataDecoder.decodeInt(encoded, prefix);
+                    assertEquals(generation, actualGeneration);
+                }
+            }
+
+            if (prefix > 0) {
+                // Fill in with data which should be ignored by decoder.
+                for (int p=0; p<prefix; p++) {
+                    encoded[p] = (byte) mRandom.nextInt();
+                }
+            }
+
+            if (suffix > 0) {
+                // Fill in with data which should be ignored by decoder.
+                for (int p=0; p<suffix; p++) {
+                    encoded[encoded.length - p - 1] = (byte) mRandom.nextInt();
+                }
+            }
+
+            Object[] decodedValues = new Object[values.length];
+            methods[DECODE_OBJECT_ARRAY].invoke(null, decodedValues, encoded);
+
+            for (int j=0; j<properties.length; j++) {
+                Object a = values[j];
+                Object b = decodedValues[j];
+                if (properties[j].getType() == byte[].class) {
+                    assertTrue(0 == TestDataEncoding.byteArrayOrNullCompare
+                               ((byte[]) a, (byte[]) b));
+                } else {
+                    assertEquals(a, b);
+                }
+            }
+
+            // Generation mismatch should throw an exception.
+            if (generation >= 0) {
+                encoded[prefix] = BOGUS_GENERATION;
+                try {
+                    methods[DECODE_OBJECT_ARRAY].invoke(null, new Object[0], encoded);
+                    fail();
+                } catch (InvocationTargetException e) {
+                    Throwable cause = e.getCause();
+                    if (cause instanceof CorruptEncodingException) {
+                        CorruptEncodingException cee = (CorruptEncodingException) cause;
+                        // Make sure error message includes actual generation.
+                        assertTrue
+                            (cee.getMessage().indexOf(String.valueOf(BOGUS_GENERATION)) >= 0);
+                    } else {
+                        throw e;
+                    }
+                }
+            }
+        }
+    }
+
+    public void test_keyEncoding_noProperties() throws Exception {
+        test_keyEncoding_noProperties(0, 0);
+    }
+
+    public void test_keyEncoding_noProperties_prefix() throws Exception {
+        test_keyEncoding_noProperties(5, 0);
+    }
+
+    public void test_keyEncoding_noProperties_suffix() throws Exception {
+        test_keyEncoding_noProperties(0, 7);
+    }
+
+    public void test_keyEncoding_noProperties_prefixAndSuffix() throws Exception {
+        test_keyEncoding_noProperties(5, 7);
+    }
+
+    private void test_keyEncoding_noProperties(int prefix, int suffix) throws Exception {
+        GenericEncodingStrategy strategy = new GenericEncodingStrategy
+            (TestStorable.class, null, prefix, suffix, 0, 0);
+
+        Method[] methods = generateCodecMethods
+            (strategy, new StorableProperty[0], new Direction[0], -1);
+
+        // Encode should return an empty array.
+        byte[] encoded = (byte[]) methods[ENCODE_OBJECT_ARRAY]
+            .invoke(null, new Object[] {new Object[0]});
+        assertEquals(encoded.length, prefix + suffix);
+
+        // Decode should not throw an exception.
+        methods[DECODE_OBJECT_ARRAY].invoke(null, new Object[0], encoded);
+    }
+
+    public void test_keyEncoding_multipleProperties() throws Exception {
+        test_keyEncoding_multipleProperties(0, 0);
+    }
+
+    public void test_keyEncoding_multipleProperties_prefix() throws Exception {
+        test_keyEncoding_multipleProperties(5, 0);
+    }
+
+    public void test_keyEncoding_multipleProperties_suffix() throws Exception {
+        test_keyEncoding_multipleProperties(0, 7);
+    }
+
+    public void test_keyEncoding_multipleProperties_prefixAndSuffix() throws Exception {
+        test_keyEncoding_multipleProperties(5, 7);
+    }
+
+    private void test_keyEncoding_multipleProperties(int prefix, int suffix) throws Exception {
+        GenericEncodingStrategy strategy = new GenericEncodingStrategy
+            (TestStorable.class, null, prefix, suffix, 0, 0);
+
+        for (int i=0; i<MEDIUM_TEST; i++) {
+            StorableProperty<TestStorable>[] properties = selectProperties(1, 50);
+            Direction[] directions = selectDirections(properties.length);
+
+            Method[] methods = generateCodecMethods(strategy, properties, directions, -1);
+            Object[] values = selectPropertyValues(properties);
+
+            byte[] encoded = (byte[]) methods[ENCODE_OBJECT_ARRAY]
+                .invoke(null, new Object[] {values});
+
+            assertTrue(encoded.length > (prefix + suffix));
+
+            // Encode using partial encoding method, but do all
+            // properties. Ensure that the encoding is exactly the same.
+            byte[] encoded2 = (byte[]) methods[ENCODE_OBJECT_ARRAY_PARTIAL]
+                .invoke(null, new Object[] {values, 0, properties.length});
+            assertTrue(Arrays.equals(encoded, encoded2));
+
+            if (prefix > 0) {
+                // Fill in with data which should be ignored by decoder.
+                for (int p=0; p<prefix; p++) {
+                    encoded[p] = (byte) mRandom.nextInt();
+                }
+            }
+
+            if (suffix > 0) {
+                // Fill in with data which should be ignored by decoder.
+                for (int p=0; p<suffix; p++) {
+                    encoded[encoded.length - p - 1] = (byte) mRandom.nextInt();
+                }
+            }
+
+            Object[] decodedValues = new Object[values.length];
+            methods[DECODE_OBJECT_ARRAY].invoke(null, decodedValues, encoded);
+
+            for (int j=0; j<properties.length; j++) {
+                Object a = values[j];
+                Object b = decodedValues[j];
+                if (properties[j].getType() == byte[].class) {
+                    assertTrue(0 == TestDataEncoding.byteArrayOrNullCompare
+                               ((byte[]) a, (byte[]) b));
+                } else {
+                    assertEquals(a, b);
+                }
+            }
+
+            // Now test partial encoding of keys.
+
+            // Clear out random affixes, since we don't need specific values
+            // anymore and it interferes with next test.
+            if (prefix > 0) {
+                for (int p=0; p<prefix; p++) {
+                    encoded[p] = 0;
+                }
+            }
+            if (suffix > 0) {
+                for (int p=0; p<suffix; p++) {
+                    encoded[encoded.length - p - 1] = 0;
+                }
+            }
+
+            for (int j=0; j<SHORT_TEST; j++) {
+                int start, end;
+                if (properties.length == 1) {
+                    start = 0;
+                    end = 1;
+                } else {
+                    start = mRandom.nextInt(properties.length - 1);
+                    // Partial encoding doesn't support zero properties, so
+                    // ensure randomly selected stride is more than zero.
+                    int stride;
+                    do {
+                        stride = mRandom.nextInt(properties.length - start);
+                    } while (stride == 0);
+                    end = start + stride + 1;
+                }
+
+                Object[] partialValues = new Object[end - start];
+                System.arraycopy(values, start, partialValues, 0, partialValues.length);
+                
+                byte[] partial = (byte[]) methods[ENCODE_OBJECT_ARRAY_PARTIAL]
+                    .invoke(null, new Object[] {partialValues, start, end});
+                
+                // Partial key must be substring of full key.
+                int searchStart = start == 0 ? 0 : prefix;
+                int index = indexOf(encoded, searchStart, encoded.length - searchStart,
+                                    partial, 0, partial.length, 0);
+
+                if (start == 0) {
+                    assertEquals(0, index);
+                } else {
+                    assertTrue(index > 0);
+                }
+
+                if (properties.length == 1) {
+                    break;
+                }
+            }
+        }
+    }
+
+    public void test_keyEncoding_ordering() throws Exception {
+        GenericEncodingStrategy strategy =
+            new GenericEncodingStrategy(TestStorable.class, null, 0, 0, 0, 0);
+
+        for (int i=0; i<MEDIUM_TEST; i++) {
+            StorableProperty<TestStorable>[] properties = selectProperties(1, 50);
+            Direction[] directions = selectDirections(properties.length);
+            Method[] methods = generateCodecMethods(strategy, properties, directions, -1);
+
+            Object[] values_1 = selectPropertyValues(properties);
+            byte[] encoded_1 = (byte[]) methods[ENCODE_OBJECT_ARRAY]
+                .invoke(null, new Object[] {values_1});
+
+            Object[] values_2 = selectPropertyValues(properties);
+            byte[] encoded_2 = (byte[]) methods[ENCODE_OBJECT_ARRAY]
+                .invoke(null, new Object[] {values_2});
+
+            int byteOrder = TestDataEncoding.byteArrayCompare(encoded_1, encoded_2);
+            int valueOrder = compareValues(properties, directions, values_1, values_2);
+
+            assertEquals(valueOrder, byteOrder);
+        }
+    }
+
+    private int compareValues(StorableProperty<?>[] properties, Direction[] directions,
+                              Object[] values_1, Object[] values_2) {
+        int length = directions.length;
+        for (int i=0; i<length; i++) {
+            StorableProperty<?> property = properties[i];
+            Direction direction = directions[i];
+
+            Object value_1 = values_1[i];
+            Object value_2 = values_2[i];
+
+            int result;
+            if (property.getType() == byte[].class) {
+                result = TestDataEncoding.byteArrayOrNullCompare
+                    ((byte[]) value_1, (byte[]) value_2);
+            } else {
+                if (value_1 == null) {
+                    if (value_2 == null) {
+                        result = 0;
+                    } else {
+                        result = 1;
+                    }
+                } else if (value_2 == null) {
+                    result = -1;
+                } else {
+                    result = Integer.signum(((Comparable) value_1).compareTo(value_2));
+                }
+            }
+
+            if (result != 0) {
+                if (direction == Direction.DESCENDING) {
+                    result = -result;
+                }
+                return result;
+            }
+        }
+
+        return 0;
+    }
+
+    // Method taken from String class and modified a bit.
+    private static int indexOf(byte[] source, int sourceOffset, int sourceCount,
+                               byte[] target, int targetOffset, int targetCount,
+                               int fromIndex) {
+        if (fromIndex >= sourceCount) {
+            return (targetCount == 0 ? sourceCount : -1);
+        }
+        if (fromIndex < 0) {
+            fromIndex = 0;
+        }
+        if (targetCount == 0) {
+            return fromIndex;
+        }
+
+        byte first  = target[targetOffset];
+        int max = sourceOffset + (sourceCount - targetCount);
+        
+        for (int i = sourceOffset + fromIndex; i <= max; i++) {
+            // Look for first byte.
+            if (source[i] != first) {
+                while (++i <= max && source[i] != first);
+            }
+            
+            // Found first byte, now look at the rest of v2
+            if (i <= max) {
+                int j = i + 1;
+                int end = j + targetCount - 1;
+                for (int k = targetOffset + 1; j < end && source[j] == target[k]; j++, k++);
+                
+                if (j == end) {
+                    // Found whole string.
+                    return i - sourceOffset;
+                }
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * First method is the encoder, second is the decoder. Both methods are
+     * static. Encoder accepts an object array of property values, and it
+     * returns a byte array. Decoder accepts an object array to receive
+     * property values, an encoded byte array, and it returns void.
+     *
+     * <p>If generating key encoding and the property count is more than zero,
+     * then third element in array is a key encoder that supports partial
+     * encoding. In addition to the object array argument, it also accepts an
+     * int start and an int end argument. The start of the range is inclusive,
+     * and the end is exclusive.
+     *
+     * @param directions when supplied, build key encoding/decoding. Otherwise,
+     * build data encoding/decoding.
+     * @param generation when non-negative, encode a storable layout generation
+     * value in one or four bytes.
+     */
+    private Method[] generateCodecMethods(GenericEncodingStrategy strategy,
+                                          StorableProperty<TestStorable>[] properties,
+                                          Direction[] directions,
+                                          int generation)
+        throws Exception
+    {
+        ClassInjector ci = ClassInjector.create(TestStorable.class.getName(), null);
+        ClassFile cf = new ClassFile(ci.getClassName());
+        cf.markSynthetic();
+        cf.setTarget("1.5");
+
+        cf.addDefaultConstructor();
+
+        TypeDesc byteArrayType = TypeDesc.forClass(byte[].class);
+        TypeDesc objectArrayType = TypeDesc.forClass(Object[].class);
+
+        // Build encode method.
+        MethodInfo mi = cf.addMethod(Modifiers.PUBLIC_STATIC, "encode", byteArrayType,
+                                     new TypeDesc[] {objectArrayType});
+        CodeBuilder b = new CodeBuilder(mi);
+        LocalVariable encodedVar;
+        if (directions != null) {
+            OrderedProperty<TestStorable>[] ordered =
+                makeOrderedProperties(properties, directions);
+            encodedVar =
+                strategy.buildKeyEncoding(b, ordered, b.getParameter(0), null, false, null, null);
+        } else {
+            encodedVar = strategy.buildDataEncoding
+                (b, properties, b.getParameter(0), null, false, generation);
+        }
+        b.loadLocal(encodedVar);
+        b.returnValue(byteArrayType);
+
+        // Build decode method.
+        mi = cf.addMethod(Modifiers.PUBLIC_STATIC, "decode", null,
+                          new TypeDesc[] {objectArrayType, byteArrayType});
+        b = new CodeBuilder(mi);
+        if (directions != null) {
+            OrderedProperty<TestStorable>[] ordered =
+                makeOrderedProperties(properties, directions);
+            strategy.buildKeyDecoding
+                (b, ordered, b.getParameter(0), null, false, b.getParameter(1));
+        } else {
+            strategy.buildDataDecoding
+                (b, properties, b.getParameter(0), null, false,
+                 generation, null, b.getParameter(1));
+        }
+        b.returnVoid();
+
+        if (directions != null && properties.length > 0) {
+            // Build encode partial key method.
+            mi = cf.addMethod
+                (Modifiers.PUBLIC_STATIC, "encodePartial", byteArrayType,
+                 new TypeDesc[] {objectArrayType, TypeDesc.INT, TypeDesc.INT});
+            b = new CodeBuilder(mi);
+            OrderedProperty<TestStorable>[] ordered =
+                makeOrderedProperties(properties, directions);
+            encodedVar =
+                strategy.buildKeyEncoding(b, ordered, b.getParameter(0), null, false,
+                                          b.getParameter(1), b.getParameter(2));
+            b.loadLocal(encodedVar);
+            b.returnValue(byteArrayType);
+        }
+
+        Class<?> clazz = ci.defineClass(cf);
+
+        Method encode = clazz.getMethod("encode", Object[].class);
+        Method decode = clazz.getMethod("decode", Object[].class, byte[].class);
+        Method encodePartial = null;
+        if (directions != null && properties.length > 0) {
+            encodePartial = clazz.getMethod("encodePartial", Object[].class, int.class, int.class);
+        }
+
+        Method[] methods = new Method[3];
+        methods[ENCODE_OBJECT_ARRAY] = encode;
+        methods[DECODE_OBJECT_ARRAY] = decode;
+        methods[ENCODE_OBJECT_ARRAY_PARTIAL] = encodePartial;
+
+        return methods;
+    }
+
+    private StorableProperty<TestStorable>[] selectProperties(int minCount, int maxCount) {
+        int length = (minCount == maxCount) ? minCount
+            : (mRandom.nextInt(maxCount - minCount + 1) + minCount);
+
+        StorableProperty<TestStorable>[] selection = new StorableProperty[length];
+
+        for (int i=length; --i>=0; ) {
+            selection[i] = mProperties[mRandom.nextInt(mProperties.length)];
+        }
+
+        return selection;
+    }
+
+    private Direction[] selectDirections(int count) {
+        Direction[] directions = new Direction[count];
+
+        for (int i=count; --i>=0; ) {
+            Direction dir;
+            switch (mRandom.nextInt(3)) {
+            default:
+                dir = Direction.UNSPECIFIED;
+                break;
+            case 1:
+                dir = Direction.ASCENDING;
+                break;
+            case 2:
+                dir = Direction.DESCENDING;
+                break;
+            }
+            directions[i] = dir;
+        }
+
+        return directions;
+    }
+
+    private OrderedProperty<TestStorable>[] makeOrderedProperties
+        (StorableProperty<TestStorable>[] properties, Direction[] directions) {
+
+        int length = properties.length;
+        OrderedProperty<TestStorable>[] ordered = new OrderedProperty[length];
+        for (int i=length; --i>=0; ) {
+            ordered[i] = OrderedProperty.get(properties[i], directions[i]);
+        }
+
+        return ordered;
+    }
+
+    /**
+     * Returns an array of the same size with randomly selected values filled
+     * in that match the property type.
+     */
+    private Object[] selectPropertyValues(StorableProperty<?>[] properties) {
+        int length = properties.length;
+        Object[] values = new Object[length];
+
+        for (int i=length; --i>=0; ) {
+            StorableProperty<?> property = properties[i];
+            TypeDesc type = TypeDesc.forClass(property.getType());
+
+            Object value;
+
+            if (property.isNullable() && mRandom.nextInt(100) == 0) {
+                value = null;
+            } else {
+                TypeDesc prim = type.toPrimitiveType();
+                if (prim != null) {
+                    switch (prim.getTypeCode()) {
+                    case TypeDesc.BOOLEAN_CODE: default:
+                        value = mRandom.nextBoolean();
+                        break;
+                    case TypeDesc.CHAR_CODE:
+                        value = (char) mRandom.nextInt();
+                        break;
+                    case TypeDesc.FLOAT_CODE:
+                        value = Float.intBitsToFloat(mRandom.nextInt());
+                        break;
+                    case TypeDesc.DOUBLE_CODE:
+                        value = Double.longBitsToDouble(mRandom.nextLong());
+                        break;
+                    case TypeDesc.BYTE_CODE:
+                        value = (byte) mRandom.nextInt();
+                        break;
+                    case TypeDesc.SHORT_CODE:
+                        value = (short) mRandom.nextInt();
+                        break;
+                    case TypeDesc.INT_CODE:
+                        value = mRandom.nextInt();
+                        break;
+                    case TypeDesc.LONG_CODE:
+                        value = mRandom.nextLong();
+                        break;
+                    }
+                } else if (type == TypeDesc.STRING) {
+                    int len = mRandom.nextInt(100);
+                    StringBuilder sb = new StringBuilder(len);
+                    for (int j=0; j<len; j++) {
+                        sb.append((char) mRandom.nextInt());
+                    }
+                    value = sb.toString();
+                } else {
+                    int len = mRandom.nextInt(100);
+                    byte[] bytes = new byte[len];
+                    for (int j=0; j<len; j++) {
+                        bytes[j] = (byte) mRandom.nextInt();
+                    }
+                    value = bytes;
+                }
+            }
+
+            values[i] = value;
+        }
+
+        return values;
+    }
+
+    /**
+     * Just a collection of storable properties. At least one property defined
+     * per supported type.
+     */
+    @PrimaryKey("byte") // I don't really care what the primary key is.
+    public static interface TestStorable extends Storable {
+        byte getByte();
+
+        void setByte(byte b);
+
+        Byte getByteObj();
+
+        void setByteObj(Byte b);
+
+        @Nullable
+        Byte getNullableByteObj();
+
+        void setNullableByteObj(Byte b);
+
+        short getShort();
+
+        void setShort(short s);
+
+        Short getShortObj();
+
+        void setShortObj(Short s);
+
+        @Nullable
+        Short getNullableShortObj();
+
+        void setNullableShortObj(Short s);
+
+        char getChar();
+
+        void setChar(char c);
+
+        Character getCharacterObj();
+
+        void setCharacterObj(Character c);
+
+        @Nullable
+        Character getNullableCharacterObj();
+
+        void setNullableCharacterObj(Character c);
+
+        int getInt();
+
+        void setInt(int i);
+
+        Integer getIntegerObj();
+
+        void setIntegerObj(Integer obj);
+
+        @Nullable
+        Integer getNullableIntegerObj();
+
+        void setNullableIntegerObj(Integer obj);
+
+        long getLong();
+
+        void setLong(long i);
+
+        Long getLongObj();
+
+        void setLongObj(Long i);
+
+        @Nullable
+        Long getNullableLongObj();
+
+        void setNullableLongObj(Long i);
+
+        float getFloat();
+
+        void setFloat(float f);
+
+        Float getFloatObj();
+
+        void setFloatObj(Float f);
+
+        @Nullable
+        Float getNullableFloatObj();
+
+        void setNullableFloatObj(Float f);
+
+        double getDouble();
+
+        void setDouble(double d);
+
+        Double getDoubleObj();
+
+        void setDoubleObj(Double d);
+
+        @Nullable
+        Double getNullableDoubleObj();
+
+        void setNullableDoubleObj(Double d);
+
+        byte[] getByteArray();
+
+        void setByteArray(byte[] b);
+
+        @Nullable
+        byte[] getNullableByteArray();
+
+        void setNullableByteArray(byte[] b);
+
+        String getString();
+
+        void setString(String s);
+
+        @Nullable
+        String getNullableString();
+
+        void setNullableString(String s);
+    }
+}
diff --git a/src/test/java/com/amazon/carbonado/raw/TestKeyEncoding.java b/src/test/java/com/amazon/carbonado/raw/TestKeyEncoding.java
new file mode 100644
index 0000000..3dba00e
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/raw/TestKeyEncoding.java
@@ -0,0 +1,571 @@
+/*
+ * 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.raw;
+
+import java.util.Random;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Test case for {@link KeyEncoder} and {@link KeyDecoder}. 
+ * <p>
+ * It generates random data values, checks that the decoding produces the
+ * original results, and it checks that the order of the encoded bytes matches
+ * the order of the original data values.
+ *
+ * @author Brian S O'Neill
+ */
+public class TestKeyEncoding extends TestCase {
+    private static final int SHORT_TEST = 100;
+    private static final int MEDIUM_TEST = 500;
+    private static final int LONG_TEST = 1000;
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(suite());
+    }
+
+    public static TestSuite suite() {
+        return new TestSuite(TestKeyEncoding.class);
+    }
+
+    private final long mSeed;
+
+    private Random mRandom;
+
+    public TestKeyEncoding(String name) {
+        super(name);
+        mSeed = 5399777425345431L;
+    }
+
+    protected void setUp() {
+        mRandom = new Random(mSeed);
+    }
+
+    protected void tearDown() {
+    }
+
+    public void test_booleanDesc() throws Exception {
+        byte[] bytes = new byte[1];
+        boolean lastValue = false;
+        byte[] lastBytes = null;
+        for (int i=0; i<SHORT_TEST; i++) {
+            boolean value = mRandom.nextBoolean();
+            KeyEncoder.encodeDesc(value, bytes, 0);
+            assertEquals(value, KeyDecoder.decodeBooleanDesc(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = -TestDataEncoding.compare(value, lastValue);
+                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_BooleanDesc() throws Exception {
+        byte[] bytes = new byte[1];
+        Boolean lastValue = false;
+        byte[] lastBytes = null;
+        for (int i=0; i<SHORT_TEST; i++) {
+            Boolean value;
+            if (mRandom.nextInt(10) == 1) {
+                value = null;
+            } else {
+                value = mRandom.nextBoolean();
+            }
+            KeyEncoder.encodeDesc(value, bytes, 0);
+            assertEquals(value, KeyDecoder.decodeBooleanObjDesc(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = -TestDataEncoding.compare(value, lastValue);
+                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_byteDesc() throws Exception {
+        byte[] bytes = new byte[1];
+        byte lastValue = 0;
+        byte[] lastBytes = null;
+        for (int i=0; i<SHORT_TEST; i++) {
+            byte value = (byte) mRandom.nextInt();
+            KeyEncoder.encodeDesc(value, bytes, 0);
+            assertEquals(value, KeyDecoder.decodeByteDesc(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = -TestDataEncoding.compare(value, lastValue);
+                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_ByteDesc() throws Exception {
+        byte[] bytes = new byte[2];
+        Byte lastValue = 0;
+        byte[] lastBytes = null;
+        for (int i=0; i<SHORT_TEST; i++) {
+            Byte value;
+            if (mRandom.nextInt(10) == 1) {
+                value = null;
+                assertEquals(1, KeyEncoder.encodeDesc(value, bytes, 0));
+            } else {
+                value = (byte) mRandom.nextInt();
+                assertEquals(2, KeyEncoder.encodeDesc(value, bytes, 0));
+            }
+            assertEquals(value, KeyDecoder.decodeByteObjDesc(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = -TestDataEncoding.compare(value, lastValue);
+                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_shortDesc() throws Exception {
+        byte[] bytes = new byte[2];
+        short lastValue = 0;
+        byte[] lastBytes = null;
+        for (int i=0; i<SHORT_TEST; i++) {
+            short value = (short) mRandom.nextInt();
+            KeyEncoder.encodeDesc(value, bytes, 0);
+            assertEquals(value, KeyDecoder.decodeShortDesc(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = -TestDataEncoding.compare(value, lastValue);
+                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_ShortDesc() throws Exception {
+        byte[] bytes = new byte[3];
+        Short lastValue = 0;
+        byte[] lastBytes = null;
+        for (int i=0; i<SHORT_TEST; i++) {
+            Short value;
+            if (mRandom.nextInt(10) == 1) {
+                value = null;
+                assertEquals(1, KeyEncoder.encodeDesc(value, bytes, 0));
+            } else {
+                value = (short) mRandom.nextInt();
+                assertEquals(3, KeyEncoder.encodeDesc(value, bytes, 0));
+            }
+            assertEquals(value, KeyDecoder.decodeShortObjDesc(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = -TestDataEncoding.compare(value, lastValue);
+                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_charDesc() throws Exception {
+        byte[] bytes = new byte[2];
+        char lastValue = 0;
+        byte[] lastBytes = null;
+        for (int i=0; i<SHORT_TEST; i++) {
+            char value = (char) mRandom.nextInt();
+            KeyEncoder.encodeDesc(value, bytes, 0);
+            assertEquals(value, KeyDecoder.decodeCharDesc(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = -TestDataEncoding.compare(value, lastValue);
+                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_CharacterDesc() throws Exception {
+        byte[] bytes = new byte[3];
+        Character lastValue = 0;
+        byte[] lastBytes = null;
+        for (int i=0; i<SHORT_TEST; i++) {
+            Character value;
+            if (mRandom.nextInt(10) == 1) {
+                value = null;
+                assertEquals(1, KeyEncoder.encodeDesc(value, bytes, 0));
+            } else {
+                value = (char) mRandom.nextInt();
+                assertEquals(3, KeyEncoder.encodeDesc(value, bytes, 0));
+            }
+            assertEquals(value, KeyDecoder.decodeCharacterObjDesc(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = -TestDataEncoding.compare(value, lastValue);
+                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_intDesc() throws Exception {
+        byte[] bytes = new byte[4];
+        int lastValue = 0;
+        byte[] lastBytes = null;
+        for (int i=0; i<SHORT_TEST; i++) {
+            int value = mRandom.nextInt();
+            KeyEncoder.encodeDesc(value, bytes, 0);
+            assertEquals(value, KeyDecoder.decodeIntDesc(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = -TestDataEncoding.compare(value, lastValue);
+                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_IntegerDesc() throws Exception {
+        byte[] bytes = new byte[5];
+        Integer lastValue = 0;
+        byte[] lastBytes = null;
+        for (int i=0; i<SHORT_TEST; i++) {
+            Integer value;
+            if (mRandom.nextInt(10) == 1) {
+                value = null;
+                assertEquals(1, KeyEncoder.encodeDesc(value, bytes, 0));
+            } else {
+                value = mRandom.nextInt();
+                assertEquals(5, KeyEncoder.encodeDesc(value, bytes, 0));
+            }
+            assertEquals(value, KeyDecoder.decodeIntegerObjDesc(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = -TestDataEncoding.compare(value, lastValue);
+                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_longDesc() throws Exception {
+        byte[] bytes = new byte[8];
+        long lastValue = 0;
+        byte[] lastBytes = null;
+        for (int i=0; i<SHORT_TEST; i++) {
+            long value = mRandom.nextLong();
+            KeyEncoder.encodeDesc(value, bytes, 0);
+            assertEquals(value, KeyDecoder.decodeLongDesc(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = -TestDataEncoding.compare(value, lastValue);
+                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_LongDesc() throws Exception {
+        byte[] bytes = new byte[9];
+        Long lastValue = 0L;
+        byte[] lastBytes = null;
+        for (int i=0; i<SHORT_TEST; i++) {
+            Long value;
+            if (mRandom.nextInt(10) == 1) {
+                value = null;
+                assertEquals(1, KeyEncoder.encodeDesc(value, bytes, 0));
+            } else {
+                value = mRandom.nextLong();
+                assertEquals(9, KeyEncoder.encodeDesc(value, bytes, 0));
+            }
+            assertEquals(value, KeyDecoder.decodeLongObjDesc(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = -TestDataEncoding.compare(value, lastValue);
+                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_floatDesc() throws Exception {
+        byte[] bytes = new byte[4];
+        float lastValue = 0;
+        byte[] lastBytes = null;
+        for (int i=0; i<LONG_TEST; i++) {
+            float value = Float.intBitsToFloat(mRandom.nextInt());
+            KeyEncoder.encodeDesc(value, bytes, 0);
+            assertEquals(value, KeyDecoder.decodeFloatDesc(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = -TestDataEncoding.compare(value, lastValue);
+                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_FloatDesc() throws Exception {
+        byte[] bytes = new byte[4];
+        Float lastValue = 0f;
+        byte[] lastBytes = null;
+        for (int i=0; i<LONG_TEST; i++) {
+            Float value;
+            if (mRandom.nextInt(10) == 1) {
+                value = null;
+            } else {
+                value = Float.intBitsToFloat(mRandom.nextInt());
+            }
+            KeyEncoder.encodeDesc(value, bytes, 0);
+            assertEquals(value, KeyDecoder.decodeFloatObjDesc(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = -TestDataEncoding.compare(value, lastValue);
+                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_doubleDesc() throws Exception {
+        byte[] bytes = new byte[8];
+        double lastValue = 0;
+        byte[] lastBytes = null;
+        for (int i=0; i<LONG_TEST; i++) {
+            double value = Double.longBitsToDouble(mRandom.nextLong());
+            KeyEncoder.encodeDesc(value, bytes, 0);
+            assertEquals(value, KeyDecoder.decodeDoubleDesc(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = -TestDataEncoding.compare(value, lastValue);
+                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_DoubleDesc() throws Exception {
+        byte[] bytes = new byte[8];
+        Double lastValue = 0d;
+        byte[] lastBytes = null;
+        for (int i=0; i<LONG_TEST; i++) {
+            Double value;
+            if (mRandom.nextInt(10) == 1) {
+                value = null;
+            } else {
+                value = Double.longBitsToDouble(mRandom.nextLong());
+            }
+            KeyEncoder.encodeDesc(value, bytes, 0);
+            assertEquals(value, KeyDecoder.decodeDoubleObjDesc(bytes, 0));
+            if (lastBytes != null) {
+                int sgn = -TestDataEncoding.compare(value, lastValue);
+                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_String() throws Exception {
+        String lastValue = null;
+        byte[] lastBytes = null;
+        String[] ref = new String[1];
+        for (int i=0; i<SHORT_TEST; i++) {
+            String value;
+            if (mRandom.nextInt(10) == 1) {
+                value = null;
+            } else {
+                int length;
+                switch (mRandom.nextInt(15)) {
+                default:
+                case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+                    length = mRandom.nextInt(100);
+                    break;
+                case 8: case 9: case 10: case 11:
+                    length = mRandom.nextInt(200);
+                    break;
+                case 12: case 13:
+                    length = mRandom.nextInt(20000);
+                    break;
+                case 14:
+                    length = mRandom.nextInt(3000000);
+                    break;
+                }
+                char[] chars = new char[length];
+                for (int j=0; j<length; j++) {
+                    char c;
+                    switch (mRandom.nextInt(7)) {
+                    default:
+                    case 0: case 1: case 2: case 3:
+                        c = (char) mRandom.nextInt(128);
+                        break;
+                    case 4: case 5:
+                        c = (char) mRandom.nextInt(4000);
+                        break;
+                    case 6:
+                        c = (char) mRandom.nextInt();
+                        break;
+                    }
+                    chars[j] = c;
+                }
+                value = new String(chars);
+            }
+
+            byte[] bytes = new byte[KeyEncoder.calculateEncodedStringLength(value)];
+            assertEquals(bytes.length, KeyEncoder.encode(value, bytes, 0));
+            assertEquals(bytes.length, KeyDecoder.decodeString(bytes, 0, ref));
+            assertEquals(value, ref[0]);
+
+            if (lastBytes != null) {
+                int sgn = TestDataEncoding.compare(value, lastValue);
+                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_StringDesc() throws Exception {
+        String lastValue = null;
+        byte[] lastBytes = null;
+        String[] ref = new String[1];
+        for (int i=0; i<SHORT_TEST; i++) {
+            String value;
+            if (mRandom.nextInt(10) == 1) {
+                value = null;
+            } else {
+                int length;
+                switch (mRandom.nextInt(15)) {
+                default:
+                case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+                    length = mRandom.nextInt(100);
+                    break;
+                case 8: case 9: case 10: case 11:
+                    length = mRandom.nextInt(200);
+                    break;
+                case 12: case 13:
+                    length = mRandom.nextInt(20000);
+                    break;
+                case 14:
+                    length = mRandom.nextInt(3000000);
+                    break;
+                }
+                char[] chars = new char[length];
+                for (int j=0; j<length; j++) {
+                    char c;
+                    switch (mRandom.nextInt(7)) {
+                    default:
+                    case 0: case 1: case 2: case 3:
+                        c = (char) mRandom.nextInt(128);
+                        break;
+                    case 4: case 5:
+                        c = (char) mRandom.nextInt(4000);
+                        break;
+                    case 6:
+                        c = (char) mRandom.nextInt();
+                        break;
+                    }
+                    chars[j] = c;
+                }
+                value = new String(chars);
+            }
+
+            byte[] bytes = new byte[KeyEncoder.calculateEncodedStringLength(value)];
+            assertEquals(bytes.length, KeyEncoder.encodeDesc(value, bytes, 0));
+            assertEquals(bytes.length, KeyDecoder.decodeStringDesc(bytes, 0, ref));
+            assertEquals(value, ref[0]);
+
+
+            if (lastBytes != null) {
+                int sgn = -TestDataEncoding.compare(value, lastValue);
+                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_byteArray() throws Exception {
+        byte[] lastValue = null;
+        byte[] lastBytes = null;
+        byte[][] ref = new byte[1][];
+        for (int i=0; i<LONG_TEST; i++) {
+            byte[] value;
+            if (mRandom.nextInt(10) == 1) {
+                value = null;
+            } else {
+                int length = mRandom.nextInt(4000);
+                value = new byte[length];
+                for (int j=0; j<length; j++) {
+                    value[j] = (byte) mRandom.nextInt();
+                }
+            }
+
+            byte[] bytes = new byte[KeyEncoder.calculateEncodedLength(value)];
+            assertEquals(bytes.length, KeyEncoder.encode(value, bytes, 0));
+            assertEquals(bytes.length, KeyDecoder.decode(bytes, 0, ref));
+            if (ref[0] == null) {
+                assertEquals(value, null);
+            } else if (value == null) {
+                assertEquals(value, ref[0]);
+            } else {
+                assertEquals(0, TestDataEncoding.byteArrayCompare(value, ref[0], value.length));
+            }
+
+            if (lastBytes != null) {
+                int sgn = TestDataEncoding.byteArrayOrNullCompare(value, lastValue);
+                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+
+    public void test_byteArrayDesc() throws Exception {
+        byte[] lastValue = null;
+        byte[] lastBytes = null;
+        byte[][] ref = new byte[1][];
+        for (int i=0; i<LONG_TEST; i++) {
+            byte[] value;
+            if (mRandom.nextInt(10) == 1) {
+                value = null;
+            } else {
+                int length = mRandom.nextInt(4000);
+                value = new byte[length];
+                for (int j=0; j<length; j++) {
+                    value[j] = (byte) mRandom.nextInt();
+                }
+            }
+
+            byte[] bytes = new byte[KeyEncoder.calculateEncodedLength(value)];
+            assertEquals(bytes.length, KeyEncoder.encodeDesc(value, bytes, 0));
+            assertEquals(bytes.length, KeyDecoder.decodeDesc(bytes, 0, ref));
+            if (ref[0] == null) {
+                assertEquals(value, null);
+            } else if (value == null) {
+                assertEquals(value, ref[0]);
+            } else {
+                assertEquals(0, TestDataEncoding.byteArrayCompare(value, ref[0], value.length));
+            }
+
+            if (lastBytes != null) {
+                int sgn = -TestDataEncoding.byteArrayOrNullCompare(value, lastValue);
+                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+            }
+            lastValue = value;
+            lastBytes = bytes.clone();
+        }
+    }
+}
diff --git a/src/test/java/com/amazon/carbonado/spi/raw/TestDataEncoding.java b/src/test/java/com/amazon/carbonado/spi/raw/TestDataEncoding.java
deleted file mode 100644
index 783a71e..0000000
--- a/src/test/java/com/amazon/carbonado/spi/raw/TestDataEncoding.java
+++ /dev/null
@@ -1,515 +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.Random;
-
-import junit.framework.TestCase;
-import junit.framework.TestSuite;
-
-/**
- * Test case for {@link DataEncoder} and {@link DataDecoder}. 
- * <p>
- * It generates random data values, checks that the decoding produces the
- * original results, and it checks that the order of the encoded bytes matches
- * the order of the original data values.
- *
- * @author Brian S O'Neill
- */
-public class TestDataEncoding extends TestCase {
-    private static final int SHORT_TEST = 100;
-    private static final int MEDIUM_TEST = 500;
-    private static final int LONG_TEST = 1000;
-
-    public static void main(String[] args) {
-        junit.textui.TestRunner.run(suite());
-    }
-
-    public static TestSuite suite() {
-        return new TestSuite(TestDataEncoding.class);
-    }
-
-    /**
-     * @return -1, 0, or 1
-     */
-    static int byteArrayCompare(byte[] aa, byte[] ab) {
-        int len = Math.min(aa.length, ab.length);
-        int result = byteArrayCompare(aa, ab, len);
-        if (result == 0 && aa.length != ab.length) {
-            if (aa.length == len) {
-                return -1;
-            }
-            if (ab.length == len) {
-                return 1;
-            }
-        }
-        return result;
-    }
-
-    /**
-     * @return -1, 0, or 1
-     */
-    static int byteArrayCompare(byte[] aa, byte[] ab, int len) {
-        for (int i=0; i<len; i++) {
-            int a = aa[i] & 0xff;
-            int b = ab[i] & 0xff;
-            if (a < b) {
-                return -1;
-            }
-            if (a > b) {
-                return 1;
-            }
-        }
-        return 0;
-    }
-
-    /**
-     * @return -1, 0, or 1
-     */
-    static int byteArrayOrNullCompare(byte[] aa, byte[] ab) {
-        if (aa == null) {
-            if (ab == null) {
-                return 0;
-            } else {
-                return 1;
-            }
-        } else if (ab == null) {
-            return -1;
-        } else {
-            return byteArrayCompare(aa, ab);
-        }
-    }
-
-    /**
-     * @return -1, 0, or 1
-     */
-    static int byteArrayOrNullCompare(byte[] aa, byte[] ab, int len) {
-        if (aa == null) {
-            if (ab == null) {
-                return 0;
-            } else {
-                return 1;
-            }
-        } else if (ab == null) {
-            return -1;
-        } else {
-            return byteArrayCompare(aa, ab, len);
-        }
-    }
-
-    /**
-     * @return -1, 0, or 1
-     */
-    static <C extends Comparable> int compare(C a, C b) {
-        if (a == null) {
-            if (b == null) {
-                return 0;
-            } else {
-                return 1;
-            }
-        } else if (b == null) {
-            return -1;
-        } else {
-            return Integer.signum(a.compareTo(b));
-        }
-    }
-
-    private final long mSeed;
-
-    private Random mRandom;
-
-    public TestDataEncoding(String name) {
-        super(name);
-        mSeed = 5399777425345431L;
-    }
-
-    protected void setUp() {
-        mRandom = new Random(mSeed);
-    }
-
-    protected void tearDown() {
-    }
-
-    public void test_boolean() throws Exception {
-        byte[] bytes = new byte[1];
-        boolean lastValue = false;
-        byte[] lastBytes = null;
-        for (int i=0; i<SHORT_TEST; i++) {
-            boolean value = mRandom.nextBoolean();
-            DataEncoder.encode(value, bytes, 0);
-            assertEquals(value, DataDecoder.decodeBoolean(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = compare(value, lastValue);
-                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_Boolean() throws Exception {
-        byte[] bytes = new byte[1];
-        Boolean lastValue = false;
-        byte[] lastBytes = null;
-        for (int i=0; i<SHORT_TEST; i++) {
-            Boolean value;
-            if (mRandom.nextInt(10) == 1) {
-                value = null;
-            } else {
-                value = mRandom.nextBoolean();
-            }
-            DataEncoder.encode(value, bytes, 0);
-            assertEquals(value, DataDecoder.decodeBooleanObj(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = compare(value, lastValue);
-                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_byte() throws Exception {
-        byte[] bytes = new byte[1];
-        byte lastValue = 0;
-        byte[] lastBytes = null;
-        for (int i=0; i<SHORT_TEST; i++) {
-            byte value = (byte) mRandom.nextInt();
-            DataEncoder.encode(value, bytes, 0);
-            assertEquals(value, DataDecoder.decodeByte(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = compare(value, lastValue);
-                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_Byte() throws Exception {
-        byte[] bytes = new byte[2];
-        Byte lastValue = 0;
-        byte[] lastBytes = null;
-        for (int i=0; i<SHORT_TEST; i++) {
-            Byte value;
-            if (mRandom.nextInt(10) == 1) {
-                value = null;
-                assertEquals(1, DataEncoder.encode(value, bytes, 0));
-            } else {
-                value = (byte) mRandom.nextInt();
-                assertEquals(2, DataEncoder.encode(value, bytes, 0));
-            }
-            assertEquals(value, DataDecoder.decodeByteObj(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = compare(value, lastValue);
-                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_short() throws Exception {
-        byte[] bytes = new byte[2];
-        short lastValue = 0;
-        byte[] lastBytes = null;
-        for (int i=0; i<SHORT_TEST; i++) {
-            short value = (short) mRandom.nextInt();
-            DataEncoder.encode(value, bytes, 0);
-            assertEquals(value, DataDecoder.decodeShort(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = compare(value, lastValue);
-                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_Short() throws Exception {
-        byte[] bytes = new byte[3];
-        Short lastValue = 0;
-        byte[] lastBytes = null;
-        for (int i=0; i<SHORT_TEST; i++) {
-            Short value;
-            if (mRandom.nextInt(10) == 1) {
-                value = null;
-                assertEquals(1, DataEncoder.encode(value, bytes, 0));
-            } else {
-                value = (short) mRandom.nextInt();
-                assertEquals(3, DataEncoder.encode(value, bytes, 0));
-            }
-            assertEquals(value, DataDecoder.decodeShortObj(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = compare(value, lastValue);
-                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_char() throws Exception {
-        byte[] bytes = new byte[2];
-        char lastValue = 0;
-        byte[] lastBytes = null;
-        for (int i=0; i<SHORT_TEST; i++) {
-            char value = (char) mRandom.nextInt();
-            DataEncoder.encode(value, bytes, 0);
-            assertEquals(value, DataDecoder.decodeChar(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = compare(value, lastValue);
-                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_Character() throws Exception {
-        byte[] bytes = new byte[3];
-        Character lastValue = 0;
-        byte[] lastBytes = null;
-        for (int i=0; i<SHORT_TEST; i++) {
-            Character value;
-            if (mRandom.nextInt(10) == 1) {
-                value = null;
-                assertEquals(1, DataEncoder.encode(value, bytes, 0));
-            } else {
-                value = (char) mRandom.nextInt();
-                assertEquals(3, DataEncoder.encode(value, bytes, 0));
-            }
-            assertEquals(value, DataDecoder.decodeCharacterObj(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = compare(value, lastValue);
-                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_int() throws Exception {
-        byte[] bytes = new byte[4];
-        int lastValue = 0;
-        byte[] lastBytes = null;
-        for (int i=0; i<SHORT_TEST; i++) {
-            int value = mRandom.nextInt();
-            DataEncoder.encode(value, bytes, 0);
-            assertEquals(value, DataDecoder.decodeInt(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = compare(value, lastValue);
-                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_Integer() throws Exception {
-        byte[] bytes = new byte[5];
-        Integer lastValue = 0;
-        byte[] lastBytes = null;
-        for (int i=0; i<SHORT_TEST; i++) {
-            Integer value;
-            if (mRandom.nextInt(10) == 1) {
-                value = null;
-                assertEquals(1, DataEncoder.encode(value, bytes, 0));
-            } else {
-                value = mRandom.nextInt();
-                assertEquals(5, DataEncoder.encode(value, bytes, 0));
-            }
-            assertEquals(value, DataDecoder.decodeIntegerObj(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = compare(value, lastValue);
-                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_long() throws Exception {
-        byte[] bytes = new byte[8];
-        long lastValue = 0;
-        byte[] lastBytes = null;
-        for (int i=0; i<SHORT_TEST; i++) {
-            long value = mRandom.nextLong();
-            DataEncoder.encode(value, bytes, 0);
-            assertEquals(value, DataDecoder.decodeLong(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = compare(value, lastValue);
-                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_Long() throws Exception {
-        byte[] bytes = new byte[9];
-        Long lastValue = 0L;
-        byte[] lastBytes = null;
-        for (int i=0; i<SHORT_TEST; i++) {
-            Long value;
-            if (mRandom.nextInt(10) == 1) {
-                value = null;
-                assertEquals(1, DataEncoder.encode(value, bytes, 0));
-            } else {
-                value = mRandom.nextLong();
-                assertEquals(9, DataEncoder.encode(value, bytes, 0));
-            }
-            assertEquals(value, DataDecoder.decodeLongObj(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = compare(value, lastValue);
-                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_float() throws Exception {
-        byte[] bytes = new byte[4];
-        float lastValue = 0;
-        byte[] lastBytes = null;
-        for (int i=0; i<LONG_TEST; i++) {
-            float value = Float.intBitsToFloat(mRandom.nextInt());
-            DataEncoder.encode(value, bytes, 0);
-            assertEquals(value, DataDecoder.decodeFloat(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = compare(value, lastValue);
-                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_Float() throws Exception {
-        byte[] bytes = new byte[4];
-        Float lastValue = 0f;
-        byte[] lastBytes = null;
-        for (int i=0; i<LONG_TEST; i++) {
-            Float value;
-            if (mRandom.nextInt(10) == 1) {
-                value = null;
-            } else {
-                value = Float.intBitsToFloat(mRandom.nextInt());
-            }
-            DataEncoder.encode(value, bytes, 0);
-            assertEquals(value, DataDecoder.decodeFloatObj(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = compare(value, lastValue);
-                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_double() throws Exception {
-        byte[] bytes = new byte[8];
-        double lastValue = 0;
-        byte[] lastBytes = null;
-        for (int i=0; i<LONG_TEST; i++) {
-            double value = Double.longBitsToDouble(mRandom.nextLong());
-            DataEncoder.encode(value, bytes, 0);
-            assertEquals(value, DataDecoder.decodeDouble(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = compare(value, lastValue);
-                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_Double() throws Exception {
-        byte[] bytes = new byte[8];
-        Double lastValue = 0d;
-        byte[] lastBytes = null;
-        for (int i=0; i<LONG_TEST; i++) {
-            Double value;
-            if (mRandom.nextInt(10) == 1) {
-                value = null;
-            } else {
-                value = Double.longBitsToDouble(mRandom.nextLong());
-            }
-            DataEncoder.encode(value, bytes, 0);
-            assertEquals(value, DataDecoder.decodeDoubleObj(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = compare(value, lastValue);
-                assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_String() throws Exception {
-        String[] ref = new String[1];
-        for (int i=0; i<SHORT_TEST; i++) {
-            String value;
-            if (mRandom.nextInt(10) == 1) {
-                value = null;
-            } else {
-                int length;
-                switch (mRandom.nextInt(15)) {
-                default:
-                case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
-                    length = mRandom.nextInt(100);
-                    break;
-                case 8: case 9: case 10: case 11:
-                    length = mRandom.nextInt(200);
-                    break;
-                case 12: case 13:
-                    length = mRandom.nextInt(20000);
-                    break;
-                case 14:
-                    length = mRandom.nextInt(3000000);
-                    break;
-                }
-                char[] chars = new char[length];
-                for (int j=0; j<length; j++) {
-                    char c;
-                    switch (mRandom.nextInt(7)) {
-                    default:
-                    case 0: case 1: case 2: case 3:
-                        c = (char) mRandom.nextInt(128);
-                        break;
-                    case 4: case 5:
-                        c = (char) mRandom.nextInt(4000);
-                        break;
-                    case 6:
-                        c = (char) mRandom.nextInt();
-                        break;
-                    }
-                    chars[j] = c;
-                }
-                value = new String(chars);
-            }
-
-            byte[] bytes = new byte[DataEncoder.calculateEncodedStringLength(value)];
-            assertEquals(bytes.length, DataEncoder.encode(value, bytes, 0));
-            assertEquals(bytes.length, DataDecoder.decodeString(bytes, 0, ref));
-            assertEquals(value, ref[0]);
-        }
-    }
-}
diff --git a/src/test/java/com/amazon/carbonado/spi/raw/TestEncodingStrategy.java b/src/test/java/com/amazon/carbonado/spi/raw/TestEncodingStrategy.java
deleted file mode 100644
index 514df34..0000000
--- a/src/test/java/com/amazon/carbonado/spi/raw/TestEncodingStrategy.java
+++ /dev/null
@@ -1,857 +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.*;
-import java.util.*;
-
-import junit.framework.TestCase;
-import junit.framework.TestSuite;
-
-import org.cojen.classfile.*;
-import org.cojen.util.*;
-
-import com.amazon.carbonado.*;
-import com.amazon.carbonado.info.*;
-import com.amazon.carbonado.spi.*;
-
-/**
- * Test case for {@link GenericEncodingStrategy}.
- * <p>
- * It generates random selections of properties, encodes with random values,
- * and checks that the decoding produces the original results. In addition, the
- * proper ordering of encoded keys is checked.
- *
- * @author Brian S O'Neill
- */
-public class TestEncodingStrategy extends TestCase {
-    private static final int SHORT_TEST = 100;
-    private static final int MEDIUM_TEST = 500;
-    private static final int LONG_TEST = 1000;
-
-    private static final int ENCODE_OBJECT_ARRAY = 0;
-    private static final int DECODE_OBJECT_ARRAY = 1;
-    private static final int ENCODE_OBJECT_ARRAY_PARTIAL = 2;
-
-    private static final int BOGUS_GENERATION = 99;
-
-    // Make sure BOGUS_GENERATION is not included.
-    private static final int[] GENERATIONS = {-1, 0, 1, 2, 127, 128, 129, Integer.MAX_VALUE};
-
-    public static void main(String[] args) {
-        junit.textui.TestRunner.run(suite());
-    }
-
-    public static TestSuite suite() {
-        return new TestSuite(TestEncodingStrategy.class);
-    }
-
-    private final long mSeed;
-    private final StorableProperty<TestStorable>[] mProperties;
-
-    private Random mRandom;
-
-    public TestEncodingStrategy(String name) {
-        super(name);
-        mSeed = 986184829029842L;
-        Collection<? extends StorableProperty<TestStorable>> properties =
-            StorableIntrospector.examine(TestStorable.class).getAllProperties().values();
-        mProperties = properties.toArray(new StorableProperty[0]);
-    }
-
-    protected void setUp() {
-        mRandom = new Random(mSeed);
-    }
-
-    protected void tearDown() {
-    }
-
-    public void test_dataEncoding_noProperties() throws Exception {
-        for (int generation : GENERATIONS) {
-            test_dataEncoding_noProperties(0, 0, generation);
-        }
-    }
-
-    public void test_dataEncoding_noProperties_prefix() throws Exception {
-        for (int generation : GENERATIONS) {
-            test_dataEncoding_noProperties(5, 0, generation);
-        }
-    }
-
-    public void test_dataEncoding_noProperties_suffix() throws Exception {
-        for (int generation : GENERATIONS) {
-            test_dataEncoding_noProperties(0, 7, generation);
-        }
-    }
-
-    public void test_dataEncoding_noProperties_prefixAndSuffix() throws Exception {
-        for (int generation : GENERATIONS) {
-            test_dataEncoding_noProperties(5, 7, generation);
-        }
-    }
-
-    private void test_dataEncoding_noProperties(int prefix, int suffix, int generation)
-        throws Exception
-    {
-        GenericEncodingStrategy strategy = new GenericEncodingStrategy
-            (TestStorable.class, null, 0, 0, prefix, suffix);
-
-        Method[] methods = generateCodecMethods
-            (strategy, new StorableProperty[0], null, generation);
-
-        byte[] encoded = (byte[]) methods[ENCODE_OBJECT_ARRAY]
-            .invoke(null, new Object[] {new Object[0]});
-
-        int generationPrefix;
-        if (generation < 0) {
-            generationPrefix = 0;
-        } else if (generation < 128) {
-            generationPrefix = 1;
-        } else {
-            generationPrefix = 4;
-        }
-
-        assertEquals(encoded.length, prefix + generationPrefix + suffix);
-
-        if (generation >= 0) {
-            if (generationPrefix == 1) {
-                assertEquals(generation, encoded[prefix]);
-            } else {
-                int actualGeneration = DataDecoder.decodeInt(encoded, prefix);
-                assertEquals(generation, actualGeneration);
-            }
-        }
-
-        // Decode should not throw an exception.
-        methods[DECODE_OBJECT_ARRAY].invoke(null, new Object[0], encoded);
-
-        // Generation mismatch should throw an exception.
-        if (generation >= 0) {
-            encoded[prefix] = BOGUS_GENERATION;
-            try {
-                methods[DECODE_OBJECT_ARRAY].invoke(null, new Object[0], encoded);
-                fail();
-            } catch (InvocationTargetException e) {
-                Throwable cause = e.getCause();
-                if (cause instanceof CorruptEncodingException) {
-                    CorruptEncodingException cee = (CorruptEncodingException) cause;
-                    // Make sure error message includes actual generation.
-                    assertTrue(cee.getMessage().indexOf(String.valueOf(BOGUS_GENERATION)) >= 0);
-                } else {
-                    throw e;
-                }
-            }
-        }
-    }
-
-    public void test_dataEncoding_multipleProperties() throws Exception {
-        for (int generation : GENERATIONS) {
-            test_dataEncoding_multipleProperties(0, 0, generation);
-        }
-    }
-
-    public void test_dataEncoding_multipleProperties_prefix() throws Exception {
-        for (int generation : GENERATIONS) {
-            test_dataEncoding_multipleProperties(5, 0, generation);
-        }
-    }
-
-    public void test_dataEncoding_multipleProperties_suffix() throws Exception {
-        for (int generation : GENERATIONS) {
-            test_dataEncoding_multipleProperties(0, 7, generation);
-        }
-    }
-
-    public void test_dataEncoding_multipleProperties_prefixAndSuffix() throws Exception {
-        for (int generation : GENERATIONS) {
-            test_dataEncoding_multipleProperties(5, 7, generation);
-        }
-    }
-
-    /**
-     * @param generation when non-negative, encode a storable layout generation
-     * value in one or four bytes.
-     */
-    private void test_dataEncoding_multipleProperties(int prefix, int suffix, int generation)
-        throws Exception
-    {
-        GenericEncodingStrategy strategy = new GenericEncodingStrategy
-            (TestStorable.class, null, 0, 0, prefix, suffix);
-
-        for (int i=0; i<SHORT_TEST; i++) {
-            StorableProperty<TestStorable>[] properties = selectProperties(1, 50);
-
-            Method[] methods = generateCodecMethods(strategy, properties, null, generation);
-            Object[] values = selectPropertyValues(properties);
-
-            byte[] encoded = (byte[]) methods[ENCODE_OBJECT_ARRAY]
-                .invoke(null, new Object[] {values});
-
-            int generationPrefix;
-            if (generation < 0) {
-                generationPrefix = 0;
-            } else if (generation < 128) {
-                generationPrefix = 1;
-            } else {
-                generationPrefix = 4;
-            }
-
-            assertTrue(encoded.length > (prefix + generationPrefix + suffix));
-
-            if (generation >= 0) {
-                if (generationPrefix == 1) {
-                    assertEquals(generation, encoded[prefix]);
-                } else {
-                    int actualGeneration = DataDecoder.decodeInt(encoded, prefix);
-                    assertEquals(generation, actualGeneration);
-                }
-            }
-
-            if (prefix > 0) {
-                // Fill in with data which should be ignored by decoder.
-                for (int p=0; p<prefix; p++) {
-                    encoded[p] = (byte) mRandom.nextInt();
-                }
-            }
-
-            if (suffix > 0) {
-                // Fill in with data which should be ignored by decoder.
-                for (int p=0; p<suffix; p++) {
-                    encoded[encoded.length - p - 1] = (byte) mRandom.nextInt();
-                }
-            }
-
-            Object[] decodedValues = new Object[values.length];
-            methods[DECODE_OBJECT_ARRAY].invoke(null, decodedValues, encoded);
-
-            for (int j=0; j<properties.length; j++) {
-                Object a = values[j];
-                Object b = decodedValues[j];
-                if (properties[j].getType() == byte[].class) {
-                    assertTrue(0 == TestDataEncoding.byteArrayOrNullCompare
-                               ((byte[]) a, (byte[]) b));
-                } else {
-                    assertEquals(a, b);
-                }
-            }
-
-            // Generation mismatch should throw an exception.
-            if (generation >= 0) {
-                encoded[prefix] = BOGUS_GENERATION;
-                try {
-                    methods[DECODE_OBJECT_ARRAY].invoke(null, new Object[0], encoded);
-                    fail();
-                } catch (InvocationTargetException e) {
-                    Throwable cause = e.getCause();
-                    if (cause instanceof CorruptEncodingException) {
-                        CorruptEncodingException cee = (CorruptEncodingException) cause;
-                        // Make sure error message includes actual generation.
-                        assertTrue
-                            (cee.getMessage().indexOf(String.valueOf(BOGUS_GENERATION)) >= 0);
-                    } else {
-                        throw e;
-                    }
-                }
-            }
-        }
-    }
-
-    public void test_keyEncoding_noProperties() throws Exception {
-        test_keyEncoding_noProperties(0, 0);
-    }
-
-    public void test_keyEncoding_noProperties_prefix() throws Exception {
-        test_keyEncoding_noProperties(5, 0);
-    }
-
-    public void test_keyEncoding_noProperties_suffix() throws Exception {
-        test_keyEncoding_noProperties(0, 7);
-    }
-
-    public void test_keyEncoding_noProperties_prefixAndSuffix() throws Exception {
-        test_keyEncoding_noProperties(5, 7);
-    }
-
-    private void test_keyEncoding_noProperties(int prefix, int suffix) throws Exception {
-        GenericEncodingStrategy strategy = new GenericEncodingStrategy
-            (TestStorable.class, null, prefix, suffix, 0, 0);
-
-        Method[] methods = generateCodecMethods
-            (strategy, new StorableProperty[0], new Direction[0], -1);
-
-        // Encode should return an empty array.
-        byte[] encoded = (byte[]) methods[ENCODE_OBJECT_ARRAY]
-            .invoke(null, new Object[] {new Object[0]});
-        assertEquals(encoded.length, prefix + suffix);
-
-        // Decode should not throw an exception.
-        methods[DECODE_OBJECT_ARRAY].invoke(null, new Object[0], encoded);
-    }
-
-    public void test_keyEncoding_multipleProperties() throws Exception {
-        test_keyEncoding_multipleProperties(0, 0);
-    }
-
-    public void test_keyEncoding_multipleProperties_prefix() throws Exception {
-        test_keyEncoding_multipleProperties(5, 0);
-    }
-
-    public void test_keyEncoding_multipleProperties_suffix() throws Exception {
-        test_keyEncoding_multipleProperties(0, 7);
-    }
-
-    public void test_keyEncoding_multipleProperties_prefixAndSuffix() throws Exception {
-        test_keyEncoding_multipleProperties(5, 7);
-    }
-
-    private void test_keyEncoding_multipleProperties(int prefix, int suffix) throws Exception {
-        GenericEncodingStrategy strategy = new GenericEncodingStrategy
-            (TestStorable.class, null, prefix, suffix, 0, 0);
-
-        for (int i=0; i<MEDIUM_TEST; i++) {
-            StorableProperty<TestStorable>[] properties = selectProperties(1, 50);
-            Direction[] directions = selectDirections(properties.length);
-
-            Method[] methods = generateCodecMethods(strategy, properties, directions, -1);
-            Object[] values = selectPropertyValues(properties);
-
-            byte[] encoded = (byte[]) methods[ENCODE_OBJECT_ARRAY]
-                .invoke(null, new Object[] {values});
-
-            assertTrue(encoded.length > (prefix + suffix));
-
-            // Encode using partial encoding method, but do all
-            // properties. Ensure that the encoding is exactly the same.
-            byte[] encoded2 = (byte[]) methods[ENCODE_OBJECT_ARRAY_PARTIAL]
-                .invoke(null, new Object[] {values, 0, properties.length});
-            assertTrue(Arrays.equals(encoded, encoded2));
-
-            if (prefix > 0) {
-                // Fill in with data which should be ignored by decoder.
-                for (int p=0; p<prefix; p++) {
-                    encoded[p] = (byte) mRandom.nextInt();
-                }
-            }
-
-            if (suffix > 0) {
-                // Fill in with data which should be ignored by decoder.
-                for (int p=0; p<suffix; p++) {
-                    encoded[encoded.length - p - 1] = (byte) mRandom.nextInt();
-                }
-            }
-
-            Object[] decodedValues = new Object[values.length];
-            methods[DECODE_OBJECT_ARRAY].invoke(null, decodedValues, encoded);
-
-            for (int j=0; j<properties.length; j++) {
-                Object a = values[j];
-                Object b = decodedValues[j];
-                if (properties[j].getType() == byte[].class) {
-                    assertTrue(0 == TestDataEncoding.byteArrayOrNullCompare
-                               ((byte[]) a, (byte[]) b));
-                } else {
-                    assertEquals(a, b);
-                }
-            }
-
-            // Now test partial encoding of keys.
-
-            // Clear out random affixes, since we don't need specific values
-            // anymore and it interferes with next test.
-            if (prefix > 0) {
-                for (int p=0; p<prefix; p++) {
-                    encoded[p] = 0;
-                }
-            }
-            if (suffix > 0) {
-                for (int p=0; p<suffix; p++) {
-                    encoded[encoded.length - p - 1] = 0;
-                }
-            }
-
-            for (int j=0; j<SHORT_TEST; j++) {
-                int start, end;
-                if (properties.length == 1) {
-                    start = 0;
-                    end = 1;
-                } else {
-                    start = mRandom.nextInt(properties.length - 1);
-                    // Partial encoding doesn't support zero properties, so
-                    // ensure randomly selected stride is more than zero.
-                    int stride;
-                    do {
-                        stride = mRandom.nextInt(properties.length - start);
-                    } while (stride == 0);
-                    end = start + stride + 1;
-                }
-
-                Object[] partialValues = new Object[end - start];
-                System.arraycopy(values, start, partialValues, 0, partialValues.length);
-                
-                byte[] partial = (byte[]) methods[ENCODE_OBJECT_ARRAY_PARTIAL]
-                    .invoke(null, new Object[] {partialValues, start, end});
-                
-                // Partial key must be substring of full key.
-                int searchStart = start == 0 ? 0 : prefix;
-                int index = indexOf(encoded, searchStart, encoded.length - searchStart,
-                                    partial, 0, partial.length, 0);
-
-                if (start == 0) {
-                    assertEquals(0, index);
-                } else {
-                    assertTrue(index > 0);
-                }
-
-                if (properties.length == 1) {
-                    break;
-                }
-            }
-        }
-    }
-
-    public void test_keyEncoding_ordering() throws Exception {
-        GenericEncodingStrategy strategy =
-            new GenericEncodingStrategy(TestStorable.class, null, 0, 0, 0, 0);
-
-        for (int i=0; i<MEDIUM_TEST; i++) {
-            StorableProperty<TestStorable>[] properties = selectProperties(1, 50);
-            Direction[] directions = selectDirections(properties.length);
-            Method[] methods = generateCodecMethods(strategy, properties, directions, -1);
-
-            Object[] values_1 = selectPropertyValues(properties);
-            byte[] encoded_1 = (byte[]) methods[ENCODE_OBJECT_ARRAY]
-                .invoke(null, new Object[] {values_1});
-
-            Object[] values_2 = selectPropertyValues(properties);
-            byte[] encoded_2 = (byte[]) methods[ENCODE_OBJECT_ARRAY]
-                .invoke(null, new Object[] {values_2});
-
-            int byteOrder = TestDataEncoding.byteArrayCompare(encoded_1, encoded_2);
-            int valueOrder = compareValues(properties, directions, values_1, values_2);
-
-            assertEquals(valueOrder, byteOrder);
-        }
-    }
-
-    private int compareValues(StorableProperty<?>[] properties, Direction[] directions,
-                              Object[] values_1, Object[] values_2) {
-        int length = directions.length;
-        for (int i=0; i<length; i++) {
-            StorableProperty<?> property = properties[i];
-            Direction direction = directions[i];
-
-            Object value_1 = values_1[i];
-            Object value_2 = values_2[i];
-
-            int result;
-            if (property.getType() == byte[].class) {
-                result = TestDataEncoding.byteArrayOrNullCompare
-                    ((byte[]) value_1, (byte[]) value_2);
-            } else {
-                if (value_1 == null) {
-                    if (value_2 == null) {
-                        result = 0;
-                    } else {
-                        result = 1;
-                    }
-                } else if (value_2 == null) {
-                    result = -1;
-                } else {
-                    result = Integer.signum(((Comparable) value_1).compareTo(value_2));
-                }
-            }
-
-            if (result != 0) {
-                if (direction == Direction.DESCENDING) {
-                    result = -result;
-                }
-                return result;
-            }
-        }
-
-        return 0;
-    }
-
-    // Method taken from String class and modified a bit.
-    private static int indexOf(byte[] source, int sourceOffset, int sourceCount,
-                               byte[] target, int targetOffset, int targetCount,
-                               int fromIndex) {
-        if (fromIndex >= sourceCount) {
-            return (targetCount == 0 ? sourceCount : -1);
-        }
-        if (fromIndex < 0) {
-            fromIndex = 0;
-        }
-        if (targetCount == 0) {
-            return fromIndex;
-        }
-
-        byte first  = target[targetOffset];
-        int max = sourceOffset + (sourceCount - targetCount);
-        
-        for (int i = sourceOffset + fromIndex; i <= max; i++) {
-            // Look for first byte.
-            if (source[i] != first) {
-                while (++i <= max && source[i] != first);
-            }
-            
-            // Found first byte, now look at the rest of v2
-            if (i <= max) {
-                int j = i + 1;
-                int end = j + targetCount - 1;
-                for (int k = targetOffset + 1; j < end && source[j] == target[k]; j++, k++);
-                
-                if (j == end) {
-                    // Found whole string.
-                    return i - sourceOffset;
-                }
-            }
-        }
-        return -1;
-    }
-
-    /**
-     * First method is the encoder, second is the decoder. Both methods are
-     * static. Encoder accepts an object array of property values, and it
-     * returns a byte array. Decoder accepts an object array to receive
-     * property values, an encoded byte array, and it returns void.
-     *
-     * <p>If generating key encoding and the property count is more than zero,
-     * then third element in array is a key encoder that supports partial
-     * encoding. In addition to the object array argument, it also accepts an
-     * int start and an int end argument. The start of the range is inclusive,
-     * and the end is exclusive.
-     *
-     * @param directions when supplied, build key encoding/decoding. Otherwise,
-     * build data encoding/decoding.
-     * @param generation when non-negative, encode a storable layout generation
-     * value in one or four bytes.
-     */
-    private Method[] generateCodecMethods(GenericEncodingStrategy strategy,
-                                          StorableProperty<TestStorable>[] properties,
-                                          Direction[] directions,
-                                          int generation)
-        throws Exception
-    {
-        ClassInjector ci = ClassInjector.create(TestStorable.class.getName(), null);
-        ClassFile cf = new ClassFile(ci.getClassName());
-        cf.markSynthetic();
-        cf.setTarget("1.5");
-
-        cf.addDefaultConstructor();
-
-        TypeDesc byteArrayType = TypeDesc.forClass(byte[].class);
-        TypeDesc objectArrayType = TypeDesc.forClass(Object[].class);
-
-        // Build encode method.
-        MethodInfo mi = cf.addMethod(Modifiers.PUBLIC_STATIC, "encode", byteArrayType,
-                                     new TypeDesc[] {objectArrayType});
-        CodeBuilder b = new CodeBuilder(mi);
-        LocalVariable encodedVar;
-        if (directions != null) {
-            OrderedProperty<TestStorable>[] ordered =
-                makeOrderedProperties(properties, directions);
-            encodedVar =
-                strategy.buildKeyEncoding(b, ordered, b.getParameter(0), null, false, null, null);
-        } else {
-            encodedVar = strategy.buildDataEncoding
-                (b, properties, b.getParameter(0), null, false, generation);
-        }
-        b.loadLocal(encodedVar);
-        b.returnValue(byteArrayType);
-
-        // Build decode method.
-        mi = cf.addMethod(Modifiers.PUBLIC_STATIC, "decode", null,
-                          new TypeDesc[] {objectArrayType, byteArrayType});
-        b = new CodeBuilder(mi);
-        if (directions != null) {
-            OrderedProperty<TestStorable>[] ordered =
-                makeOrderedProperties(properties, directions);
-            strategy.buildKeyDecoding
-                (b, ordered, b.getParameter(0), null, false, b.getParameter(1));
-        } else {
-            strategy.buildDataDecoding
-                (b, properties, b.getParameter(0), null, false,
-                 generation, null, b.getParameter(1));
-        }
-        b.returnVoid();
-
-        if (directions != null && properties.length > 0) {
-            // Build encode partial key method.
-            mi = cf.addMethod
-                (Modifiers.PUBLIC_STATIC, "encodePartial", byteArrayType,
-                 new TypeDesc[] {objectArrayType, TypeDesc.INT, TypeDesc.INT});
-            b = new CodeBuilder(mi);
-            OrderedProperty<TestStorable>[] ordered =
-                makeOrderedProperties(properties, directions);
-            encodedVar =
-                strategy.buildKeyEncoding(b, ordered, b.getParameter(0), null, false,
-                                          b.getParameter(1), b.getParameter(2));
-            b.loadLocal(encodedVar);
-            b.returnValue(byteArrayType);
-        }
-
-        Class<?> clazz = ci.defineClass(cf);
-
-        Method encode = clazz.getMethod("encode", Object[].class);
-        Method decode = clazz.getMethod("decode", Object[].class, byte[].class);
-        Method encodePartial = null;
-        if (directions != null && properties.length > 0) {
-            encodePartial = clazz.getMethod("encodePartial", Object[].class, int.class, int.class);
-        }
-
-        Method[] methods = new Method[3];
-        methods[ENCODE_OBJECT_ARRAY] = encode;
-        methods[DECODE_OBJECT_ARRAY] = decode;
-        methods[ENCODE_OBJECT_ARRAY_PARTIAL] = encodePartial;
-
-        return methods;
-    }
-
-    private StorableProperty<TestStorable>[] selectProperties(int minCount, int maxCount) {
-        int length = (minCount == maxCount) ? minCount
-            : (mRandom.nextInt(maxCount - minCount + 1) + minCount);
-
-        StorableProperty<TestStorable>[] selection = new StorableProperty[length];
-
-        for (int i=length; --i>=0; ) {
-            selection[i] = mProperties[mRandom.nextInt(mProperties.length)];
-        }
-
-        return selection;
-    }
-
-    private Direction[] selectDirections(int count) {
-        Direction[] directions = new Direction[count];
-
-        for (int i=count; --i>=0; ) {
-            Direction dir;
-            switch (mRandom.nextInt(3)) {
-            default:
-                dir = Direction.UNSPECIFIED;
-                break;
-            case 1:
-                dir = Direction.ASCENDING;
-                break;
-            case 2:
-                dir = Direction.DESCENDING;
-                break;
-            }
-            directions[i] = dir;
-        }
-
-        return directions;
-    }
-
-    private OrderedProperty<TestStorable>[] makeOrderedProperties
-        (StorableProperty<TestStorable>[] properties, Direction[] directions) {
-
-        int length = properties.length;
-        OrderedProperty<TestStorable>[] ordered = new OrderedProperty[length];
-        for (int i=length; --i>=0; ) {
-            ordered[i] = OrderedProperty.get(properties[i], directions[i]);
-        }
-
-        return ordered;
-    }
-
-    /**
-     * Returns an array of the same size with randomly selected values filled
-     * in that match the property type.
-     */
-    private Object[] selectPropertyValues(StorableProperty<?>[] properties) {
-        int length = properties.length;
-        Object[] values = new Object[length];
-
-        for (int i=length; --i>=0; ) {
-            StorableProperty<?> property = properties[i];
-            TypeDesc type = TypeDesc.forClass(property.getType());
-
-            Object value;
-
-            if (property.isNullable() && mRandom.nextInt(100) == 0) {
-                value = null;
-            } else {
-                TypeDesc prim = type.toPrimitiveType();
-                if (prim != null) {
-                    switch (prim.getTypeCode()) {
-                    case TypeDesc.BOOLEAN_CODE: default:
-                        value = mRandom.nextBoolean();
-                        break;
-                    case TypeDesc.CHAR_CODE:
-                        value = (char) mRandom.nextInt();
-                        break;
-                    case TypeDesc.FLOAT_CODE:
-                        value = Float.intBitsToFloat(mRandom.nextInt());
-                        break;
-                    case TypeDesc.DOUBLE_CODE:
-                        value = Double.longBitsToDouble(mRandom.nextLong());
-                        break;
-                    case TypeDesc.BYTE_CODE:
-                        value = (byte) mRandom.nextInt();
-                        break;
-                    case TypeDesc.SHORT_CODE:
-                        value = (short) mRandom.nextInt();
-                        break;
-                    case TypeDesc.INT_CODE:
-                        value = mRandom.nextInt();
-                        break;
-                    case TypeDesc.LONG_CODE:
-                        value = mRandom.nextLong();
-                        break;
-                    }
-                } else if (type == TypeDesc.STRING) {
-                    int len = mRandom.nextInt(100);
-                    StringBuilder sb = new StringBuilder(len);
-                    for (int j=0; j<len; j++) {
-                        sb.append((char) mRandom.nextInt());
-                    }
-                    value = sb.toString();
-                } else {
-                    int len = mRandom.nextInt(100);
-                    byte[] bytes = new byte[len];
-                    for (int j=0; j<len; j++) {
-                        bytes[j] = (byte) mRandom.nextInt();
-                    }
-                    value = bytes;
-                }
-            }
-
-            values[i] = value;
-        }
-
-        return values;
-    }
-
-    /**
-     * Just a collection of storable properties. At least one property defined
-     * per supported type.
-     */
-    @PrimaryKey("byte") // I don't really care what the primary key is.
-    public static interface TestStorable extends Storable {
-        byte getByte();
-
-        void setByte(byte b);
-
-        Byte getByteObj();
-
-        void setByteObj(Byte b);
-
-        @Nullable
-        Byte getNullableByteObj();
-
-        void setNullableByteObj(Byte b);
-
-        short getShort();
-
-        void setShort(short s);
-
-        Short getShortObj();
-
-        void setShortObj(Short s);
-
-        @Nullable
-        Short getNullableShortObj();
-
-        void setNullableShortObj(Short s);
-
-        char getChar();
-
-        void setChar(char c);
-
-        Character getCharacterObj();
-
-        void setCharacterObj(Character c);
-
-        @Nullable
-        Character getNullableCharacterObj();
-
-        void setNullableCharacterObj(Character c);
-
-        int getInt();
-
-        void setInt(int i);
-
-        Integer getIntegerObj();
-
-        void setIntegerObj(Integer obj);
-
-        @Nullable
-        Integer getNullableIntegerObj();
-
-        void setNullableIntegerObj(Integer obj);
-
-        long getLong();
-
-        void setLong(long i);
-
-        Long getLongObj();
-
-        void setLongObj(Long i);
-
-        @Nullable
-        Long getNullableLongObj();
-
-        void setNullableLongObj(Long i);
-
-        float getFloat();
-
-        void setFloat(float f);
-
-        Float getFloatObj();
-
-        void setFloatObj(Float f);
-
-        @Nullable
-        Float getNullableFloatObj();
-
-        void setNullableFloatObj(Float f);
-
-        double getDouble();
-
-        void setDouble(double d);
-
-        Double getDoubleObj();
-
-        void setDoubleObj(Double d);
-
-        @Nullable
-        Double getNullableDoubleObj();
-
-        void setNullableDoubleObj(Double d);
-
-        byte[] getByteArray();
-
-        void setByteArray(byte[] b);
-
-        @Nullable
-        byte[] getNullableByteArray();
-
-        void setNullableByteArray(byte[] b);
-
-        String getString();
-
-        void setString(String s);
-
-        @Nullable
-        String getNullableString();
-
-        void setNullableString(String s);
-    }
-}
diff --git a/src/test/java/com/amazon/carbonado/spi/raw/TestKeyEncoding.java b/src/test/java/com/amazon/carbonado/spi/raw/TestKeyEncoding.java
deleted file mode 100644
index ae09c92..0000000
--- a/src/test/java/com/amazon/carbonado/spi/raw/TestKeyEncoding.java
+++ /dev/null
@@ -1,571 +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.Random;
-
-import junit.framework.TestCase;
-import junit.framework.TestSuite;
-
-/**
- * Test case for {@link KeyEncoder} and {@link KeyDecoder}. 
- * <p>
- * It generates random data values, checks that the decoding produces the
- * original results, and it checks that the order of the encoded bytes matches
- * the order of the original data values.
- *
- * @author Brian S O'Neill
- */
-public class TestKeyEncoding extends TestCase {
-    private static final int SHORT_TEST = 100;
-    private static final int MEDIUM_TEST = 500;
-    private static final int LONG_TEST = 1000;
-
-    public static void main(String[] args) {
-        junit.textui.TestRunner.run(suite());
-    }
-
-    public static TestSuite suite() {
-        return new TestSuite(TestKeyEncoding.class);
-    }
-
-    private final long mSeed;
-
-    private Random mRandom;
-
-    public TestKeyEncoding(String name) {
-        super(name);
-        mSeed = 5399777425345431L;
-    }
-
-    protected void setUp() {
-        mRandom = new Random(mSeed);
-    }
-
-    protected void tearDown() {
-    }
-
-    public void test_booleanDesc() throws Exception {
-        byte[] bytes = new byte[1];
-        boolean lastValue = false;
-        byte[] lastBytes = null;
-        for (int i=0; i<SHORT_TEST; i++) {
-            boolean value = mRandom.nextBoolean();
-            KeyEncoder.encodeDesc(value, bytes, 0);
-            assertEquals(value, KeyDecoder.decodeBooleanDesc(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = -TestDataEncoding.compare(value, lastValue);
-                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_BooleanDesc() throws Exception {
-        byte[] bytes = new byte[1];
-        Boolean lastValue = false;
-        byte[] lastBytes = null;
-        for (int i=0; i<SHORT_TEST; i++) {
-            Boolean value;
-            if (mRandom.nextInt(10) == 1) {
-                value = null;
-            } else {
-                value = mRandom.nextBoolean();
-            }
-            KeyEncoder.encodeDesc(value, bytes, 0);
-            assertEquals(value, KeyDecoder.decodeBooleanObjDesc(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = -TestDataEncoding.compare(value, lastValue);
-                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_byteDesc() throws Exception {
-        byte[] bytes = new byte[1];
-        byte lastValue = 0;
-        byte[] lastBytes = null;
-        for (int i=0; i<SHORT_TEST; i++) {
-            byte value = (byte) mRandom.nextInt();
-            KeyEncoder.encodeDesc(value, bytes, 0);
-            assertEquals(value, KeyDecoder.decodeByteDesc(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = -TestDataEncoding.compare(value, lastValue);
-                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_ByteDesc() throws Exception {
-        byte[] bytes = new byte[2];
-        Byte lastValue = 0;
-        byte[] lastBytes = null;
-        for (int i=0; i<SHORT_TEST; i++) {
-            Byte value;
-            if (mRandom.nextInt(10) == 1) {
-                value = null;
-                assertEquals(1, KeyEncoder.encodeDesc(value, bytes, 0));
-            } else {
-                value = (byte) mRandom.nextInt();
-                assertEquals(2, KeyEncoder.encodeDesc(value, bytes, 0));
-            }
-            assertEquals(value, KeyDecoder.decodeByteObjDesc(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = -TestDataEncoding.compare(value, lastValue);
-                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_shortDesc() throws Exception {
-        byte[] bytes = new byte[2];
-        short lastValue = 0;
-        byte[] lastBytes = null;
-        for (int i=0; i<SHORT_TEST; i++) {
-            short value = (short) mRandom.nextInt();
-            KeyEncoder.encodeDesc(value, bytes, 0);
-            assertEquals(value, KeyDecoder.decodeShortDesc(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = -TestDataEncoding.compare(value, lastValue);
-                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_ShortDesc() throws Exception {
-        byte[] bytes = new byte[3];
-        Short lastValue = 0;
-        byte[] lastBytes = null;
-        for (int i=0; i<SHORT_TEST; i++) {
-            Short value;
-            if (mRandom.nextInt(10) == 1) {
-                value = null;
-                assertEquals(1, KeyEncoder.encodeDesc(value, bytes, 0));
-            } else {
-                value = (short) mRandom.nextInt();
-                assertEquals(3, KeyEncoder.encodeDesc(value, bytes, 0));
-            }
-            assertEquals(value, KeyDecoder.decodeShortObjDesc(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = -TestDataEncoding.compare(value, lastValue);
-                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_charDesc() throws Exception {
-        byte[] bytes = new byte[2];
-        char lastValue = 0;
-        byte[] lastBytes = null;
-        for (int i=0; i<SHORT_TEST; i++) {
-            char value = (char) mRandom.nextInt();
-            KeyEncoder.encodeDesc(value, bytes, 0);
-            assertEquals(value, KeyDecoder.decodeCharDesc(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = -TestDataEncoding.compare(value, lastValue);
-                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_CharacterDesc() throws Exception {
-        byte[] bytes = new byte[3];
-        Character lastValue = 0;
-        byte[] lastBytes = null;
-        for (int i=0; i<SHORT_TEST; i++) {
-            Character value;
-            if (mRandom.nextInt(10) == 1) {
-                value = null;
-                assertEquals(1, KeyEncoder.encodeDesc(value, bytes, 0));
-            } else {
-                value = (char) mRandom.nextInt();
-                assertEquals(3, KeyEncoder.encodeDesc(value, bytes, 0));
-            }
-            assertEquals(value, KeyDecoder.decodeCharacterObjDesc(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = -TestDataEncoding.compare(value, lastValue);
-                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_intDesc() throws Exception {
-        byte[] bytes = new byte[4];
-        int lastValue = 0;
-        byte[] lastBytes = null;
-        for (int i=0; i<SHORT_TEST; i++) {
-            int value = mRandom.nextInt();
-            KeyEncoder.encodeDesc(value, bytes, 0);
-            assertEquals(value, KeyDecoder.decodeIntDesc(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = -TestDataEncoding.compare(value, lastValue);
-                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_IntegerDesc() throws Exception {
-        byte[] bytes = new byte[5];
-        Integer lastValue = 0;
-        byte[] lastBytes = null;
-        for (int i=0; i<SHORT_TEST; i++) {
-            Integer value;
-            if (mRandom.nextInt(10) == 1) {
-                value = null;
-                assertEquals(1, KeyEncoder.encodeDesc(value, bytes, 0));
-            } else {
-                value = mRandom.nextInt();
-                assertEquals(5, KeyEncoder.encodeDesc(value, bytes, 0));
-            }
-            assertEquals(value, KeyDecoder.decodeIntegerObjDesc(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = -TestDataEncoding.compare(value, lastValue);
-                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_longDesc() throws Exception {
-        byte[] bytes = new byte[8];
-        long lastValue = 0;
-        byte[] lastBytes = null;
-        for (int i=0; i<SHORT_TEST; i++) {
-            long value = mRandom.nextLong();
-            KeyEncoder.encodeDesc(value, bytes, 0);
-            assertEquals(value, KeyDecoder.decodeLongDesc(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = -TestDataEncoding.compare(value, lastValue);
-                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_LongDesc() throws Exception {
-        byte[] bytes = new byte[9];
-        Long lastValue = 0L;
-        byte[] lastBytes = null;
-        for (int i=0; i<SHORT_TEST; i++) {
-            Long value;
-            if (mRandom.nextInt(10) == 1) {
-                value = null;
-                assertEquals(1, KeyEncoder.encodeDesc(value, bytes, 0));
-            } else {
-                value = mRandom.nextLong();
-                assertEquals(9, KeyEncoder.encodeDesc(value, bytes, 0));
-            }
-            assertEquals(value, KeyDecoder.decodeLongObjDesc(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = -TestDataEncoding.compare(value, lastValue);
-                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_floatDesc() throws Exception {
-        byte[] bytes = new byte[4];
-        float lastValue = 0;
-        byte[] lastBytes = null;
-        for (int i=0; i<LONG_TEST; i++) {
-            float value = Float.intBitsToFloat(mRandom.nextInt());
-            KeyEncoder.encodeDesc(value, bytes, 0);
-            assertEquals(value, KeyDecoder.decodeFloatDesc(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = -TestDataEncoding.compare(value, lastValue);
-                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_FloatDesc() throws Exception {
-        byte[] bytes = new byte[4];
-        Float lastValue = 0f;
-        byte[] lastBytes = null;
-        for (int i=0; i<LONG_TEST; i++) {
-            Float value;
-            if (mRandom.nextInt(10) == 1) {
-                value = null;
-            } else {
-                value = Float.intBitsToFloat(mRandom.nextInt());
-            }
-            KeyEncoder.encodeDesc(value, bytes, 0);
-            assertEquals(value, KeyDecoder.decodeFloatObjDesc(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = -TestDataEncoding.compare(value, lastValue);
-                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_doubleDesc() throws Exception {
-        byte[] bytes = new byte[8];
-        double lastValue = 0;
-        byte[] lastBytes = null;
-        for (int i=0; i<LONG_TEST; i++) {
-            double value = Double.longBitsToDouble(mRandom.nextLong());
-            KeyEncoder.encodeDesc(value, bytes, 0);
-            assertEquals(value, KeyDecoder.decodeDoubleDesc(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = -TestDataEncoding.compare(value, lastValue);
-                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_DoubleDesc() throws Exception {
-        byte[] bytes = new byte[8];
-        Double lastValue = 0d;
-        byte[] lastBytes = null;
-        for (int i=0; i<LONG_TEST; i++) {
-            Double value;
-            if (mRandom.nextInt(10) == 1) {
-                value = null;
-            } else {
-                value = Double.longBitsToDouble(mRandom.nextLong());
-            }
-            KeyEncoder.encodeDesc(value, bytes, 0);
-            assertEquals(value, KeyDecoder.decodeDoubleObjDesc(bytes, 0));
-            if (lastBytes != null) {
-                int sgn = -TestDataEncoding.compare(value, lastValue);
-                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_String() throws Exception {
-        String lastValue = null;
-        byte[] lastBytes = null;
-        String[] ref = new String[1];
-        for (int i=0; i<SHORT_TEST; i++) {
-            String value;
-            if (mRandom.nextInt(10) == 1) {
-                value = null;
-            } else {
-                int length;
-                switch (mRandom.nextInt(15)) {
-                default:
-                case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
-                    length = mRandom.nextInt(100);
-                    break;
-                case 8: case 9: case 10: case 11:
-                    length = mRandom.nextInt(200);
-                    break;
-                case 12: case 13:
-                    length = mRandom.nextInt(20000);
-                    break;
-                case 14:
-                    length = mRandom.nextInt(3000000);
-                    break;
-                }
-                char[] chars = new char[length];
-                for (int j=0; j<length; j++) {
-                    char c;
-                    switch (mRandom.nextInt(7)) {
-                    default:
-                    case 0: case 1: case 2: case 3:
-                        c = (char) mRandom.nextInt(128);
-                        break;
-                    case 4: case 5:
-                        c = (char) mRandom.nextInt(4000);
-                        break;
-                    case 6:
-                        c = (char) mRandom.nextInt();
-                        break;
-                    }
-                    chars[j] = c;
-                }
-                value = new String(chars);
-            }
-
-            byte[] bytes = new byte[KeyEncoder.calculateEncodedStringLength(value)];
-            assertEquals(bytes.length, KeyEncoder.encode(value, bytes, 0));
-            assertEquals(bytes.length, KeyDecoder.decodeString(bytes, 0, ref));
-            assertEquals(value, ref[0]);
-
-            if (lastBytes != null) {
-                int sgn = TestDataEncoding.compare(value, lastValue);
-                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_StringDesc() throws Exception {
-        String lastValue = null;
-        byte[] lastBytes = null;
-        String[] ref = new String[1];
-        for (int i=0; i<SHORT_TEST; i++) {
-            String value;
-            if (mRandom.nextInt(10) == 1) {
-                value = null;
-            } else {
-                int length;
-                switch (mRandom.nextInt(15)) {
-                default:
-                case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
-                    length = mRandom.nextInt(100);
-                    break;
-                case 8: case 9: case 10: case 11:
-                    length = mRandom.nextInt(200);
-                    break;
-                case 12: case 13:
-                    length = mRandom.nextInt(20000);
-                    break;
-                case 14:
-                    length = mRandom.nextInt(3000000);
-                    break;
-                }
-                char[] chars = new char[length];
-                for (int j=0; j<length; j++) {
-                    char c;
-                    switch (mRandom.nextInt(7)) {
-                    default:
-                    case 0: case 1: case 2: case 3:
-                        c = (char) mRandom.nextInt(128);
-                        break;
-                    case 4: case 5:
-                        c = (char) mRandom.nextInt(4000);
-                        break;
-                    case 6:
-                        c = (char) mRandom.nextInt();
-                        break;
-                    }
-                    chars[j] = c;
-                }
-                value = new String(chars);
-            }
-
-            byte[] bytes = new byte[KeyEncoder.calculateEncodedStringLength(value)];
-            assertEquals(bytes.length, KeyEncoder.encodeDesc(value, bytes, 0));
-            assertEquals(bytes.length, KeyDecoder.decodeStringDesc(bytes, 0, ref));
-            assertEquals(value, ref[0]);
-
-
-            if (lastBytes != null) {
-                int sgn = -TestDataEncoding.compare(value, lastValue);
-                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_byteArray() throws Exception {
-        byte[] lastValue = null;
-        byte[] lastBytes = null;
-        byte[][] ref = new byte[1][];
-        for (int i=0; i<LONG_TEST; i++) {
-            byte[] value;
-            if (mRandom.nextInt(10) == 1) {
-                value = null;
-            } else {
-                int length = mRandom.nextInt(4000);
-                value = new byte[length];
-                for (int j=0; j<length; j++) {
-                    value[j] = (byte) mRandom.nextInt();
-                }
-            }
-
-            byte[] bytes = new byte[KeyEncoder.calculateEncodedLength(value)];
-            assertEquals(bytes.length, KeyEncoder.encode(value, bytes, 0));
-            assertEquals(bytes.length, KeyDecoder.decode(bytes, 0, ref));
-            if (ref[0] == null) {
-                assertEquals(value, null);
-            } else if (value == null) {
-                assertEquals(value, ref[0]);
-            } else {
-                assertEquals(0, TestDataEncoding.byteArrayCompare(value, ref[0], value.length));
-            }
-
-            if (lastBytes != null) {
-                int sgn = TestDataEncoding.byteArrayOrNullCompare(value, lastValue);
-                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-
-    public void test_byteArrayDesc() throws Exception {
-        byte[] lastValue = null;
-        byte[] lastBytes = null;
-        byte[][] ref = new byte[1][];
-        for (int i=0; i<LONG_TEST; i++) {
-            byte[] value;
-            if (mRandom.nextInt(10) == 1) {
-                value = null;
-            } else {
-                int length = mRandom.nextInt(4000);
-                value = new byte[length];
-                for (int j=0; j<length; j++) {
-                    value[j] = (byte) mRandom.nextInt();
-                }
-            }
-
-            byte[] bytes = new byte[KeyEncoder.calculateEncodedLength(value)];
-            assertEquals(bytes.length, KeyEncoder.encodeDesc(value, bytes, 0));
-            assertEquals(bytes.length, KeyDecoder.decodeDesc(bytes, 0, ref));
-            if (ref[0] == null) {
-                assertEquals(value, null);
-            } else if (value == null) {
-                assertEquals(value, ref[0]);
-            } else {
-                assertEquals(0, TestDataEncoding.byteArrayCompare(value, ref[0], value.length));
-            }
-
-            if (lastBytes != null) {
-                int sgn = -TestDataEncoding.byteArrayOrNullCompare(value, lastValue);
-                assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
-            }
-            lastValue = value;
-            lastBytes = bytes.clone();
-        }
-    }
-}
-- 
cgit v1.2.3