barrel_docdb_codec_cbor (barrel_docdb v1.1.1)

View Source

CBOR Document Codec with Structural Index

Implements a Cosmos-DB-like document storage codec using canonical CBOR (RFC 8949) with a structural index for BSON-like navigation without full document decoding.

Record Format: [ HEADER | INDEX | PAYLOAD ]

Features: - Canonical CBOR encoding (deterministic, sorted map keys) - Structural index for O(1) peek and path navigation - SHA-256 hash of payload for revision IDs - Iterator API for streaming access - JSON import/export via OTP stdlib json module

Summary

Functions

Decode a CBOR record to Erlang term.

Decode any CBOR binary (indexed or plain) to Erlang term. Auto-detects format: indexed CBOR (starts with "CB") or plain CBOR.

Decode CBOR binary to Erlang term.

Decode a value using a ValueRef.

Decode a varint from binary, returning {Value, Rest}.

Encode an Erlang term to a CBOR record with structural index.

Encode with options.

Encode an Erlang term to canonical CBOR.

Encode an unsigned integer as a varint (LEB128).

Find a value by path.

Parse JSON and encode to CBOR record using callbacks. This builds CBOR directly during JSON parsing without intermediate terms.

Get a value at path without full decode, returns undefined if not found. Works like maps:get/2 but for CBOR records.

Get a value at path without full decode, returns Default if not found. Works like maps:get/3 but for CBOR records.

Get and decode value at path in one call. Returns {ok, Value} or not_found.

Get the SHA-256 hash of the payload.

Get the raw index binary.

Check if a key exists at top level without full decode. Uses the index for O(1) lookup.

Get all top-level keys without full decode. Uses the index for O(1) access to key list.

Merge metadata map into raw CBOR document body. This is used for zero-copy CBOR responses where we have raw CBOR body and need to add metadata (id, rev, etc.) without full decode/re-encode.

Create a new iterator over a CBOR record.

Get the next entry from the iterator.

Get the raw payload binary.

Peek at a top-level key without iteration.

Set a value at path, returns new CBOR record. Note: This requires decode → modify → re-encode, so it's not optimized. Use for occasional updates, not bulk operations.

Get number of top-level entries without full decode. Uses the index for O(1) access.

Convert CBOR record to JSON binary.

Convert CBOR record to JSON iolist using iterator (no full decode). This traverses the CBOR structure via the index and emits JSON directly.

Convert CBOR record to Erlang map (full decode). Alias for decode/1.

Types

cbor_type/0

-type cbor_type() ::
          uint | nint | bytes | text | array | map | tag | simple | float_type | true | false | null.

container/0

-type container() ::
          #container{id :: non_neg_integer(),
                     kind :: map | array,
                     start_off :: non_neg_integer(),
                     end_off :: non_neg_integer(),
                     parent_id :: non_neg_integer(),
                     first_entry :: non_neg_integer(),
                     entry_count :: non_neg_integer()}.

enc_state/0

-type enc_state() ::
          #enc_state{offset :: non_neg_integer(),
                     next_container_id :: pos_integer(),
                     container_stack :: [non_neg_integer()],
                     containers :: [container()],
                     entries :: [entry()],
                     top_keys :: [{binary(), non_neg_integer()}]}.

entry/0

-type entry() ::
          #entry{key :: binary() | non_neg_integer(),
                 value_off :: non_neg_integer(),
                 value_len :: non_neg_integer(),
                 value_type :: cbor_type(),
                 container_id :: non_neg_integer() | undefined,
                 owner_id :: non_neg_integer()}.

iter/0

-type iter() ::
          #iter{record :: binary(),
                index :: parsed_index(),
                payload_start :: non_neg_integer(),
                container_id :: non_neg_integer(),
                remaining_entries :: [entry()]}.

parsed_index/0

-type parsed_index() ::
          #parsed_index{containers :: #{non_neg_integer() => container()},
                        entries :: tuple(),
                        top_keys :: #{binary() => non_neg_integer()}}.

path/0

-type path() :: [binary() | non_neg_integer()].

record_bin/0

-type record_bin() :: binary().

vref/0

-type vref() ::
          #vref{offset :: non_neg_integer(),
                length :: non_neg_integer(),
                type :: cbor_type(),
                container_id :: non_neg_integer() | undefined}.

Functions

decode(RecordBin)

-spec decode(record_bin()) -> term().

Decode a CBOR record to Erlang term.

decode_any(RecordBin)

-spec decode_any(binary()) -> term().

Decode any CBOR binary (indexed or plain) to Erlang term. Auto-detects format: indexed CBOR (starts with "CB") or plain CBOR.

decode_cbor(Bin)

-spec decode_cbor(binary()) -> term().

Decode CBOR binary to Erlang term.

decode_value(RecordBin, Vref)

-spec decode_value(record_bin(), vref()) -> {ok, term()} | {error, term()}.

Decode a value using a ValueRef.

decode_varint(Bin)

-spec decode_varint(binary()) -> {non_neg_integer(), binary()}.

Decode a varint from binary, returning {Value, Rest}.

encode(Term)

-spec encode(term()) -> record_bin().

Encode an Erlang term to a CBOR record with structural index.

encode(Term, Opts)

-spec encode(term(), map()) -> record_bin().

Encode with options.

encode_bytes(Bin)

encode_cbor(N)

-spec encode_cbor(term()) -> binary().

Encode an Erlang term to canonical CBOR.

encode_varint(N)

-spec encode_varint(non_neg_integer()) -> binary().

Encode an unsigned integer as a varint (LEB128).

find_path(RecordBin, Path)

-spec find_path(record_bin(), path()) -> {ok, {cbor_type(), vref()}} | not_found | {error, term()}.

Find a value by path.

from_json(JsonBin)

-spec from_json(binary()) -> record_bin().

Parse JSON and encode to CBOR record using callbacks. This builds CBOR directly during JSON parsing without intermediate terms.

get(RecordBin, Path)

-spec get(record_bin(), path()) -> term() | undefined.

Get a value at path without full decode, returns undefined if not found. Works like maps:get/2 but for CBOR records.

get(RecordBin, Path, Default)

-spec get(record_bin(), path(), term()) -> term().

Get a value at path without full decode, returns Default if not found. Works like maps:get/3 but for CBOR records.

get_value(RecordBin, Path)

-spec get_value(record_bin(), path()) -> {ok, term()} | not_found | {error, term()}.

Get and decode value at path in one call. Returns {ok, Value} or not_found.

hash(RecordBin)

-spec hash(record_bin()) -> binary().

Get the SHA-256 hash of the payload.

index_bin(RecordBin)

-spec index_bin(record_bin()) -> binary().

Get the raw index binary.

is_key(RecordBin, Key)

-spec is_key(record_bin(), binary()) -> boolean().

Check if a key exists at top level without full decode. Uses the index for O(1) lookup.

keys(RecordBin)

-spec keys(record_bin()) -> [binary()].

Get all top-level keys without full decode. Uses the index for O(1) access to key list.

merge_into_cbor(CborBin, MetaMap)

-spec merge_into_cbor(binary(), map()) -> binary().

Merge metadata map into raw CBOR document body. This is used for zero-copy CBOR responses where we have raw CBOR body and need to add metadata (id, rev, etc.) without full decode/re-encode.

For plain CBOR (non-indexed), we decode, merge, and re-encode. This is still efficient as it avoids the HTTP-layer overhead.

new_iterator(RecordBin)

-spec new_iterator(record_bin()) -> {ok, iter()} | {error, term()}.

Create a new iterator over a CBOR record.

next(Iter)

-spec next(iter()) -> {ok, {binary() | non_neg_integer(), cbor_type(), vref()}, iter()} | done.

Get the next entry from the iterator.

payload(RecordBin)

-spec payload(record_bin()) -> binary().

Get the raw payload binary.

peek(RecordBin, Key)

-spec peek(record_bin(), binary()) -> {ok, {cbor_type(), vref()}} | not_found | {error, term()}.

Peek at a top-level key without iteration.

set(RecordBin, Path, Value)

-spec set(record_bin(), path(), term()) -> record_bin().

Set a value at path, returns new CBOR record. Note: This requires decode → modify → re-encode, so it's not optimized. Use for occasional updates, not bulk operations.

size(RecordBin)

-spec size(record_bin()) -> non_neg_integer().

Get number of top-level entries without full decode. Uses the index for O(1) access.

to_json(RecordBin)

-spec to_json(record_bin()) -> binary().

Convert CBOR record to JSON binary.

to_json_iolist(RecordBin)

-spec to_json_iolist(record_bin()) -> iolist().

Convert CBOR record to JSON iolist using iterator (no full decode). This traverses the CBOR structure via the index and emits JSON directly.

to_map(RecordBin)

-spec to_map(record_bin()) -> map().

Convert CBOR record to Erlang map (full decode). Alias for decode/1.