xz: Use lzma_str_from_filters().

Two uses: Displaying encoder filter chain when compressing with -vv,
and displaying the decoder filter chain in --list -vv.
This commit is contained in:
Lasse Collin 2022-11-28 21:57:47 +02:00
parent cedeeca2ea
commit 7484744af6
2 changed files with 28 additions and 175 deletions

View File

@ -52,10 +52,13 @@ typedef struct {
uint64_t memusage; uint64_t memusage;
/// The filter chain of this Block in human-readable form /// The filter chain of this Block in human-readable form
char filter_chain[FILTERS_STR_SIZE]; char *filter_chain;
} block_header_info; } block_header_info;
#define BLOCK_HEADER_INFO_INIT { .filter_chain = NULL }
#define block_header_info_end(bhi) free((bhi)->filter_chain)
/// Strings ending in a colon. These are used for lines like /// Strings ending in a colon. These are used for lines like
/// " Foo: 123 MiB". These are grouped because translated strings /// " Foo: 123 MiB". These are grouped because translated strings
@ -566,10 +569,19 @@ parse_block_header(file_pair *pair, const lzma_index_iter *iter,
} }
// Convert the filter chain to human readable form. // Convert the filter chain to human readable form.
message_filters_to_str(bhi->filter_chain, filters, false); const lzma_ret str_ret = lzma_str_from_filters(
&bhi->filter_chain, filters,
LZMA_STR_DECODER | LZMA_STR_GETOPT_LONG, NULL);
// Free the memory allocated by lzma_block_header_decode(). // Free the memory allocated by lzma_block_header_decode().
lzma_filters_free(filters, NULL); lzma_filters_free(filters, NULL);
// Check if the stringification succeeded.
if (str_ret != LZMA_OK) {
message_error("%s: %s", pair->src_name, message_strm(str_ret));
return true;
}
return false; return false;
data_error: data_error:
@ -864,9 +876,6 @@ print_info_adv(xz_file_info *xfi, file_pair *pair)
// Cache the verbosity level to a local variable. // Cache the verbosity level to a local variable.
const bool detailed = message_verbosity_get() >= V_DEBUG; const bool detailed = message_verbosity_get() >= V_DEBUG;
// Information collected from Block Headers
block_header_info bhi;
// Print information about the Blocks but only if there is // Print information about the Blocks but only if there is
// at least one Block. // at least one Block.
if (lzma_index_block_count(xfi->idx) > 0) { if (lzma_index_block_count(xfi->idx) > 0) {
@ -916,6 +925,9 @@ print_info_adv(xz_file_info *xfi, file_pair *pair)
// Iterate over the Blocks. // Iterate over the Blocks.
while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)) { while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)) {
// If in detailed mode, collect the information from
// Block Header before starting to print the next line.
block_header_info bhi = BLOCK_HEADER_INFO_INIT;
if (detailed && parse_details(pair, &iter, &bhi, xfi)) if (detailed && parse_details(pair, &iter, &bhi, xfi))
return true; return true;
@ -1001,6 +1013,7 @@ print_info_adv(xz_file_info *xfi, file_pair *pair)
} }
putchar('\n'); putchar('\n');
block_header_info_end(&bhi);
} }
} }
@ -1058,9 +1071,9 @@ print_info_robot(xz_file_info *xfi, file_pair *pair)
iter.stream.padding); iter.stream.padding);
lzma_index_iter_rewind(&iter); lzma_index_iter_rewind(&iter);
block_header_info bhi;
while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)) { while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)) {
block_header_info bhi = BLOCK_HEADER_INFO_INIT;
if (message_verbosity_get() >= V_DEBUG if (message_verbosity_get() >= V_DEBUG
&& parse_details( && parse_details(
pair, &iter, &bhi, xfi)) pair, &iter, &bhi, xfi))
@ -1091,6 +1104,7 @@ print_info_robot(xz_file_info *xfi, file_pair *pair)
bhi.filter_chain); bhi.filter_chain);
putchar('\n'); putchar('\n');
block_header_info_end(&bhi);
} }
} }

View File

@ -900,181 +900,20 @@ message_mem_needed(enum message_verbosity v, uint64_t memusage)
} }
/// \brief Convert uint32_t to a nice string for --lzma[12]=dict=SIZE
///
/// The idea is to use KiB or MiB suffix when possible.
static const char *
uint32_to_optstr(uint32_t num)
{
static char buf[16];
if ((num & ((UINT32_C(1) << 20) - 1)) == 0)
snprintf(buf, sizeof(buf), "%" PRIu32 "MiB", num >> 20);
else if ((num & ((UINT32_C(1) << 10) - 1)) == 0)
snprintf(buf, sizeof(buf), "%" PRIu32 "KiB", num >> 10);
else
snprintf(buf, sizeof(buf), "%" PRIu32, num);
return buf;
}
extern void
message_filters_to_str(char buf[FILTERS_STR_SIZE],
const lzma_filter *filters, bool all_known)
{
char *pos = buf;
size_t left = FILTERS_STR_SIZE;
for (size_t i = 0; filters[i].id != LZMA_VLI_UNKNOWN; ++i) {
// Add the dashes for the filter option. A space is
// needed after the first and later filters.
my_snprintf(&pos, &left, "%s", i == 0 ? "--" : " --");
switch (filters[i].id) {
case LZMA_FILTER_LZMA1:
case LZMA_FILTER_LZMA2: {
const lzma_options_lzma *opt = filters[i].options;
const char *mode = NULL;
const char *mf = NULL;
if (all_known) {
switch (opt->mode) {
case LZMA_MODE_FAST:
mode = "fast";
break;
case LZMA_MODE_NORMAL:
mode = "normal";
break;
default:
mode = "UNKNOWN";
break;
}
switch (opt->mf) {
case LZMA_MF_HC3:
mf = "hc3";
break;
case LZMA_MF_HC4:
mf = "hc4";
break;
case LZMA_MF_BT2:
mf = "bt2";
break;
case LZMA_MF_BT3:
mf = "bt3";
break;
case LZMA_MF_BT4:
mf = "bt4";
break;
default:
mf = "UNKNOWN";
break;
}
}
// Add the filter name and dictionary size, which
// is always known.
my_snprintf(&pos, &left, "lzma%c=dict=%s",
filters[i].id == LZMA_FILTER_LZMA2
? '2' : '1',
uint32_to_optstr(opt->dict_size));
// With LZMA1 also lc/lp/pb are known when
// decompressing, but this function is never
// used to print information about .lzma headers.
assert(filters[i].id == LZMA_FILTER_LZMA2
|| all_known);
// Print the rest of the options, which are known
// only when compressing.
if (all_known)
my_snprintf(&pos, &left,
",lc=%" PRIu32 ",lp=%" PRIu32
",pb=%" PRIu32
",mode=%s,nice=%" PRIu32 ",mf=%s"
",depth=%" PRIu32,
opt->lc, opt->lp, opt->pb,
mode, opt->nice_len, mf, opt->depth);
break;
}
case LZMA_FILTER_X86:
case LZMA_FILTER_POWERPC:
case LZMA_FILTER_IA64:
case LZMA_FILTER_ARM:
case LZMA_FILTER_ARMTHUMB:
case LZMA_FILTER_SPARC: {
static const char bcj_names[][9] = {
"x86",
"powerpc",
"ia64",
"arm",
"armthumb",
"sparc",
};
const lzma_options_bcj *opt = filters[i].options;
my_snprintf(&pos, &left, "%s", bcj_names[filters[i].id
- LZMA_FILTER_X86]);
// Show the start offset only when really needed.
if (opt != NULL && opt->start_offset != 0)
my_snprintf(&pos, &left, "=start=%" PRIu32,
opt->start_offset);
break;
}
case LZMA_FILTER_ARM64: {
// FIXME TODO: Merge with the above generic BCJ list
// once the Filter ID is changed to the final value.
const lzma_options_bcj *opt = filters[i].options;
my_snprintf(&pos, &left, "arm64");
// Show the start offset only when really needed.
if (opt != NULL && opt->start_offset != 0)
my_snprintf(&pos, &left, "=start=%" PRIu32,
opt->start_offset);
break;
}
case LZMA_FILTER_DELTA: {
const lzma_options_delta *opt = filters[i].options;
my_snprintf(&pos, &left, "delta=dist=%" PRIu32,
opt->dist);
break;
}
default:
// This should be possible only if liblzma is
// newer than the xz tool.
my_snprintf(&pos, &left, "UNKNOWN");
break;
}
}
return;
}
extern void extern void
message_filters_show(enum message_verbosity v, const lzma_filter *filters) message_filters_show(enum message_verbosity v, const lzma_filter *filters)
{ {
if (v > verbosity) if (v > verbosity)
return; return;
char buf[FILTERS_STR_SIZE]; char *buf;
message_filters_to_str(buf, filters, true); const lzma_ret ret = lzma_str_from_filters(&buf, filters,
LZMA_STR_ENCODER | LZMA_STR_GETOPT_LONG, NULL);
if (ret != LZMA_OK)
message_fatal("%s", message_strm(ret));
fprintf(stderr, _("%s: Filter chain: %s\n"), progname, buf); fprintf(stderr, _("%s: Filter chain: %s\n"), progname, buf);
free(buf);
return; return;
} }