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}