Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,8 +250,9 @@ int64_t decompressed_size = zxc_decompress(src, src_size, dst, dst_capacity, &d_

The same options structs drive the other entry points: `zxc_stream_compress()` /
`zxc_stream_decompress()` for multi-threaded file streaming, reusable `zxc_cctx` / `zxc_dctx`
contexts for tight loops where per-call `malloc`/`free` overhead matters (settings are **sticky**,
so passing `NULL` reuses those given at creation), and `.seekable = 1` to append a seek table for
contexts for tight loops where per-call `malloc`/`free` overhead matters (settings are **sticky**:
passing `NULL` reuses the last successful call's, starting from those given at creation), and
`.seekable = 1` to append a seek table for
O(1) random-access decompression.

**[👉 See complete examples and advanced usage](docs/EXAMPLES.md)** — stream API, reusable contexts,
Expand Down
20 changes: 13 additions & 7 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,7 @@ ZXC_EXPORT int64_t zxc_compress_block(
size_t src_size,
void* dst,
size_t dst_capacity,
const zxc_compress_opts_t* opts // NULL = defaults
const zxc_compress_opts_t* opts // NULL = sticky settings
);
```

Expand All @@ -669,7 +669,8 @@ use the frame API (`zxc_compress`) or streaming API (`zxc_cstream_*`), which
chunk transparently into format-conformant blocks.

**Returns**: compressed block size (> 0) on success, or negative `zxc_error_t`.
Returns `ZXC_ERROR_BAD_BLOCK_SIZE` if `src_size > ZXC_BLOCK_SIZE_MAX`.
Returns `ZXC_ERROR_BAD_BLOCK_SIZE` if `src_size > ZXC_BLOCK_SIZE_MAX`, or if
`block_size` is not a power of two in range.

### `zxc_decompress_block`

Expand Down Expand Up @@ -776,7 +777,9 @@ ZXC_EXPORT zxc_cctx* zxc_create_cctx(const zxc_compress_opts_t* opts);

Creates a heap-allocated compression context.
When `opts` is non-NULL, internal buffers are pre-allocated (eager init).
When `opts` is NULL, allocation is deferred to first use (lazy init).
When `opts` is NULL, allocation is deferred to first use (lazy init).
The creation options are also what a zero `level` or `block_size` in a later
call falls back to (the library defaults for NULL).

**Returns**: context pointer, or `NULL` on allocation failure.

Expand All @@ -802,17 +805,20 @@ ZXC_EXPORT int64_t zxc_compress_cctx(
```

Same as `zxc_compress()` but reuses internal buffers from `cctx`.
Automatically re-initializes when `block_size` or `level` changes. A zero
The workspace is re-carved when the block size changes, or when a level raise
into `ZXC_LEVEL_DENSITY` needs the optimal-parser scratch. A zero
`src_size` writes the empty archive, as `zxc_compress()` does, without carving
the workspace. Dictionary options are honoured as in `zxc_compress()` but are
not sticky; the shared literal table is rebuilt only when it changes. A static
context returns `ZXC_ERROR_DICT_UNSUPPORTED` for any dictionary. `seekable`
appends the seek table as `zxc_compress()` does, without allocating, static
contexts included.

Each entry point remembers the options it uses: `level`, `block_size`,
`checksum_enabled` and `seekable` here, the first three for
`zxc_compress_block()`.
Options are **sticky**: `NULL` reuses the last successful call's `level`,
`block_size`, `checksum_enabled` and `seekable` (the first three for
`zxc_compress_block()`), starting from the creation options. A zero `level` or
`block_size` in a non-NULL `opts` means the value the context was created
with, never an earlier call's. A failed call leaves them untouched.

### `zxc_create_dctx`

Expand Down
12 changes: 7 additions & 5 deletions docs/EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,9 +230,11 @@ ZXC provides opaque reusable contexts.
Internal buffers are only reallocated when the **block size changes**, so the
common case (same block size, different data) is completely allocation-free.

Options are **sticky**: settings passed via `opts` to `zxc_create_cctx()` or
`zxc_compress_cctx()` are remembered and reused on subsequent calls where
`opts` is `NULL`.
Options are **sticky**: `zxc_compress_cctx()` remembers the options of its
last successful call, starting from those given to `zxc_create_cctx()`, and
reuses them when `opts` is `NULL`. In a non-NULL `opts`, a zero `level` or
`block_size` means the creation value, so a level or block-size override needs
only that field; `checksum_enabled` and `seekable` are taken as given (0 = off).

```c
#include "zxc.h"
Expand Down Expand Up @@ -283,8 +285,8 @@ int main(void) {
}
}

// Override sticky settings for a single call (e.g. switch to level 5).
// This also updates the sticky settings for future NULL calls.
// Switch to level 5. block_size 0 keeps the one from create_opts, so no
// re-allocation; later NULL calls reuse level 5.
zxc_compress_opts_t high_opts = { .level = 5 };
int64_t csz = zxc_compress_cctx(cctx, src, BLOCK_SIZE, comp, comp_cap, &high_opts);
printf("Level-5 compressed: %lld bytes\n", (long long)csz);
Expand Down
25 changes: 15 additions & 10 deletions include/zxc_buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ ZXC_EXPORT uint64_t zxc_decompress_block_bound(const size_t uncompressed_size);
* @param[in] src_size Source size in bytes, in [1, @ref ZXC_BLOCK_SIZE_MAX].
* @param[out] dst Destination buffer.
* @param[in] dst_capacity Capacity of @p dst (see zxc_compress_block_bound()).
* @param[in] opts Compression options, or NULL for defaults.
* @param[in] opts Options, or NULL to reuse the sticky settings.
* @c level, @c block_size, @c checksum_enabled and
* the dictionary fields are used; the shared table
* is rebuilt only when it changes. @c seekable is
Expand All @@ -383,7 +383,8 @@ ZXC_EXPORT uint64_t zxc_decompress_block_bound(const size_t uncompressed_size);
*
* @return Compressed block size (> 0), or a negative @ref zxc_error_t.
* @ref ZXC_ERROR_BAD_BLOCK_SIZE if @p src_size exceeds
* @ref ZXC_BLOCK_SIZE_MAX; @ref ZXC_ERROR_BAD_LEVEL on a static
* @ref ZXC_BLOCK_SIZE_MAX or @c block_size is not a power of two in
* range; @ref ZXC_ERROR_BAD_LEVEL on a static
* context for a level raise its workspace cannot accommodate (levels
* above @ref ZXC_LEVEL_ULTRA are otherwise silently clamped);
* @ref ZXC_ERROR_DICT_UNSUPPORTED on a static context with a dictionary.
Expand Down Expand Up @@ -535,10 +536,13 @@ ZXC_EXPORT void zxc_free_cctx(zxc_cctx* cctx);
* raise returns @ref ZXC_ERROR_BAD_LEVEL instead, since the workspace cannot
* grow.
*
* Options are **sticky**: each entry point remembers the options it uses for
* later calls that pass NULL, starting from zxc_create_cctx(): level,
* block_size, checksum_enabled and seekable here, the first three for
* zxc_compress_block(). Levels above @ref ZXC_LEVEL_ULTRA are silently clamped.
* Options are **sticky**: NULL @p opts reuses the last successful call's level,
* block_size, checksum_enabled and seekable (the first three for
* zxc_compress_block()), starting from zxc_create_cctx(). In a non-NULL
* @p opts, a zero level or block_size means the creation value (the library
* default for a NULL zxc_create_cctx()), never an earlier call's. A failed call
* leaves them untouched, workspace re-carving aside. Levels above
* @ref ZXC_LEVEL_ULTRA are silently clamped.
* Dictionary options are the exception: honoured as in zxc_compress() but
* never remembered, so pass them on every call; the shared table is rebuilt
* only when it changes. A static context returns
Expand Down Expand Up @@ -709,10 +713,11 @@ ZXC_EXPORT size_t zxc_static_cctx_workspace_size(const size_t block_size, const
* @par Locked parameters
* @c block_size, @c level and @c checksum_enabled are pinned at init time. A
* later @ref zxc_compress_cctx call asking for a different @c block_size
* returns @ref ZXC_ERROR_BAD_BLOCK_SIZE without re-initialising. A different
* @c level / @c checksum_enabled is honoured per call without re-partitioning,
* except a raise into @ref ZXC_LEVEL_DENSITY on a workspace carved below it:
* the optimal-parser scratch is absent, so the call returns
* returns @ref ZXC_ERROR_BAD_BLOCK_SIZE without re-initialising; a zero
* @c block_size or @c level means the pinned one. A different @c level /
* @c checksum_enabled is honoured per call without re-partitioning, except a
* raise into @ref ZXC_LEVEL_DENSITY on a workspace carved below it: the
* optimal-parser scratch is absent, so the call returns
* @ref ZXC_ERROR_BAD_LEVEL. Dictionaries are rejected too:
* @ref ZXC_ERROR_DICT_UNSUPPORTED.
*
Expand Down
Loading
Loading