Skip to content

Unbind cudf::size_type from offsets used by list columns - #23607

Open
davidwendt wants to merge 4 commits into
NVIDIA:mainfrom
davidwendt:offsets-type
Open

Unbind cudf::size_type from offsets used by list columns#23607
davidwendt wants to merge 4 commits into
NVIDIA:mainfrom
davidwendt:offsets-type

Conversation

@davidwendt

Copy link
Copy Markdown
Contributor

Description

In preparation for evaluating a change of cudf::size_type from int32_t to int64_t, this PR decouples LIST column offsets from size_type.

Per the Apache Arrow columnar format, the offsets buffer width is part of the type, not an implementation detail — List<T> has int32 offsets and LargeList<T> has int64 offsets, and these are distinct types:

"A list type is specified like List, where T is any type (primitive or nested). In these
examples we use 32-bit offsets where the 64-bit offset version would be denoted by
LargeList."

and from format/Schema.fbs, on LargeList:

"Same as List, but with 64-bit offsets, allowing to represent extremely large data values."

The same split appears in the C Data Interface format strings: +l is a list with int32 offsets, +L is a large list with int64 offsets.

libcudf has type_id::LIST and no LARGE_LIST. So a LIST column's offsets child is int32 by definition of the type — not "int32 because that happens to be size_type today."

Many places in the code, however, spelled that type as cudf::size_type, via type_to_id<size_type>(), data<size_type>(), element<size_type>(), size_type const*, etc. Those uses are correct only by the coincidence that size_type == int32_t. Under a 64-bit size_type they would either allocate INT64 offsets children — producing columns cuDF cannot export to Arrow, and that contiguous_split and the JNI layer would misinterpret — or reinterpret an int32 buffer as int64. Both are silent data corruption rather than a compile error, which is what makes them worth flushing out ahead of any size_type change rather than during one.

This PR replaces those with explicit int32_t / type_id::INT32 at LIST offsets creation and read sites.

STRING offsets are deliberately untouched: they are already dynamically int32/int64 and are handled through the offsetalator.

Why this isn't a new constraint: The codebase already depends on 32-bit list offsets everywhere it is forced to be explicit.
This PR makes that existing, already-relied-upon invariant explicit at the sites that were spelling it as size_type.

Follow-on work would now allow support of LARGE_LIST in libcudf without explicitly adding a new type_id by simply checking the column's child offset type much like how LARGE_STRING is supported in interop today. The offsetalator would similarly be employed to read/write the offset values correctly without need a special dispatch for the column type.

Checklist

  • I am familiar with the Contributing Guidelines.
  • New or existing tests cover these changes.
  • The documentation is up to date with these changes.

@davidwendt davidwendt self-assigned this Aug 10, 2026
@davidwendt davidwendt added the 3 - Ready for Review Ready for review by team label Aug 10, 2026
@davidwendt
davidwendt requested a review from a team as a code owner August 10, 2026 15:57
@davidwendt davidwendt added the libcudf Affects libcudf (C++/CUDA) code. label Aug 10, 2026
@davidwendt
davidwendt requested a review from a team as a code owner August 10, 2026 15:57
@davidwendt davidwendt added improvement Improvement / enhancement to an existing function non-breaking Non-breaking change labels Aug 10, 2026
@github-actions github-actions Bot added the Java Affects Java cuDF API. label Aug 10, 2026
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Refactor
    • Standardized list, string, and t-digest offset handling to fixed 32-bit storage across supported operations and data formats.
    • Improved consistency when reading, generating, slicing, gathering, grouping, and exporting nested data.
  • Bug Fixes
    • Added validation to detect offset totals that exceed the supported 32-bit range, preventing invalid results from oversized columns.
    • Clarified the documented offset format for list columns.

Walkthrough

The PR standardizes list, string, JSON, and TDigest offsets on fixed-width INT32 storage and access. It adds int32_t overflow validation, updates public views and helpers, and propagates the type through list operations, I/O, analytics, and Java JNI paths.

Changes

Fixed-width offset migration

Layer / File(s) Summary
Offset contracts and validation
cpp/include/cudf/column/column_factories.hpp, cpp/include/cudf/detail/sizes_to_offsets_iterator.cuh, cpp/include/cudf/lists/*view*, cpp/include/cudf_test/column_wrapper.hpp, cpp/src/io/json/nested_json.hpp
Documentation, public offset iterators, JSON child-offset storage, test helpers, and offset construction now use INT32 and validate cumulative values against int32_t.
List construction and transformations
cpp/include/cudf/lists/detail/*, cpp/src/lists/*, cpp/src/groupby/*, cpp/src/rolling/detail/*
List factories, copying, gathering, scattering, nesting, concatenation, interleaving, sequencing, rolling, groupby, and offset utilities now allocate and access offsets as INT32.
I/O and analytical consumers
cpp/src/io/*, cpp/src/strings/*, cpp/src/transform/*, cpp/src/hash/*, cpp/src/text/*
JSON, ORC, statistics, string processing, row transforms, hashing, and text processing now interpret list offsets as int32_t.
Interop and TDigest propagation
java/src/main/native/src/*, cpp/src/quantiles/tdigest/*, cpp/include/cudf/tdigest/*
Java JNI casts and generated columns use INT32; TDigest public views, kernels, spans, pinned buffers, and aggregation paths use int32_t.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested reviewers: shrshi, pointkernel, bdice

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.68% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely states the main change: separating LIST column offsets from cudf::size_type.
Description check ✅ Passed The description directly explains the LIST offset changes, their rationale, affected types, and preparation for a possible size_type change.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cpp/src/groupby/sort/group_collect.cu (1)

66-70: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Declare explicit return types on the changed device lambdas.

Both changed lambdas are passed to device algorithms. Their return types should be explicit to prevent type deduction from becoming part of the CUDA algorithm contract.

  • cpp/src/groupby/sort/group_collect.cu#L66-L70: declare the lambda return type as size_type.
  • cpp/src/rolling/detail/rolling_collect_list.cu#L61-L65: declare the lambda return type as bool.

As per coding guidelines, extended device lambdas passed to device algorithms must declare explicit return types.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/src/groupby/sort/group_collect.cu` around lines 66 - 70, Declare explicit
return types on both changed device lambdas passed to device algorithms: in
cpp/src/groupby/sort/group_collect.cu lines 66-70, make the lambda returning the
null-count result return size_type; in
cpp/src/rolling/detail/rolling_collect_list.cu lines 61-65, make the
corresponding lambda return bool.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cpp/include/cudf/detail/sizes_to_offsets_iterator.cuh`:
- Around line 289-292: Update the final STRING-offset construction in
ngrams_tokenize to call cudf::strings::detail::make_offsets_child_column instead
of the generic sizes-to-offsets helper, preserving the configured dynamic offset
type and allowing large-string totals. Keep the existing tokenization and offset
values unchanged.
- Around line 321-322: Prevent oversized cudf::size_type values from wrapping
before validation: in the generic path around the device lambda in
sizes_to_offsets_iterator.cuh, keep the scan input wide and perform the checked
int32_t conversion only after validating against total_elements; in
cpp/include/cudf/lists/detail/scatter.cuh lines 234-239, replace the direct
lv->view().size() narrowing with a checked int32_t step and preserve the
existing range-validation behavior.

In `@cpp/src/groupby/sort/group_collect.cu`:
- Around line 88-94: Enforce checked INT32 range handling for LIST offsets at
all four sites: in cpp/src/groupby/sort/group_collect.cu lines 88-94, validate
every group_offsets value before copying into the INT32 offsets column; in
cpp/include/cudf_test/column_wrapper.hpp lines 1614-1629, validate cumulative
nested child sizes before int32_t conversion; in
cpp/include/cudf_test/column_wrapper.hpp lines 1669-1675, validate c->size()
before constructing the INT32 offsets column; and in
cpp/src/io/utilities/column_buffer.cpp lines 107-110, reject or safely
checked-convert oversized string offsets before creating the LIST column. Ensure
no conversion can wrap or produce invalid memory access.

In `@cpp/src/lists/combine/concatenate_rows.cu`:
- Around line 108-116: Guard every size_type-to-int32_t LIST offset conversion:
in cpp/src/lists/combine/concatenate_rows.cu lines 108-116, accumulate into a
wider temporary and reject cumulative child counts above INT32_MAX before the
exclusive scan; in cpp/src/lists/copying/concatenate.cu lines 66-72, accumulate
shift in a wide type, validate its range, and return an explicit int32_t from
the device transform; in cpp/src/io/json/host_tree_algorithms.cu line 216,
validate row_offsets before scattering and use an int32-compatible scan; in
cpp/src/lists/utilities.cu lines 35-38, ensure labels_to_offsets rejects
label-derived values outside the int32_t range.

In `@cpp/src/lists/copying/copying.cu`:
- Around line 43-45: Keep LIST offset storage explicitly int32_t at all affected
sites: in cpp/src/lists/copying/copying.cu lines 43-45, allocate out_offsets as
rmm::device_uvector<int32_t>; in
cpp/src/lists/combine/concatenate_list_elements.cu lines 50-52, create the
empty-inner-child fallback with int32_t zero offsets; and in
cpp/src/io/json/parser_features.cpp line 78, use a LIST-specific int32_t
zero-offset helper while leaving string offsets unchanged.

In `@cpp/src/lists/sequences.cu`:
- Line 159: Update the internal LIST offset pointer declarations in
tabulator::offsets, sequences_dispatcher::operator(), and
sequences_functor::invoke from size_type const* to int32_t const*. Preserve
size_type for row counts and element indices so LIST offsets remain independent
of cudf::size_type.

In `@java/src/main/native/src/ColumnViewJni.cu`:
- Around line 182-183: Update the generate_list_offsets implementation to read
the validated INT32 list_length input using int32_t iterators, replacing the
cudf::size_type begin/end iterator types while preserving the existing
offset-generation logic.

---

Outside diff comments:
In `@cpp/src/groupby/sort/group_collect.cu`:
- Around line 66-70: Declare explicit return types on both changed device
lambdas passed to device algorithms: in cpp/src/groupby/sort/group_collect.cu
lines 66-70, make the lambda returning the null-count result return size_type;
in cpp/src/rolling/detail/rolling_collect_list.cu lines 61-65, make the
corresponding lambda return bool.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 32ae0536-4e3a-478e-ba27-395feed032d0

📥 Commits

Reviewing files that changed from the base of the PR and between 1474ca0 and 6d369ec.

📒 Files selected for processing (46)
  • cpp/include/cudf/column/column_factories.hpp
  • cpp/include/cudf/detail/sizes_to_offsets_iterator.cuh
  • cpp/include/cudf/lists/detail/gather.cuh
  • cpp/include/cudf/lists/detail/scatter.cuh
  • cpp/include/cudf/lists/list_device_view.cuh
  • cpp/include/cudf/lists/lists_column_device_view.cuh
  • cpp/include/cudf/lists/lists_column_view.hpp
  • cpp/include/cudf/tdigest/tdigest_column_view.hpp
  • cpp/include/cudf_test/column_wrapper.hpp
  • cpp/src/groupby/groupby.cu
  • cpp/src/groupby/sort/group_collect.cu
  • cpp/src/groupby/sort/group_merge_lists.cu
  • cpp/src/hash/md5_hash.cu
  • cpp/src/io/json/host_tree_algorithms.cu
  • cpp/src/io/json/nested_json.hpp
  • cpp/src/io/json/parser_features.cpp
  • cpp/src/io/orc/stripe_enc.cu
  • cpp/src/io/orc/writer_impl.cu
  • cpp/src/io/statistics/statistics.cuh
  • cpp/src/io/utilities/column_buffer.cpp
  • cpp/src/io/utilities/column_buffer_strings.cu
  • cpp/src/lists/combine/concatenate_list_elements.cu
  • cpp/src/lists/combine/concatenate_rows.cu
  • cpp/src/lists/copying/concatenate.cu
  • cpp/src/lists/copying/copying.cu
  • cpp/src/lists/copying/gather.cu
  • cpp/src/lists/copying/scatter_helper.cu
  • cpp/src/lists/dremel.cu
  • cpp/src/lists/extract.cu
  • cpp/src/lists/interleave_columns.cu
  • cpp/src/lists/lists_column_factories.cu
  • cpp/src/lists/lists_column_view.cu
  • cpp/src/lists/reverse.cu
  • cpp/src/lists/segmented_sort.cu
  • cpp/src/lists/sequences.cu
  • cpp/src/lists/stream_compaction/apply_boolean_mask.cu
  • cpp/src/lists/utilities.cu
  • cpp/src/rolling/detail/rolling_collect_list.cu
  • cpp/src/rolling/detail/rolling_collect_list.cuh
  • cpp/src/rolling/detail/rolling_operators.cuh
  • cpp/src/strings/convert/convert_lists.cu
  • cpp/src/strings/repeat_strings.cu
  • cpp/src/transform/row_bit_count.cu
  • cpp/tests/groupby/collect_list_tests.cpp
  • java/src/main/native/src/ColumnViewJni.cpp
  • java/src/main/native/src/ColumnViewJni.cu

Comment thread cpp/include/cudf/detail/sizes_to_offsets_iterator.cuh
Comment thread cpp/include/cudf/detail/sizes_to_offsets_iterator.cuh Outdated
Comment thread cpp/src/groupby/sort/group_collect.cu
Comment thread cpp/src/lists/combine/concatenate_rows.cu
Comment thread cpp/src/lists/copying/copying.cu
Comment thread cpp/src/lists/sequences.cu
Comment thread java/src/main/native/src/ColumnViewJni.cu

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
cpp/src/quantiles/tdigest/tdigest_util.cuh (1)

18-18: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Include <cstdint> directly in this header.

This header now declares int32_t, but it does not include <cstdint>. Add the direct include instead of relying on transitive includes.

As per coding guidelines, include headers directly for every used symbol.

Proposed include
 `#pragma` once
 
+#include <cstdint>
+
 `#include` <cudf/detail/iterator.cuh>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/src/quantiles/tdigest/tdigest_util.cuh` at line 18, Update the header
containing the t-digest offsets declaration to directly include the standard
<cstdint> header before using int32_t, without relying on transitive includes.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cpp/src/quantiles/tdigest/tdigest.cu`:
- Line 362: In cpp/src/quantiles/tdigest/tdigest.cu at lines 362-362, reject
input.size() == INT32_MAX before allocating input.size() + 1 offsets; for
non-empty output, compute input.size() * percentiles.size() in a widened type
and reject values above INT32_MAX before num_output_values, grid sizing, and
exclusive scan. In cpp/src/strings/search/find_multiple.cu at lines 72-73,
reject strings_count == INT32_MAX before evaluating strings_count + 1, while
preserving the existing product check for the final offset.

---

Nitpick comments:
In `@cpp/src/quantiles/tdigest/tdigest_util.cuh`:
- Line 18: Update the header containing the t-digest offsets declaration to
directly include the standard <cstdint> header before using int32_t, without
relying on transitive includes.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 09ad97ba-5310-4520-99d2-f94c3b951c60

📥 Commits

Reviewing files that changed from the base of the PR and between b7f310a and 3d4ca87.

📒 Files selected for processing (5)
  • cpp/src/quantiles/tdigest/tdigest.cu
  • cpp/src/quantiles/tdigest/tdigest_aggregation.cu
  • cpp/src/quantiles/tdigest/tdigest_util.cuh
  • cpp/src/strings/search/find_multiple.cu
  • cpp/src/text/minhash.cu

Comment thread cpp/src/quantiles/tdigest/tdigest.cu

@PointKernel PointKernel left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes look good to me. For my own learning, @davidwendt, how did you find all the places where size_type was being misused as an offset type? Or was it mostly AI effort?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3 - Ready for Review Ready for review by team improvement Improvement / enhancement to an existing function Java Affects Java cuDF API. libcudf Affects libcudf (C++/CUDA) code. non-breaking Non-breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants