Skip to content

Repository files navigation

CToon Long Logo

The fastest implementation of the TOON format — a compact, human-readable serialisation format designed to minimise LLM token usage. Achieves 30-60% token reduction versus JSON while remaining fully readable and structured.

CToon is built on a high-performance C core and exposes the same logic through idiomatic bindings for C++, Python, Go, Rust, Zig, and MATLAB. The name reflects its foundation: C + TOON.

Format Overview

# JSON  (352 bytes)                  # TOON  (165 bytes, -53 %)
{                                    order:
  "order": {                           id: ORD-12345
    "id": "ORD-12345",                 status: completed
    "items": [                         customer:
      {"product":"Book","qty":2},        name: John Doe
      {"product":"Pen","qty":5}          email: john@example.com
    ]                                  items[2]{product,qty}:
  }                                      Book,2
}                                        Pen,5

Quick Start

CLI

cmake -B build && cmake --build build
./build/ctoon data.json           # JSON  -> TOON
./build/ctoon data.toon           # TOON  -> JSON
cat data.json | ./build/ctoon     # stdin -> TOON

C

#include "ctoon.h"

ctoon_doc *doc = ctoon_read("name: Alice\nage: 30", 20, 0);
ctoon_val *root = ctoon_doc_get_root(doc);

ctoon_val *name = ctoon_obj_get(root, "name");
printf("%s\n", ctoon_get_str(name));     /* Alice */

size_t len;
char *toon = ctoon_write(doc, &len);     /* caller must free() */
free(toon);
ctoon_doc_free(doc);

/* JSON output (on by default; define CTOON_DISABLE_JSON to turn off) */
char *json = ctoon_doc_to_json(doc, 2, CTOON_WRITE_NOFLAG, NULL, &len, NULL);
free(json);

C++

#include "ctoon.hpp"

/* Parse TOON */
auto doc  = ctoon::document::parse("name: Alice\nage: 30");
auto root = doc.root();
std::cout << root["name"].get_str().str() << "\n";  // Alice
std::cout << root["age"].get_uint()        << "\n";  // 30

/* Serialise */
std::cout << doc.to_string().c_str()    << "\n";  // TOON
std::cout << doc.to_json(2).c_str() << "\n";  // JSON

/* Build a mutable document */
auto mdoc = ctoon::make_document();
auto obj  = mdoc.make_obj();
mdoc.set_root(obj);
obj.obj_put(mdoc.make_str("city"), mdoc.make_str("Tehran"));
obj.obj_put(mdoc.make_str("pop"),  mdoc.make_uint(9'000'000));
std::cout << mdoc.to_json(0).c_str() << "\n";  // {"city":"Tehran","pop":9000000}

Python

import ctoon

# encode / decode
toon = ctoon.dumps({"name": "Alice", "age": 30})
data = ctoon.loads(toon)

# file I/O
ctoon.dump(data, "out.toon")
data = ctoon.load("out.toon")

# JSON
json_str = ctoon.dumps_json(data, indent=2)
data     = ctoon.loads_json(json_str)

Go

import ctoon "github.com/mohammadraziei/ctoon"

// encode / decode
toon, _ := ctoon.Dumps(map[string]interface{}{"name": "Alice", "age": int64(30)})
val,  _ := ctoon.Loads(toon)

// file I/O
ctoon.EncodeToFile(data, "out.toon", ctoon.DefaultEncodeOptions())
val, _ = ctoon.DecodeFromFile("out.toon", ctoon.DefaultDecodeOptions())

MATLAB

% Install once (compiles MEX and adds to MATLAB path permanently)
cd src/bindings/matlab
ctoon_install

% Versin / info
ctoon.version
v = ctoon.version
[v, info] = ctoon.version

% Encode / decode
s = ctoon.encode(struct('name', 'Alice', 'age', uint64(30), 'active', true));
v = ctoon.decode(s);
v.name    % -> 'Alice'
v.age     % -> uint64(30)
v.active  % -> true

% Python-style aliases
s = ctoon.dumps(v);
v = ctoon.loads(s);

% File I/O  (filename)
ctoon.write(v, 'config.toon');
v = ctoon.read('config.toon');

% File I/O  (open fid)
fid = fopen('config.toon', 'w');
ctoon.dump(v, fid);
fclose(fid);

fid = fopen('config.toon', 'r');
v = ctoon.load(fid);
fclose(fid);

MATLAB type mapping:

MATLAB TOON
[] null
logical bool
double scalar real
int64 scalar sint
uint64 scalar uint
char str
cell array
struct object

Requires MATLAB R2014b+ and a C compiler configured for MEX (mex -setup C).


Build

cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j$(nproc) --target ctoon_test     # Run all tests
cmake --build build -j$(nproc) --target ctoon_coverage # Generate coverage reports

CMake options

Option Default Description
CTOON_BUILD_TESTS ON Build C and C++ tests
CTOON_BUILD_PYTHON OFF Build Python extension (nanobind)
CTOON_BUILD_DOCS OFF Build documentation
CTOON_BUILD_ALL_LANGS OFF Require every self-building language's toolchain (Go, Rust, Zig, MATLAB) — error instead of skip if any is missing
CTOON_BUILD_GO OFF Require the Go toolchain (error instead of skip if missing)
CTOON_BUILD_RUST OFF Require the Rust toolchain (error instead of skip if missing)
CTOON_BUILD_ZIG OFF Require the Zig toolchain (error instead of skip if missing)
CTOON_BUILD_MATLAB OFF Require the MATLAB toolchain (error instead of skip if missing)

Go, Rust, Zig, and MATLAB each compile themselves with their own external toolchain (go build, cargo, zig build, MEX) rather than through this project's own CMake graph — the CTOON_BUILD_<LANG> flags above don't turn on a build step, they change what happens when tests/ or docs/ detect that toolchain is missing: silently skip that language (the default, useful on a machine with only some toolchains installed), or fail the configure step outright once you've explicitly opted in. (benchmarks/ is a separate, standalone CMake project and always warn-and-skips regardless — its whole point is running whatever it can find, see benchmarks/README.md.)

JSON support is on by default. Define CTOON_DISABLE_JSON to turn it off. No external JSON library required.

Python package

pip install ctoon

Go module

go get github.com/mohammadraziei/ctoon

Requires Go 1.21+. Uses CGo to call the C core.

MATLAB MEX

# Via CMake
cmake -B build -DCTOON_BUILD_MATLAB=ON -DMatlab_ROOT_DIR=/path/to/matlab
cmake --build build --target ctoon_build_mex

# Or directly from MATLAB
cd src/bindings/matlab
ctoon_install        % compile + add to path permanently

CLI Reference

# Encode  JSON -> TOON
ctoon input.json
ctoon input.json -o output.toon
ctoon input.json --delimiter pipe      # comma (default), tab, pipe
ctoon input.json --length-marker       # items[#3]: ...
ctoon input.json --stats               # show byte savings

# Decode  TOON -> JSON
ctoon input.toon
ctoon input.toon -o output.json
ctoon input.toon -i 4                  # 4-space JSON indent

# Force format (auto-detected from extension by default)
ctoon -e input.json    # force encode
ctoon -d input.toon    # force decode

# Stdin
cat data.json | ctoon -e -
cat data.toon | ctoon -d -

C API Reference

Parsing

/* from memory */
ctoon_doc *ctoon_read(const char *toon, size_t len, ctoon_read_flag flags);
ctoon_doc *ctoon_read_opts(char *toon, size_t len, ctoon_read_flag flags,
                            const ctoon_alc *alc, ctoon_read_err *err);
ctoon_doc *ctoon_read_opts_indent(char *toon, size_t len, ctoon_read_flag flags,
                                   int indent_size, const ctoon_alc *alc,
                                   ctoon_read_err *err);
/* from file */
ctoon_doc *ctoon_read_file(const char *path, ctoon_read_flag flags,
                            const ctoon_alc *alc, ctoon_read_err *err);
/* from JSON */
ctoon_doc *ctoon_read_json(char *json, size_t len, ctoon_read_flag flags,
                            const ctoon_alc *alc, ctoon_read_err *err);
ctoon_doc *ctoon_read_json_file(const char *path, ctoon_read_flag flags,
                                 const ctoon_alc *alc, ctoon_read_err *err);

Writing

/* TOON output */
char *ctoon_write(const ctoon_doc *doc, size_t *len);
char *ctoon_write_opts(const ctoon_doc *doc, const ctoon_write_options *opts,
                        const ctoon_alc *alc, size_t *len, ctoon_write_err *err);
char *ctoon_mut_write(const ctoon_mut_doc *doc, size_t *len);

/* JSON output */
char *ctoon_doc_to_json(const ctoon_doc *doc, int indent,
                         ctoon_write_flag flags, const ctoon_alc *alc,
                         size_t *len, ctoon_write_err *err);

Access

/* document */
ctoon_val *ctoon_doc_get_root(const ctoon_doc *doc);
void       ctoon_doc_free(ctoon_doc *doc);

/* type checks */
bool ctoon_is_null(ctoon_val *v);   bool ctoon_is_bool(ctoon_val *v);
bool ctoon_is_true(ctoon_val *v);   bool ctoon_is_false(ctoon_val *v);
bool ctoon_is_uint(ctoon_val *v);   bool ctoon_is_sint(ctoon_val *v);
bool ctoon_is_real(ctoon_val *v);   bool ctoon_is_str(ctoon_val *v);
bool ctoon_is_arr(ctoon_val *v);    bool ctoon_is_obj(ctoon_val *v);

/* getters */
const char *ctoon_get_str(ctoon_val *v);
uint64_t    ctoon_get_uint(ctoon_val *v);
int64_t     ctoon_get_sint(ctoon_val *v);
double      ctoon_get_real(ctoon_val *v);
bool        ctoon_get_bool(ctoon_val *v);

/* array */
size_t     ctoon_arr_size(ctoon_val *arr);
ctoon_val *ctoon_arr_get(ctoon_val *arr, size_t idx);   /* O(1) */

/* object */
size_t     ctoon_obj_size(ctoon_val *obj);
ctoon_val *ctoon_obj_get(ctoon_val *obj, const char *key);

/* iteration */
ctoon_obj_iter ctoon_obj_iter_with(ctoon_val *obj);
ctoon_val     *ctoon_obj_iter_next(ctoon_obj_iter *iter);
ctoon_val     *ctoon_obj_iter_get_val(ctoon_val *key);

Mutable documents

ctoon_mut_doc *ctoon_mut_doc_new(const ctoon_alc *alc);
void           ctoon_mut_doc_free(ctoon_mut_doc *doc);
void           ctoon_mut_doc_set_root(ctoon_mut_doc *doc, ctoon_mut_val *root);

ctoon_mut_val *ctoon_mut_null(ctoon_mut_doc *doc);
ctoon_mut_val *ctoon_mut_bool(ctoon_mut_doc *doc, bool val);
ctoon_mut_val *ctoon_mut_uint(ctoon_mut_doc *doc, uint64_t val);
ctoon_mut_val *ctoon_mut_sint(ctoon_mut_doc *doc, int64_t val);
ctoon_mut_val *ctoon_mut_real(ctoon_mut_doc *doc, double val);
ctoon_mut_val *ctoon_mut_str(ctoon_mut_doc *doc, const char *str);
ctoon_mut_val *ctoon_mut_arr(ctoon_mut_doc *doc);
ctoon_mut_val *ctoon_mut_obj(ctoon_mut_doc *doc);

bool ctoon_mut_arr_append(ctoon_mut_val *arr, ctoon_mut_val *item);
bool ctoon_mut_obj_put(ctoon_mut_val *obj, ctoon_mut_val *key, ctoon_mut_val *val);

/* convert between immutable and mutable */
ctoon_mut_doc *ctoon_doc_mut_copy(ctoon_doc *doc, const ctoon_alc *alc);
ctoon_doc     *ctoon_mut_doc_imut_copy(ctoon_mut_doc *doc, const ctoon_alc *alc);

Complexity

Operation Time Notes
ctoon_read / ctoon_read_json O(n) n = input bytes
ctoon_write / ctoon_doc_to_json O(n) zero extra copy for immutable doc
ctoon_arr_get(arr, i) O(1) direct index into flat arena
ctoon_obj_get(obj, key) O(k) linear scan; k = key count
ctoon_doc_free O(chunks) ~= O(1) arena freed in one shot

Thread Safety

Scenario Safe?
Multiple threads reading different documents Yes
Multiple threads reading the same document Yes
Building a document from multiple threads No — arena not thread-safe

Requirements

Component Requirement
C core C99, no dependencies
C++ binding C++11, header-only
Python binding Python 3.9+, nanobind >= 2.0, CMake 3.19+
Go binding Go 1.21+, CGo
Rust binding Rust 1.70+
Zig binding Zig 0.16.0+
MATLAB binding MATLAB R2014b+, C compiler for MEX
CLI C++17

License

MIT — see LICENSE.

Related

Releases

Packages

Contributors

Languages