1
0
mirror of https://github.com/lz4/lz4.git synced 2026-10-24 01:48:57 +03:00
Commit Graph

793 Commits

Author SHA1 Message Date
Yann Collet
317389aaff cli: share file name building of multiple-files modes
Frame and legacy compression of multiple files each built the name of
the compressed file, with the same buffer management : initial
allocation, growth, strcpy + strcat of the suffix, and stdout special
case. They now share LZ4IO_compressedFileName(), and decompression of
multiple files shares its buffer management, LZ4IO_reserveFileName().
2026-10-21 12:44:20 -07:00
Yann Collet
f11eeb613c cli: legacy format honors --rm and keeps file attributes
Single and multi-threaded frame compression duplicated the opening of
files, the preparation of preferences, and the end of file processing :
closing files, copying owner, permissions and modification time, --rm,
final status. Legacy compression had none of this end : `lz4 -l` ignored
`--rm`, and the compressed file didn't keep the source's attributes.

The end of file processing is now LZ4IO_finishCompressedFile(), shared by
frame and legacy compression, and LZ4IO_compressFilename_extRess() opens
files and prepares preferences for both frame compressors, which only
compress.

Also, LZ4IO_compressFilename_extRess() now reports a processed size of 0
when a file can't be opened, instead of leaving it uninitialized : with
-m, a missing file added a stale or garbage size to the final speed
summary (found by scan-build).
2026-10-21 10:30:59 -07:00
Yann Collet
dcd73e8e4d Merge pull request #1915 from akashdeshmukh-git/cli-reject-malformed-long-args
cli: reject malformed --fast= and --threads arguments
2026-10-20 15:49:17 -07:00
akashdeshmukh-git
cebeba6e3e cli: reject malformed --fast= and --threads arguments
`--fast=5x` was accepted as `--fast=5`: characters following the
number were silently ignored, unlike `--threads=5x` which is rejected.

`--threadsX 2` was accepted as `--threads 2`, since the long command
was matched as a prefix only.

Both are now rejected with the usage message.
2026-10-20 08:32:54 +00:00
akashdeshmukh-git
26f121584a lz4io: detect truncated skippable frames in regular files
fseek() succeeds beyond end of file, so a skippable frame whose
declared size exceeds the remaining input was silently accepted
when reading from a file (`lz4 -t`, `lz4 -d` and `lz4 --list` all
reported success), while the same input from a pipe correctly failed
with "Error 43 : cannot skip skippable area".

fseek_u32() now seeks to the last byte of the area and reads it,
so truncation is detected the same way for files and pipes.
2026-10-20 08:32:54 +00:00
Yann Collet
bf2b488182 bench: decode-only mode shares the whole result buffer
`lz4 -b -d` reserved 255x each frame's compressed size in the result
buffer, but capped that buffer at LZ4_MAX_INPUT_SIZE. With multiple
large files, later frames pointed past the buffer : 4 files of 4 MB
crash. The decode loop also recomputed each capacity with an INT_MAX
cap instead, so a frame decoding to more than LZ4_MAX_INPUT_SIZE wrote
out of bounds.

Decode-only output is not checked, so all frames now decode at the
start of the result buffer, each with its whole capacity. The capacity
is computed once, when the block table is laid out, and reused by the
decode loop.

Bug since v1.9.4. Based on #1770 by @arshsmith1
2026-10-19 20:46:34 -07:00
Yann Collet
fe6efeb514 cli: legacy decoder counts decoded bytes in its writer thread
The multithreaded legacy decoder updated the decoded-byte counter
in its decoding thread, while the writer thread read it to display progress :
a data race, reported by ThreadSanitizer on any multi-block legacy stream.
The writer thread is now the only one to update and read it.
The final count is read after both threads complete.

Found by @agamal026 in #1890
2026-10-19 19:05:22 -07:00
Yann Collet
ac2eba0f1a cli: accept the Linux kernel's legacy end marker
The kernel's legacy decoder also stops at a zero Block Compressed Size,
and initramfs builders (mkinitcpio) append these four zero bytes
to delimit concatenated archives.
lz4 passed it to LZ4_decompress_safe() as an empty block,
and failed with "Error 64 : Decoding Failed".

A zero block size now ends the legacy frame,
in both legacy decoders and in --list,
and decoding continues with the next frame, if any.
It was never a valid block : a compressed block is at least 1 byte.

The format spec (v1.6.5) documents it as an extension :
encoders should not produce it, since older decoders reject it.

Fixes #1889, based on #1890 by @agamal026
2026-10-19 19:05:22 -07:00
Yann Collet
c2ecde85b9 bench: fix compressed buffer size with small blocks
BMK_benchMem() sized the compressed buffer with a margin of 1024 bytes
per block, computed as `maxNbBlocks * 1024` in 32-bit unsigned
arithmetic. With 4M blocks or more (e.g. `-B32` on a 128 MiB input),
the product wrapped around, and the buffer could be smaller than the
LZ4_compressBound() of all blocks, making the benchmark write past its end.

LZ4_compressBound() adds exactly 16 bytes per block, so the margin is
now `maxNbBlocks * 16`, computed in size_t.
2026-10-19 16:28:38 -07:00
Yann Collet
70860f1790 cli: decode Firefox's mozlz4 files
Firefox stores its .jsonlz4 / .mozlz4 files (session, bookmark backups,
search engines) as "mozLz40\0", the decompressed size (LE32),
then a single LZ4 block extending to the end of the file
(see MozLZ4 in Firefox's xpcom/ioutils/IOUtils.cpp).

`lz4 -d`, `lz4 -t` and `lz4cat` now recognize this format,
and decode it like Firefox's reader :
- the decompressed size is only used as decoding capacity
- empty content has no block (Firefox doesn't write one)

Allocations are bounded : sizes > LZ4_MAX_INPUT_SIZE are rejected,
as they can't be compressed into a single block,
and input is read up to LZ4_COMPRESSBOUND(size), the largest valid block.

Data starting with the first half of the magic only ("mozL") keeps
the previous behavior : `lz4cat` passes it through unchanged,
including the bytes read to check the second half.

Decoding only : lz4 doesn't produce this non-standard format.

Based on #1259 by @UnitedMarsupials, rewritten for current dev
(no mmap, bounded allocations, full magic check, tests).
2026-10-19 10:49:30 -07:00
Yann Collet
ccfc64e7b9 legacy format: same level to acceleration mapping as the frame format
The legacy compressor used acceleration = -cLevel,
while lz4frame and bench use -cLevel + 1.
Legacy levels were shifted by one: `lz4 -l --fast=1` produced the same
output as `lz4 -l -1`, and `-l --fast=2` matched the frame `--fast=1`.

Use the same mapping, capped at LZ4_ACCELERATION_MAX
(avoids a signed overflow with --fast=2147483647).
Levels >= 0 are unchanged.

test-lz4-basic.sh: `-l --fast=1` must compress less than `-l -1`
(fails on dev), and `-l --fast=2147483647` runs.
2026-10-18 11:20:02 -07:00
Yann Collet
97c68c2cd9 cap acceleration at LZ4_ACCELERATION_MAX, now defined in lz4.h
Follow-up to "lz4frame: avoid signed overflow", per the #1824 review:
saturate at LZ4_ACCELERATION_MAX rather than INT_MAX,
since higher values make no difference.

LZ4_ACCELERATION_DEFAULT and LZ4_ACCELERATION_MAX move from lz4.c
to the LZ4_STATIC_LINKING_ONLY section of lz4.h, which lz4.c, lz4frame.c,
bench.c and frametest.c already include, so their values derive from it.
The LZ4_compress_fast() documentation already referred to both constants.

bench.c computed the acceleration the same way (-cLevel + 1),
so `lz4 -b --fast=2147483647` hit the same signed overflow:
apply the same cap there.

test-lz4-basic.sh: --fast=2147483647 must produce the same frame
as --fast=65536 (fails on dev, where acceleration wraps to the default),
and its benchmark runs (caught by the UBSan CI job before this fix).

lz4.1: --fast values above 65536 make no difference.
2026-10-18 11:20:02 -07:00
Yann Collet
38b3ed8a33 cli: -b --fast=# benchmarks a single level
cLevelLast defaulted to -10000, meaning "unset".
With --fast=# and # > 10000, cLevel is below this sentinel,
so -b benchmarked all levels from -# up to -10000:
--fast=10003 ran 4 benchmarks, --fast=2147483647 ~2 billions.
Use INT_MIN as the sentinel instead.
2026-10-18 11:19:49 -07:00
Akash Deshmukh
3796322233 cli: reject numeric arguments that overflow
readU32FromChar() silently wrapped around on values larger than 32 bits,
and compression levels were then cast to int without a range check.
As a result, out-of-range arguments did something unrelated instead of
failing, for example:

  lz4 --fast=3000000000   compressed at maximum (HC) level
  lz4 -3000000000         used the fastest level (output larger than input)
  lz4 -4294967305         used level 9
  lz4 -B4294967300        used 64 KB blocks
  lz4 -B4194305K          used 1 KB blocks

readU32FromChar() now exits with an error when the value (including
K/M suffixes) overflows 32 bits, and compression level arguments
(-#, -e#, --fast=#) are rejected when larger than INT_MAX.
LZ4_CLEVEL and LZ4_NBWORKERS values that overflow are ignored with the
existing warning, like other invalid values.

Also fix the --fast=0 and --fast=-1 tests in test-lz4-basic.sh: they
used a misspelled file name (dg20K instead of dg20k), so lz4 failed
because the file was missing rather than because of the option.
2026-10-18 11:19:49 -07:00
jmestwa-coder
b65a5cc90a reject truncated frames in --list by validating full reads 2026-10-17 16:22:29 -07:00
Yann Collet
36306a14a0 Merge pull request #1881 from lz4/fix_multifile_suffix_size
lz4io: size multi-file output names for the suffix
2026-10-17 15:56:45 -07:00
Yann Collet
29aac801d5 lz4io: size multi-file output names for the suffix
When the output filename buffer was too small,
LZ4IO_compressMultipleFilenames() and its _Legacy variant
reallocated it to ifnSize + 20, ignoring the suffix length.
A suffix longer than 19 bytes would overflow it in strcat().

Not reachable from the CLI, which only passes ".lz4".
Reported in #1773.
2026-10-17 15:17:19 -07:00
hzy
eaf7baf1b6 util: check UTIL_realloc result before computing *bufEnd in UTIL_prepareFileList
In both the _WIN32 and the POSIX variant of UTIL_prepareFileList(),
the buffer-growth branch computed

    *bufEnd = *bufStart + newListSize;

before checking whether UTIL_realloc() succeeded. UTIL_realloc frees
the old block and returns NULL on failure, so the statement performs
pointer arithmetic on a null pointer (undefined behavior, C17 6.5.6)
and leaves *bufEnd holding a bogus non-NULL extent.

Found via cppcheck nullPointerArithmeticRedundantCheck warnings
(util.h:411 and util.h:468); confirmed end-to-end by building the real
lz4 CLI with clang -fsanitize=pointer-overflow and failing the growth
realloc via LD_PRELOAD: 'util.h:468:37: runtime error: applying
non-zero offset 16384 to null pointer'. With this patch the same run
completes with no sanitizer report.
2026-10-17 15:02:25 -07:00
Yann Collet
aa787570e6 Windows CI: test lz4 -l on an empty file, NEWS for #1740
On Windows, TPool_jobsCompleted() only waited on an auto-reset event,
set when the pending job count drops to 0 :
- with no job submitted, the event is never set and the wait never ends.
  `lz4 -l` on an empty file submits no write job,
  and decoding the resulting stream (magic number only) submits no decoding job.
- ResetEvent() + increment in TPool_submitJob() race with
  decrement + SetEvent() in a worker : the event can stay set while a job
  is still pending, so the wait could end before the last write.
#1740 checks the pending job count before waiting, and after each wake up.

The Windows CI never ran lz4 on files :
_test.bat now compresses and decodes an empty file in legacy format.
2026-10-17 09:01:28 -07:00
Yann Collet
1902c946a8 Merge #1740 : predicate-based wait in Windows TPool_jobsCompleted() 2026-10-17 09:00:38 -07:00
arshiya tabasum
28cdacfb85 read full frame header for dictID frames in --list 2026-10-17 07:58:27 -07:00
Yann Collet
096e541cb1 lz4io: multi-file test mode decodes into /dev/null instead of stdout
LZ4IO_decompressMultipleFilenames() always opened stdout as the shared
destination, even when the suffix is nulmark (test mode). As a result,
`lz4 -t -m` wrote decoded content to stdout, for every decoder which
does not skip writes in test mode (multithreaded decoders, legacy format).

Now, the shared destination is opened from the suffix (stdout or /dev/null),
and only when needed. Decoders no longer need to be aware of test mode.
2026-10-14 19:27:41 -07:00
Yann Collet
edc576f92b doc: --favor-decSpeed size cost, NEWS entry for #1855
The man page said --favor-decSpeed costs ~0.5% in size;
measured at levels 10-12, it is now closer to 1%.
2026-10-14 15:18:57 -07:00
Yann Collet
b008b0858a cli: honor custom block sizes in multithreaded compression (fixes #1810)
The multithreaded compressor cut input into 4 MB jobs,
each compressed with a single LZ4F_compressUpdate() invocation,
and single-job inputs were compressed with LZ4F_compressFrame().
In both cases, blocks were cut at the block size ID maximum (64 KB minimum),
silently ignoring io_prefs->blockSize.

Now each job contains a whole number of blocks,
and is compressed one block per LZ4F_compressUpdate() invocation (stableSrc),
which is how LZ4F supports custom block sizes (with autoFlush).
Output buffers are sized accordingly.
Multithreading is preserved for custom block sizes,
and output for standard block sizes is unchanged.

Also: LZ4IO_defaultPreferences() now sets a blockSize consistent with blockSizeId
(it was 0, which the compressors cannot use).
2026-10-07 23:53:20 -07:00
Bryan M
4cf98b0324 cli: keep source after decompression failure
The --rm option promises to remove the source only after successful decompression, but the recoverable trailing-data error path reaches source removal with a nonzero result. Keep the input whenever decoding reports failure, and cover the affected skippable-frame sequence with a regression.
2026-10-07 15:03:43 -07:00
jmestwa-coder
adbf1719e7 avoid hang by using predicate-based wait in TPool_jobsCompleted 2026-06-08 10:28:32 +05:30
Yann Collet
446a35fb65 Merge pull request #1697 from jlee303/add_content_sz_warning
Add warning message when content size flag is used with stdin
2026-01-26 09:06:39 -08:00
Yann Collet
6f8f65d2c4 fix minor yet complex issue involving thread pool
in the context of multiple Makefile invoking each other.
2026-01-25 12:02:46 -08:00
Yann Collet
c57dfafe27 bench: align display when speed > 10000 MB/s
as cpus get faster, it's now possible for some of them to reach speeds > 10000 MB/s on single core.
Ensure that the display remains properly aligned in this case.
2026-01-24 22:40:20 -08:00
jlee303
f3aedd1344 add warning message when content size flag is used with stdin 2026-01-21 11:39:34 -08:00
Yann Collet
b1e83528d3 static library built with multiconf.make 2025-05-05 17:12:23 -07:00
Yann Collet
c9c9bf8c12 added multiconf doc 2025-05-01 16:53:52 -07:00
Yann Collet
cde9d9c98b fixed: LIBDIR variable name no longer employed in programs/Makefile
since this variable can theoretically be used
to guide some custom installation process of the library.
2025-04-30 17:06:44 -07:00
Yann Collet
e77b14f8ad update multiconf.make 2025-04-30 17:06:44 -07:00
Yann Collet
5387a1595c support *_SRCDIRS 2025-04-30 17:06:44 -07:00
Yann Collet
1d05c07a37 also works for lz4 on Windows (mingw) 2025-04-30 17:06:44 -07:00
Yann Collet
3df0068022 premiere application de multiconf.make
limitee a programs/
2025-04-30 17:06:43 -07:00
Yann Collet
2719f42eb5 removed year from header (#1605)
as it's a burden to maintain, and wasn't consistently updated.

Now rely on git dating.
2025-04-27 10:55:25 -08:00
Yann Collet
f369c3a98e Fix theoretical risk of double-free in UTIL_realloc()
if the function was called with a `0` argument, which is never the case.
replaces #1565
2025-02-25 08:13:52 -08:00
Yann Collet
217bdb2f2f update -H documentation regarding compression levels 2024-09-04 11:03:28 -07:00
Yann Collet
6410f177c6 slighly better cpu time measurement for Windows
correctly skips the initialization part of the programs,
so that it's now on par with posix's clock().

In the future, would allow to measure cpu time between arbitrary points in the program execution, though for the time being, it's used for full session.
2024-08-03 09:51:55 -07:00
Yann Collet
f7e65802dc fix process time counter on Windows for very large files 2024-08-02 23:39:45 -07:00
Yann Collet
9d298e9540 -h only documents -T# when multithreading is enabled
fixes #1480
2024-07-28 21:30:29 -07:00
Ikko Eltociear Ashimine
7739164446 docs: update lz4conf.h
overriden -> overridden
2024-07-26 01:24:23 +09:00
Yann Collet
36df905aa6 update API and man page documentation for v1.10 2024-07-20 15:21:39 -07:00
Yann Collet
de6531b45b fixed minor conversion warning 2024-07-20 14:32:43 -07:00
Yann Collet
8ce9e94cd5 improved lorem ipsum generator speed by a factor > x8 2024-07-20 14:29:58 -07:00
Yann Collet
2a1de07855 improved speed of lorem ipsum generator
by > +80%.

This should help complete tests faster
2024-07-20 12:35:41 -07:00
Yann Collet
21837b9621 fixed traces 2024-07-19 18:19:07 -07:00
Yann Collet
d31aaac2ed Level 2 is now compatible with dictionary attach mode
but it requires that dictionary state is loaded with Level 2
2024-07-19 17:30:33 -07:00