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