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