How to Integrate the Kanzi C++ API for Compression into Your Application
To integrate the Kanzi C++ API for compression, link your application against libkanzi, include kanzi/api/Compressor.hpp, configure a cData struct with your desired transform and entropy codec, and execute the initCompressor(), compress(), and disposeCompressor() sequence to process data blocks.
The Kanzi C++ library (flanglet/kanzi-cpp) provides a modular, high-performance lossless compression pipeline designed for modern C++ applications. Whether you require a stable ABI through the C API or prefer direct C++ class instantiation, integrating Kanzi requires understanding its transform-entropy architecture and proper initialization workflow.
Understanding the Kanzi Compression Architecture
Kanzi implements a clear pipeline that processes data through distinct stages to maximize compression efficiency.
The Pipeline Flow
Input → Transform(s) → Entropy Encoder → CompressedOutputStream → File/Memory
- Transforms (e.g., LZ, BWT, RLT) increase data redundancy before encoding. Implementations reside in
src/transform/(seeLZCodec.cpp,BWT.cpp,ZRLT.cpp). - Entropy codecs (Huffman, ANS0/1, Range, FPAQ) convert transformed symbols into bit-streams. These live in
src/entropy/(e.g.,HuffmanEncoder.cpp,FPAQEncoder.cpp). CompressedOutputStream(src/io/CompressedOutputStream.hpp) buffers data, writes block headers (unless headerless mode is enabled), and forwards encoded bits to a file descriptor.Context(src/Context.hpp) serves as the central configuration holder, storing parameters such as block size, thread count, transform/entropy names, checksum mode, and headerless flags. All high-level objects (BlockCompressor,BlockDecompressor, and the C API) read settings from aContextinstance.
The C API Wrapper
The C API (src/api/Compressor.hpp, src/api/Compressor.cpp) provides an ABI-stable wrapper around the C++ pipeline. This avoids exposing template-heavy internals while granting full control over compression options. The typical compress flow involves:
- Fill a
cDatastructure with parameters (transform, entropy, block size, jobs, checksum, headerless). - Call
initCompressor(&cData, FILE* dst, &cContext*), which creates aCompressedOutputStream, validates names, and stores the pointer in an opaquecContext. - For each block, invoke
compress(cContext, src, inSize, &outSize), which transforms, entropy-encodes, and writes the data. - Call
disposeCompressor(&cContext, &writtenBytes)to flush pending data and free resources.
For pure C++ projects, you can bypass the C wrapper and instantiate BlockCompressor directly (see src/app/BlockCompressor.hpp). This class builds a Context internally and exposes compress(uint64& written) to process entire input streams.
Step-by-Step Integration Guide
Build and Link the Library
Clone the repository and build the static (libkanzi.a) or shared (libkanzi.so/.dll) library using the provided CMake configuration. Link your target with kanzi (static) or kanzi_shared (shared) and include the Threads::Threads dependency.
Key file: CMakeLists.txt – link
Include the Public Headers
For the C API:
#include <kanzi/api/Compressor.hpp>
For the C++ class API:
#include <kanzi/app/BlockCompressor.hpp>
Headers are installed under include/kanzi/… when running make install.
Key files: src/api/Compressor.hpp – link, src/app/BlockCompressor.hpp – link
Configure Compression Parameters
Fill a cData struct (C API) or Context object (C++). Example configuration for the C API:
kanzi::cData params{};
strcpy(params.transform, "LZ"); // Transform chain
strcpy(params.entropy, "ANS0"); // Entropy codec
params.blockSize = 1 << 16; // 64 KiB blocks
params.jobs = 1; // Thread count
params.checksum = 32; // 32-bit checksum per block
params.headerless = 0; // Standard headered stream
Key file: src/api/Compressor.hpp – link
Initialize and Compress
Initialize the compressor:
kanzi::cContext* ctx = nullptr;
if (kanzi::initCompressor(¶ms, outFile, &ctx) != 0) {
// Handle error
}
Compress data blocks in a loop:
size_t outSize = 0;
kanzi::compress(ctx, srcBuffer, srcSize, &outSize);
Key file: src/api/Compressor.cpp – link
Finalize and Cleanup
Call disposeCompressor to flush remaining data and retrieve total bytes written:
size_t totalWritten = 0;
kanzi::disposeCompressor(&ctx, &totalWritten);
The output file now contains a valid Kanzi bit-stream (or raw blocks if headerless was enabled).
Code Examples
Minimal C API Example
// compress_demo.cpp
#include <cstdio>
#include <cstring>
#include <kanzi/api/Compressor.hpp>
int main()
{
FILE* out = fopen("example.knz", "wb");
if (!out) return 1;
kanzi::cData params{};
strcpy(params.transform, "LZ");
strcpy(params.entropy, "ANS0");
params.blockSize = 1 << 16;
params.jobs = 1;
params.checksum = 32;
params.headerless = 0;
kanzi::cContext* ctx = nullptr;
if (kanzi::initCompressor(¶ms, out, &ctx) != 0) {
fclose(out);
return 2;
}
const char* txt = "The quick brown fox jumps over the lazy dog.";
size_t txtSize = strlen(txt);
size_t outSize = 0;
if (kanzi::compress(ctx,
reinterpret_cast<const unsigned char*>(txt),
txtSize,
&outSize) != 0) {
kanzi::disposeCompressor(&ctx, nullptr);
fclose(out);
return 3;
}
size_t flushed = 0;
kanzi::disposeCompressor(&ctx, &flushed);
fclose(out);
return 0;
}
Implementation details:
cDatafields are validated and rewritten byinitCompressor(see lines 96-108 inCompressor.cpp).- Block sizes are automatically rounded to multiples of 16 (
pData->blockSize = (pData->blockSize + 15) & -16). - Error codes are defined in
src/Error.hpp.
Pure C++ Class API Example
// cpp_compress_demo.cpp
#include <fstream>
#include <kanzi/app/BlockCompressor.hpp>
#include <kanzi/Context.hpp>
int main()
{
kanzi::Context ctx;
ctx.putString("transform", "LZ");
ctx.putString("entropy", "ANS0");
ctx.putInt("blockSize", 1 << 16);
ctx.putInt("jobs", 4);
ctx.putInt("checksum", 32);
ctx.putInt("headerless", 0);
ctx.putString("outputName", "example_cpp.knz");
ctx.putString("inputName", "input.txt");
kanzi::BlockCompressor compressor(ctx);
uint64_t written = 0;
int result = compressor.compress(written);
return (result == 0) ? 0 : 1;
}
Notes:
BlockCompressorreadsinputNameandoutputNamefrom theContext. If omitted, it defaults tostdin/stdout.- The heavy lifting (transform selection, entropy encoder creation) occurs inside
BlockCompressor::compress(seesrc/app/BlockCompressor.cpparound line 1125).
Python Integration via C API
The repository includes a Python wrapper demonstrating cross-language usage:
from kanzi import Compressor, KanziError
import tempfile
def compress_bytes(data: bytes) -> bytes:
with tempfile.NamedTemporaryFile(delete=False) as tmp:
with Compressor(tmp.name,
transform=b"LZ",
entropy=b"ANS0",
block_size=1<<16,
jobs=1,
checksum=32,
headerless=0) as c:
c.compress(data)
with open(tmp.name, "rb") as f:
return f.read()
This wrapper internally invokes the C functions defined in src/api/Compressor.cpp.
Key Implementation Files
| Category | File | Purpose | Link |
|---|---|---|---|
| Public C API | src/api/Compressor.hpp |
Declares cData, cContext, initCompressor(), compress(), disposeCompressor() |
View |
src/api/Compressor.cpp |
Implements C API wrapper, creates CompressedOutputStream, validates parameters |
View | |
src/api/Decompressor.hpp/cpp |
Symmetric decompression API | Decompressor.hpp | |
| C++ Class API | src/app/BlockCompressor.hpp |
High-level C++ interface using Context |
View |
src/app/BlockCompressor.cpp |
Core compression logic, multi-threading support | View | |
| I/O Layer | src/io/CompressedOutputStream.hpp/cpp |
Block buffering, header writing, bit-packing | CompressedOutputStream.hpp |
| Configuration | src/Context.hpp |
Key-value store for compression parameters | View |
| Error Handling | src/Error.hpp |
Error code definitions for API returns | View |
Summary
- Link against
libkanzi(static or shared) built via the providedCMakeLists.txt, ensuring you linkThreads::Threadsfor multi-threading support. - Choose your API layer: Use
kanzi/api/Compressor.hppfor stable C ABI compatibility across compilers, orkanzi/app/BlockCompressor.hppfor direct C++ integration withContext-based configuration. - Configure via
cDataorContext: Settransform(e.g., "LZ", "BWT"),entropy(e.g., "ANS0", "Huffman"),blockSize(rounded to multiples of 16),jobs(thread count), andheaderlessmode. - Execute the lifecycle:
initCompressor()→compress()(loop for blocks) →disposeCompressor()to flush buffers and retrieve total bytes written. - Handle errors: Check return codes against definitions in
src/Error.hpp; validation of transform/entropy names occurs duringinitCompressor.
Frequently Asked Questions
Should I use the C API or the C++ BlockCompressor class?
Use the C API (src/api/Compressor.hpp) when you need ABI stability across different compiler versions or when binding to other languages like Python. The cData struct and opaque cContext pointer provide a clean, stable interface. Use the C++ BlockCompressor class (src/app/BlockCompressor.hpp) for native C++ projects where you want direct access to the Context object and prefer object-oriented resource management without extern "C" linkage overhead.
How do I enable multi-threaded compression?
Set the jobs parameter in your cData struct (C API) or call ctx.putInt("jobs", 4) in your Context object (C++ API). The BlockCompressor implementation in src/app/BlockCompressor.cpp handles thread pool creation and block-level parallelism automatically. Note that you must link against Threads::Threads in your CMake configuration to support concurrent execution.
Can I compress data without headers for custom protocols?
Yes. Set params.headerless = 1 (C API) or ctx.putInt("headerless", 1) (C++ API). In headerless mode, CompressedOutputStream (defined in src/io/CompressedOutputStream.hpp) skips writing the Kanzi file signature and block metadata. You must manually track block sizes and checksums if your protocol requires them, as the decompressor will need identical parameters to decode the raw bit-stream correctly.
What error codes does the C API return?
The C API returns integer error codes defined in src/Error.hpp. Common values include ERR_OK (0) for success, ERR_INVALID_PARAM for malformed transform or entropy names, and ERR_IO for file operation failures. During initCompressor, the library validates cData fields and rewrites them (e.g., rounding blockSize to multiples of 16) before allocating the cContext structure. Always check return values before proceeding to compress() calls.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →