feat(cli): move list formatting to Rust

Move the per-file and multi-file --list row formatters out of programs/fileio.c. C still owns filesystem access, frame analysis, private fileInfo_t storage, and exact status diagnostics; Rust now formats the rows into byte buffers and sends them through a synchronous output callback. This keeps filenames byte-preserving and prevents the private C record from crossing the ABI while preserving the original display thresholds, human-readable size policy, unavailable-content spacing, checksum order, and total-row behavior.

Replace the old display callbacks with one size-delimited output callback shared by the single-file and multi-file projections. Add explicit Rust ABI layout checks, formatter fixtures for normal/unavailable/verbose/checksum/total output, and callback-order assertions for the policy boundary.

Test Plan:
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/cli/Cargo.toml --all-targets -- -D warnings
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/cli/Cargo.toml --all-targets (208 passed)
- ulimit -v 41943040; make -j1
- ulimit -v 41943040; make -j1 -C tests test (all original tests completed successfully)
- git diff --check
This commit is contained in:
2026-07-21 15:24:47 +02:00
parent c42f69e756
commit 13a271c420
2 changed files with 331 additions and 163 deletions
+12 -96
View File
@@ -5109,18 +5109,17 @@ typedef int (*FIO_listFileGetInfoFn)(void* opaque, const char* fileName,
typedef void (*FIO_listFileDisplayStatusFn)(void* opaque, int action,
const char* fileName,
int displayLevel);
typedef void (*FIO_listFileDisplayInfoFn)(void* opaque, const char* fileName,
const fileInfo_t* info,
int displayLevel);
typedef void (*FIO_listFileWriteOutputFn)(void* opaque, const char* data,
size_t size);
/* This callback state is a deliberate repr(C) bridge: filesystem access,
* frame analysis, exact diagnostics, and row formatting stay in C while Rust
* owns the per-file policy/order around them. */
* frame analysis, and exact diagnostics stay in C while Rust owns row
* formatting and the per-file policy/order around it. */
typedef struct {
void* opaque;
FIO_listFileGetInfoFn getInfo;
FIO_listFileDisplayStatusFn displayStatus;
FIO_listFileDisplayInfoFn displayInfo;
FIO_listFileWriteOutputFn writeOutput;
} FIO_listFileCallbacks_t;
typedef char FIO_rust_list_file_callbacks_get_info_offset[
@@ -5128,8 +5127,8 @@ typedef char FIO_rust_list_file_callbacks_get_info_offset[
typedef char FIO_rust_list_file_callbacks_status_offset[
(offsetof(FIO_listFileCallbacks_t, displayStatus)
== sizeof(void*) + sizeof(FIO_listFileGetInfoFn)) ? 1 : -1];
typedef char FIO_rust_list_file_callbacks_info_offset[
(offsetof(FIO_listFileCallbacks_t, displayInfo)
typedef char FIO_rust_list_file_callbacks_output_offset[
(offsetof(FIO_listFileCallbacks_t, writeOutput)
== sizeof(void*) + sizeof(FIO_listFileGetInfoFn)
+ sizeof(FIO_listFileDisplayStatusFn)) ? 1 : -1];
@@ -5141,8 +5140,7 @@ typedef struct {
void (*displayHeader)(void* opaque);
int (*listFile)(void* opaque, const char* fileName, int displayLevel,
FIO_listFileInfoProjection_t* info);
void (*displayTotal)(void* opaque,
const FIO_listFileInfoProjection_t* total);
FIO_listFileWriteOutputFn writeOutput;
} FIO_listMultipleFilesCallbacks_t;
int FIO_rust_listFile(const char* inputName, int displayLevel,
@@ -5261,61 +5259,6 @@ getFileInfo(fileInfo_t* info, const char* srcFileName)
}
static void
displayInfo(const char* inFileName, const fileInfo_t* info, int displayLevel)
{
UTIL_HumanReadableSize_t const window_hrs = UTIL_makeHumanReadableSize(info->windowSize);
UTIL_HumanReadableSize_t const compressed_hrs = UTIL_makeHumanReadableSize(info->compressedSize);
UTIL_HumanReadableSize_t const decompressed_hrs = UTIL_makeHumanReadableSize(info->decompressedSize);
double const ratio = (info->compressedSize == 0) ? 0 : ((double)info->decompressedSize)/(double)info->compressedSize;
const char* const checkString = (info->usesCheck ? "XXH64" : "None");
if (displayLevel <= 2) {
if (!info->decompUnavailable) {
DISPLAYOUT("%6d %5d %6.*f%4s %8.*f%4s %5.3f %5s %s\n",
info->numSkippableFrames + info->numActualFrames,
info->numSkippableFrames,
compressed_hrs.precision, compressed_hrs.value, compressed_hrs.suffix,
decompressed_hrs.precision, decompressed_hrs.value, decompressed_hrs.suffix,
ratio, checkString, inFileName);
} else {
DISPLAYOUT("%6d %5d %6.*f%4s %5s %s\n",
info->numSkippableFrames + info->numActualFrames,
info->numSkippableFrames,
compressed_hrs.precision, compressed_hrs.value, compressed_hrs.suffix,
checkString, inFileName);
}
} else {
DISPLAYOUT("%s \n", inFileName);
DISPLAYOUT("# Zstandard Frames: %d\n", info->numActualFrames);
if (info->numSkippableFrames)
DISPLAYOUT("# Skippable Frames: %d\n", info->numSkippableFrames);
DISPLAYOUT("DictID: %u\n", info->dictID);
DISPLAYOUT("Window Size: %.*f%s (%llu B)\n",
window_hrs.precision, window_hrs.value, window_hrs.suffix,
(unsigned long long)info->windowSize);
DISPLAYOUT("Compressed Size: %.*f%s (%llu B)\n",
compressed_hrs.precision, compressed_hrs.value, compressed_hrs.suffix,
(unsigned long long)info->compressedSize);
if (!info->decompUnavailable) {
DISPLAYOUT("Decompressed Size: %.*f%s (%llu B)\n",
decompressed_hrs.precision, decompressed_hrs.value, decompressed_hrs.suffix,
(unsigned long long)info->decompressedSize);
DISPLAYOUT("Ratio: %.4f\n", ratio);
}
if (info->usesCheck && info->numActualFrames == 1) {
DISPLAYOUT("Check: %s %02x%02x%02x%02x\n", checkString,
info->checksum[3], info->checksum[2],
info->checksum[1], info->checksum[0]
);
} else {
DISPLAYOUT("Check: %s\n", checkString);
}
DISPLAYOUT("\n");
}
}
static int
FIO_listFileGetInfo(void* opaque, const char* inFileName, fileInfo_t* info)
{
@@ -5352,11 +5295,10 @@ FIO_listFileDisplayStatus(void* opaque, int action,
}
static void
FIO_listFileDisplayInfo(void* opaque, const char* inFileName,
const fileInfo_t* info, int displayLevel)
FIO_listFileWriteOutput(void* opaque, const char* data, size_t size)
{
(void)opaque;
displayInfo(inFileName, info, displayLevel);
(void)fwrite(data, 1, size, stdout);
}
/* Rust owns this stateless --list input-name predicate. */
@@ -5397,36 +5339,10 @@ FIO_listFileCallback(void* opaque, const char* fileName, int displayLevel,
callbacks.opaque = NULL;
callbacks.getInfo = FIO_listFileGetInfo;
callbacks.displayStatus = FIO_listFileDisplayStatus;
callbacks.displayInfo = FIO_listFileDisplayInfo;
callbacks.writeOutput = FIO_listFileWriteOutput;
return FIO_rust_listFile(fileName, displayLevel, info, &callbacks);
}
static void
FIO_listFileDisplayTotal(void* opaque,
const FIO_listFileInfoProjection_t* total)
{
UTIL_HumanReadableSize_t const compressed_hrs = UTIL_makeHumanReadableSize(total->compressedSize);
UTIL_HumanReadableSize_t const decompressed_hrs = UTIL_makeHumanReadableSize(total->decompressedSize);
double const ratio = (total->compressedSize == 0) ? 0 : ((double)total->decompressedSize)/(double)total->compressedSize;
const char* const checkString = (total->usesCheck ? "XXH64" : "");
(void)opaque;
DISPLAYOUT("----------------------------------------------------------------- \n");
if (total->decompUnavailable) {
DISPLAYOUT("%6d %5d %6.*f%4s %5s %u files\n",
total->numSkippableFrames + total->numActualFrames,
total->numSkippableFrames,
compressed_hrs.precision, compressed_hrs.value, compressed_hrs.suffix,
checkString, (unsigned)total->nbFiles);
} else {
DISPLAYOUT("%6d %5d %6.*f%4s %8.*f%4s %5.3f %5s %u files\n",
total->numSkippableFrames + total->numActualFrames,
total->numSkippableFrames,
compressed_hrs.precision, compressed_hrs.value, compressed_hrs.suffix,
decompressed_hrs.precision, decompressed_hrs.value, decompressed_hrs.suffix,
ratio, checkString, (unsigned)total->nbFiles);
}
}
int FIO_listMultipleFiles(unsigned numFiles, const char** filenameTable, int displayLevel)
{
FIO_listMultipleFilesCallbacks_t callbacks;
@@ -5436,7 +5352,7 @@ int FIO_listMultipleFiles(unsigned numFiles, const char** filenameTable, int dis
callbacks.displayNoFilesError = FIO_listFileDisplayNoFilesError;
callbacks.displayHeader = FIO_listFileDisplayHeader;
callbacks.listFile = FIO_listFileCallback;
callbacks.displayTotal = FIO_listFileDisplayTotal;
callbacks.writeOutput = FIO_listFileWriteOutput;
return FIO_rust_listMultipleFiles(numFiles, filenameTable, displayLevel, &callbacks);
}