When the gather map passed to cudf::lists::segmented_gather is a sliced view, the result column has non-canonical offsets: its offsets child starts at a nonzero value even though the materialized column's own offset is 0.
cpp/src/lists/copying/segmented_gather.cu builds the output offsets by copying gather_map.offsets() entries [gather_map.offset(), +size+1) verbatim. For a map sliced at row k, those entries are the parent buffer's cumulative counts starting at o_k, so the result has offsets[0] == K != 0 and offsets.back() != child.size(). The gathered child itself has exactly the sliced element count, so every consumer that interprets offset values as child positions reads out of bounds or misaddressed rows.
Reproduced with libcudf 26.10 nightly: values [10,11],[22],[33,44,55], full map rows {0},{1},{2},{0,1},{2} sliced to rows 2..4 returned a column whose raw offsets child was [2,4,5,7] while an unsliced control returned [0,2,3,5]. Consuming row by row through the stored spans read past the end of the child buffer. The gathered content bytes were correct; only the offsets are shifted.
Note that existing test coverage misses this because CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT normalizes each side by its own first offset before comparing, so a shifted result always compares equal.
Suggested fix: subtract the first copied entry (the value at gather_map.offsets()[gather_map.offset()]) from all copied output offsets, which is a no-op when the map is unsliced since offsets[0] is then 0.
When the gather map passed to
cudf::lists::segmented_gatheris a sliced view, the result column has non-canonical offsets: its offsets child starts at a nonzero value even though the materialized column's own offset is 0.cpp/src/lists/copying/segmented_gather.cubuilds the output offsets by copyinggather_map.offsets()entries[gather_map.offset(), +size+1)verbatim. For a map sliced at row k, those entries are the parent buffer's cumulative counts starting ato_k, so the result hasoffsets[0] == K != 0andoffsets.back() != child.size(). The gathered child itself has exactly the sliced element count, so every consumer that interprets offset values as child positions reads out of bounds or misaddressed rows.Reproduced with libcudf 26.10 nightly: values
[10,11],[22],[33,44,55], full map rows{0},{1},{2},{0,1},{2}sliced to rows 2..4 returned a column whose raw offsets child was[2,4,5,7]while an unsliced control returned[0,2,3,5]. Consuming row by row through the stored spans read past the end of the child buffer. The gathered content bytes were correct; only the offsets are shifted.Note that existing test coverage misses this because
CUDF_TEST_EXPECT_COLUMNS_EQUIVALENTnormalizes each side by its own first offset before comparing, so a shifted result always compares equal.Suggested fix: subtract the first copied entry (the value at
gather_map.offsets()[gather_map.offset()]) from all copied output offsets, which is a no-op when the map is unsliced sinceoffsets[0]is then 0.