View Javadoc

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