|
15 | 15 | import org.junit.jupiter.params.provider.MethodSource; |
16 | 16 |
|
17 | 17 | import java.lang.foreign.Arena; |
| 18 | +import java.util.Random; |
18 | 19 | import java.util.stream.Stream; |
19 | 20 |
|
20 | 21 | import static org.assertj.core.api.Assertions.assertThat; |
@@ -83,4 +84,62 @@ void encode_i32_metadata_bitWidth_isNonZero() throws Exception { |
83 | 84 |
|
84 | 85 | assertThat(meta.bit_width()).isGreaterThan(0); |
85 | 86 | } |
| 87 | + |
| 88 | + /// Property: lossless pack/unpack at **every** bit width. Each array forces a specific |
| 89 | + /// width by including its max value `2^W - 1`, fills the rest with random values masked to |
| 90 | + /// W bits, and checks the boundary widths (1, 7, 8, 31/32, 63/64) explicitly. The random |
| 91 | + /// arrays' values don't deterministically exercise every width, so this pins them all. |
| 92 | + @ParameterizedTest(name = "u32 width={0}") |
| 93 | + @MethodSource("u32Widths") |
| 94 | + void encodeDecode_u32_everyBitWidth(int width, int[] data) { |
| 95 | + EncodeResult encoded = ENCODER.encode(DTypes.U32, data, EncodeTestHelper.testCtx()); |
| 96 | + DecodeContext ctx = DecodeTestHelper.toDecodeContext(encoded, data.length, DTypes.U32, REGISTRY); |
| 97 | + Array result = DECODER.decode(ctx); |
| 98 | + |
| 99 | + var seg = result.materialize(Arena.ofAuto()); |
| 100 | + for (int i = 0; i < data.length; i++) { |
| 101 | + assertThat(seg.get(PTypeIO.LE_INT, (long) i * 4)).as("width %d index %d", width, i).isEqualTo(data[i]); |
| 102 | + } |
| 103 | + } |
| 104 | + |
| 105 | + @ParameterizedTest(name = "u64 width={0}") |
| 106 | + @MethodSource("u64Widths") |
| 107 | + void encodeDecode_u64_everyBitWidth(int width, long[] data) { |
| 108 | + EncodeResult encoded = ENCODER.encode(DTypes.U64, data, EncodeTestHelper.testCtx()); |
| 109 | + DecodeContext ctx = DecodeTestHelper.toDecodeContext(encoded, data.length, DTypes.U64, REGISTRY); |
| 110 | + Array result = DECODER.decode(ctx); |
| 111 | + |
| 112 | + var seg = result.materialize(Arena.ofAuto()); |
| 113 | + for (int i = 0; i < data.length; i++) { |
| 114 | + assertThat(seg.get(PTypeIO.LE_LONG, (long) i * 8)).as("width %d index %d", width, i).isEqualTo(data[i]); |
| 115 | + } |
| 116 | + } |
| 117 | + |
| 118 | + static Stream<Arguments> u32Widths() { |
| 119 | + Random rng = new Random(0xB17BACEDL); |
| 120 | + return Stream.iterate(1, w -> w <= 32, w -> w + 1).map(w -> { |
| 121 | + int mask = w == 32 ? -1 : (1 << w) - 1; // -1 == 0xFFFFFFFF for the full width |
| 122 | + int[] a = new int[40]; |
| 123 | + a[0] = 0; |
| 124 | + a[1] = mask; // 2^w - 1 — forces the encoder to pick width w |
| 125 | + for (int i = 2; i < a.length; i++) { |
| 126 | + a[i] = rng.nextInt() & mask; |
| 127 | + } |
| 128 | + return Arguments.of(w, a); |
| 129 | + }); |
| 130 | + } |
| 131 | + |
| 132 | + static Stream<Arguments> u64Widths() { |
| 133 | + Random rng = new Random(0xB17BACE2L); |
| 134 | + return Stream.iterate(1, w -> w <= 64, w -> w + 1).map(w -> { |
| 135 | + long mask = w == 64 ? -1L : (1L << w) - 1L; |
| 136 | + long[] a = new long[40]; |
| 137 | + a[0] = 0L; |
| 138 | + a[1] = mask; |
| 139 | + for (int i = 2; i < a.length; i++) { |
| 140 | + a[i] = rng.nextLong() & mask; |
| 141 | + } |
| 142 | + return Arguments.of(w, a); |
| 143 | + }); |
| 144 | + } |
86 | 145 | } |
0 commit comments