AndreaBozzo opened a new pull request, #10916:
URL: https://github.com/apache/arrow-rs/pull/10916

   # Which issue does this PR close?
   
   Closes #10910.
   
   # Rationale for this change
   
   `ImportedArrowArray::buffer_len` short-circuited the values buffer (buffer 
2) of
   `Utf8`/`Binary` and `LargeUtf8`/`LargeBinary` arrays to `0` whenever
   `ArrowArray.length` was `0`, without taking `ArrowArray.offset` into 
account. The
   offsets, however, are read *at* the array offset, so importing a zero-length 
array
   at a non-zero offset produced an `ArrayData` whose values buffer is shorter 
than
   its own offsets describe. `from_ffi` builds it with `new_unchecked`, so 
nothing is
   reported at import time; the inconsistency only surfaces once something 
validates
   the result:
   
   ```
   Invalid argument error: First offset 1 of Utf8 is larger than values length 0
   ```
   
   The shape is not reachable through arrow-rs's own slicing — `slice(n, 0)` 
normalises
   the offset back to 0 — so it takes a producer on the other side of the 
interface.
   pyarrow produces one readily with `pa.array(["x", "aa", "bb"]).slice(1, 0)`, 
and that
   is how this was found.
   
   The `is_empty()` guard came in with #5964 ("Fix FFI array offset handling"), 
which
   changed the values length from `end - start` to `end`. It exists so that a 
length-0
   array whose offsets buffer carries no meaningful last offset is not sized 
from it —
   the C Data Interface permits a null pointer for a buffer whose size in bytes 
would
   be 0, and producers also emit arbitrary trailing offsets for empty arrays 
(see the
   existing `test_empty_string_with_non_zero_offset`, whose lone offset is 
`123`). That
   reasoning only holds at offset 0: with a non-zero offset the producer must 
supply
   `offset + length + 1` offsets, so the buffer is present and its window has a 
real
   last offset.
   
   # What changes are included in this PR?
   
   Narrow the guard to `offset == 0` in both arms, so a zero-length array at a 
non-zero
   offset takes the same path as a non-empty one and reads its values length 
from the
   last offset of the window.
   
   The stale safety comment above the `- 1` is corrected too: it claimed the 
array is
   non-empty, whereas the invariant that makes the index sound is `len + offset 
>= 1`.
   
   The list types are untouched: for `List`/`LargeList`/`Map`, buffer 2 is 
child data
   rather than a buffer, so `buffer_len` never reaches these arms for them.
   
   # Are these changes tested?
   
   Yes — `tests_from_ffi::test_zero_length_bytes_at_non_zero_offset` sweeps 
offsets
   `0..4` over `Utf8`, `Binary`, `LargeUtf8` and `LargeBinary`, asserting the 
round trip
   passes `validate_full`, matches the exported `ArrayData`, and sizes the 
values buffer
   from the window's last offset.
   
   I checked that it fails without the fix, and that it discriminates the two 
arms
   independently: reverting only the `i32` arm fails on `Utf8`, reverting only 
the `i64`
   arm fails on `LargeUtf8`, both with the error above. It does not separately
   discriminate `Binary` from `Utf8` (the loop stops at the first failure), but 
each pair
   shares a single match arm, so there is no distinct code path uncovered.
   
   `cargo test -p arrow-array --features ffi` is green (771 unit + 205 doc 
tests), as is
   `--features ffi,force_validate`; `cargo fmt --check` and `cargo clippy 
--all-targets`
   are clean.
   
   One thing I looked at while reviewing: whether widening the dereference can 
now be
   reached with a null offsets pointer. It cannot. `buffers()` resolves buffer 
1 before
   buffer 2 (`map` is lazy and `collect::<Result<_>>` short-circuits), and 
`buffer_len(1)`
   is `(length + 1) * width`, never 0 — so a null offsets pointer always fails 
at index 1
   with `The external buffer at position 1 is null.` before `buffer_len(2)` is 
called.
   
   # Are there any user-facing changes?
   
   No API changes. Zero-length `Utf8`/`Binary`/`LargeUtf8`/`LargeBinary` arrays 
imported
   at a non-zero offset now yield a valid `ArrayData` instead of one that fails
   validation. There is no breaking change.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to