This is an automated email from the ASF dual-hosted git repository.

pitrou pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow.git


The following commit(s) were added to refs/heads/main by this push:
     new 0efbdec7a10 GH-51223: [C++] Fix copying sliced boolean arrays (#51240)
0efbdec7a10 is described below

commit 0efbdec7a10da1a4043fc1210c61e73c88bc9b4f
Author: Abdul Azeem Makarim <[email protected]>
AuthorDate: Wed Sep 23 14:35:43 2026 +0100

    GH-51223: [C++] Fix copying sliced boolean arrays (#51240)
    
    ### Rationale for this change
    
    Boolean values are bit-packed. When copying values from a sliced boolean 
array,
    the copy path did not include the array's slice offset. This caused
    `fill_null_forward`, `fill_null_backward`, and `replace_with_mask` to read
    values from earlier positions in the parent array.
    
    ### What changes are included in this PR?
    
    - Apply `ArraySpan::offset` to the bit index when copying boolean values 
from
      an `ArraySpan`.
    - Add regression coverage for sliced boolean inputs in `replace_with_mask`,
      `fill_null_forward`, and `fill_null_backward`.
    
    ### Are these changes tested?
    
    Yes. I ran `arrow-compute-vector-test` locally.
    
    ### Are there any user-facing changes?
    
    Only a bugfix.
    
    ### This PR contains a "Critical Fix".
    
    This fixes a bug that caused compute operations on sliced boolean arrays 
with
    a non-zero offset to produce incorrect values.
    
    * GitHub Issue: #51223
    
    Authored-by: Abdul Azeem Makarim 
<[email protected]>
    Signed-off-by: Antoine Pitrou <[email protected]>
---
 cpp/src/arrow/compute/kernels/copy_data_internal.h   |  4 +++-
 cpp/src/arrow/compute/kernels/vector_replace_test.cc | 18 ++++++++++++++++++
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/cpp/src/arrow/compute/kernels/copy_data_internal.h 
b/cpp/src/arrow/compute/kernels/copy_data_internal.h
index 735c2034c3f..3dd8e14c699 100644
--- a/cpp/src/arrow/compute/kernels/copy_data_internal.h
+++ b/cpp/src/arrow/compute/kernels/copy_data_internal.h
@@ -42,8 +42,10 @@ struct CopyDataUtils<BooleanType> {
 
   static void CopyData(const DataType&, const ArraySpan& in, const int64_t 
in_offset,
                        uint8_t* out, const int64_t out_offset, const int64_t 
length) {
+    // Boolean values are bit-packed, so apply the array's offset to the bit 
index
+    // rather than to the data pointer.
     const auto in_arr = in.GetValues<uint8_t>(1, /*absolute_offset=*/0);
-    CopyData(*in.type, in_arr, in_offset, out, out_offset, length);
+    CopyData(*in.type, in_arr, in.offset + in_offset, out, out_offset, length);
   }
 };
 
diff --git a/cpp/src/arrow/compute/kernels/vector_replace_test.cc 
b/cpp/src/arrow/compute/kernels/vector_replace_test.cc
index dc63bae39a5..65cb9126b15 100644
--- a/cpp/src/arrow/compute/kernels/vector_replace_test.cc
+++ b/cpp/src/arrow/compute/kernels/vector_replace_test.cc
@@ -545,6 +545,14 @@ TEST_F(TestReplaceBoolean, ReplaceWithMask) {
   }
 }
 
+TEST_F(TestReplaceBoolean, ReplaceWithMaskSlicedInput) {
+  auto input =
+      this->array("[true, false, null, true, true, false, null, 
true]")->Slice(3, 5);
+
+  this->Assert(ReplaceWithMask, input, this->mask("[true, false, false, false, 
false]"),
+               this->array("[false]"), this->array("[false, true, false, null, 
true]"));
+}
+
 // Regression test: ReplaceMaskChunked (the ChunkedArray path of 
replace_with_mask)
 // sized each output chunk's data buffer via byte_width(), which is 0 for 
boolean
 // (bit-packed), the same GH-45086 buffer-overflow pattern fixed elsewhere in 
this
@@ -2117,6 +2125,16 @@ TYPED_TEST(TestFillNullBinary, FillBackwardChunkedArray) 
{
                            R"(["qup"])", R"(["qup", "mnz"])"}));
 }
 
+TEST_F(TestFillNullBoolean, FillNullSlicedArray) {
+  auto input =
+      this->array("[true, false, null, true, true, false, null, 
true]")->Slice(3, 5);
+
+  this->AssertFillNullArray(FillNullForward, input,
+                            this->array("[true, true, false, false, true]"));
+  this->AssertFillNullArray(FillNullBackward, input,
+                            this->array("[true, true, false, true, true]"));
+}
+
 // Regression test for GH-45086: FillNullForwardChunked/FillNullBackwardChunked
 // size each output chunk's data buffer as `type->byte_width() * 
chunk->length()`.
 // For BooleanType, byte_width() returns 0 (it is bit-packed, not 
byte-addressable),

Reply via email to