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