Standalone C library for reading and writing the pqcap capture format: PCAP-NG compatible packet storage with an embedded Parquet metadata index and a fixed 44-byte footer locator for range-read friendly access.
Normative format contract: pqcap SPEC.
The 44-byte footer is only a locator, not a container for Parquet data and not a size limit on the format.
| Layer | Size | Role |
|---|---|---|
Footer block (PQCAPFTR) |
Fixed 44 bytes | Holds metadata_parquet_offset + metadata_parquet_length (uint64 each) for tail/range reads |
| Embedded Parquet bytes | Variable (opaque) | Copied verbatim from the writer; may be as large and as feature-rich as any standalone .parquet file |
libpqcap treats Parquet as an opaque byte blob: row groups, compression, optional columns, and multi-GB files are all determined by your Parquet writer, not by libpqcap. The library never parses or rewrites Parquet — it only wraps, locates, and extracts the exact bytes you provide.
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
ctest --test-dir build --output-on-failureSee examples/README.md for small CLI programs (convert_pcapng, extract_metadata, scan_packets) and a fixture smoke script:
cmake --build build
bash examples/run_examples.sh
# or via ctest:
ctest --test-dir build -R examples_smoke --output-on-failurelibpqcap tests are self-contained: C unit tests, fixture embed/extract checks, and tshark wire-readability validation. No pqcap binary or DuckDB dependency is required.
cmake --build build
ctest --test-dir build --output-on-failureCross-testing against the pqcap reference CLI (optional, outside this repo's CI) can be done manually in the pqcap tree — libpqcap does not invoke it.
Converting plain PCAP-NG to pqcap format must retain wire readability. The pcapng_to_pqcap_convert test:
- Asserts input is a plain capture (no pqcap footer yet)
- Converts with
pqcap_convert_file/pqcap_convert_cli - Asserts output is indexed, prefix bytes unchanged, and Parquet extract matches
- Verifies
tshark/capinfosstill decode the same application traffic (udp,http, GET/200) - Rejects double-convert on an already-indexed file
ctest --test-dir build -R pcapng_to_pqcap_convert --output-on-failureSet LIBPQCAP_SKIP_TSHARK=1 only to skip wire-tool checks locally when tshark is unavailable. CI installs tshark on Linux and macOS.
Regenerate committed Parquet/PCAP-NG fixtures (optional):
bash scripts/generate_fixtures.shInstall (optional):
cmake --install build --prefix /usr/local#include "pqcap.h"
#include <stdio.h>
#include <stdlib.h>
int main(void) {
/* Read footer from an indexed capture */
pqcap_footer_t footer;
int err = pqcap_read_footer_from_file("capture.pqcapng", &footer);
if (err != PQCAP_OK) {
fprintf(stderr, "footer: %s\n", pqcap_strerror(err));
return 1;
}
/* Embed metadata into a plain capture (in memory) */
uint8_t *capture = ...;
size_t capture_len = ...;
uint8_t *parquet = ...;
size_t parquet_len = ...;
uint8_t *out = NULL;
size_t out_len = 0;
err = pqcap_embed(capture, capture_len, parquet, parquet_len, &out, &out_len);
if (err != PQCAP_OK) {
fprintf(stderr, "embed: %s\n", pqcap_strerror(err));
return 1;
}
pqcap_free(out);
return 0;
}| Function | Purpose |
|---|---|
pqcap_footer_parse / pqcap_footer_build |
Parse or build the 44-byte PQCAPFTR footer block |
pqcap_read_footer_from_file |
Read footer from the last 44 bytes of a file |
pqcap_has_footer / pqcap_is_plain_capture |
Detect indexed vs plain captures |
pqcap_validate_range |
Overflow-safe footer range checks |
pqcap_embed |
Append metadata block + footer to a plain capture |
pqcap_embed_file |
Append metadata block + footer to a plain capture (path-based) |
pqcap_convert_file |
Convert plain PCAP-NG to pqcap format (alias of pqcap_embed_file) |
pqcap_extract_parquet |
Slice embedded Parquet bytes using a parsed footer |
pqcap_extract_parquet_file |
Extract embedded Parquet to a standalone file |
pqcap_extract_parquet_from_buffer |
Footer-first extract with legacy block scan fallback |
pqcap_read_file / pqcap_write_file |
Whole-file I/O helpers |
pqcap_scan_packets |
Scan PCAP-NG EPB (0x06) and legacy packet (0x03) locations |
pqcap_free |
Release buffers returned by the library |
pqcap_strerror |
Human-readable error strings |
| Code | Meaning |
|---|---|
PQCAP_OK |
Success |
PQCAP_ERR_INVALID_ARG |
NULL pointer or invalid parameter |
PQCAP_ERR_BAD_MAGIC |
Block type, enterprise ID, or magic mismatch |
PQCAP_ERR_BAD_VERSION |
Unsupported footer/metadata version |
PQCAP_ERR_RANGE |
Invalid offset/length or overflow |
PQCAP_ERR_IO |
File I/O failure |
PQCAP_ERR_NOMEM |
Allocation failure |
PQCAP_ERR_ALREADY_INDEXED |
Capture already contains pqcap metadata |
PQCAP_ERR_NOT_FOUND |
No pqcap metadata present |
The pqcap repository tracks this library as a git submodule:
git clone --recurse-submodules https://github.com/sipcapture/pqcap.git
cd pqcap/libpqcap
cmake -S . -B build && cmake --build build && ctest --test-dir buildDevelop, commit, and push from inside libpqcap/; bump the submodule pointer in pqcap when releasing a new libpqcap revision.
v0.1 — footer parse/build, metadata embed/extract, PCAP-NG packet offset scan, file helpers, and self-contained fixture/integration tests (tshark wire compatibility). Parquet schema construction and L4 decode are out of scope; use your own Parquet writer and join with pqcap_scan_packets offsets.
MIT — see LICENSE.