Collin Funk [Sat, 9 May 2026 22:43:39 +0000 (15:43 -0700)]
shred: don't block when opening FIFOs with no readers
* NEWS: Mention the bug fix.
* src/shred.c (wipefile): Open the file with O_NONBLOCK.
* tests/shred/fifo.sh: New file.
* tests/local.mk (all_tests): Add the new test.
Pádraig Brady [Fri, 8 May 2026 18:39:00 +0000 (19:39 +0100)]
doc: help2man: preserve layout of tables
This is useful in many man pages like date , dd, od, tr, ...
where there are tables presented, where extraneous lines
between each entry are best avoided.
* man/help2man: Use .PD 0 (Paragraph Distance)
to avoid extraneous blank lines within .TP delineated tables.
Also use explicit widths with .TP in such tables,
to preserve the alignment from the --help output.
* src/shuf.c (print_number): New function.
(write_permuted_numbers, write_random_numbers): Use it.
* tests/shuf/shuf.sh: Add a test case to run 'shuf -i' with varying
numbers of digits to check that the string conversion is correct.
* tests/ls/acl.sh: Check that the gap between the '+' indicator and
the link count is the same whether the listing contains one or
several ACL entries.
https://github.com/coreutils/coreutils/pull/251
Pádraig Brady [Mon, 4 May 2026 14:34:32 +0000 (15:34 +0100)]
ls: exclude newline from consideration in line width
* src/ls.c (calculate_columns): Treat width as inclusive max.
(print_with_separator): Likewise. Also handle commas explicitly,
since they're not catered for by an implicit newline in the count.
* tests/ls/w-option.sh: Adjust exact-fit column tests.
Note this change also makes the existing `ls -w4 -x -T0 a b` test
behave consistently with other output width limits.
Also add the test case from:
https://github.com/coreutils/coreutils/pull/213
* tests/ls/m-option.sh: Add a test case to ensure appropriate
wrapping when trailing comma at the line limit.
* NEWS: Mention the change in behavior.
Pádraig Brady [Mon, 4 May 2026 14:34:01 +0000 (15:34 +0100)]
ls: fix too few display columns edge case
In the edge case where the right most column would
consist of only files taking 1 or 2 cells, we accounted
for 3 cells, which would result in using 1 column too few.
This should have been part of commit v8.24-59-ge71be1292
* src/ls.c (init_column_info): Don't account for separator
in the the final column.
* tests/ls/w-option.sh: Add a test case.
Pádraig Brady [Sun, 3 May 2026 10:28:19 +0000 (11:28 +0100)]
tests: tee: ensure tee is non buffered
* tests/tee/tee.sh: tests/misc/responsive.sh only confirms
responsiveness to lines, so add a test for non line terminated.
https://github.com/coreutils/coreutils/pull/259
Previously the string from strerror would only be printed for some
algorithms:
$ cksum -a sha2 -l 256 /dev/null > /dev/full
cksum: write error: No space left on device
$ cksum -a sysv /dev/null > /dev/full
cksum: write error
This patch fixes it so that the error information is always printed:
$ ./src/cksum -a sha2 -l 256 /dev/null > /dev/full
cksum: write error: No space left on device
$ ./src/cksum -a sysv /dev/null > /dev/full
cksum: write error: No space left on device
* src/cksum.c (output_file): Don't check for a write error here.
(main): Check for it here instead.
* tests/misc/io-errors.sh: Add a 'cksum' invocation for each supported
algorithm.
https://github.com/coreutils/coreutils/issues/258
* src/unexpand.c (unexpand): Use xinmalloc() to gracefully
handle overflow. Also use the runtime locale specific MB_CUR_MAX
rather than the worst case MB_LEN_MAX.
* tests/unexpand/mb.sh: Add a test case that fails in a default
glibc build with either MB_CUR_MAX or MB_LEN_MAX.
* NEWS: Mention the bug fix.
Reported by Michał Majchrowicz.
* src/cut.c: We use the mcel interface to populate these arrays,
so restrict to the mcel supported max. This is more efficient
as generally stateful encodings are not used or supported.
sort: use more dynamic memory allocation with pipes
The default memory allocation with pipes was too passive/static,
resulting in not allocating enough memory to enable threading.
By dynamically reallocating the buffer when reading from
unknown sized inputs we better use available memory and threads.
$ time seq 10000000 -1 0 | sort-old >/dev/null
real 0m16.523s
user 0m16.900s
sys 0m0.167s
$ time seq 10000000 -1 0 | sort-old -S1G >/dev/null
real 0m12.263s
user 0m29.646s
sys 0m0.527s
$ time seq 10000000 -1 0 | sort-new >/dev/null
real 0m12.994s
user 0m31.266s
sys 0m0.716s
It also avoids the overhead of writing to temp files
for modestly sized inputs. For example the following
input would induce interaction with temp storage:
$ seq 125000 | wc -c
763895
* src/sort.c (sort_buffer_size): Rename to ...
(sort_buffer_policy): ... here, and adjust to set
an initial size and limit, rather than just a size.
(fillbuf): Add a POLICY parameter, and use that
to call maybe_growbuf() as needed.
(maybe_growbuf): Return true if POLICY dictates we
should grow the buffer, and try_growbuf() was
able to reallocate the larger buffer.
* tests/sort/sort-buffer-size.sh: Add a new test.
* tests/local.mk: Reference new test.
* NEWS: Mention the improvement.
Related to https://bugs.gnu.org/10877
doc: apply man page formatting suggestions from manpage-l10n
* src/basenc.c: Add (1) to base64 and base32 references.
* src/cksum.c. Likewise for cksum references.
* src/echo.c: Use "bell" in descriptions rather than BEL.
* src/printf.c: Likewise.
* src/id.c: Separate -u,-U,-G so marked up appropriately.
Fixes https://bugs.gnu.org/80904
tests: all: ensure closed stdout is handled appropriately
* tests/misc/io-errors.sh: Add a check to ensure we diagose
writing to closed stdout. Also ensure we exit with failure
in other cases.
* tests/misc/write-errors.sh: Likewise.
Bruno Haible [Fri, 24 Apr 2026 13:36:51 +0000 (15:36 +0200)]
doc: remove redundant "Report translation bugs to" in --help
* configure.ac: Define PACKAGE_L10N_BUGREPORT.
* man/local.mk (run_help2man): Set the IN_HELP2MAN environment variable.
* src/system.h (emit_ancillary_info): Don't emit "Report any translation
bugs to" line; this is already done by emit_bug_reporting_address() in
version-etc.c.
Fixes https://bugs.gnu.org/80886
I noticed a single failure on cfarm29 (Linux 6.12 ppc64le),
where the output was missing:
-tail: directory containing watched file was removed
-tail: inotify cannot be used, reverting to polling
-tail: 'dir/file' has appeared; following new file
* tests/tail/inotify-dir-recreate.sh: Leverage the new tail --debug
output to sync to a point where we know the inotify watches are in
place. Also normalize the file argument quoting a bit.
* src/dd.c (parse_integer): Use iterative rather than recursive parsing,
to avoid potential stack overflow.
* tests/dd/bytes.sh: Add a test case.
https://github.com/coreutils/coreutils/issues/254
* NEWS: Mention the bug fix.
* src/comm.c (usage): Remove mention that FILE1 and FILE2 cannot both be
standard input.
(compare_files): Only close standard input once.
* doc/coreutils.texi (comm invocation): Document the behavior of
'comm - -' which is not portable to all implementations.
* tests/comm/dash-dash.sh: New file.
* tests/misc/comm.pl: Move to tests/comm/comm.pl.
* tests/local.mk (all_tests): Add the new test. Rename the existing
test.
* src/yes.c (splice_write): Always drain what we've written
to an internal pipe, so there is no possibility of vmsplice() blocking.
I.e., be defensive in the case that fcntl() fails, and
our default buffer size (currently 16kiB) is larger than the pipe.
https://github.com/coreutils/coreutils/issues/253
Bruno Haible [Fri, 17 Apr 2026 13:46:19 +0000 (15:46 +0200)]
tests: reinstate logname error verification
* tests/misc/user.sh: Modify the "unshare -U logname" test, so that it
does not fail on glibc versions (< 2.28, >= 2.38) that do a fallback
lookup based on the tty.
* tests/date/date-locale-hour.sh: Just strip the char before the last x,
assuming it's going to be the newline output by `locale`.
dash doesn't support $'' within ${}.
* tests/misc/io-errors.sh: Allow a generic error string on musl since
the first line is emitted immediately instead of being buffered as
expected.
Reported by Bruno Haible.
* m4/jm-macros.m4: AIX has a splice() function for TCP,
so check for vmsplice() instead.
* src/splice.h: Define HAVE_SPLICE if vmsplice available.
Reported by Bruno Haible.
* tests/dd/no-allocate.sh: Run getlimits_ to ensure
we have $SSIZE_MAX etc. available. Also give some buffer
for the configured ulimit to leave space for returns_ to work.
* tests/cp/sparse-perf.sh: Old Centos 7 can give EINVAL
from SEEK_DATA on sparse files being copied from /dev/shm.
Avoid this failure as it's not practical to fix.
* tests/date/date-locale-hour.sh: Ensure `locale date_fmt`
is propagated exactly, even when it contains trailing new lines,
as was seen with Serbian locales on Centos 7.
As noted in https://bugs.gnu.org/9089
ksh gives intermittent ECONNRESET errors with closed pipes.
This can be seen reliably on Linux with:
ksh -c 'yes | (sleep .1; head -n10 >/dev/null)'
* tests/misc/io-errors.sh: Avoid part of test on ksh.
* tests/misc/write-errors.sh: Likewise.
As described in commit v9.10-283-g5cb0cca3b
avoid using ulimit with $SHELL -c, and explict
invocation of the shell like that can require lots of memory,
often due to loading the locale archive.
* tests/basenc/bounded-memory.sh: Avoid $SHELL -c with ulimit.
* tests/cut/bounded-memory.sh: Likewise.
* tests/expand/bounded-memory.sh Likewise.
* tests/pr/bounded-memory.sh Likewise.
* tests/unexpand/bounded-memory.sh Likewise.
* tests/fold/fold-zero-width.sh: Bring the ulimit bound
down to the standard 6M over the determined limit.
* tests/misc/tty-eof.pl: Fix a race that commit v9.10-269-gf312af49a
made more apparent, and resulted in intermittent failure like:
"... (with input) didn't produce expected output".
Now Instead of waiting for overlapping echo and output,
just wait for EOF, and if received, use exp->before()
to inspect the accumulated output.
Verified with:
make -j4 PREFERABLY_POSIX_SHELL=/bin/ksh SUBDIRS=. check
* tests/fold/fold-zero-width.sh: Don't timeout $SHELL -c ...
as the ulimit induces a failure in the subshell depending
on the order of the allocations it does. The main issue is
disparity between the probed ulimit and that needed by $SHELL -c.
Such subshells load the often very large locale archive, thus
if there are any allocations done after the now too low ulimit is set,
then the $SHELL command fails. Note we timeout fold rather than
the whole pipeline so any 124 exit status is propagated.
$ strace -e silence=exit -e trace=unlink,rmdir \
mktemp -d > /dev/full
unlink("/tmp/tmp.ZBuPmS9ZGD") = -1 EISDIR (Is a directory)
rmdir("/tmp/tmp.ZBuPmS9ZGD") = 0
mktemp: write error: No space left on device
In the above invocation we know that we created a directory, so we
should not remove a regular file that must have been created by another
process:
$ strace -e silence=exit -e trace=unlink,rmdir \
./src/mktemp -d > /dev/full
rmdir("/tmp/tmp.hGbME1HmJr") = 0
mktemp: write error: No space left on device
* src/mktemp.c (main): Prefer rmdir and unlink depending on whether we
created a directory or regular file.
* bootstrap.conf (gnulib_modules): Remove the remove module.
* src/cat.c (splice_cat): Don't bother resizing input as it generally
doesn't help perf, and also save an fstat per input. Don't close the
intermediate pipe once created, unless there is an error reading from
it.
* tests/misc/responsive.sh: Test commands that should output immediately
upon receiving input, and that there is no unecessary buffering.
* cfg.mk: Avoid false failure in sc_prohibit_test_backticks.
* tests/local.mk: Reference the new test.
* tests/tty/tty-eof.pl: Remove command specific logic,
and adjust commands to support general input.
Also add cut -b, as cut_bytes has its own read loop.
* tests/date/date.pl: Set the max supported year to INT_MAX.
Most systems support INT_MAX+1900, but mktime() on OpenBSD 7.8
limits the passed tm_year to INT_MAX.
Reported by Bruno Haible.
tests: numfmt: avoid false failure on systems without long double
* tests/numfmt/numfmt.pl: Move recently added test that depends
on long double support to the appropriately guarded set.
Also reduce the value to be in the definitely safe long double range.
Reported by Bruno Haible.
Seen on GCC 15.2.1 with GLIBC 2.43 on Arch
Not seen on GCC 15.2.1 on GLIBC 2.42 on Fedora
* src/cut.c (search_bytes): Cast the return from memchr()
to avoid const propagation.
(find_field_delim): Adjust the return from strstr() similarly.
https://github.com/coreutils/coreutils/issues/244
tests: cat: avoid false failure on systems without splice
* tests/cat/splice.sh: Ensure splice is called multiple times
before we check specific invocation counts.
On Linux kernel 5.10 for example, splice from /dev/zero
returns EINVAL.
* NEWS: Mention the improvement.
* src/cat.c: Include isapipe.h, splice.h, and unistd--.h.
(splice_cat): New function.
(main): Use it.
* src/local.mk (noinst_HEADERS): Add src/splice.h.
* src/splice.h: New file, based on definitions from src/yes.c.
* src/yes.c: Include splice.h.
(pipe_splice_size): Use increase_pipe_size from src/splice.h.
(SPLICE_PIPE_SIZE): Remove definition, moved to src/splice.h.
* tests/cat/splice.sh: New file, based on some tests in
tests/misc/yes.sh.
* tests/local.mk (all_tests): Add the new test.
* tests/date/date.pl: Add the test case.
Add test case for https://github.com/uutils/coreutils/issues/9774
to verify with large dates.
https://github.com/coreutils/coreutils/pull/237
Paul Eggert [Mon, 6 Apr 2026 18:12:46 +0000 (11:12 -0700)]
maint: revert “avoid pthread_sigmask lock”
* configure.ac (GNULIB_SIGACTION_SINGLE_THREAD): Remove.
This never worked (it was a misspelling) and the properly-spelled
identifier (whose spelling has since been renamed) is useful
mostly for programs like gzip that do not need Gnulib’s ‘lock’ module.
For coreutils, which needs ‘lock’ for other reasons, it’s overkill.
maint: avoid pthread_sigmask lock overhead
This matters only for MS-Windows.
* configure.ac (GNULIB_PTHREAD_SIGMASK_SINGLE_THREAD):
Define this instead of defining GNULIB_SIGACTION_SINGLE_THREAD.
The latter was a typo, and Gnulib has evolved anyway.
tests: expr: add short-circuit tests with parenthesized branches
* tests/expr/expr.pl: Add tests to verify that short-circuit
evaluation of | and & correctly skips parenthesized dead branches,
including nested parenthesized expressions containing division by zero.
https://github.com/uutils/coreutils/pull/11395
https://github.com/coreutils/coreutils/pull/238
tests: split: verify non-UTF-8 bytes are preserved in filenames
* tests/split/non-utf8.sh: New test to ensure that non-UTF-8 bytes
in the prefix and --additional-suffix are preserved as-is in output
filenames, rather than being replaced by UTF-8 replacement characters.
* tests/local.mk: Register new test.
https://github.com/uutils/coreutils/pull/11397
https://github.com/coreutils/coreutils/pull/239
tests: ln: add test for non-UTF-8 source names in target-dir mode
* tests/ln/non-utf8-src.sh: New test ensuring ln handles source
filenames containing non-UTF-8 bytes when linking into a target
directory, for both hard links and symbolic links with -t.
* tests/local.mk: Register the new test.
https://github.com/uutils/coreutils/pull/11403
https://github.com/coreutils/coreutils/pull/240
test: od: verify -t f defaults to double precision
* tests/od/od-float.sh: Add cases to ensure -t f = -t fD,
and also verify the resulting number.
https://github.com/uutils/coreutils/pull/11396
https://github.com/coreutils/coreutils/pull/241
tests: ls: add quoting-utf8 test for Unicode quotes in UTF-8 locales
* tests/ls/quoting-utf8.sh: New test verifying that
--quoting-style=locale and --quoting-style=clocale use Unicode
left/right single quotation marks in UTF-8 locales, and that
embedded apostrophes and double quotes are not escaped when the
delimiters are different characters.
Also check C locale fallback to ASCII quotes.
* tests/local.mk: Reference the new test.
https://github.com/coreutils/coreutils/pull/243
Pádraig Brady [Thu, 12 Mar 2026 17:49:27 +0000 (17:49 +0000)]
doc: document cut(1) multi-byte and interface consolidation
This patch set updates cut(1) to be multi-byte aware.
It also reduces interface divergence across implementations.
multi-byte awareness was added to the existing -c, n, and -d options.
Also considered for compatibility are the -w, -F, and -O options,
as these are present on at least two other common implementations.
= Interface / New functionality =
macOS, i18n, uutils, Toybox, Busybox, GNU
-c x x x x x x
-n x x x
-w x x x
-F x x x
-O x x x
-c is needed anyway as specified by all, including POSIX.
-n is needed also as specified by i18n/macOS/POSIX
-w is somewhat less important, but seeing as it's
on two other common platforms (and its functionality is
provided on two more), providing it is worthwhile for compat.
-F and -O are really just aliases to other options
so trivial to add, and probably worthwhile for compatibility.
Interface / functionality notes:
There is a slight divergence between -n implementations.
There was already a difference between FreeBSD and i18n, and
we've aligned with the more sensible FreeBSD implementation.
Note the i18n -n implementation is otherwise buggy in any case,
so I doubt this will be a practical compatibility concern.
Actually -n is specified by POSIX, and it matches FreeBSD.
Specifically our -n will not output a character unless the
byte range encompasses _the end_ of the multi-byte character.
I.e. the -b is a limit that is not passed, and thus ensures
we don't output overlapping characters for separate cut
invocations that do not have overlapping byte ranges.
-d <regex> from toybox is not implemented.
That's edge case functionality IMHO and not well suited to cut(1).
This functionality is supported by awk, and regex functionality
is best restricted to awk I think.
cut is a significant part of the i18n patch, so it will be good
to avoid that downstream divergence. Unfortunately there were
no tests with the cut i18n implementation.
Note the i18n cut implementation used fread() as so was
not reponsive to new data < BUFSIZ, whereas this implementation
uses read() and thus is responsive to data as it becomes available.
= Performance =
General performance notes:
We prefer byte searching (with -d) as that can be much faster
than character by character processing, and it's supported
on single byte and UTF-8 charsets. We also use byte searching
with -w on uni-byte locales.
This was seen to give up to 100x perf increase over the i18n patch.
Where we do use per character processing, we avoid conversion to
wide char when processing ASCII data (mcel provides this optimization).
This was seen to give a 14x performance increase over the i18n patch.
We prefer memchr() and strstr() as these are tuned for specific
platforms on glibc, even if memchr2() or memmem()
are algorithmically better.
We maintain the important memory behavior
of only buffering when necessary.
Performance testing:
There are _lots_ of combinations and optimziation opportunities.
I performance tested this patch set with the following setup:
$ yes | head -n10M > sl.in
$ yes $(yes eeeaae | head -n10K | paste -s -d,) | head -n10K > ll.in
$ yes $(yes eeeaae | head -n9 | paste -s -d,) | head -n1M > as.in
$ yes $(yes éééááé | head -n9 | paste -s -d,) | head -n1M \
> mb.in
$ for type in sl ll as mb; do
cat $type.in >/dev/null;
for imp in '' src/; do # '' maps to the system i18n ver on Fedora
echo ============ "${imp:-i18n}" $type ==============;
for d in -d, -dc -d, -dç -w -b -c; do
fields='-f1 -f10 -f100'
test "$d" = "-b" && { fields='-b1 -b10 -b100'; d=''; }
test "$d" = "-c" && { fields='-c1 -c10 -c100'; d=''; }
for f in $fields; do
for loc in C C.UTF-8; do
# SKip -b for UTF-8 as no different
test "$loc" = C.UTF-8 && echo "$f" | grep -q -- -b \
&& continue
# Skip multi-byte delimiter for C and not allowed
test "$loc" = C && test $(echo -n "$d" | wc -c) -ge 4 \
&& continue
LC_ALL=$loc ${imp}cut $f $d /dev/null 2>/dev/null &&
hyperfine -m2 -M4 \
"LC_ALL=$loc ${imp}cut $f $d $type.in >/dev/null" ||
printf 'Benchmark 1: %s\n unsupported\n\n' \
"LC_ALL=$loc ${imp}cut $f $d $type.in >/dev/null"
done;
done;
done;
done;
done
After a little post-processing of the results, we get:
-- cut-i18n
| command | sl | ll | as | mb |
| --------------- | -------- | -------- | -------- | -------- |
| C -f1 -d, | 66.3 ms | 1.605 s | 145.9 ms | 366.4 ms |
| UTF8 -f1 -d, | 65.8 ms | 1.593 s | 145.8 ms | 370.0 ms |
| C -f10 -d, | 301.4 ms | 1.590 s | 161.8 ms | 126.7 ms |
| UTF8 -f10 -d, | 303.5 ms | 1.599 s | 161.8 ms | 124.6 ms |
| C -f100 -d, | 300.6 ms | 1.596 s | 162.1 ms | 126.7 ms |
| UTF8 -f100 -d, | 301.3 ms | 1.595 s | 162.0 ms | 124.9 ms |
| C -f1 -dc | 66.6 ms | 1.845 s | 179.1 ms | 365.7 ms |
| UTF8 -f1 -dc | 73.8 ms | 1.878 s | 179.1 ms | 363.1 ms |
| C -f10 -dc | 300.7 ms | 349.8 ms | 76.0 ms | 125.3 ms |
| UTF8 -f10 -dc | 300.4 ms | 347.2 ms | 75.7 ms | 124.8 ms |
| C -f100 -dc | 300.1 ms | 348.1 ms | 76.5 ms | 125.5 ms |
| UTF8 -f100 -dc | 300.8 ms | 348.7 ms | 76.4 ms | 125.8 ms |
| UTF8 -f1 -d, | 563.5 ms | 21.775 s | 1.963 s | 1.665 s |
| UTF8 -f10 -d, | 833.6 ms | 20.504 s | 2.022 s | 1.612 s |
| UTF8 -f100 -d, | 825.2 ms | 20.448 s | 2.009 s | 1.616 s |
| UTF8 -f1 -dç | 563.7 ms | 21.827 s | 1.964 s | 2.319 s |
| UTF8 -f10 -dç | 825.3 ms | 21.713 s | 2.011 s | 2.248 s |
| UTF8 -f100 -dç | 831.6 ms | 20.505 s | 2.019 s | 2.276 s |
| C -f1 -w | - | - | - | - |
| UTF8 -f1 -w | - | - | - | - |
| C -f10 -w | - | - | - | - |
| UTF8 -f10 -w | - | - | - | - |
| C -f100 -w | - | - | - | - |
| UTF8 -f100 -w | - | - | - | - |
| C -b1 | 60.8 ms | 1.596 s | 154.8 ms | 313.7 ms |
| C -b10 | 51.6 ms | 1.594 s | 154.3 ms | 310.8 ms |
| C -b100 | 51.4 ms | 1.594 s | 153.0 ms | 312.2 ms |
| C -c1 | 60.7 ms | 1.597 s | 153.8 ms | 313.0 ms |
| UTF8 -c1 | 526.5 ms | 14.662 s | 1.362 s | 1.573 s |
| C -c10 | 51.8 ms | 1.591 s | 153.3 ms | 311.4 ms |
| UTF8 -c10 | 436.9 ms | 14.450 s | 1.336 s | 1.563 s |
| C -c100 | 51.0 ms | 1.593 s | 152.7 ms | 313.2 ms |
| UTF8 -c100 | 426.7 ms | 14.429 s | 1.344 s | 1.551 s |
-- src/cut
| command | sl | ll | as | mb |
| --------------- | -------- | -------- | -------- | -------- |
| C -f1 -d, | 4.6 ms | 108.2 ms | 45.4 ms | 24.2 ms |
| UTF8 -f1 -d, | 4.8 ms | 108.4 ms | 45.4 ms | 24.5 ms |
| C -f10 -d, | 4.5 ms | 109.3 ms | 123.7 ms | 24.3 ms |
| UTF8 -f10 -d, | 4.9 ms | 114.1 ms | 124.1 ms | 24.5 ms |
| C -f100 -d, | 4.7 ms | 119.2 ms | 124.1 ms | 24.5 ms |
| UTF8 -f100 -d, | 4.8 ms | 120.0 ms | 125.1 ms | 24.5 ms |
| C -f1 -dc | 4.4 ms | 120.5 ms | 11.9 ms | 24.1 ms |
| UTF8 -f1 -dc | 4.9 ms | 120.5 ms | 12.1 ms | 24.6 ms |
| C -f10 -dc | 4.7 ms | 125.3 ms | 11.8 ms | 24.1 ms |
| UTF8 -f10 -dc | 4.8 ms | 126.7 ms | 12.0 ms | 24.4 ms |
| C -f100 -dc | 4.6 ms | 127.0 ms | 11.9 ms | 24.3 ms |
| UTF8 -f100 -dc | 4.7 ms | 126.4 ms | 12.0 ms | 24.4 ms |
| UTF8 -f1 -d, | 6.0 ms | 169.4 ms | 15.6 ms | 67.4 ms |
| UTF8 -f10 -d, | 6.1 ms | 173.9 ms | 15.6 ms | 237.2 ms |
| UTF8 -f100 -d, | 6.1 ms | 174.0 ms | 15.6 ms | 237.8 ms |
| UTF8 -f1 -dç | 6.3 ms | 170.8 ms | 15.7 ms | 32.2 ms |
| UTF8 -f10 -dç | 6.0 ms | 172.9 ms | 15.9 ms | 32.1 ms |
| UTF8 -f100 -dç | 6.7 ms | 173.1 ms | 15.5 ms | 32.3 ms |
| C -f1 -w | 159.6 ms | 170.1 ms | 69.1 ms | 98.9 ms |
| UTF8 -f1 -w | 128.1 ms | 2.525 s | 246.5 ms | 1.086 s |
| C -f10 -w | 183.3 ms | 199.2 ms | 74.6 ms | 105.0 ms |
| UTF8 -f10 -w | 130.3 ms | 2.659 s | 276.5 ms | 1.099 s |
| C -f100 -w | 183.8 ms | 202.5 ms | 74.1 ms | 103.6 ms |
| UTF8 -f100 -w | 130.1 ms | 2.663 s | 276.6 ms | 1.097 s |
| C -b1 | 65.0 ms | 110.2 ms | 22.4 ms | 35.6 ms |
| C -b10 | 48.7 ms | 109.6 ms | 24.2 ms | 36.7 ms |
| C -b100 | 48.7 ms | 110.6 ms | 19.0 ms | 36.6 ms |
| C -c1 | 65.8 ms | 109.5 ms | 22.4 ms | 35.6 ms |
| UTF8 -c1 | 63.2 ms | 1.130 s | 116.9 ms | 610.2 ms |
| C -c10 | 48.7 ms | 109.8 ms | 24.3 ms | 36.8 ms |
| UTF8 -c10 | 39.7 ms | 1.133 s | 118.7 ms | 610.0 ms |
| C -c100 | 48.3 ms | 110.7 ms | 18.9 ms | 36.7 ms |
| UTF8 -c100 | 39.4 ms | 1.141 s | 115.0 ms | 598.8 ms |
In summary, compared to the i18n patch we're now as fast in all cases,
and much faster in most cases.
We can see the -f byte searching performing well,
being 120x faster in the no matching delimiter case,
to at least 3x faster in the matching delimiter case.
When we resort to per character processing we also compare well,
being 14x faster in the ASCII processing case
(due to mcel short-circuiting the wide char conversion).
Note the processing mb.in results above also show a 2x win
in per character processing cases, but the i18n patch would have
also picked that win up as it's achieved separately to this patch set:
https://lists.gnu.org/r/coreutils/2026-03/msg00117.html
cut,fold,expand,unexpand: ensure we process all available characters
* gl/lib/mbbuf.h: Adjust mbbuf_fill() to process full characters
in the slop at the end of a read(). Previously valid characters
in the last MCEL_LEN_MAX bytes were ignored until the next read().
* src/cut.c (cut_fields_bytesearch): Adjust to the new naming.
* NEWS: Mention the fold(1) responsiveness fix, which was
improved with the change from fread() to read(),
and completed with this patch.