001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.util;
019
020import static org.junit.Assert.assertArrayEquals;
021import static org.junit.Assert.assertEquals;
022import static org.junit.Assert.assertTrue;
023import static org.junit.Assert.fail;
024
025import java.math.BigDecimal;
026import java.util.Arrays;
027import java.util.Collections;
028import org.apache.hadoop.hbase.HBaseClassTestRule;
029import org.apache.hadoop.hbase.testclassification.MiscTests;
030import org.apache.hadoop.hbase.testclassification.SmallTests;
031import org.junit.ClassRule;
032import org.junit.Test;
033import org.junit.experimental.categories.Category;
034
035@Category({MiscTests.class, SmallTests.class})
036public class TestOrderedBytes {
037
038  @ClassRule
039  public static final HBaseClassTestRule CLASS_RULE =
040      HBaseClassTestRule.forClass(TestOrderedBytes.class);
041
042  // integer constants for testing Numeric code paths
043  static final Long[] I_VALS =
044    { 0L, 1L, 10L, 99L, 100L, 1234L, 9999L, 10000L, 10001L, 12345L, 123450L, Long.MAX_VALUE,
045      -1L, -10L, -99L, -100L, -123L, -999L, -10000L, -10001L, -12345L, -123450L, Long.MIN_VALUE };
046  static final int[] I_LENGTHS =
047    { 1, 2, 2, 2, 2, 3, 3, 2, 4, 4, 4, 11, 2, 2, 2, 2, 3, 3, 2, 4, 4, 4, 11 };
048
049  // real constants for testing Numeric code paths
050  static final Double[] D_VALS =
051    { 0.0, 0.00123, 0.0123, 0.123, 1.0, 10.0, 12.345, 99.0, 99.01, 99.0001, 100.0, 100.01,
052      100.1, 1234.0, 1234.5, 9999.0, 9999.000001, 9999.000009, 9999.00001, 9999.00009,
053      9999.000099, 9999.0001, 9999.001, 9999.01, 9999.1, 10000.0, 10001.0, 12345.0, 123450.0,
054      Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NaN, Double.MAX_VALUE,
055      -0.00123, -0.0123, -0.123, -1.0, -10.0, -12.345, -99.0, -99.01, -99.0001, -100.0, -100.01,
056      -100.1, -1234.0, -1234.5, -9999.0, -9999.000001, -9999.000009, -9999.00001, -9999.00009,
057      -9999.000099, -9999.0001, -9999.001, -9999.01, -9999.1, -10000.0, -10001.0, -12345.0,
058      -123450.0 };
059  static final int[] D_LENGTHS =
060    { 1, 4, 4, 4, 2, 2, 4, 2, 3, 4, 2, 4,
061      4, 3, 4, 3, 6, 6, 6, 6,
062      6, 5, 5, 4, 4, 2, 4, 4, 4,
063      1, 1, 1, 11,
064      4, 4, 4, 2, 2, 4, 2, 3, 4, 2, 4,
065      4, 3, 4, 3, 6, 6, 6, 6,
066      6, 5, 5, 4, 4, 2, 4, 4,
067      4 };
068
069  // fill in other gaps in Numeric code paths
070  static final BigDecimal[] BD_VALS =
071    { null, BigDecimal.valueOf(Long.MAX_VALUE), BigDecimal.valueOf(Long.MIN_VALUE),
072      BigDecimal.valueOf(Double.MAX_VALUE), BigDecimal.valueOf(Double.MIN_VALUE),
073      BigDecimal.valueOf(Long.MAX_VALUE).multiply(BigDecimal.valueOf(100)) };
074  static final int[] BD_LENGTHS =
075    { 1, 11, 11, 11, 4, 12 };
076
077  /*
078   * This is the smallest difference between two doubles in D_VALS
079   */
080  static final double MIN_EPSILON = 0.000001;
081
082  /**
083   * Expected lengths of equivalent values should match
084   */
085  @Test
086  public void testVerifyTestIntegrity() {
087    for (int i = 0; i < I_VALS.length; i++) {
088      for (int d = 0; d < D_VALS.length; d++) {
089        if (Math.abs(I_VALS[i] - D_VALS[d]) < MIN_EPSILON) {
090          assertEquals(
091            "Test inconsistency detected: expected lengths for " + I_VALS[i] + " do not match.",
092            I_LENGTHS[i], D_LENGTHS[d]);
093        }
094      }
095    }
096  }
097
098  /**
099   * Tests the variable uint64 encoding.
100   * <p>
101   * Building sqlite4 with -DVARINT_TOOL provides this reference:<br />
102   * <code>$ ./varint_tool 240 2287 67823 16777215 4294967295 1099511627775
103   *   281474976710655 72057594037927935 18446744073709551615<br />
104   * 240 = f0<br />
105   * 2287 = f8ff<br />
106   * 67823 = f9ffff<br />
107   * 16777215 = faffffff<br />
108   * 4294967295 = fbffffffff<br />
109   * 1099511627775 = fcffffffffff<br />
110   * 281474976710655 = fdffffffffffff<br />
111   * 72057594037927935 = feffffffffffffff<br />
112   * 9223372036854775807 = ff7fffffffffffffff (Long.MAX_VAL)<br />
113   * 9223372036854775808 = ff8000000000000000 (Long.MIN_VAL)<br />
114   * 18446744073709551615 = ffffffffffffffffff<br /></code>
115   * </p>
116   */
117  @Test
118  public void testVaruint64Boundaries() {
119    long[] vals = {
120      239L, 240L, 2286L, 2287L, 67822L, 67823L, 16777214L, 16777215L, 4294967294L, 4294967295L,
121      1099511627774L, 1099511627775L, 281474976710654L, 281474976710655L, 72057594037927934L,
122      72057594037927935L, Long.MAX_VALUE - 1, Long.MAX_VALUE, Long.MIN_VALUE + 1,
123      Long.MIN_VALUE, -2L, -1L
124    };
125    int[] lens = { 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9 };
126    assertEquals("Broken test!", vals.length, lens.length);
127
128    /*
129     * assert encoded values match decoded values. encode into target buffer
130     * starting at an offset to detect over/underflow conditions.
131     */
132    for (boolean comp : new boolean[] { true, false }) {
133      for (int i = 0; i < vals.length; i++) {
134        // allocate a buffer 2-bytes larger than necessary and place our range over the center.
135        byte[] a = new byte[lens[i] + 2];
136        PositionedByteRange buf = new SimplePositionedMutableByteRange(a, 1, lens[i]);
137
138        // verify encode
139        assertEquals("Surprising return value.",
140          lens[i], OrderedBytes.putVaruint64(buf, vals[i], comp));
141        assertEquals("Surprising serialized length.", lens[i], buf.getPosition());
142        assertEquals("Buffer underflow.", 0, a[0]);
143        assertEquals("Buffer overflow.", 0, a[a.length - 1]);
144
145        // verify skip
146        buf.setPosition(0);
147        assertEquals("Surprising return value.",
148          lens[i], OrderedBytes.skipVaruint64(buf, comp));
149        assertEquals("Did not skip enough bytes.", lens[i], buf.getPosition());
150
151        // verify decode
152        buf.setPosition(0);
153        assertEquals("Deserialization failed.", vals[i], OrderedBytes.getVaruint64(buf, comp));
154        assertEquals("Did not consume enough bytes.", lens[i], buf.getPosition());
155      }
156    }
157  }
158
159  /**
160   * Test integer encoding. Example input values come from reference wiki
161   * page.
162   */
163  @Test
164  public void testNumericInt() {
165    /*
166     * assert encoded values match decoded values. encode into target buffer
167     * starting at an offset to detect over/underflow conditions.
168     */
169    for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
170      for (int i = 0; i < I_VALS.length; i++) {
171        // allocate a buffer 3-bytes larger than necessary to detect over/underflow
172        byte[] a = new byte[I_LENGTHS[i] + 3];
173        PositionedByteRange buf1 = new SimplePositionedMutableByteRange(a, 1, I_LENGTHS[i] + 1);
174        buf1.setPosition(1);
175
176        // verify encode
177        assertEquals("Surprising return value.",
178          I_LENGTHS[i], OrderedBytes.encodeNumeric(buf1, I_VALS[i], ord));
179        assertEquals("Broken test: serialization did not consume entire buffer.",
180          buf1.getLength(), buf1.getPosition());
181        assertEquals("Surprising serialized length.", I_LENGTHS[i], buf1.getPosition() - 1);
182        assertEquals("Buffer underflow.", 0, a[0]);
183        assertEquals("Buffer underflow.", 0, a[1]);
184        assertEquals("Buffer overflow.", 0, a[a.length - 1]);
185
186        // verify skip
187        buf1.setPosition(1);
188        assertEquals("Surprising return value.", I_LENGTHS[i], OrderedBytes.skip(buf1));
189        assertEquals("Did not skip enough bytes.", I_LENGTHS[i], buf1.getPosition() - 1);
190
191        // verify decode
192        buf1.setPosition(1);
193        assertEquals("Deserialization failed.",
194          I_VALS[i].longValue(), OrderedBytes.decodeNumericAsLong(buf1));
195        assertEquals("Did not consume enough bytes.", I_LENGTHS[i], buf1.getPosition() - 1);
196      }
197    }
198
199    /*
200     * assert natural sort order is preserved by the codec.
201     */
202    for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
203      byte[][] encoded = new byte[I_VALS.length][];
204      PositionedByteRange pbr = new SimplePositionedMutableByteRange();
205      for (int i = 0; i < I_VALS.length; i++) {
206        encoded[i] = new byte[I_LENGTHS[i]];
207        OrderedBytes.encodeNumeric(pbr.set(encoded[i]), I_VALS[i], ord);
208      }
209
210      Arrays.sort(encoded, Bytes.BYTES_COMPARATOR);
211      Long[] sortedVals = Arrays.copyOf(I_VALS, I_VALS.length);
212
213      if (ord == Order.ASCENDING) {
214        Arrays.sort(sortedVals);
215      } else {
216        Arrays.sort(sortedVals, Collections.reverseOrder());
217      }
218
219      for (int i = 0; i < sortedVals.length; i++) {
220        pbr.set(encoded[i]);
221        long decoded = OrderedBytes.decodeNumericAsLong(pbr);
222        assertEquals(
223          String.format(
224            "Encoded representations do not preserve natural order: <%s>, <%s>, %s",
225            sortedVals[i], decoded, ord),
226          sortedVals[i].longValue(), decoded);
227      }
228    }
229  }
230
231  /**
232   * Test real encoding. Example input values come from reference wiki page.
233   */
234  @Test
235  public void testNumericReal() {
236    /*
237     * assert encoded values match decoded values. encode into target buffer
238     * starting at an offset to detect over/underflow conditions.
239     */
240    for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
241      for (int i = 0; i < D_VALS.length; i++) {
242        // allocate a buffer 3-bytes larger than necessary to detect over/underflow
243        byte[] a = new byte[D_LENGTHS[i] + 3];
244        PositionedByteRange buf1 = new SimplePositionedMutableByteRange(a, 1, D_LENGTHS[i] + 1);
245        buf1.setPosition(1);
246
247        // verify encode
248        assertEquals("Surprising return value.",
249          D_LENGTHS[i], OrderedBytes.encodeNumeric(buf1, D_VALS[i], ord));
250        assertEquals("Broken test: serialization did not consume entire buffer.",
251          buf1.getLength(), buf1.getPosition());
252        assertEquals("Surprising serialized length.", D_LENGTHS[i], buf1.getPosition() - 1);
253        assertEquals("Buffer underflow.", 0, a[0]);
254        assertEquals("Buffer underflow.", 0, a[1]);
255        assertEquals("Buffer overflow.", 0, a[a.length - 1]);
256
257        // verify skip
258        buf1.setPosition(1);
259        assertEquals("Surprising return value.", D_LENGTHS[i], OrderedBytes.skip(buf1));
260        assertEquals("Did not skip enough bytes.", D_LENGTHS[i], buf1.getPosition() - 1);
261
262        // verify decode
263        buf1.setPosition(1);
264        assertEquals("Deserialization failed.", D_VALS[i],
265          OrderedBytes.decodeNumericAsDouble(buf1), MIN_EPSILON);
266        assertEquals("Did not consume enough bytes.", D_LENGTHS[i], buf1.getPosition() - 1);
267      }
268    }
269
270    /*
271     * assert natural sort order is preserved by the codec.
272     */
273    for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
274      byte[][] encoded = new byte[D_VALS.length][];
275      PositionedByteRange pbr = new SimplePositionedMutableByteRange();
276      for (int i = 0; i < D_VALS.length; i++) {
277        encoded[i] = new byte[D_LENGTHS[i]];
278        OrderedBytes.encodeNumeric(pbr.set(encoded[i]), D_VALS[i], ord);
279      }
280
281      Arrays.sort(encoded, Bytes.BYTES_COMPARATOR);
282      Double[] sortedVals = Arrays.copyOf(D_VALS, D_VALS.length);
283
284      if (ord == Order.ASCENDING) {
285        Arrays.sort(sortedVals);
286      } else {
287        Arrays.sort(sortedVals, Collections.reverseOrder());
288      }
289
290      for (int i = 0; i < sortedVals.length; i++) {
291        pbr.set(encoded[i]);
292        double decoded = OrderedBytes.decodeNumericAsDouble(pbr);
293        assertEquals(
294          String.format(
295            "Encoded representations do not preserve natural order: <%s>, <%s>, %s",
296            sortedVals[i], decoded, ord), sortedVals[i], decoded, MIN_EPSILON);
297      }
298    }
299  }
300
301  /**
302   * Fill gaps in Numeric encoding testing.
303   */
304  @Test
305  public void testNumericOther() {
306    /*
307     * assert encoded values match decoded values. encode into target buffer
308     * starting at an offset to detect over/underflow conditions.
309     */
310    for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
311      for (int i = 0; i < BD_VALS.length; i++) {
312        // allocate a buffer 3-bytes larger than necessary to detect over/underflow
313        byte[] a = new byte[BD_LENGTHS[i] + 3];
314        PositionedByteRange buf1 = new SimplePositionedMutableByteRange(a, 1, BD_LENGTHS[i] + 1);
315        buf1.setPosition(1);
316
317        // verify encode
318        assertEquals("Surprising return value.",
319          BD_LENGTHS[i], OrderedBytes.encodeNumeric(buf1, BD_VALS[i], ord));
320        assertEquals("Broken test: serialization did not consume entire buffer.",
321          buf1.getLength(), buf1.getPosition());
322        assertEquals("Surprising serialized length.", BD_LENGTHS[i], buf1.getPosition() - 1);
323        assertEquals("Buffer underflow.", 0, a[0]);
324        assertEquals("Buffer underflow.", 0, a[1]);
325        assertEquals("Buffer overflow.", 0, a[a.length - 1]);
326
327        // verify skip
328        buf1.setPosition(1);
329        assertEquals("Surprising return value.", BD_LENGTHS[i], OrderedBytes.skip(buf1));
330        assertEquals("Did not skip enough bytes.", BD_LENGTHS[i], buf1.getPosition() - 1);
331
332        // verify decode
333        buf1.setPosition(1);
334        BigDecimal decoded = OrderedBytes.decodeNumericAsBigDecimal(buf1);
335        if (null == BD_VALS[i]) {
336          assertEquals(BD_VALS[i], decoded);
337        } else {
338          assertEquals("Deserialization failed.", 0, BD_VALS[i].compareTo(decoded));
339        }
340        assertEquals("Did not consume enough bytes.", BD_LENGTHS[i], buf1.getPosition() - 1);
341      }
342    }
343  }
344
345  /**
346   * Verify Real and Int encodings are compatible.
347   */
348  @Test
349  public void testNumericIntRealCompatibility() {
350    for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
351      for (int i = 0; i < I_VALS.length; i++) {
352        // verify primitives
353        PositionedByteRange pbri = new SimplePositionedMutableByteRange(I_LENGTHS[i]);
354        PositionedByteRange pbrr = new SimplePositionedMutableByteRange(I_LENGTHS[i]);
355        OrderedBytes.encodeNumeric(pbri, I_VALS[i], ord);
356        OrderedBytes.encodeNumeric(pbrr, I_VALS[i], ord);
357        assertArrayEquals("Integer and real encodings differ.", pbri.getBytes(), pbrr.getBytes());
358        pbri.setPosition(0);
359        pbrr.setPosition(0);
360        assertEquals((long) I_VALS[i], OrderedBytes.decodeNumericAsLong(pbri));
361        assertEquals((long) I_VALS[i], (long) OrderedBytes.decodeNumericAsDouble(pbrr));
362
363        // verify BigDecimal for Real encoding
364        BigDecimal bd = BigDecimal.valueOf(I_VALS[i]);
365        PositionedByteRange pbrbd = new SimplePositionedMutableByteRange(I_LENGTHS[i]);
366        OrderedBytes.encodeNumeric(pbrbd, bd, ord);
367        assertArrayEquals("Integer and BigDecimal encodings differ.",
368          pbri.getBytes(), pbrbd.getBytes());
369        pbri.setPosition(0);
370        assertEquals("Value not preserved when decoding as Long",
371          0, bd.compareTo(BigDecimal.valueOf(OrderedBytes.decodeNumericAsLong(pbri))));
372      }
373    }
374  }
375
376  /**
377   * Test int8 encoding.
378   */
379  @Test
380  public void testInt8() {
381    Byte[] vals =
382      { Byte.MIN_VALUE, Byte.MIN_VALUE / 2, 0, Byte.MAX_VALUE / 2, Byte.MAX_VALUE };
383
384    /*
385     * assert encoded values match decoded values. encode into target buffer
386     * starting at an offset to detect over/underflow conditions.
387     */
388    for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
389      for (Byte val : vals) {
390        // allocate a buffer 3-bytes larger than necessary to detect over/underflow
391        byte[] a = new byte[2 + 3];
392        PositionedByteRange buf1 = new SimplePositionedMutableByteRange(a, 1, 2 + 1);
393        buf1.setPosition(1);
394
395        // verify encode
396        assertEquals("Surprising return value.", 2, OrderedBytes.encodeInt8(buf1, val, ord));
397        assertEquals("Broken test: serialization did not consume entire buffer.", buf1.getLength(),
398          buf1.getPosition());
399        assertEquals("Surprising serialized length.", 2, buf1.getPosition() - 1);
400        assertEquals("Buffer underflow.", 0, a[0]);
401        assertEquals("Buffer underflow.", 0, a[1]);
402        assertEquals("Buffer overflow.", 0, a[a.length - 1]);
403
404        // verify skip
405        buf1.setPosition(1);
406        assertEquals("Surprising return value.", 2, OrderedBytes.skip(buf1));
407        assertEquals("Did not skip enough bytes.", 2, buf1.getPosition() - 1);
408
409        // verify decode
410        buf1.setPosition(1);
411        assertEquals("Deserialization failed.", val.byteValue(), OrderedBytes.decodeInt8(buf1));
412        assertEquals("Did not consume enough bytes.", 2, buf1.getPosition() - 1);
413      }
414    }
415
416    /*
417     * assert natural sort order is preserved by the codec.
418     */
419    for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
420      byte[][] encoded = new byte[vals.length][2];
421      PositionedByteRange pbr = new SimplePositionedMutableByteRange();
422      for (int i = 0; i < vals.length; i++) {
423        OrderedBytes.encodeInt8(pbr.set(encoded[i]), vals[i], ord);
424      }
425
426      Arrays.sort(encoded, Bytes.BYTES_COMPARATOR);
427      Byte[] sortedVals = Arrays.copyOf(vals, vals.length);
428
429      if (ord == Order.ASCENDING) {
430        Arrays.sort(sortedVals);
431      } else {
432        Arrays.sort(sortedVals, Collections.reverseOrder());
433      }
434
435      for (int i = 0; i < sortedVals.length; i++) {
436        int decoded = OrderedBytes.decodeInt8(pbr.set(encoded[i]));
437        assertEquals(
438          String.format(
439            "Encoded representations do not preserve natural order: <%s>, <%s>, %s",
440            sortedVals[i], decoded, ord),
441            sortedVals[i].byteValue(), decoded);
442      }
443    }
444  }
445
446  /**
447   * Test int16 encoding.
448   */
449  @Test
450  public void testInt16() {
451    Short[] vals =
452      { Short.MIN_VALUE, Short.MIN_VALUE / 2, 0, Short.MAX_VALUE / 2, Short.MAX_VALUE };
453
454    /*
455     * assert encoded values match decoded values. encode into target buffer
456     * starting at an offset to detect over/underflow conditions.
457     */
458    for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
459      for (Short val : vals) {
460        // allocate a buffer 3-bytes larger than necessary to detect over/underflow
461        byte[] a = new byte[3 + 3];
462        PositionedByteRange buf1 = new SimplePositionedMutableByteRange(a, 1, 3 + 1);
463        buf1.setPosition(1);
464
465        // verify encode
466        assertEquals("Surprising return value.", 3, OrderedBytes.encodeInt16(buf1, val, ord));
467        assertEquals("Broken test: serialization did not consume entire buffer.", buf1.getLength(),
468          buf1.getPosition());
469        assertEquals("Surprising serialized length.", 3, buf1.getPosition() - 1);
470        assertEquals("Buffer underflow.", 0, a[0]);
471        assertEquals("Buffer underflow.", 0, a[1]);
472        assertEquals("Buffer overflow.", 0, a[a.length - 1]);
473
474        // verify skip
475        buf1.setPosition(1);
476        assertEquals("Surprising return value.", 3, OrderedBytes.skip(buf1));
477        assertEquals("Did not skip enough bytes.", 3, buf1.getPosition() - 1);
478
479        // verify decode
480        buf1.setPosition(1);
481        assertEquals("Deserialization failed.", val.shortValue(), OrderedBytes.decodeInt16(buf1));
482        assertEquals("Did not consume enough bytes.", 3, buf1.getPosition() - 1);
483      }
484    }
485
486    /*
487     * assert natural sort order is preserved by the codec.
488     */
489    for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
490      byte[][] encoded = new byte[vals.length][3];
491      PositionedByteRange pbr = new SimplePositionedMutableByteRange();
492      for (int i = 0; i < vals.length; i++) {
493        OrderedBytes.encodeInt16(pbr.set(encoded[i]), vals[i], ord);
494      }
495
496      Arrays.sort(encoded, Bytes.BYTES_COMPARATOR);
497      Short[] sortedVals = Arrays.copyOf(vals, vals.length);
498
499      if (ord == Order.ASCENDING) {
500        Arrays.sort(sortedVals);
501      } else {
502        Arrays.sort(sortedVals, Collections.reverseOrder());
503      }
504
505      for (int i = 0; i < sortedVals.length; i++) {
506        int decoded = OrderedBytes.decodeInt16(pbr.set(encoded[i]));
507        assertEquals(
508          String.format(
509            "Encoded representations do not preserve natural order: <%s>, <%s>, %s",
510            sortedVals[i], decoded, ord),
511            sortedVals[i].shortValue(), decoded);
512      }
513    }
514  }
515
516  /**
517   * Test int32 encoding.
518   */
519  @Test
520  public void testInt32() {
521    Integer[] vals =
522      { Integer.MIN_VALUE, Integer.MIN_VALUE / 2, 0, Integer.MAX_VALUE / 2, Integer.MAX_VALUE };
523
524    /*
525     * assert encoded values match decoded values. encode into target buffer
526     * starting at an offset to detect over/underflow conditions.
527     */
528    for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
529      for (Integer val : vals) {
530        // allocate a buffer 3-bytes larger than necessary to detect over/underflow
531        byte[] a = new byte[5 + 3];
532        PositionedByteRange buf1 = new SimplePositionedMutableByteRange(a, 1, 5 + 1);
533        buf1.setPosition(1);
534
535        // verify encode
536        assertEquals("Surprising return value.", 5, OrderedBytes.encodeInt32(buf1, val, ord));
537        assertEquals("Broken test: serialization did not consume entire buffer.", buf1.getLength(),
538          buf1.getPosition());
539        assertEquals("Surprising serialized length.", 5, buf1.getPosition() - 1);
540        assertEquals("Buffer underflow.", 0, a[0]);
541        assertEquals("Buffer underflow.", 0, a[1]);
542        assertEquals("Buffer overflow.", 0, a[a.length - 1]);
543
544        // verify skip
545        buf1.setPosition(1);
546        assertEquals("Surprising return value.", 5, OrderedBytes.skip(buf1));
547        assertEquals("Did not skip enough bytes.", 5, buf1.getPosition() - 1);
548
549        // verify decode
550        buf1.setPosition(1);
551        assertEquals("Deserialization failed.", val.intValue(), OrderedBytes.decodeInt32(buf1));
552        assertEquals("Did not consume enough bytes.", 5, buf1.getPosition() - 1);
553      }
554    }
555
556    /*
557     * assert natural sort order is preserved by the codec.
558     */
559    for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
560      byte[][] encoded = new byte[vals.length][5];
561      PositionedByteRange pbr = new SimplePositionedMutableByteRange();
562      for (int i = 0; i < vals.length; i++) {
563        OrderedBytes.encodeInt32(pbr.set(encoded[i]), vals[i], ord);
564      }
565
566      Arrays.sort(encoded, Bytes.BYTES_COMPARATOR);
567      Integer[] sortedVals = Arrays.copyOf(vals, vals.length);
568
569      if (ord == Order.ASCENDING) {
570        Arrays.sort(sortedVals);
571      } else {
572        Arrays.sort(sortedVals, Collections.reverseOrder());
573      }
574
575      for (int i = 0; i < sortedVals.length; i++) {
576        int decoded = OrderedBytes.decodeInt32(pbr.set(encoded[i]));
577        assertEquals(
578          String.format(
579            "Encoded representations do not preserve natural order: <%s>, <%s>, %s",
580            sortedVals[i], decoded, ord),
581            sortedVals[i].intValue(), decoded);
582      }
583    }
584  }
585
586  /**
587   * Test int64 encoding.
588   */
589  @Test
590  public void testInt64() {
591    Long[] vals = { Long.MIN_VALUE, Long.MIN_VALUE / 2, 0L, Long.MAX_VALUE / 2, Long.MAX_VALUE };
592
593    /*
594     * assert encoded values match decoded values. encode into target buffer
595     * starting at an offset to detect over/underflow conditions.
596     */
597    for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
598      for (Long val : vals) {
599        // allocate a buffer 3-bytes larger than necessary to detect over/underflow
600        byte[] a = new byte[9 + 3];
601        PositionedByteRange buf1 = new SimplePositionedMutableByteRange(a, 1, 9 + 1);
602        buf1.setPosition(1);
603
604        // verify encode
605        assertEquals("Surprising return value.", 9, OrderedBytes.encodeInt64(buf1, val, ord));
606        assertEquals("Broken test: serialization did not consume entire buffer.", buf1.getLength(),
607          buf1.getPosition());
608        assertEquals("Surprising serialized length.", 9, buf1.getPosition() - 1);
609        assertEquals("Buffer underflow.", 0, a[0]);
610        assertEquals("Buffer underflow.", 0, a[1]);
611        assertEquals("Buffer overflow.", 0, a[a.length - 1]);
612
613        // verify skip
614        buf1.setPosition(1);
615        assertEquals("Surprising return value.", 9, OrderedBytes.skip(buf1));
616        assertEquals("Did not skip enough bytes.", 9, buf1.getPosition() - 1);
617
618        // verify decode
619        buf1.setPosition(1);
620        assertEquals("Deserialization failed.", val.longValue(), OrderedBytes.decodeInt64(buf1));
621        assertEquals("Did not consume enough bytes.", 9, buf1.getPosition() - 1);
622      }
623    }
624
625    /*
626     * assert natural sort order is preserved by the codec.
627     */
628    for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
629      byte[][] encoded = new byte[vals.length][9];
630      PositionedByteRange pbr = new SimplePositionedMutableByteRange();
631      for (int i = 0; i < vals.length; i++) {
632        OrderedBytes.encodeInt64(pbr.set(encoded[i]), vals[i], ord);
633      }
634
635      Arrays.sort(encoded, Bytes.BYTES_COMPARATOR);
636      Long[] sortedVals = Arrays.copyOf(vals, vals.length);
637
638      if (ord == Order.ASCENDING) {
639        Arrays.sort(sortedVals);
640      } else {
641        Arrays.sort(sortedVals, Collections.reverseOrder());
642      }
643
644      for (int i = 0; i < sortedVals.length; i++) {
645        long decoded = OrderedBytes.decodeInt64(pbr.set(encoded[i]));
646        assertEquals(
647          String.format(
648            "Encoded representations do not preserve natural order: <%s>, <%s>, %s",
649            sortedVals[i], decoded, ord),
650            sortedVals[i].longValue(), decoded);
651      }
652    }
653  }
654
655  /**
656   * Test float32 encoding.
657   */
658  @Test
659  public void testFloat32() {
660    Float[] vals =
661      { Float.MIN_VALUE, Float.MIN_VALUE + 1.0f, 0.0f, Float.MAX_VALUE / 2.0f, Float.MAX_VALUE };
662
663    /*
664     * assert encoded values match decoded values. encode into target buffer
665     * starting at an offset to detect over/underflow conditions.
666     */
667    for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
668      for (Float val : vals) {
669        // allocate a buffer 3-bytes larger than necessary to detect over/underflow
670        byte[] a = new byte[5 + 3];
671        PositionedByteRange buf1 = new SimplePositionedMutableByteRange(a, 1, 5 + 1);
672        buf1.setPosition(1);
673
674        // verify encode
675        assertEquals("Surprising return value.", 5, OrderedBytes.encodeFloat32(buf1, val, ord));
676        assertEquals("Broken test: serialization did not consume entire buffer.", buf1.getLength(),
677          buf1.getPosition());
678        assertEquals("Surprising serialized length.", 5, buf1.getPosition() - 1);
679        assertEquals("Buffer underflow.", 0, a[0]);
680        assertEquals("Buffer underflow.", 0, a[1]);
681        assertEquals("Buffer overflow.", 0, a[a.length - 1]);
682
683        // verify skip
684        buf1.setPosition(1);
685        assertEquals("Surprising return value.", 5, OrderedBytes.skip(buf1));
686        assertEquals("Did not skip enough bytes.", 5, buf1.getPosition() - 1);
687
688        // verify decode
689        buf1.setPosition(1);
690        assertEquals("Deserialization failed.", Float.floatToIntBits(val),
691          Float.floatToIntBits(OrderedBytes.decodeFloat32(buf1)));
692        assertEquals("Did not consume enough bytes.", 5, buf1.getPosition() - 1);
693      }
694    }
695
696    /*
697     * assert natural sort order is preserved by the codec.
698     */
699    for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
700      byte[][] encoded = new byte[vals.length][5];
701      PositionedByteRange pbr = new SimplePositionedMutableByteRange();
702      for (int i = 0; i < vals.length; i++) {
703        OrderedBytes.encodeFloat32(pbr.set(encoded[i]), vals[i], ord);
704      }
705
706      Arrays.sort(encoded, Bytes.BYTES_COMPARATOR);
707      Float[] sortedVals = Arrays.copyOf(vals, vals.length);
708
709      if (ord == Order.ASCENDING) {
710        Arrays.sort(sortedVals);
711      } else {
712        Arrays.sort(sortedVals, Collections.reverseOrder());
713      }
714
715      for (int i = 0; i < sortedVals.length; i++) {
716        float decoded = OrderedBytes.decodeFloat32(pbr.set(encoded[i]));
717        assertEquals(
718          String.format(
719            "Encoded representations do not preserve natural order: <%s>, <%s>, %s",
720            sortedVals[i], decoded, ord),
721            Float.floatToIntBits(sortedVals[i]),
722            Float.floatToIntBits(decoded));
723      }
724    }
725  }
726
727  /**
728   * Test float64 encoding.
729   */
730  @Test
731  public void testFloat64() {
732    Double[] vals =
733      { Double.MIN_VALUE, Double.MIN_VALUE + 1.0, 0.0, Double.MAX_VALUE / 2.0, Double.MAX_VALUE };
734
735    /*
736     * assert encoded values match decoded values. encode into target buffer
737     * starting at an offset to detect over/underflow conditions.
738     */
739    for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
740      for (Double val : vals) {
741        // allocate a buffer 3-bytes larger than necessary to detect over/underflow
742        byte[] a = new byte[9 + 3];
743        PositionedByteRange buf1 = new SimplePositionedMutableByteRange(a, 1, 9 + 1);
744        buf1.setPosition(1);
745
746        // verify encode
747        assertEquals("Surprising return value.", 9, OrderedBytes.encodeFloat64(buf1, val, ord));
748        assertEquals("Broken test: serialization did not consume entire buffer.", buf1.getLength(),
749          buf1.getPosition());
750        assertEquals("Surprising serialized length.", 9, buf1.getPosition() - 1);
751        assertEquals("Buffer underflow.", 0, a[0]);
752        assertEquals("Buffer underflow.", 0, a[1]);
753        assertEquals("Buffer overflow.", 0, a[a.length - 1]);
754
755        // verify skip
756        buf1.setPosition(1);
757        assertEquals("Surprising return value.", 9, OrderedBytes.skip(buf1));
758        assertEquals("Did not skip enough bytes.", 9, buf1.getPosition() - 1);
759
760        // verify decode
761        buf1.setPosition(1);
762        assertEquals("Deserialization failed.", Double.doubleToLongBits(val),
763          Double.doubleToLongBits(OrderedBytes.decodeFloat64(buf1)));
764        assertEquals("Did not consume enough bytes.", 9, buf1.getPosition() - 1);
765      }
766    }
767
768    /*
769     * assert natural sort order is preserved by the codec.
770     */
771    for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
772      byte[][] encoded = new byte[vals.length][9];
773      PositionedByteRange pbr = new SimplePositionedMutableByteRange();
774      for (int i = 0; i < vals.length; i++) {
775        OrderedBytes.encodeFloat64(pbr.set(encoded[i]), vals[i], ord);
776      }
777
778      Arrays.sort(encoded, Bytes.BYTES_COMPARATOR);
779      Double[] sortedVals = Arrays.copyOf(vals, vals.length);
780
781      if (ord == Order.ASCENDING) {
782        Arrays.sort(sortedVals);
783      } else {
784        Arrays.sort(sortedVals, Collections.reverseOrder());
785      }
786
787      for (int i = 0; i < sortedVals.length; i++) {
788        double decoded = OrderedBytes.decodeFloat64(pbr.set(encoded[i]));
789        assertEquals(
790          String.format(
791            "Encoded representations do not preserve natural order: <%s>, <%s>, %s",
792            sortedVals[i], decoded, ord),
793            Double.doubleToLongBits(sortedVals[i]),
794            Double.doubleToLongBits(decoded));
795      }
796    }
797  }
798
799  /**
800   * Test string encoding.
801   */
802  @Test
803  public void testString() {
804    String[] vals = { "foo", "baaaar", "bazz" };
805    int[] expectedLengths = { 5, 8, 6 };
806
807    /*
808     * assert encoded values match decoded values. encode into target buffer
809     * starting at an offset to detect over/underflow conditions.
810     */
811    for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
812      for (int i = 0; i < vals.length; i++) {
813        // allocate a buffer 3-bytes larger than necessary to detect over/underflow
814        byte[] a = new byte[expectedLengths[i] + 3];
815        PositionedByteRange buf1 = new SimplePositionedMutableByteRange(a, 1,
816            expectedLengths[i] + 1);
817        buf1.setPosition(1);
818
819        // verify encode
820        assertEquals("Surprising return value.",
821          expectedLengths[i], OrderedBytes.encodeString(buf1, vals[i], ord));
822        assertEquals("Broken test: serialization did not consume entire buffer.",
823          buf1.getLength(), buf1.getPosition());
824        assertEquals("Surprising serialized length.", expectedLengths[i], buf1.getPosition() - 1);
825        assertEquals("Buffer underflow.", 0, a[0]);
826        assertEquals("Buffer underflow.", 0, a[1]);
827        assertEquals("Buffer overflow.", 0, a[a.length - 1]);
828
829        // verify skip
830        buf1.setPosition(1);
831        assertEquals("Surprising return value.", expectedLengths[i], OrderedBytes.skip(buf1));
832        assertEquals("Did not skip enough bytes.", expectedLengths[i], buf1.getPosition() - 1);
833
834        // verify decode
835        buf1.setPosition(1);
836        assertEquals("Deserialization failed.", vals[i], OrderedBytes.decodeString(buf1));
837        assertEquals("Did not consume enough bytes.", expectedLengths[i], buf1.getPosition() - 1);
838      }
839    }
840
841    /*
842     * assert natural sort order is preserved by the codec.
843     */
844    for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
845      byte[][] encoded = new byte[vals.length][];
846      PositionedByteRange pbr = new SimplePositionedMutableByteRange();
847      for (int i = 0; i < vals.length; i++) {
848        encoded[i] = new byte[expectedLengths[i]];
849        OrderedBytes.encodeString(pbr.set(encoded[i]), vals[i], ord);
850      }
851
852      Arrays.sort(encoded, Bytes.BYTES_COMPARATOR);
853      String[] sortedVals = Arrays.copyOf(vals, vals.length);
854
855      if (ord == Order.ASCENDING) {
856        Arrays.sort(sortedVals);
857      } else {
858        Arrays.sort(sortedVals, Collections.reverseOrder());
859      }
860
861      for (int i = 0; i < sortedVals.length; i++) {
862        pbr.set(encoded[i]);
863        String decoded = OrderedBytes.decodeString(pbr);
864        assertEquals(
865          String.format(
866            "Encoded representations do not preserve natural order: <%s>, <%s>, %s",
867            sortedVals[i], decoded, ord),
868          sortedVals[i], decoded);
869      }
870    }
871  }
872
873  @Test(expected = IllegalArgumentException.class)
874  public void testStringNoNullChars() {
875    PositionedByteRange buff = new SimplePositionedMutableByteRange(3);
876    OrderedBytes.encodeString(buff, "\u0000", Order.ASCENDING);
877  }
878
879  /**
880   * Test length estimation algorithms for BlobVar encoding. Does not cover
881   * 0-length input case properly.
882   */
883  @Test
884  public void testBlobVarLencodedLength() {
885    int[][] values = {
886        /* decoded length, encoded length
887         * ceil((n bytes * 8 bits/input byte) / 7 bits/encoded byte) + 1 header
888         */
889        { 1, 3 }, { 2, 4 }, { 3, 5 }, { 4, 6 },
890        { 5, 7 }, { 6, 8 }, { 7, 9 }, { 8, 11 }
891      };
892
893    for (int[] pair : values) {
894      assertEquals(pair[1], OrderedBytes.blobVarEncodedLength(pair[0]));
895      assertEquals(pair[0], OrderedBytes.blobVarDecodedLength(pair[1]));
896    }
897  }
898
899  /**
900   * Test BlobVar encoding.
901   */
902  @Test
903  public void testBlobVar() {
904    byte[][] vals =
905      { Bytes.toBytes(""),
906        Bytes.toBytes("foo"),
907        Bytes.toBytes("foobarbazbub"),
908        { (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
909          (byte) 0xaa, /* 7 bytes of alternating bits; testing around HBASE-9893 */ },
910        { (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
911          (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa },
912        { (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
913          (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
914          (byte) 0xaa, (byte) 0xaa, /* 14 bytes of alternating bits; testing around HBASE-9893 */ },
915        { (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55,
916          (byte) 0x55, /* 7 bytes of alternating bits; testing around HBASE-9893 */ },
917        { (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55,
918          (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55 },
919        { (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55,
920          (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55,
921          (byte) 0x55, (byte) 0x55, /* 14 bytes of alternating bits; testing around HBASE-9893 */ },
922        Bytes.toBytes("1"),
923        Bytes.toBytes("22"),
924        Bytes.toBytes("333"),
925        Bytes.toBytes("4444"),
926        Bytes.toBytes("55555"),
927        Bytes.toBytes("666666"),
928        Bytes.toBytes("7777777"),
929        Bytes.toBytes("88888888")
930      };
931
932    /*
933     * assert encoded values match decoded values. encode into target buffer
934     * starting at an offset to detect over/underflow conditions.
935     */
936    for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
937      for (byte[] val : vals) {
938        // allocate a buffer 3-bytes larger than necessary to detect over/underflow
939        int expectedLen = OrderedBytes.blobVarEncodedLength(val.length);
940        byte[] a = new byte[expectedLen + 3];
941        PositionedByteRange buf1 = new SimplePositionedMutableByteRange(a, 1, expectedLen + 1);
942        buf1.setPosition(1);
943
944        // verify encode
945        assertEquals("Surprising return value.",
946          expectedLen, OrderedBytes.encodeBlobVar(buf1, val, ord));
947        assertEquals("Broken test: serialization did not consume entire buffer.",
948          buf1.getLength(), buf1.getPosition());
949        assertEquals("Surprising serialized length.", expectedLen, buf1.getPosition() - 1);
950        assertEquals("Buffer underflow.", 0, a[0]);
951        assertEquals("Buffer underflow.", 0, a[1]);
952        assertEquals("Buffer overflow.", 0, a[a.length - 1]);
953
954        // verify skip
955        buf1.setPosition(1);
956        assertEquals("Surprising return value.", expectedLen, OrderedBytes.skip(buf1));
957        assertEquals("Did not skip enough bytes.", expectedLen, buf1.getPosition() - 1);
958
959        // verify decode
960        buf1.setPosition(1);
961        assertArrayEquals("Deserialization failed.", val, OrderedBytes.decodeBlobVar(buf1));
962        assertEquals("Did not consume enough bytes.", expectedLen, buf1.getPosition() - 1);
963      }
964    }
965
966    /*
967     * assert natural sort order is preserved by the codec.
968     */
969    for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
970      byte[][] encoded = new byte[vals.length][];
971      PositionedByteRange pbr = new SimplePositionedMutableByteRange();
972      for (int i = 0; i < vals.length; i++) {
973        encoded[i] = new byte[OrderedBytes.blobVarEncodedLength(vals[i].length)];
974        OrderedBytes.encodeBlobVar(pbr.set(encoded[i]), vals[i], ord);
975      }
976
977      Arrays.sort(encoded, Bytes.BYTES_COMPARATOR);
978      byte[][] sortedVals = Arrays.copyOf(vals, vals.length);
979
980      if (ord == Order.ASCENDING) {
981        Arrays.sort(sortedVals, Bytes.BYTES_COMPARATOR);
982      } else {
983        Arrays.sort(sortedVals, Collections.reverseOrder(Bytes.BYTES_COMPARATOR));
984      }
985
986      for (int i = 0; i < sortedVals.length; i++) {
987        pbr.set(encoded[i]);
988        byte[] decoded = OrderedBytes.decodeBlobVar(pbr);
989        assertArrayEquals(
990          String.format(
991            "Encoded representations do not preserve natural order: <%s>, <%s>, %s",
992            Arrays.toString(sortedVals[i]), Arrays.toString(decoded), ord),
993          sortedVals[i], decoded);
994      }
995    }
996  }
997
998  /**
999   * Test BlobCopy encoding.
1000   */
1001  @Test
1002  public void testBlobCopy() {
1003    byte[][] vals =
1004      { Bytes.toBytes(""),
1005        Bytes.toBytes("foo"),
1006        Bytes.toBytes("foobarbazbub"),
1007        { (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
1008          (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa },
1009        { (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55,
1010          (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55 },
1011      };
1012
1013    /*
1014     * assert encoded values match decoded values. encode into target buffer
1015     * starting at an offset to detect over/underflow conditions.
1016     */
1017    for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
1018      for (byte[] val : vals) {
1019        // allocate a buffer 3-bytes larger than necessary to detect over/underflow
1020        int expectedLen = val.length + (Order.ASCENDING == ord ? 1 : 2);
1021        byte[] a = new byte[expectedLen + 3];
1022        PositionedByteRange buf1 = new SimplePositionedMutableByteRange(a, 1, expectedLen + 1);
1023        buf1.setPosition(1);
1024
1025        // verify encode
1026        assertEquals("Surprising return value.",
1027          expectedLen, OrderedBytes.encodeBlobCopy(buf1, val, ord));
1028        assertEquals("Broken test: serialization did not consume entire buffer.",
1029          buf1.getLength(), buf1.getPosition());
1030        assertEquals("Surprising serialized length.", expectedLen, buf1.getPosition() - 1);
1031        assertEquals("Buffer underflow.", 0, a[0]);
1032        assertEquals("Buffer underflow.", 0, a[1]);
1033        assertEquals("Buffer overflow.", 0, a[a.length - 1]);
1034
1035        // verify skip
1036        buf1.setPosition(1);
1037        assertEquals("Surprising return value.", expectedLen, OrderedBytes.skip(buf1));
1038        assertEquals("Did not skip enough bytes.", expectedLen, buf1.getPosition() - 1);
1039
1040        // verify decode
1041        buf1.setPosition(1);
1042        assertArrayEquals("Deserialization failed.", val, OrderedBytes.decodeBlobCopy(buf1));
1043        assertEquals("Did not consume enough bytes.", expectedLen, buf1.getPosition() - 1);
1044      }
1045    }
1046
1047    /*
1048     * assert natural sort order is preserved by the codec.
1049     */
1050    for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
1051      byte[][] encoded = new byte[vals.length][];
1052      PositionedByteRange pbr = new SimplePositionedMutableByteRange();
1053      for (int i = 0; i < vals.length; i++) {
1054        encoded[i] = new byte[vals[i].length + (Order.ASCENDING == ord ? 1 : 2)];
1055        OrderedBytes.encodeBlobCopy(pbr.set(encoded[i]), vals[i], ord);
1056      }
1057
1058      Arrays.sort(encoded, Bytes.BYTES_COMPARATOR);
1059      byte[][] sortedVals = Arrays.copyOf(vals, vals.length);
1060
1061      if (ord == Order.ASCENDING) {
1062        Arrays.sort(sortedVals, Bytes.BYTES_COMPARATOR);
1063      } else {
1064        Arrays.sort(sortedVals, Collections.reverseOrder(Bytes.BYTES_COMPARATOR));
1065      }
1066
1067      for (int i = 0; i < sortedVals.length; i++) {
1068        pbr.set(encoded[i]);
1069        byte[] decoded = OrderedBytes.decodeBlobCopy(pbr);
1070        assertArrayEquals(
1071          String.format(
1072            "Encoded representations do not preserve natural order: <%s>, <%s>, %s",
1073            Arrays.toString(sortedVals[i]), Arrays.toString(decoded), ord),
1074          sortedVals[i], decoded);
1075      }
1076    }
1077
1078    /*
1079     * assert byte[] segments are serialized correctly.
1080     */
1081    for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
1082      byte[] a = new byte[3 + (Order.ASCENDING == ord ? 1 : 2) + 2];
1083      PositionedByteRange buf =
1084          new SimplePositionedMutableByteRange(a, 1, 3 + (Order.ASCENDING == ord ? 1 : 2));
1085      OrderedBytes.encodeBlobCopy(buf, Bytes.toBytes("foobarbaz"), 3, 3, ord);
1086      buf.setPosition(0);
1087      assertArrayEquals(Bytes.toBytes("bar"), OrderedBytes.decodeBlobCopy(buf));
1088    }
1089  }
1090
1091  /**
1092   * Assert invalid input byte[] are rejected by BlobCopy
1093   */
1094  @Test(expected = IllegalArgumentException.class)
1095  public void testBlobCopyNoZeroBytes() {
1096    byte[] val = { 0x01, 0x02, 0x00, 0x03 };
1097    // TODO: implementation detail leaked here.
1098    byte[] ascExpected = { 0x38, 0x01, 0x02, 0x00, 0x03 };
1099    PositionedByteRange buf = new SimplePositionedMutableByteRange(val.length + 1);
1100    OrderedBytes.encodeBlobCopy(buf, val, Order.ASCENDING);
1101    assertArrayEquals(ascExpected, buf.getBytes());
1102    buf.set(val.length + 2);
1103    OrderedBytes.encodeBlobCopy(buf, val, Order.DESCENDING);
1104    fail("test should never get here.");
1105  }
1106
1107  /**
1108   * Test generic skip logic
1109   */
1110  @Test
1111  public void testSkip() {
1112    BigDecimal longMax = BigDecimal.valueOf(Long.MAX_VALUE);
1113    double negInf = Double.NEGATIVE_INFINITY;
1114    BigDecimal negLarge = longMax.multiply(longMax).negate();
1115    BigDecimal negMed = new BigDecimal("-10.0");
1116    BigDecimal negSmall = new BigDecimal("-0.0010");
1117    long zero = 0L;
1118    BigDecimal posSmall = negSmall.negate();
1119    BigDecimal posMed = negMed.negate();
1120    BigDecimal posLarge = negLarge.negate();
1121    double posInf = Double.POSITIVE_INFINITY;
1122    double nan = Double.NaN;
1123    byte int8 = 100;
1124    short int16 = 100;
1125    int int32 = 100;
1126    long int64 = 100L;
1127    float float32 = 100.0f;
1128    double float64 = 100.0d;
1129    String text = "hello world.";
1130    byte[] blobVar = Bytes.toBytes("foo");
1131    byte[] blobCopy = Bytes.toBytes("bar");
1132
1133    for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
1134      PositionedByteRange buff = new SimplePositionedMutableByteRange(30);
1135      int o;
1136      o = OrderedBytes.encodeNull(buff, ord);
1137      buff.setPosition(0);
1138      assertEquals(o, OrderedBytes.skip(buff));
1139
1140      buff.setPosition(0);
1141      o = OrderedBytes.encodeNumeric(buff, negInf, ord);
1142      buff.setPosition(0);
1143      assertEquals(o, OrderedBytes.skip(buff));
1144
1145      buff.setPosition(0);
1146      o = OrderedBytes.encodeNumeric(buff, negLarge, ord);
1147      buff.setPosition(0);
1148      assertEquals(o, OrderedBytes.skip(buff));
1149
1150      buff.setPosition(0);
1151      o = OrderedBytes.encodeNumeric(buff, negMed, ord);
1152      buff.setPosition(0);
1153      assertEquals(o, OrderedBytes.skip(buff));
1154
1155      buff.setPosition(0);
1156      o = OrderedBytes.encodeNumeric(buff, negSmall, ord);
1157      buff.setPosition(0);
1158      assertEquals(o, OrderedBytes.skip(buff));
1159
1160      buff.setPosition(0);
1161      o = OrderedBytes.encodeNumeric(buff, zero, ord);
1162      buff.setPosition(0);
1163      assertEquals(o, OrderedBytes.skip(buff));
1164
1165      buff.setPosition(0);
1166      o = OrderedBytes.encodeNumeric(buff, posSmall, ord);
1167      buff.setPosition(0);
1168      assertEquals(o, OrderedBytes.skip(buff));
1169
1170      buff.setPosition(0);
1171      o = OrderedBytes.encodeNumeric(buff, posMed, ord);
1172      buff.setPosition(0);
1173      assertEquals(o, OrderedBytes.skip(buff));
1174
1175      buff.setPosition(0);
1176      o = OrderedBytes.encodeNumeric(buff, posLarge, ord);
1177      buff.setPosition(0);
1178      assertEquals(o, OrderedBytes.skip(buff));
1179
1180      buff.setPosition(0);
1181      o = OrderedBytes.encodeNumeric(buff, posInf, ord);
1182      buff.setPosition(0);
1183      assertEquals(o, OrderedBytes.skip(buff));
1184
1185      buff.setPosition(0);
1186      o = OrderedBytes.encodeNumeric(buff, nan, ord);
1187      buff.setPosition(0);
1188      assertEquals(o, OrderedBytes.skip(buff));
1189
1190      buff.setPosition(0);
1191      o = OrderedBytes.encodeInt8(buff, int8, ord);
1192      buff.setPosition(0);
1193      assertEquals(o, OrderedBytes.skip(buff));
1194
1195      buff.setPosition(0);
1196      o = OrderedBytes.encodeInt16(buff, int16, ord);
1197      buff.setPosition(0);
1198      assertEquals(o, OrderedBytes.skip(buff));
1199
1200      buff.setPosition(0);
1201      o = OrderedBytes.encodeInt32(buff, int32, ord);
1202      buff.setPosition(0);
1203      assertEquals(o, OrderedBytes.skip(buff));
1204
1205      buff.setPosition(0);
1206      o = OrderedBytes.encodeInt64(buff, int64, ord);
1207      buff.setPosition(0);
1208      assertEquals(o, OrderedBytes.skip(buff));
1209
1210      buff.setPosition(0);
1211      o = OrderedBytes.encodeFloat32(buff, float32, ord);
1212      buff.setPosition(0);
1213      assertEquals(o, OrderedBytes.skip(buff));
1214
1215      buff.setPosition(0);
1216      o = OrderedBytes.encodeFloat64(buff, float64, ord);
1217      buff.setPosition(0);
1218      assertEquals(o, OrderedBytes.skip(buff));
1219
1220      buff.setPosition(0);
1221      o = OrderedBytes.encodeString(buff, text, ord);
1222      buff.setPosition(0);
1223      assertEquals(o, OrderedBytes.skip(buff));
1224
1225      buff.setPosition(0);
1226      o = OrderedBytes.encodeBlobVar(buff, blobVar, ord);
1227      buff.setPosition(0);
1228      assertEquals(o, OrderedBytes.skip(buff));
1229
1230      // blobCopy is special in that it runs to the end of the target buffer.
1231      buff.set(blobCopy.length + (Order.ASCENDING == ord ? 1 : 2));
1232      o = OrderedBytes.encodeBlobCopy(buff, blobCopy, ord);
1233      buff.setPosition(0);
1234      assertEquals(o, OrderedBytes.skip(buff));
1235    }
1236  }
1237
1238  /**
1239   * Test encoded value check
1240   */
1241  @Test
1242  public void testEncodedValueCheck() {
1243    BigDecimal longMax = BigDecimal.valueOf(Long.MAX_VALUE);
1244    double negInf = Double.NEGATIVE_INFINITY;
1245    BigDecimal negLarge = longMax.multiply(longMax).negate();
1246    BigDecimal negMed = new BigDecimal("-10.0");
1247    BigDecimal negSmall = new BigDecimal("-0.0010");
1248    long zero = 0L;
1249    BigDecimal posSmall = negSmall.negate();
1250    BigDecimal posMed = negMed.negate();
1251    BigDecimal posLarge = negLarge.negate();
1252    double posInf = Double.POSITIVE_INFINITY;
1253    double nan = Double.NaN;
1254    byte int8 = 100;
1255    short int16 = 100;
1256    int int32 = 100;
1257    long int64 = 100L;
1258    float float32 = 100.0f;
1259    double float64 = 100.0d;
1260    String text = "hello world.";
1261    byte[] blobVar = Bytes.toBytes("foo");
1262
1263    int cnt = 0;
1264    PositionedByteRange buff = new SimplePositionedMutableByteRange(1024);
1265    for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
1266      int o;
1267      o = OrderedBytes.encodeNull(buff, ord); cnt++;
1268      o = OrderedBytes.encodeNumeric(buff, negInf, ord); cnt++;
1269      o = OrderedBytes.encodeNumeric(buff, negLarge, ord); cnt++;
1270      o = OrderedBytes.encodeNumeric(buff, negMed, ord); cnt++;
1271      o = OrderedBytes.encodeNumeric(buff, negSmall, ord); cnt++;
1272      o = OrderedBytes.encodeNumeric(buff, zero, ord); cnt++;
1273      o = OrderedBytes.encodeNumeric(buff, posSmall, ord); cnt++;
1274      o = OrderedBytes.encodeNumeric(buff, posMed, ord); cnt++;
1275      o = OrderedBytes.encodeNumeric(buff, posLarge, ord); cnt++;
1276      o = OrderedBytes.encodeNumeric(buff, posInf, ord); cnt++;
1277      o = OrderedBytes.encodeNumeric(buff, nan, ord); cnt++;
1278      o = OrderedBytes.encodeInt8(buff, int8, ord); cnt++;
1279      o = OrderedBytes.encodeInt16(buff, int16, ord); cnt++;
1280      o = OrderedBytes.encodeInt32(buff, int32, ord); cnt++;
1281      o = OrderedBytes.encodeInt64(buff, int64, ord); cnt++;
1282      o = OrderedBytes.encodeFloat32(buff, float32, ord); cnt++;
1283      o = OrderedBytes.encodeFloat64(buff, float64, ord); cnt++;
1284      o = OrderedBytes.encodeString(buff, text, ord); cnt++;
1285      o = OrderedBytes.encodeBlobVar(buff, blobVar, ord); cnt++;
1286    }
1287
1288    buff.setPosition(0);
1289    assertEquals(OrderedBytes.length(buff), cnt);
1290    for (int i = 0; i < cnt; i++) {
1291      assertTrue(OrderedBytes.isEncodedValue(buff));
1292      OrderedBytes.skip(buff);
1293    }
1294  }
1295}