Skip to content

join: report the field number the user gave - #13995

Open
MsfPablo wants to merge 2 commits into
uutils:mainfrom
MsfPablo:join-exact-field-numbers
Open

join: report the field number the user gave#13995
MsfPablo wants to merge 2 commits into
uutils:mainfrom
MsfPablo:join-exact-field-numbers

Conversation

@MsfPablo

Copy link
Copy Markdown
Contributor

Fixes #13376.

Both halves of the issue come down to the reported field number not matching the input.

The k + 1 overflow. parse_field_number deliberately clamps an out-of-range field to usize::MAX, and get_field_number then renders it one-based with k + 1. That aborts under overflow-checks and wraps to 0 in a normal build. Now saturates.

The rounding. This one is not join-specific, so I fixed it where it lives. translate! stringifies a value, re-parses it as i64 or f64, and sets it on Fluent as a number — but FluentNumber is f64-backed, so anything past 2^53 rounds. Note that taking the i64 branch is not enough to stay exact; -j 9007199254740993 parses fine as i64 and still comes out as …992. So the check is whether f64 can hold the value exactly, not whether i64 can:

  • exactly representable → still set as a number, so plural rules and number formatting are untouched
  • an integer past that → passed through as its exact decimal string
  • anything else → unchanged

That also fixes the csplit case you mentioned:

$ printf 'a\nb\nc\n' | csplit - 9007199254740995 9007199254740995
csplit: warning: line number '9007199254740995' is the same as preceding line number

All the values from the issue now match GNU, including 18446744073709551615 for the clamped case.

Tests: unit tests in locale.rs for the representability boundary, and an integration test in test_join.rs covering the four field values above.

Since this touches uucore, I ran the suites for csplit, nl, split, head, expr, seq, tail, wc, fold and sort as well as join — 982 tests, all passing.

Two ways the 'incompatible join fields' message diverged from its input.

parse_field_number clamps an out-of-range field to usize::MAX, and
get_field_number rendered it one-based with k + 1: an abort under
overflow-checks, a wrap to 0 otherwise. Saturate instead.

Separately, translate! stringifies a value and re-parses it as i64 or
f64 before handing it to Fluent, whose number type is f64-backed. Every
integer above 2^53 was silently rounded. Route those through as exact
decimal strings, keeping the number path for values f64 holds exactly so
plural rules and number formatting are unaffected.

Fixes uutils#13376
@github-actions

Copy link
Copy Markdown

GNU testsuite comparison:

Skip an intermittent issue tests/date/date-locale-hour (fails in this run but passes in the 'main' branch)
Skip an intermittent issue tests/tail/tail-n0f (fails in this run but passes in the 'main' branch)
Skipping an intermittent issue tests/cut/bounded-memory (passes in this run but fails in the 'main' branch)
Skipping an intermittent issue tests/date/resolution (passes in this run but fails in the 'main' branch)

@codspeed-hq

codspeed-hq Bot commented Aug 17, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 13.22%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 5 improved benchmarks
✅ 231 untouched benchmarks
⏩ 171 skipped benchmarks1

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Simulation shuf_lines[100000] 72.1 ms 54.7 ms +31.81%
Simulation shuf_repeat_sampling[50000] 14 ms 12.3 ms +14.2%
Simulation expand_many_lines[100000] 116.4 ms 107.3 ms +8.55%
Simulation expand_custom_tabstops[50000] 31.5 ms 29.2 ms +7.93%
Simulation cksum_crc32b 42.4 ms 40.2 ms +5.49%

Tip

Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.


Comparing MsfPablo:join-exact-field-numbers (bfd55ff) with main (b2a617e)2

Open in CodSpeed

Footnotes

  1. 171 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on main (e275cd5) during the generation of this report, so b2a617e was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

parse_field_number uses usize (matching GNU join's size_t), so a field
value at or above usize::MAX saturates to it. The previous test hard-coded
64-bit saturation ceilings (u64::MAX and 2^53+1), which failed on 32-bit
targets (i686, i686-musl, i686-windows) where usize::MAX is 4294967295.

Build the expected text from usize::MAX and gate the sub-usize::MAX
above-f64-precision case on target_pointer_width.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

join: incompatible-fields error reports a wrong field number for a large -j/-1/-2

1 participant