tensogram-ffi 0.21.0

C FFI bindings for the Tensogram N-tensor message format library
Documentation

tensogram-ffi

C FFI bindings for Tensogram. Two output names depending on the build path: the cargo-c / distribution flow produces a C-callable libtensogram.{so,dylib,a} (with SONAME, pkg-config descriptor, and installed header), while the in-tree contributor flow cargo build -p tensogram-ffi produces libtensogram_ffi.{so,dylib,a} (no SONAME, no pkg-config — used directly by the in-repo CMake build). The tensogram.h header generated by cbindgen is the same in both flows.

All public functions are prefixed tgm_. Opaque handles are returned by tgm_* constructors and released by the matching tgm_*_free function.

Quickstart (C)

A self-contained, copy-pasteable encode example. The C API guide has the matching tgm_decode round-trip: https://sites.ecmwf.int/docs/tensogram/main/guide/c-api.html.

The snippet assumes the installed layout (<prefix>/include/tensogram/tensogram.h) — produced by either of the first two install paths below. For the in-tree contributor flow (cargo build -p tensogram-ffi, third path) the header lives at rust/tensogram-ffi/tensogram.h; replace the include with #include "tensogram.h" and pass -Irust/tensogram-ffi to your compiler.

#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <tensogram/tensogram.h>

const char* meta_json =
    "{\"descriptors\": [{"
    "\"type\": \"ntensor\", \"ndim\": 2, \"shape\": [100, 200], "
    "\"strides\": [200, 1], \"dtype\": \"float32\", "
    "\"byte_order\": \"little\", \"encoding\": \"none\", "
    "\"filter\": \"none\", \"compression\": \"none\"}]}";

/* Replace these two with your own data: a single-object encode takes
   one (uint8_t*, size_t) pair per descriptor.  Here we encode a 100×200
   float32 grid (= 80000 bytes of zeros) for illustration. */
float            payload_data[100 * 200] = {0};
const uint8_t*   payload_bytes           = (const uint8_t*)payload_data;
const size_t     payload_len             = sizeof(payload_data);

const uint8_t* ptrs[1] = { payload_bytes };
const size_t   lens[1] = { payload_len };

tgm_bytes_t out;
tgm_error err = tgm_encode(meta_json, ptrs, lens, 1, "xxh3", 0, &out);
if (err != TGM_ERROR_OK) {
    fprintf(stderr, "encode failed: %s\n", tgm_last_error());
    return 1;
}

/* Use out.data / out.len ... */
tgm_bytes_free(out);

A thread-local error string is available via tgm_last_error() after any tgm_* call returns non-TGM_ERROR_OK.

Install

Three install paths. Pick whichever matches your context.

Pre-built binary (no Rust toolchain)

Download the tarball for your platform from the GitHub Releases page, then extract under /usr/local:

VERSION=<release-version>      # e.g. 0.20.0
PLATFORM=linux-x86_64          # or linux-aarch64 / macos-x86_64 / macos-aarch64
ASSET="tensogram-ffi-${VERSION}-${PLATFORM}.tar.gz"

curl -LO "https://github.com/ecmwf/tensogram/releases/download/${VERSION}/${ASSET}"
sudo tar --no-same-owner -C /usr/local -xzf "${ASSET}"
sudo ldconfig                  # Linux only
pkg-config --modversion tensogram

Available platforms: linux-x86_64, linux-aarch64, macos-x86_64, macos-aarch64. The bundled tensogram.pc hard-codes prefix=/usr/local, so the default extract path matters; see the C API guide for non-default prefixes.

cargo cinstall (custom prefix)

cargo-c builds and installs the versioned shared library, static library, pkg-config descriptor, and header in one step:

cargo install cargo-c
# --libdir=lib pins the layout; on Debian-style multiarch systems
# cargo-c would otherwise pick lib/<triplet>, hiding the .pc file from
# the PKG_CONFIG_PATH below.
cargo cinstall --release -p tensogram-ffi \
    --prefix="$HOME/.local" --libdir=lib
export PKG_CONFIG_PATH="$HOME/.local/lib/pkgconfig:$PKG_CONFIG_PATH"
pkg-config --modversion tensogram

Build from source (contributor flow)

cargo build --release -p tensogram-ffi

Outputs:

  • target/release/libtensogram_ffi.{a,so,dylib} — the library (the _ffi suffix is the cargo-build artefact name; the in-tree cpp/CMakeLists.txt consumes this path)
  • rust/tensogram-ffi/tensogram.h — the cbindgen-generated header, regenerated on every build by build.rs

Plain cargo build does not produce a SONAME, a pkg-config file, or an installed header. Use cargo cinstall for those.

Linking

After installing via either pre-built tarball or cargo cinstall:

cc $(pkg-config --cflags tensogram) my_program.c \
   $(pkg-config --libs tensogram) \
   -o my_program

pkg-config --cflags returns -I<includedir> so #include <tensogram/tensogram.h> resolves; pkg-config --libs returns -L<libdir> -ltensogram.

crates.io

cargo add tensogram-ffi

Most C / C++ consumers do not need this — depend on the pre-built binary or cargo cinstall instead. The crate is published primarily so other Rust crates can re-export the C ABI.

Versioning

Pre-1.0 SONAME policy: the library SONAME is MAJOR.MINOR (libtensogram.so.0.20 for 0.20.x). Every minor release bumps the SONAME, so consumers must rebuild on each minor. Patch releases keep the SONAME stable.

The wire-format version (independent of SONAME) is exposed via the TGM_WIRE_VERSION constant in tensogram.h.

See the C API guide for the full policy.

Documentation

License

Copyright 2026- European Centre for Medium-Range Weather Forecasts (ECMWF).

Licensed under the Apache License, Version 2.0. See LICENSE for details.