dtob-sys 0.1.1

Raw FFI bindings to the dtob C library (encoder + decoder).
Documentation
# dtob-doc

C implementation of the DTOB binary format (Delineated Trinary Over Binary).

## Architecture

```
lib/          public headers (dtob.h, json.h, dtob_types.h, html.h)
src/          library source → builds into libdtob.a
  trit.c      trit encoding/decoding (byte ↔ 00/01/10 pairs, 11 = padding)
  lexer.c     byte-aligned tokenizer (ctrl words, data payloads)
  parser.c    tokens → DtobValue AST (handles types header, custom types)
  ast.c       AST construction, accessors, memory, DtobWriter, type registry
  encoder.c   DtobValue AST → encoded bytes
  encode_json.c   JSON text → DtobValue AST
  encode_xml.c    XML/HTML text → DtobValue AST
  html_rules.c    HTML void elements, raw text tags, implicit close rules
cli/          command-line tools (not part of libdtob.a)
  main.c          dtob dispatch (encode, represent, extract, info)
  main_encode.c   dtob-encode (JSON/XML/HTML → .dtob)
  main_represent.c  dtob-represent (.dtob → human-readable)
  represent.c     colored representation output
  typed_encode.c  schema inference for typed encoding
  greedy.c        HTML asset inlining (images, CSS, scripts)
  http.c          URL fetching via libcurl
  io.c            file I/O helpers
test/         test files
```

## Wire format

- every document starts with magic bytes `13032026` (ASCII, not trit-encoded)
- control words: 2 bytes, top 3 bits = `110`, 13-bit code
- data: trit-encoded (00/01/10 pairs), padded to byte boundary with `11` pairs
- codes 0-15: built-in (open, close_arr, close_kv, close_types, raw, float, double, int types)
- codes 16-8190: custom (must be declared in types header)
- code 8191: blast (ignored, for analog resilience)
- see spec.bnf for the full grammar

## Custom types

Applications define custom types via a types header at the start of the document. Three kinds:
- **nullable** (0 opcodes): just the code, no payload — used as flags/sentinels
- **enum** (2+ opcodes): code + inner_code selects which variant
- **struct** (ordered fields): code + open + field values + close_arr

Use `dtob_types.h` macros (`DTOB_DEFINE_CUSTOM_TYPES`, `DTOB_CUSTOM_TYPE_RAW`, etc.) to define types without boilerplate.

## JSON convention layer

`json.h` provides suggested code assignments for JSON primitives (codes 16-19). These are NOT reserved — applications can freely reassign codes 16+ for their own types. If you use json.h codes, the types header must declare them.

Use `dtob_raw()` instead of `json_string()` when you don't need the JSON convention layer — raw values don't require a types header.

## Key API patterns

```c
// decode
DtobValue *root = dtob_decode(buf, len);                       // auto-parses types header
DtobValue *root = dtob_decode_with_types(buf, len, &types);    // also returns types

// encode
uint8_t *out = dtob_encode(root, &out_len);                    // no types header
uint8_t *out = dtob_encode_with_types(root, &types, &out_len); // with types header

// AST construction
DtobValue *v = dtob_raw(data, len);
DtobValue *v = dtob_int(42);
DtobValue *v = dtob_uint(100);
DtobValue *v = dtob_array();
DtobValue *kvs = dtob_kvset();
dtob_array_push(arr, val);
dtob_kvset_put(kvs, "key", val);

// KV-set accessors
DtobValue *v = dtob_kvset_get(kvs, "key");
uint64_t n   = dtob_kvset_uint(kvs, "key");
size_t len   = dtob_kvset_str(kvs, "key", buf, sizeof(buf));

// DtobWriter — for manual stream construction (used by prlents for types header)
DtobWriter w;
dtob_writer_init(&w);
dtob_writer_ctrl(&w, DTOB_CODE_OPEN);
dtob_writer_data(&w, bytes, len);
dtob_writer_value(&w, val);           // without type resolution
dtob_writer_value_typed(&w, val, &types);  // with type resolution

// memory
dtob_free(root);
```

## Build

```
make              # builds libdtob.a + CLI tools
make test         # runs test suite
```

No external dependencies for `libdtob.a` (just libc + libm). The `dtob-encode` CLI needs libcurl for URL fetching.

## Downstream users

- **prlents** — links libdtob.a, uses custom types (ENTS_NAME, ENTS_FILE, etc.) with DtobWriter for manual stream construction including types header
- **glass** — links libdtob.a transitively through libprlents.a
- **alock** — links libdtob.a for lock state serialization

## Common pitfalls

- `dtob_encode()` does NOT write a types header. If your AST contains custom-typed values, the output will fail to decode. Use `dtob_encode_with_types()` or `DtobWriter` to manually write the types header.
- `json_string()` creates values with custom code 16. If you use `dtob_encode()` without a types header, decoders will reject code 16. Use `dtob_raw()` for plain strings that don't need the JSON convention.
- `dtob_kvset_put()` replaces existing keys — safe to call multiple times with the same key.
- all `dtob_*` constructors allocate — caller must `dtob_free()` the root when done.