From b07274fc0a17c0515771cfe6f355bc8e218f3ca3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rou=C3=A9l?= Date: Mon, 6 Apr 2026 21:01:57 +0200 Subject: [PATCH 1/3] GH-3466 Improve `RunLengthBitPackingHybridDecoder.readNext` to avoid per-call buffer allocation and `DataInputStream` wrapping --- .../TestRunLengthBitPackingHybridEncoder.java | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/parquet-column/src/test/java/org/apache/parquet/column/values/rle/TestRunLengthBitPackingHybridEncoder.java b/parquet-column/src/test/java/org/apache/parquet/column/values/rle/TestRunLengthBitPackingHybridEncoder.java index d730613166..105dec084e 100644 --- a/parquet-column/src/test/java/org/apache/parquet/column/values/rle/TestRunLengthBitPackingHybridEncoder.java +++ b/parquet-column/src/test/java/org/apache/parquet/column/values/rle/TestRunLengthBitPackingHybridEncoder.java @@ -352,6 +352,49 @@ public void testTruncatedPackedRunClearsReusedBytes() throws Exception { assertThat(decoder.readInt()).isEqualTo(0); } + @Test + public void testTruncatedPackedRunAfterFullPackedRunDoesNotReuseStaleBytes() throws Exception { + int bitWidth = 3; + BytePacker packer = Packer.LITTLE_ENDIAN.newBytePacker(bitWidth); + + int[] firstRunValues = new int[8]; + Arrays.fill(firstRunValues, 7); + byte[] firstRunPacked = new byte[bitWidth]; + packer.pack8Values(firstRunValues, 0, firstRunPacked, 0); + + int[] secondRunValues = {1, 2, 3, 4, 5, 6, 7, 0}; + byte[] secondRunPacked = new byte[bitWidth]; + packer.pack8Values(secondRunValues, 0, secondRunPacked, 0); + + byte[] encoded = { + (byte) ((1 << 1) | 1), + firstRunPacked[0], + firstRunPacked[1], + firstRunPacked[2], + (byte) ((1 << 1) | 1), + secondRunPacked[0] + }; + + RunLengthBitPackingHybridDecoder decoder = + new RunLengthBitPackingHybridDecoder(bitWidth, new ByteArrayInputStream(encoded)); + + for (int ignored = 0; ignored < 8; ignored++) { + assertEquals(7, decoder.readInt()); + } + + int[] actualSecondRun = new int[8]; + for (int i = 0; i < 8; i++) { + actualSecondRun[i] = decoder.readInt(); + } + + byte[] expectedSecondPacked = new byte[bitWidth]; + expectedSecondPacked[0] = secondRunPacked[0]; + int[] expectedSecondRun = new int[8]; + packer.unpack8Values(expectedSecondPacked, 0, expectedSecondRun, 0); + + assertArrayEquals(expectedSecondRun, actualSecondRun); + } + private static List unpack(int bitWidth, int numValues, ByteArrayInputStream is) throws Exception { BytePacker packer = Packer.LITTLE_ENDIAN.newBytePacker(bitWidth); From 5b4f56d9e2992a7cc0d277f9aecdb67e4af29735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rou=C3=A9l?= Date: Tue, 16 Jun 2026 18:12:56 +0200 Subject: [PATCH 2/3] Update RunLengthBitPackingHybridDecoder.java --- .../column/values/rle/RunLengthBitPackingHybridDecoder.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/parquet-column/src/main/java/org/apache/parquet/column/values/rle/RunLengthBitPackingHybridDecoder.java b/parquet-column/src/main/java/org/apache/parquet/column/values/rle/RunLengthBitPackingHybridDecoder.java index 2f143d2a07..25ec743b1a 100644 --- a/parquet-column/src/main/java/org/apache/parquet/column/values/rle/RunLengthBitPackingHybridDecoder.java +++ b/parquet-column/src/main/java/org/apache/parquet/column/values/rle/RunLengthBitPackingHybridDecoder.java @@ -123,11 +123,15 @@ private void readNext() throws IOException { LOG.debug("reading {} values BIT PACKED", currentCount); if (currentBuffer.length < currentCount) { currentBuffer = new int[currentCount]; + } else { + Arrays.fill(currentBuffer, currentCount, currentBuffer.length, 0); } currentBufferPosition = 0; int bytesRequired = numGroups * bitWidth; if (packedBytes.length < bytesRequired) { packedBytes = new byte[bytesRequired]; + } else { + Arrays.fill(packedBytes, bytesRequired, packedBytes.length, (byte) 0); } // At the end of the file RLE data though, there might not be that many bytes left. int bytesToRead = (int) Math.ceil(currentCount * bitWidth / 8.0); From 4f3c5b8f8164e2d0d24bc5aa028e1a24bd7d51cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rou=C3=A9l?= Date: Fri, 30 Oct 2026 13:28:14 +0100 Subject: [PATCH 3/3] Update TestRunLengthBitPackingHybridEncoder.java --- .../values/rle/TestRunLengthBitPackingHybridEncoder.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/parquet-column/src/test/java/org/apache/parquet/column/values/rle/TestRunLengthBitPackingHybridEncoder.java b/parquet-column/src/test/java/org/apache/parquet/column/values/rle/TestRunLengthBitPackingHybridEncoder.java index 105dec084e..95b2e744d7 100644 --- a/parquet-column/src/test/java/org/apache/parquet/column/values/rle/TestRunLengthBitPackingHybridEncoder.java +++ b/parquet-column/src/test/java/org/apache/parquet/column/values/rle/TestRunLengthBitPackingHybridEncoder.java @@ -22,6 +22,7 @@ import java.io.ByteArrayInputStream; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.apache.parquet.bytes.BytesUtils; import org.apache.parquet.bytes.DirectByteBufferAllocator; @@ -379,7 +380,7 @@ public void testTruncatedPackedRunAfterFullPackedRunDoesNotReuseStaleBytes() thr new RunLengthBitPackingHybridDecoder(bitWidth, new ByteArrayInputStream(encoded)); for (int ignored = 0; ignored < 8; ignored++) { - assertEquals(7, decoder.readInt()); + assertThat(decoder.readInt()).isEqualTo(7); } int[] actualSecondRun = new int[8]; @@ -392,7 +393,7 @@ public void testTruncatedPackedRunAfterFullPackedRunDoesNotReuseStaleBytes() thr int[] expectedSecondRun = new int[8]; packer.unpack8Values(expectedSecondPacked, 0, expectedSecondRun, 0); - assertArrayEquals(expectedSecondRun, actualSecondRun); + assertThat(actualSecondRun).containsExactly(expectedSecondRun); } private static List unpack(int bitWidth, int numValues, ByteArrayInputStream is) throws Exception {