|
17 | 17 | package org.apache.arrow.vector; |
18 | 18 |
|
19 | 19 | import static org.junit.jupiter.api.Assertions.assertEquals; |
20 | | -import static org.junit.jupiter.api.Assertions.assertThrows; |
21 | 20 | import static org.junit.jupiter.api.Assertions.assertFalse; |
22 | 21 | import static org.junit.jupiter.api.Assertions.assertSame; |
| 22 | +import static org.junit.jupiter.api.Assertions.assertThrows; |
23 | 23 | import static org.junit.jupiter.api.Assertions.assertTrue; |
24 | 24 | import static org.junit.jupiter.api.Assertions.fail; |
25 | 25 |
|
26 | 26 | import java.math.BigDecimal; |
27 | 27 | import java.math.BigInteger; |
| 28 | +import java.util.function.BiConsumer; |
28 | 29 | import org.apache.arrow.memory.ArrowBuf; |
29 | 30 | import org.apache.arrow.memory.BufferAllocator; |
30 | 31 | import org.apache.arrow.vector.types.pojo.ArrowType; |
@@ -265,7 +266,7 @@ public void decimalBE2NE() { |
265 | 266 | } catch (IllegalArgumentException ex) { |
266 | 267 | assertTrue( |
267 | 268 | ex.getMessage() |
268 | | - .equals("Invalid decimal value length. Valid length in [1 - 16], got 17")); |
| 269 | + .equals("Invalid decimal value length. Valid length in [0 - 16], got 17")); |
269 | 270 | } |
270 | 271 | decimalVector.setValueCount(insertionIdx); |
271 | 272 |
|
@@ -416,80 +417,75 @@ private void verifyWritingArrowBufWithBigEndianBytes( |
416 | 417 | } |
417 | 418 | } |
418 | 419 |
|
419 | | - // ---- Oversized raw writes ------------------------------------------------------------------- |
420 | | - // The value buffer and validity buffer are adjacent slices of one allocation (value buffer first), |
421 | | - // so a write that overruns the last value slot lands in the validity buffer. That makes an overrun |
422 | | - // observable from Java via isNull() without touching memory outside the allocation. Each test |
423 | | - // marks the first 8 values valid, attempts an oversized write into the last slot whose spilled |
424 | | - // byte is zero, and asserts the write is rejected without clearing any validity bit. |
425 | | - |
426 | 420 | private static final int OVERSIZED_LENGTH = DecimalVector.TYPE_WIDTH + 1; |
427 | 421 |
|
428 | 422 | private DecimalVector newVectorWithFirstEightValid() { |
429 | | - DecimalVector vector = TestUtils.newVector( |
430 | | - DecimalVector.class, "decimal", new ArrowType.Decimal(38, 0, 128), allocator); |
| 423 | + DecimalVector vector = new DecimalVector("decimal", allocator, 38, 0); |
431 | 424 | vector.allocateNew(64); |
432 | 425 | for (int i = 0; i < 8; i++) { |
433 | 426 | vector.setSafe(i, BigDecimal.valueOf(i)); |
434 | 427 | } |
435 | | - vector.setValueCount(8); |
436 | 428 | return vector; |
437 | 429 | } |
438 | 430 |
|
| 431 | + // Validity bits sit right after the value buffer, so an overrun of the last slot clobbers them. |
439 | 432 | private static void assertFirstEightValid(DecimalVector vector) { |
440 | 433 | for (int i = 0; i < 8; i++) { |
441 | 434 | assertFalse(vector.isNull(i), "validity bit " + i + " was overwritten by an oversized write"); |
442 | 435 | assertEquals(BigDecimal.valueOf(i), vector.getObject(i)); |
443 | 436 | } |
444 | 437 | } |
445 | 438 |
|
446 | | - @Test |
447 | | - public void setBigEndianSafeRejectsOversizedByteArrayWithoutWriting() { |
448 | | - try (DecimalVector vector = newVectorWithFirstEightValid()) { |
449 | | - final int last = vector.getValueCapacity() - 1; |
450 | | - final byte[] oversized = new byte[OVERSIZED_LENGTH]; |
451 | | - assertThrows( |
452 | | - IllegalArgumentException.class, () -> vector.setBigEndianSafe(last, oversized)); |
453 | | - assertFirstEightValid(vector); |
454 | | - } |
455 | | - } |
456 | | - |
457 | | - @Test |
458 | | - public void setSafeFromArrowBufRejectsOversizedLengthWithoutWriting() { |
| 439 | + private void assertOversizedWriteRejected(BiConsumer<DecimalVector, ArrowBuf> write) { |
459 | 440 | try (DecimalVector vector = newVectorWithFirstEightValid(); |
460 | 441 | ArrowBuf src = allocator.buffer(OVERSIZED_LENGTH)) { |
461 | 442 | src.setZero(0, OVERSIZED_LENGTH); |
462 | | - final int last = vector.getValueCapacity() - 1; |
463 | | - assertThrows( |
464 | | - IllegalArgumentException.class, () -> vector.setSafe(last, 0, src, OVERSIZED_LENGTH)); |
| 443 | + assertThrows(IllegalArgumentException.class, () -> write.accept(vector, src)); |
465 | 444 | assertFirstEightValid(vector); |
466 | 445 | } |
467 | 446 | } |
468 | 447 |
|
469 | 448 | @Test |
470 | | - public void setBigEndianSafeFromArrowBufRejectsOversizedLengthWithoutWriting() { |
471 | | - try (DecimalVector vector = newVectorWithFirstEightValid(); |
472 | | - ArrowBuf src = allocator.buffer(OVERSIZED_LENGTH)) { |
473 | | - src.setZero(0, OVERSIZED_LENGTH); |
474 | | - final int last = vector.getValueCapacity() - 1; |
475 | | - assertThrows( |
476 | | - IllegalArgumentException.class, |
477 | | - () -> vector.setBigEndianSafe(last, 0, src, OVERSIZED_LENGTH)); |
478 | | - assertFirstEightValid(vector); |
479 | | - } |
| 449 | + public void setBigEndianSafeRejectsOversizedByteArray() { |
| 450 | + assertOversizedWriteRejected( |
| 451 | + (vector, src) -> |
| 452 | + vector.setBigEndianSafe(vector.getValueCapacity() - 1, new byte[OVERSIZED_LENGTH])); |
| 453 | + } |
| 454 | + |
| 455 | + @Test |
| 456 | + public void setSafeFromArrowBufRejectsOversizedLength() { |
| 457 | + assertOversizedWriteRejected( |
| 458 | + (vector, src) -> vector.setSafe(vector.getValueCapacity() - 1, 0, src, OVERSIZED_LENGTH)); |
| 459 | + } |
| 460 | + |
| 461 | + @Test |
| 462 | + public void setBigEndianSafeFromArrowBufRejectsOversizedLength() { |
| 463 | + assertOversizedWriteRejected( |
| 464 | + (vector, src) -> |
| 465 | + vector.setBigEndianSafe(vector.getValueCapacity() - 1, 0, src, OVERSIZED_LENGTH)); |
480 | 466 | } |
481 | 467 |
|
482 | 468 | @Test |
483 | 469 | public void setSafeFromArrowBufWithZeroLengthDoesNotReadBeforeStart() { |
484 | | - try (DecimalVector vector = TestUtils.newVector( |
485 | | - DecimalVector.class, "decimal", new ArrowType.Decimal(38, 0, 128), allocator); |
| 470 | + try (DecimalVector vector = new DecimalVector("decimal", allocator, 38, 0); |
486 | 471 | ArrowBuf src = allocator.buffer(2)) { |
487 | 472 | vector.allocateNew(1); |
488 | 473 | // A negative byte immediately before `start` must not be used for sign extension. |
489 | 474 | src.setByte(0, (byte) 0xFF); |
490 | 475 | src.setByte(1, (byte) 0x00); |
491 | 476 | vector.setSafe(0, 1, src, 0); |
492 | | - vector.setValueCount(1); |
| 477 | + assertEquals(BigDecimal.ZERO, vector.getObject(0)); |
| 478 | + } |
| 479 | + } |
| 480 | + |
| 481 | + @Test |
| 482 | + public void setBigEndianSafeFromArrowBufWithZeroLengthDoesNotReadAtStart() { |
| 483 | + try (DecimalVector vector = new DecimalVector("decimal", allocator, 38, 0); |
| 484 | + ArrowBuf src = allocator.buffer(1)) { |
| 485 | + vector.allocateNew(1); |
| 486 | + // A negative byte at `start` must not be used for sign extension of an empty value. |
| 487 | + src.setByte(0, (byte) 0xFF); |
| 488 | + vector.setBigEndianSafe(0, 0, src, 0); |
493 | 489 | assertEquals(BigDecimal.ZERO, vector.getObject(0)); |
494 | 490 | } |
495 | 491 | } |
|
0 commit comments