Packages

macula

3.10.1
7.1.0 7.0.0 6.0.0 5.2.2 5.2.1 5.2.0 5.1.0 5.0.0 4.8.0 4.7.1 4.7.0 4.6.0 4.5.0 4.4.10 4.4.9 4.4.8 4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.1 4.3.0 4.2.9 4.2.8 4.2.7 4.2.6 4.2.5 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.1 4.1.0 4.0.0 3.16.0 3.15.3 3.15.2 3.15.1 3.14.0 3.13.0 3.12.1 3.12.0 3.11.1 3.11.0 3.10.3 3.10.2 3.10.1 3.9.0 3.8.0 3.7.0 3.5.0 3.4.0 3.3.0 3.2.0 3.1.0 3.0.0 2.1.1 2.1.0 2.0.0 1.5.2 1.5.1 1.4.30 1.4.29 1.4.28 1.4.27 1.4.26 1.4.25 1.4.24 1.4.23 1.4.22 1.4.21 1.4.20 1.4.19 1.4.18 1.4.17 1.4.16 1.4.15 1.4.14 1.4.13 1.4.11 1.4.10 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.1 1.3.0 1.2.0 1.1.0 1.0.10 1.0.9 1.0.8 1.0.7 1.0.6 1.0.5 1.0.4 1.0.3 1.0.2 1.0.1 1.0.0 0.48.6 0.48.5 0.48.4 0.48.3 0.48.2 0.48.1 0.48.0 0.47.1 0.47.0 0.46.3 0.46.1 0.46.0 0.45.3 0.45.2 0.45.1 0.45.0 0.44.2 0.44.1 0.44.0 0.43.3 0.43.2 0.43.1 0.43.0 0.42.9 0.42.8 0.42.7 0.42.6 0.42.5 0.42.4 0.42.3 0.42.2 0.42.1 0.42.0 0.41.1 0.41.0 0.40.1 0.40.0 0.39.9 0.39.8 0.39.7 0.39.6 0.39.5 0.39.4 0.39.3 0.39.2 0.39.1 0.39.0 0.38.8 0.38.7 0.38.6 0.38.5 0.38.4 0.38.3 0.38.2 0.38.1 0.38.0 0.37.7 0.37.6 0.37.5 0.37.4 0.37.3 0.37.2 0.37.1 0.37.0 0.36.6 0.36.5 0.36.4 0.36.3 0.36.2 0.36.1 0.36.0 0.35.4 0.35.3 0.35.2 0.35.1 0.35.0 0.34.1 0.34.0 0.33.1 0.33.0 0.32.5 0.32.4 0.32.3 0.32.2 0.32.1 0.32.0 0.31.9 0.31.8 0.31.7 0.31.6 0.31.5 0.31.4 0.31.3 0.31.2 0.31.1 0.31.0 0.30.10 0.30.9 0.30.8 0.30.7 0.30.6 0.30.5 0.30.4 0.30.3 0.30.2 0.30.1 0.30.0 0.29.0 0.28.3 0.28.2 0.28.1 0.28.0 0.27.1 0.27.0 0.26.1 0.26.0 0.25.6 0.25.5 0.25.4 0.25.3 0.25.2 0.25.1 0.25.0 0.24.6 0.24.5 0.24.4 0.24.3 0.24.2 0.24.1 0.24.0 0.23.3 0.23.2 0.23.1 0.23.0 0.22.12 0.22.11 0.22.10 0.22.9 0.22.8 0.22.7 0.22.6 0.22.5 0.22.4 0.22.3 0.22.2 0.22.1 0.22.0 0.21.7 0.21.6 0.21.5 0.21.4 0.21.2 0.21.1 0.21.0 0.20.25 0.20.24 0.20.23 0.20.22 0.20.21 0.20.20 0.20.19 0.20.18 0.20.17 0.20.16 0.20.15 0.20.14 0.20.13 0.20.12 0.20.11 0.20.10 0.20.9 0.20.8 0.20.7 0.20.6 0.20.5 0.20.3 0.20.2 0.20.1 0.20.0 0.19.2 0.19.1 0.19.0 0.18.1 0.18.0 0.17.4 0.17.3 0.17.2 0.17.1 0.17.0 0.16.6 0.16.5 0.16.4 0.16.3 0.16.2 0.16.1 0.16.0 0.15.1 0.15.0 0.14.3 0.14.2 0.14.1 0.14.0 0.12.6 0.12.5 0.12.3 0.11.3 0.10.2 0.10.1 0.10.0 0.9.2 0.9.1 0.9.0 0.8.25 0.8.24 0.8.23 0.8.22 0.8.21 0.8.20 0.8.19 0.8.18 0.8.17 0.8.16 0.8.15 0.8.14 0.8.13 0.8.12 0.8.11 0.8.10 0.8.9 0.8.8 0.8.7 0.8.6 0.8.5 0.8.4 0.8.3 0.8.2 0.8.1 0.8.0 0.7.30 0.7.29 0.7.28 0.7.27 0.7.26 0.7.25 0.7.24 0.7.23 0.7.22 0.7.21 0.7.20 0.7.19 0.7.18 0.7.17 0.7.16 0.7.15 0.7.14 0.7.13 0.7.12 0.7.11 0.7.10 0.7.9 0.7.8 0.7.7 0.7.6 0.7.5 0.7.4 0.7.3 0.7.2 0.7.1 0.7.0 0.6.7 0.6.6 0.6.5 0.6.4 0.6.3 0.6.2 0.6.1 0.6.0 0.5.0 0.4.4 0.4.3 0.4.2 0.4.1 0.4.0 0.3.4 0.3.3 0.3.2 0.3.1

Macula HTTP/3 Mesh SDK — connect, subscribe, publish, call, advertise

Current section

Files

Jump to
macula src macula_record_cbor.erl
Raw

src/macula_record_cbor.erl

%% @doc Deterministic CBOR encoder/decoder.
%%
%% Implements the subset of RFC 8949 needed by Macula records:
%% unsigned ints, byte strings, text strings, arrays, maps, and `null'.
%%
%% Encoding follows RFC 8949 §4.2.1 (deterministic):
%% <ul>
%% <li>Smallest length encoding.</li>
%% <li>Definite lengths only (no indefinite items).</li>
%% <li>Map keys sorted by bytewise lexicographic order of their
%% deterministic encoding.</li>
%% </ul>
%%
%% Internal value representation:
%% <ul>
%% <li>`non_neg_integer()' — uint (major 0)</li>
%% <li>`binary()' — byte string (major 2)</li>
%% <li>`{text, binary()}' — UTF-8 text string (major 3)</li>
%% <li>`[value()]' — array (major 4)</li>
%% <li>`#{value() => value()}' — map (major 5)</li>
%% <li>`null' — simple null (major 7, value 22)</li>
%% </ul>
-module(macula_record_cbor).
-export([encode/1, decode/1]).
-export_type([value/0]).
-type value() ::
non_neg_integer()
| binary()
| {text, binary()}
| [value()]
| #{value() => value()}
| null.
-define(MAX_UINT64, 16#FFFFFFFFFFFFFFFF).
%%------------------------------------------------------------------
%% Encode
%%------------------------------------------------------------------
-spec encode(value()) -> binary().
encode(N) when is_integer(N), N >= 0, N =< ?MAX_UINT64 ->
head(0, N);
encode({text, B}) when is_binary(B) ->
<<(head(3, byte_size(B)))/binary, B/binary>>;
encode(B) when is_binary(B) ->
<<(head(2, byte_size(B)))/binary, B/binary>>;
encode(L) when is_list(L) ->
encode_array(L);
encode(M) when is_map(M) ->
encode_map(M);
encode(null) ->
<<16#F6>>.
encode_array(L) ->
Body = << <<(encode(E))/binary>> || E <- L >>,
<<(head(4, length(L)))/binary, Body/binary>>.
encode_map(M) ->
%% Encode each k/v independently, then sort by encoded key bytes
%% (Erlang binary comparison is bytewise — exactly what the spec wants).
Pairs = [ {encode(K), encode(V)} || {K, V} <- maps:to_list(M) ],
Sorted = lists:sort(Pairs),
Body = << <<K/binary, V/binary>> || {K, V} <- Sorted >>,
<<(head(5, maps:size(M)))/binary, Body/binary>>.
%% Type byte + length prefix using the smallest encoding.
head(MT, N) when N =< 23 ->
<<MT:3, N:5>>;
head(MT, N) when N =< 16#FF ->
<<MT:3, 24:5, N:8>>;
head(MT, N) when N =< 16#FFFF ->
<<MT:3, 25:5, N:16>>;
head(MT, N) when N =< 16#FFFFFFFF ->
<<MT:3, 26:5, N:32>>;
head(MT, N) when N =< ?MAX_UINT64 ->
<<MT:3, 27:5, N:64>>.
%%------------------------------------------------------------------
%% Decode
%%------------------------------------------------------------------
-spec decode(binary()) -> value().
decode(Bin) when is_binary(Bin) ->
{V, <<>>} = decode_one(Bin),
V.
%% Major 7, value 22 = null. Anything else with major 7 is unsupported.
decode_one(<<7:3, 22:5, R/binary>>) ->
{null, R};
decode_one(<<MT:3, AI:5, Rest/binary>>) ->
{N, R} = decode_count(AI, Rest),
decode_value(MT, N, R).
decode_count(AI, R) when AI =< 23 -> {AI, R};
decode_count(24, <<N, R/binary>>) -> {N, R};
decode_count(25, <<N:16, R/binary>>) -> {N, R};
decode_count(26, <<N:32, R/binary>>) -> {N, R};
decode_count(27, <<N:64, R/binary>>) -> {N, R}.
decode_value(0, N, R) ->
{N, R};
decode_value(2, Len, R) ->
<<B:Len/binary, Rest/binary>> = R,
{B, Rest};
decode_value(3, Len, R) ->
<<B:Len/binary, Rest/binary>> = R,
{{text, B}, Rest};
decode_value(4, Len, R) ->
decode_array(Len, R, []);
decode_value(5, Len, R) ->
decode_map(Len, R, #{}).
decode_array(0, R, Acc) ->
{lists:reverse(Acc), R};
decode_array(N, R, Acc) ->
{V, R1} = decode_one(R),
decode_array(N - 1, R1, [V | Acc]).
decode_map(0, R, Acc) ->
{Acc, R};
decode_map(N, R, Acc) ->
{K, R1} = decode_one(R),
{V, R2} = decode_one(R1),
decode_map(N - 1, R2, Acc#{K => V}).