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.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 2-bytes larger than necessary and place our range over the center.
164         byte[] a = new byte[I_LENGTHS[i] + 2];
165         PositionedByteRange buf1 = new SimplePositionedByteRange(a, 1, I_LENGTHS[i]);
166 
167         // verify encode
168         assertEquals("Surprising return value.",
169           I_LENGTHS[i], OrderedBytes.encodeNumeric(buf1, I_VALS[i], ord));
170         assertEquals("Surprising serialized length.", I_LENGTHS[i], buf1.getPosition());
171         assertEquals("Buffer underflow.", 0, a[0]);
172         assertEquals("Buffer overflow.", 0, a[a.length - 1]);
173 
174         // verify skip
175         buf1.setPosition(0);
176         assertEquals("Surprising return value.", I_LENGTHS[i], OrderedBytes.skip(buf1));
177         assertEquals("Did not skip enough bytes.", I_LENGTHS[i], buf1.getPosition());
178 
179         // verify decode
180         buf1.setPosition(0);
181         assertEquals("Deserialization failed.",
182           I_VALS[i].longValue(), OrderedBytes.decodeNumericAsLong(buf1));
183         assertEquals("Did not consume enough bytes.", I_LENGTHS[i], buf1.getPosition());
184       }
185     }
186 
187     /*
188      * assert natural sort order is preserved by the codec.
189      */
190     for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
191       byte[][] encoded = new byte[I_VALS.length][];
192       PositionedByteRange pbr = new SimplePositionedByteRange();
193       for (int i = 0; i < I_VALS.length; i++) {
194         encoded[i] = new byte[I_LENGTHS[i]];
195         OrderedBytes.encodeNumeric(pbr.set(encoded[i]), I_VALS[i], ord);
196       }
197 
198       Arrays.sort(encoded, Bytes.BYTES_COMPARATOR);
199       Long[] sortedVals = Arrays.copyOf(I_VALS, I_VALS.length);
200       if (ord == Order.ASCENDING) Arrays.sort(sortedVals);
201       else Arrays.sort(sortedVals, Collections.reverseOrder());
202 
203       for (int i = 0; i < sortedVals.length; i++) {
204         pbr.set(encoded[i]);
205         long decoded = OrderedBytes.decodeNumericAsLong(pbr);
206         assertEquals(
207           String.format(
208             "Encoded representations do not preserve natural order: <%s>, <%s>, %s",
209             sortedVals[i], decoded, ord),
210           sortedVals[i].longValue(), decoded);
211       }
212     }
213   }
214 
215   /**
216    * Test real encoding. Example input values come from reference wiki page.
217    */
218   @Test
219   public void testNumericReal() {
220     /*
221      * assert encoded values match decoded values. encode into target buffer
222      * starting at an offset to detect over/underflow conditions.
223      */
224     for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
225       for (int i = 0; i < D_VALS.length; i++) {
226         // allocate a buffer 2-bytes larger than necessary and place our range over the center.
227         byte[] a = new byte[D_LENGTHS[i] + 2];
228         PositionedByteRange buf1 = new SimplePositionedByteRange(a, 1, D_LENGTHS[i]);
229 
230         // verify encode
231         assertEquals("Surprising return value.",
232           D_LENGTHS[i], OrderedBytes.encodeNumeric(buf1, D_VALS[i], ord));
233         assertEquals("Surprising serialized length.", D_LENGTHS[i], buf1.getPosition());
234         assertEquals("Buffer underflow.", 0, a[0]);
235         assertEquals("Buffer overflow.", 0, a[a.length - 1]);
236 
237         // verify skip
238         buf1.setPosition(0);
239         assertEquals("Surprising return value.", D_LENGTHS[i], OrderedBytes.skip(buf1));
240         assertEquals("Did not skip enough bytes.", D_LENGTHS[i], buf1.getPosition());
241 
242         // verify decode
243         buf1.setPosition(0);
244         assertEquals("Deserialization failed.",
245           D_VALS[i].doubleValue(), OrderedBytes.decodeNumericAsDouble(buf1), MIN_EPSILON);
246         assertEquals("Did not consume enough bytes.", D_LENGTHS[i], buf1.getPosition());
247       }
248     }
249 
250     /*
251      * assert natural sort order is preserved by the codec.
252      */
253     for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
254       byte[][] encoded = new byte[D_VALS.length][];
255       PositionedByteRange pbr = new SimplePositionedByteRange();
256       for (int i = 0; i < D_VALS.length; i++) {
257         encoded[i] = new byte[D_LENGTHS[i]];
258         OrderedBytes.encodeNumeric(pbr.set(encoded[i]), D_VALS[i], ord);
259       }
260 
261       Arrays.sort(encoded, Bytes.BYTES_COMPARATOR);
262       Double[] sortedVals = Arrays.copyOf(D_VALS, D_VALS.length);
263       if (ord == Order.ASCENDING) Arrays.sort(sortedVals);
264       else Arrays.sort(sortedVals, Collections.reverseOrder());
265 
266       for (int i = 0; i < sortedVals.length; i++) {
267         pbr.set(encoded[i]);
268         double decoded = OrderedBytes.decodeNumericAsDouble(pbr);
269         assertEquals(
270           String.format(
271             "Encoded representations do not preserve natural order: <%s>, <%s>, %s",
272             sortedVals[i], decoded, ord),
273             sortedVals[i].doubleValue(), decoded, MIN_EPSILON);
274       }
275     }
276   }
277 
278   /**
279    * Fill gaps in Numeric encoding testing.
280    */
281   @Test
282   public void testNumericOther() {
283     /*
284      * assert encoded values match decoded values. encode into target buffer
285      * starting at an offset to detect over/underflow conditions.
286      */
287     for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
288       for (int i = 0; i < BD_VALS.length; i++) {
289         // allocate a buffer 2-bytes larger than necessary and place our range over the center.
290         byte[] a = new byte[BD_LENGTHS[i] + 2];
291         PositionedByteRange buf1 = new SimplePositionedByteRange(a, 1, BD_LENGTHS[i]);
292 
293         // verify encode
294         assertEquals("Surprising return value.",
295           BD_LENGTHS[i], OrderedBytes.encodeNumeric(buf1, BD_VALS[i], ord));
296         assertEquals("Surprising serialized length.", BD_LENGTHS[i], buf1.getPosition());
297         assertEquals("Buffer underflow.", 0, a[0]);
298         assertEquals("Buffer overflow.", 0, a[a.length - 1]);
299 
300         // verify skip
301         buf1.setPosition(0);
302         assertEquals("Surprising return value.", BD_LENGTHS[i], OrderedBytes.skip(buf1));
303         assertEquals("Did not skip enough bytes.", BD_LENGTHS[i], buf1.getPosition());
304 
305         // verify decode
306         buf1.setPosition(0);
307         BigDecimal decoded = OrderedBytes.decodeNumericAsBigDecimal(buf1);
308         if (null == BD_VALS[i]) {
309           assertEquals(BD_VALS[i], decoded);
310         } else {
311           assertEquals("Deserialization failed.", 0, BD_VALS[i].compareTo(decoded));
312         }
313         assertEquals("Did not consume enough bytes.", BD_LENGTHS[i], buf1.getPosition());
314       }
315     }
316   }
317 
318   /**
319    * Verify Real and Int encodings are compatible.
320    */
321   @Test
322   public void testNumericIntRealCompatibility() {
323     for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
324       for (int i = 0; i < I_VALS.length; i++) {
325         // verify primitives
326         PositionedByteRange pbri = new SimplePositionedByteRange(I_LENGTHS[i]);
327         PositionedByteRange pbrr = new SimplePositionedByteRange(I_LENGTHS[i]);
328         OrderedBytes.encodeNumeric(pbri, I_VALS[i], ord);
329         OrderedBytes.encodeNumeric(pbrr, I_VALS[i], ord);
330         assertArrayEquals("Integer and real encodings differ.", pbri.getBytes(), pbrr.getBytes());
331         pbri.setPosition(0);
332         pbrr.setPosition(0);
333         assertEquals((long) I_VALS[i], OrderedBytes.decodeNumericAsLong(pbri));
334         assertEquals((long) I_VALS[i], (long) OrderedBytes.decodeNumericAsDouble(pbrr));
335 
336         // verify BigDecimal for Real encoding
337         BigDecimal bd = BigDecimal.valueOf(I_VALS[i]);
338         PositionedByteRange pbrbd = new SimplePositionedByteRange(I_LENGTHS[i]);
339         OrderedBytes.encodeNumeric(pbrbd, bd, ord);
340         assertArrayEquals("Integer and BigDecimal encodings differ.",
341           pbri.getBytes(), pbrbd.getBytes());
342         pbri.setPosition(0);
343         assertEquals("Value not preserved when decoding as Long",
344           0, bd.compareTo(BigDecimal.valueOf(OrderedBytes.decodeNumericAsLong(pbri))));
345       }
346     }
347   }
348 
349   /**
350    * Test int32 encoding.
351    */
352   @Test
353   public void testInt32() {
354     Integer[] vals =
355       { Integer.MIN_VALUE, Integer.MIN_VALUE / 2, 0, Integer.MAX_VALUE / 2, Integer.MAX_VALUE };
356 
357     /*
358      * assert encoded values match decoded values. encode into target buffer
359      * starting at an offset to detect over/underflow conditions.
360      */
361     for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
362       for (int i = 0; i < vals.length; i++) {
363         // allocate a buffer 2-bytes larger than necessary and place our range over the center.
364         byte[] a = new byte[5 + 2];
365         PositionedByteRange buf1 = new SimplePositionedByteRange(a, 1, 5);
366 
367         // verify encode
368         assertEquals("Surprising return value.",
369           5, OrderedBytes.encodeInt32(buf1, vals[i], ord));
370         assertEquals("Surprising serialized length.", 5, buf1.getPosition());
371         assertEquals("Buffer underflow.", 0, a[0]);
372         assertEquals("Buffer overflow.", 0, a[a.length - 1]);
373 
374         // verify skip
375         buf1.setPosition(0);
376         assertEquals("Surprising return value.", 5, OrderedBytes.skip(buf1));
377         assertEquals("Did not skip enough bytes.", 5, buf1.getPosition());
378 
379         // verify decode
380         buf1.setPosition(0);
381         assertEquals("Deserialization failed.",
382           vals[i].intValue(), OrderedBytes.decodeInt32(buf1));
383         assertEquals("Did not consume enough bytes.", 5, buf1.getPosition());
384       }
385     }
386 
387     /*
388      * assert natural sort order is preserved by the codec.
389      */
390     for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
391       byte[][] encoded = new byte[vals.length][5];
392       PositionedByteRange pbr = new SimplePositionedByteRange();
393       for (int i = 0; i < vals.length; i++) {
394         OrderedBytes.encodeInt32(pbr.set(encoded[i]), vals[i], ord);
395       }
396 
397       Arrays.sort(encoded, Bytes.BYTES_COMPARATOR);
398       Integer[] sortedVals = Arrays.copyOf(vals, vals.length);
399       if (ord == Order.ASCENDING) Arrays.sort(sortedVals);
400       else Arrays.sort(sortedVals, Collections.reverseOrder());
401 
402       for (int i = 0; i < sortedVals.length; i++) {
403         int decoded = OrderedBytes.decodeInt32(pbr.set(encoded[i]));
404         assertEquals(
405           String.format(
406             "Encoded representations do not preserve natural order: <%s>, <%s>, %s",
407             sortedVals[i], decoded, ord),
408             sortedVals[i].intValue(), decoded);
409       }
410     }
411   }
412 
413   /**
414    * Test int64 encoding.
415    */
416   @Test
417   public void testInt64() {
418     Long[] vals = { Long.MIN_VALUE, Long.MIN_VALUE / 2, 0L, Long.MAX_VALUE / 2, Long.MAX_VALUE };
419 
420     /*
421      * assert encoded values match decoded values. encode into target buffer
422      * starting at an offset to detect over/underflow conditions.
423      */
424     for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
425       for (int i = 0; i < vals.length; i++) {
426         // allocate a buffer 2-bytes larger than necessary and place our range over the center.
427         byte[] a = new byte[9 + 2];
428         PositionedByteRange buf1 = new SimplePositionedByteRange(a, 1, 9);
429 
430         // verify encode
431         assertEquals("Surprising return value.",
432           9, OrderedBytes.encodeInt64(buf1, vals[i], ord));
433         assertEquals("Surprising serialized length.", 9, buf1.getPosition());
434         assertEquals("Buffer underflow.", 0, a[0]);
435         assertEquals("Buffer overflow.", 0, a[a.length - 1]);
436 
437         // verify skip
438         buf1.setPosition(0);
439         assertEquals("Surprising return value.", 9, OrderedBytes.skip(buf1));
440         assertEquals("Did not skip enough bytes.", 9, buf1.getPosition());
441 
442         // verify decode
443         buf1.setPosition(0);
444         assertEquals("Deserialization failed.",
445           vals[i].longValue(), OrderedBytes.decodeInt64(buf1));
446         assertEquals("Did not consume enough bytes.", 9, buf1.getPosition());
447       }
448     }
449 
450     /*
451      * assert natural sort order is preserved by the codec.
452      */
453     for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
454       byte[][] encoded = new byte[vals.length][9];
455       PositionedByteRange pbr = new SimplePositionedByteRange();
456       for (int i = 0; i < vals.length; i++) {
457         OrderedBytes.encodeInt64(pbr.set(encoded[i]), vals[i], ord);
458       }
459 
460       Arrays.sort(encoded, Bytes.BYTES_COMPARATOR);
461       Long[] sortedVals = Arrays.copyOf(vals, vals.length);
462       if (ord == Order.ASCENDING) Arrays.sort(sortedVals);
463       else Arrays.sort(sortedVals, Collections.reverseOrder());
464 
465       for (int i = 0; i < sortedVals.length; i++) {
466         long decoded = OrderedBytes.decodeInt64(pbr.set(encoded[i]));
467         assertEquals(
468           String.format(
469             "Encoded representations do not preserve natural order: <%s>, <%s>, %s",
470             sortedVals[i], decoded, ord),
471             sortedVals[i].longValue(), decoded);
472       }
473     }
474   }
475 
476   /**
477    * Test float32 encoding.
478    */
479   @Test
480   public void testFloat32() {
481     Float[] vals =
482       { Float.MIN_VALUE, Float.MIN_VALUE + 1.0f, 0.0f, Float.MAX_VALUE / 2.0f, Float.MAX_VALUE };
483 
484     /*
485      * assert encoded values match decoded values. encode into target buffer
486      * starting at an offset to detect over/underflow conditions.
487      */
488     for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
489       for (int i = 0; i < vals.length; i++) {
490         // allocate a buffer 2-bytes larger than necessary and place our range over the center.
491         byte[] a = new byte[5 + 2];
492         PositionedByteRange buf1 = new SimplePositionedByteRange(a, 1, 5);
493 
494         // verify encode
495         assertEquals("Surprising return value.",
496           5, OrderedBytes.encodeFloat32(buf1, vals[i], ord));
497         assertEquals("Surprising serialized length.", 5, buf1.getPosition());
498         assertEquals("Buffer underflow.", 0, a[0]);
499         assertEquals("Buffer overflow.", 0, a[a.length - 1]);
500 
501         // verify skip
502         buf1.setPosition(0);
503         assertEquals("Surprising return value.", 5, OrderedBytes.skip(buf1));
504         assertEquals("Did not skip enough bytes.", 5, buf1.getPosition());
505 
506         // verify decode
507         buf1.setPosition(0);
508         assertEquals("Deserialization failed.",
509           Float.floatToIntBits(vals[i].floatValue()),
510           Float.floatToIntBits(OrderedBytes.decodeFloat32(buf1)));
511         assertEquals("Did not consume enough bytes.", 5, buf1.getPosition());
512       }
513     }
514 
515     /*
516      * assert natural sort order is preserved by the codec.
517      */
518     for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
519       byte[][] encoded = new byte[vals.length][5];
520       PositionedByteRange pbr = new SimplePositionedByteRange();
521       for (int i = 0; i < vals.length; i++) {
522         OrderedBytes.encodeFloat32(pbr.set(encoded[i]), vals[i], ord);
523       }
524 
525       Arrays.sort(encoded, Bytes.BYTES_COMPARATOR);
526       Float[] sortedVals = Arrays.copyOf(vals, vals.length);
527       if (ord == Order.ASCENDING) Arrays.sort(sortedVals);
528       else Arrays.sort(sortedVals, Collections.reverseOrder());
529 
530       for (int i = 0; i < sortedVals.length; i++) {
531         float decoded = OrderedBytes.decodeFloat32(pbr.set(encoded[i]));
532         assertEquals(
533           String.format(
534             "Encoded representations do not preserve natural order: <%s>, <%s>, %s",
535             sortedVals[i], decoded, ord),
536             Float.floatToIntBits(sortedVals[i].floatValue()),
537             Float.floatToIntBits(decoded));
538       }
539     }
540   }
541 
542   /**
543    * Test float64 encoding.
544    */
545   @Test
546   public void testFloat64() {
547     Double[] vals =
548       { Double.MIN_VALUE, Double.MIN_VALUE + 1.0, 0.0, Double.MAX_VALUE / 2.0, Double.MAX_VALUE };
549 
550     /*
551      * assert encoded values match decoded values. encode into target buffer
552      * starting at an offset to detect over/underflow conditions.
553      */
554     for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
555       for (int i = 0; i < vals.length; i++) {
556         // allocate a buffer 2-bytes larger than necessary and place our range over the center.
557         byte[] a = new byte[9 + 2];
558         PositionedByteRange buf1 = new SimplePositionedByteRange(a, 1, 9);
559 
560         // verify encode
561         assertEquals("Surprising return value.",
562           9, OrderedBytes.encodeFloat64(buf1, vals[i], ord));
563         assertEquals("Surprising serialized length.", 9, buf1.getPosition());
564         assertEquals("Buffer underflow.", 0, a[0]);
565         assertEquals("Buffer overflow.", 0, a[a.length - 1]);
566 
567         // verify skip
568         buf1.setPosition(0);
569         assertEquals("Surprising return value.", 9, OrderedBytes.skip(buf1));
570         assertEquals("Did not skip enough bytes.", 9, buf1.getPosition());
571 
572         // verify decode
573         buf1.setPosition(0);
574         assertEquals("Deserialization failed.",
575           Double.doubleToLongBits(vals[i].doubleValue()),
576           Double.doubleToLongBits(OrderedBytes.decodeFloat64(buf1)));
577         assertEquals("Did not consume enough bytes.", 9, buf1.getPosition());
578       }
579     }
580 
581     /*
582      * assert natural sort order is preserved by the codec.
583      */
584     for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
585       byte[][] encoded = new byte[vals.length][9];
586       PositionedByteRange pbr = new SimplePositionedByteRange();
587       for (int i = 0; i < vals.length; i++) {
588         OrderedBytes.encodeFloat64(pbr.set(encoded[i]), vals[i], ord);
589       }
590 
591       Arrays.sort(encoded, Bytes.BYTES_COMPARATOR);
592       Double[] sortedVals = Arrays.copyOf(vals, vals.length);
593       if (ord == Order.ASCENDING) Arrays.sort(sortedVals);
594       else Arrays.sort(sortedVals, Collections.reverseOrder());
595 
596       for (int i = 0; i < sortedVals.length; i++) {
597         double decoded = OrderedBytes.decodeFloat64(pbr.set(encoded[i]));
598         assertEquals(
599           String.format(
600             "Encoded representations do not preserve natural order: <%s>, <%s>, %s",
601             sortedVals[i], decoded, ord),
602             Double.doubleToLongBits(sortedVals[i].doubleValue()),
603             Double.doubleToLongBits(decoded));
604       }
605     }
606   }
607 
608   /**
609    * Test string encoding.
610    */
611   @Test
612   public void testString() {
613     String[] vals = { "foo", "baaaar", "bazz" };
614     int expectedLengths[] = { 5, 8, 6 };
615 
616     /*
617      * assert encoded values match decoded values. encode into target buffer
618      * starting at an offset to detect over/underflow conditions.
619      */
620     for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
621       for (int i = 0; i < vals.length; i++) {
622         // allocate a buffer 2-bytes larger than necessary and place our range over the center.
623         byte[] a = new byte[expectedLengths[i] + 2];
624         PositionedByteRange buf1 = new SimplePositionedByteRange(a, 1, expectedLengths[i]);
625 
626         // verify encode
627         assertEquals("Surprising return value.",
628           expectedLengths[i], OrderedBytes.encodeString(buf1, vals[i], ord));
629         assertEquals("Surprising serialized length.", expectedLengths[i], buf1.getPosition());
630         assertEquals("Buffer underflow.", 0, a[0]);
631         assertEquals("Buffer overflow.", 0, a[a.length - 1]);
632 
633         // verify skip
634         buf1.setPosition(0);
635         assertEquals("Surprising return value.", expectedLengths[i], OrderedBytes.skip(buf1));
636         assertEquals("Did not skip enough bytes.", expectedLengths[i], buf1.getPosition());
637 
638         // verify decode
639         buf1.setPosition(0);
640         assertEquals("Deserialization failed.", vals[i], OrderedBytes.decodeString(buf1));
641         assertEquals("Did not consume enough bytes.", expectedLengths[i], buf1.getPosition());
642       }
643     }
644 
645     /*
646      * assert natural sort order is preserved by the codec.
647      */
648     for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
649       byte[][] encoded = new byte[vals.length][];
650       PositionedByteRange pbr = new SimplePositionedByteRange();
651       for (int i = 0; i < vals.length; i++) {
652         encoded[i] = new byte[expectedLengths[i]];
653         OrderedBytes.encodeString(pbr.set(encoded[i]), vals[i], ord);
654       }
655 
656       Arrays.sort(encoded, Bytes.BYTES_COMPARATOR);
657       String[] sortedVals = Arrays.copyOf(vals, vals.length);
658       if (ord == Order.ASCENDING) Arrays.sort(sortedVals);
659       else Arrays.sort(sortedVals, Collections.reverseOrder());
660 
661       for (int i = 0; i < sortedVals.length; i++) {
662         pbr.set(encoded[i]);
663         String decoded = OrderedBytes.decodeString(pbr);
664         assertEquals(
665           String.format(
666             "Encoded representations do not preserve natural order: <%s>, <%s>, %s",
667             sortedVals[i], decoded, ord),
668           sortedVals[i], decoded);
669       }
670     }
671   }
672 
673   @Test(expected = IllegalArgumentException.class)
674   public void testStringNoNullChars() {
675     PositionedByteRange buff = new SimplePositionedByteRange(3);
676     OrderedBytes.encodeString(buff, "\u0000", Order.ASCENDING);
677   }
678 
679   /**
680    * Test length estimation algorithms for BlobVar encoding. Does not cover
681    * 0-length input case properly.
682    */
683   @Test
684   public void testBlobVarLencodedLength() {
685     int[][] values = {
686         /* decoded length, encoded length
687          * ceil((n bytes * 8 bits/input byte) / 7 bits/encoded byte) + 1 header
688          */
689         { 1, 3 }, { 2, 4 }, { 3, 5 }, { 4, 6 },
690         { 5, 7 }, { 6, 8 }, { 7, 9 }, { 8, 11 }
691       };
692 
693     for (int[] pair : values) {
694       assertEquals(pair[1], OrderedBytes.blobVarEncodedLength(pair[0]));
695       assertEquals(pair[0], OrderedBytes.blobVarDecodedLength(pair[1]));
696     }
697   }
698 
699   /**
700    * Test BlobVar encoding.
701    */
702   @Test
703   public void testBlobVar() {
704     byte[][] vals =
705         { "".getBytes(), "foo".getBytes(), "foobarbazbub".getBytes(),
706           { (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
707             (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa },
708           { (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55,
709             (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55 },
710           "1".getBytes(), "22".getBytes(), "333".getBytes(), "4444".getBytes(),
711           "55555".getBytes(), "666666".getBytes(), "7777777".getBytes(), "88888888".getBytes()
712         };
713 
714     /*
715      * assert encoded values match decoded values. encode into target buffer
716      * starting at an offset to detect over/underflow conditions.
717      */
718     for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
719       for (byte[] val : vals) {
720         // allocate a buffer 2-bytes larger than necessary and place our range over the center.
721         int expectedLen = OrderedBytes.blobVarEncodedLength(val.length);
722         byte[] a = new byte[expectedLen + 2];
723         PositionedByteRange buf1 =
724             new SimplePositionedByteRange(a, 1, expectedLen);
725 
726         // verify encode
727         assertEquals("Surprising return value.",
728           expectedLen, OrderedBytes.encodeBlobVar(buf1, val, ord));
729         assertEquals("Surprising serialized length.", expectedLen, buf1.getPosition());
730         assertEquals("Buffer underflow.", 0, a[0]);
731         assertEquals("Buffer overflow.", 0, a[a.length - 1]);
732 
733         // verify skip
734         buf1.setPosition(0);
735         assertEquals("Surprising return value.", expectedLen, OrderedBytes.skip(buf1));
736         assertEquals("Did not skip enough bytes.", expectedLen, buf1.getPosition());
737 
738         // verify decode
739         buf1.setPosition(0);
740         assertArrayEquals("Deserialization failed.", val, OrderedBytes.decodeBlobVar(buf1));
741         assertEquals("Did not consume enough bytes.", expectedLen, buf1.getPosition());
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][];
750       PositionedByteRange pbr = new SimplePositionedByteRange();
751       for (int i = 0; i < vals.length; i++) {
752         encoded[i] = new byte[OrderedBytes.blobVarEncodedLength(vals[i].length)];
753         OrderedBytes.encodeBlobVar(pbr.set(encoded[i]), vals[i], ord);
754       }
755 
756       Arrays.sort(encoded, Bytes.BYTES_COMPARATOR);
757       byte[][] sortedVals = Arrays.copyOf(vals, vals.length);
758       if (ord == Order.ASCENDING) Arrays.sort(sortedVals, Bytes.BYTES_COMPARATOR);
759       else Arrays.sort(sortedVals, Collections.reverseOrder(Bytes.BYTES_COMPARATOR));
760 
761       for (int i = 0; i < sortedVals.length; i++) {
762         pbr.set(encoded[i]);
763         byte[] decoded = OrderedBytes.decodeBlobVar(pbr);
764         assertArrayEquals(
765           String.format(
766             "Encoded representations do not preserve natural order: <%s>, <%s>, %s",
767             sortedVals[i], decoded, ord),
768           sortedVals[i], decoded);
769       }
770     }
771   }
772 
773   /**
774    * Test BlobCopy encoding.
775    */
776   @Test
777   public void testBlobCopy() {
778     byte[][] vals =
779       { "".getBytes(), "foo".getBytes(), "foobarbazbub".getBytes(),
780         { (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
781           (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa },
782         { (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55,
783           (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55 },
784       };
785 
786     /*
787      * assert encoded values match decoded values. encode into target buffer
788      * starting at an offset to detect over/underflow conditions.
789      */
790     for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
791       for (byte[] val : vals) {
792         // allocate a buffer 2-bytes larger than necessary and place our range over the center.
793         int expectedLen = val.length + (Order.ASCENDING == ord ? 1 : 2);
794         byte[] a = new byte[expectedLen + 2];
795         PositionedByteRange buf1 =
796             new SimplePositionedByteRange(a, 1, expectedLen);
797 
798         // verify encode
799         assertEquals("Surprising return value.",
800           expectedLen, OrderedBytes.encodeBlobCopy(buf1, val, ord));
801         assertEquals("Surprising serialized length.", expectedLen, buf1.getPosition());
802         assertEquals("Buffer underflow.", 0, a[0]);
803         assertEquals("Buffer overflow.", 0, a[a.length - 1]);
804 
805         // verify skip
806         buf1.setPosition(0);
807         assertEquals("Surprising return value.", expectedLen, OrderedBytes.skip(buf1));
808         assertEquals("Did not skip enough bytes.", expectedLen, buf1.getPosition());
809 
810         // verify decode
811         buf1.setPosition(0);
812         assertArrayEquals("Deserialization failed.", val, OrderedBytes.decodeBlobCopy(buf1));
813         assertEquals("Did not consume enough bytes.", expectedLen, buf1.getPosition());
814       }
815     }
816 
817     /*
818      * assert natural sort order is preserved by the codec.
819      */
820     for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
821       byte[][] encoded = new byte[vals.length][];
822       PositionedByteRange pbr = new SimplePositionedByteRange();
823       for (int i = 0; i < vals.length; i++) {
824         encoded[i] = new byte[vals[i].length + (Order.ASCENDING == ord ? 1 : 2)];
825         OrderedBytes.encodeBlobCopy(pbr.set(encoded[i]), vals[i], ord);
826       }
827 
828       Arrays.sort(encoded, Bytes.BYTES_COMPARATOR);
829       byte[][] sortedVals = Arrays.copyOf(vals, vals.length);
830       if (ord == Order.ASCENDING) Arrays.sort(sortedVals, Bytes.BYTES_COMPARATOR);
831       else Arrays.sort(sortedVals, Collections.reverseOrder(Bytes.BYTES_COMPARATOR));
832 
833       for (int i = 0; i < sortedVals.length; i++) {
834         pbr.set(encoded[i]);
835         byte[] decoded = OrderedBytes.decodeBlobCopy(pbr);
836         assertArrayEquals(
837           String.format(
838             "Encoded representations do not preserve natural order: <%s>, <%s>, %s",
839             sortedVals[i], decoded, ord),
840           sortedVals[i], decoded);
841       }
842     }
843 
844     /*
845      * assert byte[] segments are serialized correctly.
846      */
847     for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
848       byte[] a = new byte[3 + (Order.ASCENDING == ord ? 1 : 2) + 2];
849       PositionedByteRange buf =
850           new SimplePositionedByteRange(a, 1, 3 + (Order.ASCENDING == ord ? 1 : 2));
851       OrderedBytes.encodeBlobCopy(buf, "foobarbaz".getBytes(), 3, 3, ord);
852       buf.setPosition(0);
853       assertArrayEquals("bar".getBytes(), OrderedBytes.decodeBlobCopy(buf));
854     }
855   }
856 
857   /**
858    * Assert invalid input byte[] are rejected by BlobCopy
859    */
860   @Test(expected = IllegalArgumentException.class)
861   public void testBlobCopyNoZeroBytes() {
862     byte[] val = { 0x01, 0x02, 0x00, 0x03 };
863     byte[] ascExpected = { 0x36, 0x01, 0x02, 0x00, 0x03 };
864     PositionedByteRange buf = new SimplePositionedByteRange(val.length + 1);
865     OrderedBytes.encodeBlobCopy(buf, val, Order.ASCENDING);
866     assertArrayEquals(ascExpected, buf.getBytes());
867     buf.set(val.length + 2);
868     OrderedBytes.encodeBlobCopy(buf, val, Order.DESCENDING);
869     fail("test should never get here.");
870   }
871 
872   /**
873    * Test generic skip logic
874    */
875   @Test
876   public void testSkip() {
877     BigDecimal longMax = BigDecimal.valueOf(Long.MAX_VALUE);
878     double negInf = Double.NEGATIVE_INFINITY;
879     BigDecimal negLarge = longMax.multiply(longMax).negate();
880     BigDecimal negMed = new BigDecimal("-10.0");
881     BigDecimal negSmall = new BigDecimal("-0.0010");
882     long zero = 0l;
883     BigDecimal posSmall = negSmall.negate();
884     BigDecimal posMed = negMed.negate();
885     BigDecimal posLarge = negLarge.negate();
886     double posInf = Double.POSITIVE_INFINITY;
887     double nan = Double.NaN;
888     int int32 = 100;
889     long int64 = 100l;
890     float float32 = 100.0f;
891     double float64 = 100.0d;
892     String text = "hello world.";
893     byte[] blobVar = Bytes.toBytes("foo");
894     byte[] blobCopy = Bytes.toBytes("bar");
895 
896     for (Order ord : new Order[] { Order.ASCENDING, Order.DESCENDING }) {
897       PositionedByteRange buff = new SimplePositionedByteRange(30);
898       int o;
899       o = OrderedBytes.encodeNull(buff, ord);
900       buff.setPosition(0);
901       assertEquals(o, OrderedBytes.skip(buff));
902 
903       buff.setPosition(0);
904       o = OrderedBytes.encodeNumeric(buff, negInf, ord);
905       buff.setPosition(0);
906       assertEquals(o, OrderedBytes.skip(buff));
907 
908       buff.setPosition(0);
909       o = OrderedBytes.encodeNumeric(buff, negLarge, ord);
910       buff.setPosition(0);
911       assertEquals(o, OrderedBytes.skip(buff));
912 
913       buff.setPosition(0);
914       o = OrderedBytes.encodeNumeric(buff, negMed, ord);
915       buff.setPosition(0);
916       assertEquals(o, OrderedBytes.skip(buff));
917 
918       buff.setPosition(0);
919       o = OrderedBytes.encodeNumeric(buff, negSmall, ord);
920       buff.setPosition(0);
921       assertEquals(o, OrderedBytes.skip(buff));
922 
923       buff.setPosition(0);
924       o = OrderedBytes.encodeNumeric(buff, zero, ord);
925       buff.setPosition(0);
926       assertEquals(o, OrderedBytes.skip(buff));
927 
928       buff.setPosition(0);
929       o = OrderedBytes.encodeNumeric(buff, posSmall, ord);
930       buff.setPosition(0);
931       assertEquals(o, OrderedBytes.skip(buff));
932 
933       buff.setPosition(0);
934       o = OrderedBytes.encodeNumeric(buff, posMed, ord);
935       buff.setPosition(0);
936       assertEquals(o, OrderedBytes.skip(buff));
937 
938       buff.setPosition(0);
939       o = OrderedBytes.encodeNumeric(buff, posLarge, ord);
940       buff.setPosition(0);
941       assertEquals(o, OrderedBytes.skip(buff));
942 
943       buff.setPosition(0);
944       o = OrderedBytes.encodeNumeric(buff, posInf, ord);
945       buff.setPosition(0);
946       assertEquals(o, OrderedBytes.skip(buff));
947 
948       buff.setPosition(0);
949       o = OrderedBytes.encodeNumeric(buff, nan, ord);
950       buff.setPosition(0);
951       assertEquals(o, OrderedBytes.skip(buff));
952 
953       buff.setPosition(0);
954       o = OrderedBytes.encodeInt32(buff, int32, ord);
955       buff.setPosition(0);
956       assertEquals(o, OrderedBytes.skip(buff));
957 
958       buff.setPosition(0);
959       o = OrderedBytes.encodeInt64(buff, int64, ord);
960       buff.setPosition(0);
961       assertEquals(o, OrderedBytes.skip(buff));
962 
963       buff.setPosition(0);
964       o = OrderedBytes.encodeFloat32(buff, float32, ord);
965       buff.setPosition(0);
966       assertEquals(o, OrderedBytes.skip(buff));
967 
968       buff.setPosition(0);
969       o = OrderedBytes.encodeFloat64(buff, float64, ord);
970       buff.setPosition(0);
971       assertEquals(o, OrderedBytes.skip(buff));
972 
973       buff.setPosition(0);
974       o = OrderedBytes.encodeString(buff, text, ord);
975       buff.setPosition(0);
976       assertEquals(o, OrderedBytes.skip(buff));
977 
978       buff.setPosition(0);
979       o = OrderedBytes.encodeBlobVar(buff, blobVar, ord);
980       buff.setPosition(0);
981       assertEquals(o, OrderedBytes.skip(buff));
982 
983       // blobCopy is special in that it runs to the end of the target buffer.
984       buff.set(blobCopy.length + (Order.ASCENDING == ord ? 1 : 2));
985       o = OrderedBytes.encodeBlobCopy(buff, blobCopy, ord);
986       buff.setPosition(0);
987       assertEquals(o, OrderedBytes.skip(buff));
988     }
989   }
990 }