Current section
Files
Jump to
Current section
Files
src/gbor@decode.erl
-module(gbor@decode).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-define(FILEPATH, "src/gbor/decode.gleam").
-export([tagged_decoder/3, cbor_to_dynamic/1, from_bit_array/1]).
-export_type([cbor_decode_error/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(" Tools for decoding binary CBOR data into Gleam types\n").
-type cbor_decode_error() :: {dynamic_decode_error,
list(gleam@dynamic@decode:decode_error())} |
{major_type_error, integer()} |
reserved_error |
{unimplemented_error, binary()} |
unassigned_error.
-file("src/gbor/decode.gleam", 60).
?DOC(
" Decode a tagged CBOR value.\n"
" \n"
" Provided tag is the expected tag number for the value, and the decoder is run\n"
" on the value corresponding to the tag.\n"
" \n"
" For example, for a CBOR value with a tag number of `0`, the expected data item\n"
" is a text string representing a standard time string, so one would call:\n"
" \n"
" ```gleam\n"
" import gleam/dynamic/decode as gdd\n"
" tagged_decoder(0, gdd.string, \"\")\n"
" ```\n"
" \n"
" Reference: [RFC 8949 : 3.4 Tagging of Items](https://www.rfc-editor.org/rfc/rfc8949.html#name-tagging-of-items)\n"
).
-spec tagged_decoder(integer(), gleam@dynamic@decode:decoder(EKY), EKY) -> gleam@dynamic@decode:decoder(EKY).
tagged_decoder(Expected_tag, Decoder, Zero) ->
gleam@dynamic@decode:field(
0,
gleam@erlang@atom:decoder(),
fun(Cbor_tag) ->
gleam@bool:guard(
Cbor_tag /= erlang:binary_to_atom(<<"cbor_tagged__"/utf8>>),
gleam@dynamic@decode:failure(Zero, <<"CBOR tagged value"/utf8>>),
fun() ->
gleam@dynamic@decode:field(
1,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Tag) ->
gleam@bool:guard(
Tag /= Expected_tag,
gleam@dynamic@decode:failure(
Zero,
erlang:integer_to_binary(Expected_tag)
),
fun() ->
gleam@dynamic@decode:at([2], Decoder)
end
)
end
)
end
)
end
).
-file("src/gbor/decode.gleam", 127).
-spec decode_uint(integer(), bitstring()) -> {ok, {gbor:c_b_o_r(), bitstring()}} |
{error, cbor_decode_error()}.
decode_uint(Min, Data) ->
case {Min, Data} of
{24, <<Val:8/integer-unsigned, Rest/bitstring>>} ->
{ok, {{c_b_int, Val}, Rest}};
{25, <<Val@1:16/integer-unsigned, Rest@1/bitstring>>} ->
{ok, {{c_b_int, Val@1}, Rest@1}};
{26, <<Val@2:32/integer-unsigned, Rest@2/bitstring>>} ->
{ok, {{c_b_int, Val@2}, Rest@2}};
{27, <<Val@3:64/integer-unsigned, Rest@3/bitstring>>} ->
{ok, {{c_b_int, Val@3}, Rest@3}};
{Min@1, <<Rest@4/bitstring>>} when Min@1 =< 23 ->
{ok, {{c_b_int, Min@1}, Rest@4}};
{Min@2, <<_/bitstring>>} when (30 >= Min@2) andalso (Min@2 >= 28) ->
{error, reserved_error};
{Min@3, V} ->
Err = <<"Did not find a valid uint, min: "/utf8,
(gleam@string:inspect(Min@3))/binary>>,
_pipe = gleam@dynamic@decode:decode_error(
Err,
gleam_stdlib:identity(V)
),
_pipe@1 = {dynamic_decode_error, _pipe},
{error, _pipe@1}
end.
-file("src/gbor/decode.gleam", 147).
-spec decode_int(integer(), bitstring()) -> {ok, {gbor:c_b_o_r(), bitstring()}} |
{error, cbor_decode_error()}.
decode_int(Min, Data) ->
gleam@result:'try'(case {Min, Data} of
{24, <<Val:8/integer, Rest/bitstring>>} ->
{ok, {Val, Rest}};
{25, <<Val@1:16/integer, Rest@1/bitstring>>} ->
{ok, {Val@1, Rest@1}};
{26, <<Val@2:32/integer, Rest@2/bitstring>>} ->
{ok, {Val@2, Rest@2}};
{27, <<Val@3:64/integer, Rest@3/bitstring>>} ->
{ok, {Val@3, Rest@3}};
{Val@4, <<Rest@4/bitstring>>} when Val@4 < 24 ->
{ok, {Val@4, Rest@4}};
{Val@5, <<_/bitstring>>} when (30 >= Val@5) andalso (Val@5 >= 28) ->
{error, reserved_error};
{Val@6, V} ->
Err = <<"Did not find a valid int, min: "/utf8,
(gleam@string:inspect(Val@6))/binary>>,
_pipe = gleam@dynamic@decode:decode_error(
Err,
gleam_stdlib:identity(V)
),
_pipe@1 = {dynamic_decode_error, _pipe},
{error, _pipe@1}
end, fun(_use0) ->
{V@1, Rest@5} = _use0,
{ok, {{c_b_int, -1 - V@1}, Rest@5}}
end).
-file("src/gbor/decode.gleam", 197).
-spec decode_float(bitstring(), bitstring()) -> {ok,
{gbor:c_b_o_r(), bitstring()}} |
{error, cbor_decode_error()}.
decode_float(Data, Rest) ->
gleam@result:'try'(case erlang:bit_size(Data) of
16 ->
{ok, ieee_float:from_bytes_16_be(Data)};
32 ->
{ok, ieee_float:from_bytes_32_be(Data)};
64 ->
{ok, ieee_float:from_bytes_64_be(Data)};
N ->
_pipe = gleam@dynamic@decode:decode_error(
<<"Did not find a valid float size"/utf8>>,
gleam_stdlib:identity(N)
),
_pipe@1 = {dynamic_decode_error, _pipe},
{error, _pipe@1}
end, fun(V) -> case ieee_float:to_finite(V) of
{ok, V@1} ->
{ok, {{c_b_float, V@1}, Rest}};
{error, _} ->
_pipe@2 = gleam@dynamic@decode:decode_error(
<<"Valid float, but got NaN/Inf"/utf8>>,
gleam@dynamic:nil()
),
_pipe@3 = {dynamic_decode_error, _pipe@2},
{error, _pipe@3}
end end).
-file("src/gbor/decode.gleam", 169).
-spec decode_float_or_simple_value(integer(), bitstring()) -> {ok,
{gbor:c_b_o_r(), bitstring()}} |
{error, cbor_decode_error()}.
decode_float_or_simple_value(Min, Data) ->
case {Min, Data} of
{20, <<Rest/bitstring>>} ->
{ok, {{c_b_bool, false}, Rest}};
{21, <<Rest@1/bitstring>>} ->
{ok, {{c_b_bool, true}, Rest@1}};
{22, <<Rest@2/bitstring>>} ->
{ok, {c_b_null, Rest@2}};
{23, <<Rest@3/bitstring>>} ->
{ok, {c_b_undefined, Rest@3}};
{25, <<V:2/binary, Rest@4/bitstring>>} ->
decode_float(V, Rest@4);
{26, <<V@1:4/binary, Rest@5/bitstring>>} ->
decode_float(V@1, Rest@5);
{27, <<V@2:8/binary, Rest@6/bitstring>>} ->
decode_float(V@2, Rest@6);
{N, <<_:Min/binary, _/bitstring>>} when N =< 19 ->
{error, unassigned_error};
{N@1, <<_/bitstring>>} when (N@1 >= 24) andalso (N@1 =< 31) ->
{error, reserved_error};
{N@2, <<_/bitstring>>} when N@2 >= 32 ->
{error, unassigned_error};
{N@3, V@3} ->
Err = <<"Did not find a valid float or simple value, min: "/utf8,
(gleam@string:inspect(N@3))/binary>>,
_pipe = gleam@dynamic@decode:decode_error(
Err,
gleam_stdlib:identity(V@3)
),
_pipe@1 = {dynamic_decode_error, _pipe},
{error, _pipe@1}
end.
-file("src/gbor/decode.gleam", 256).
-spec decode_bytes_helper(integer(), bitstring()) -> {ok,
{bitstring(), bitstring()}} |
{error, cbor_decode_error()}.
decode_bytes_helper(Min, Data) ->
case {Min, Data} of
{24, <<N:8/integer-unsigned, V:N/binary, Rest/bitstring>>} ->
{ok, {V, Rest}};
{25, <<N@1:16/integer-unsigned, V@1:N@1/binary, Rest@1/bitstring>>} ->
{ok, {V@1, Rest@1}};
{26, <<N@2:32/integer-unsigned, V@2:N@2/binary, Rest@2/bitstring>>} ->
{ok, {V@2, Rest@2}};
{27, <<N@3:64/integer-unsigned, V@3:N@3/binary, Rest@3/bitstring>>} ->
{ok, {V@3, Rest@3}};
{N@4, <<V@4:Min/binary, Rest@4/bitstring>>} when N@4 < 24 ->
{ok, {V@4, Rest@4}};
{31, <<_/bitstring>>} ->
{error,
{unimplemented_error,
<<"Indeterminate sizes not supported yet."/utf8>>}};
{N@5, V@5} ->
Err = <<"For byte/strings, handling minor: "/utf8,
(gleam@string:inspect(N@5))/binary>>,
_pipe = gleam@dynamic@decode:decode_error(
Err,
gleam_stdlib:identity(V@5)
),
_pipe@1 = {dynamic_decode_error, _pipe},
{error, _pipe@1}
end.
-file("src/gbor/decode.gleam", 228).
-spec decode_bytes(integer(), bitstring()) -> {ok,
{gbor:c_b_o_r(), bitstring()}} |
{error, cbor_decode_error()}.
decode_bytes(Min, Data) ->
gleam@result:'try'(
decode_bytes_helper(Min, Data),
fun(_use0) ->
{V, Rest} = _use0,
{ok, {{c_b_binary, V}, Rest}}
end
).
-file("src/gbor/decode.gleam", 236).
-spec decode_string(integer(), bitstring()) -> {ok,
{gbor:c_b_o_r(), bitstring()}} |
{error, cbor_decode_error()}.
decode_string(Min, Data) ->
gleam@result:'try'(
decode_bytes_helper(Min, Data),
fun(_use0) ->
{V, Rest} = _use0,
V@1 = begin
_pipe = gleam@bit_array:to_string(V),
_pipe@1 = gleam@result:map(_pipe, fun(S) -> {c_b_string, S} end),
gleam@result:map_error(
_pipe@1,
fun(_) ->
{dynamic_decode_error,
gleam@dynamic@decode:decode_error(
<<"Expected a string"/utf8>>,
gleam_stdlib:identity(V)
)}
end
)
end,
gleam@result:'try'(V@1, fun(V@2) -> {ok, {V@2, Rest}} end)
end
).
-file("src/gbor/decode.gleam", 27).
?DOC(" Convert a CBOR Gleam value to a dynamic value for use with `gleam/dynamic/decode`\n").
-spec cbor_to_dynamic(gbor:c_b_o_r()) -> gleam@dynamic:dynamic_().
cbor_to_dynamic(Cbor) ->
case Cbor of
{c_b_int, V} ->
gleam_stdlib:identity(V);
{c_b_string, V@1} ->
gleam_stdlib:identity(V@1);
{c_b_float, V@2} ->
gleam_stdlib:identity(V@2);
{c_b_map, V@3} ->
gleam@dynamic:properties(
gleam@list:map(
V@3,
fun(V@4) ->
{cbor_to_dynamic(erlang:element(1, V@4)),
cbor_to_dynamic(erlang:element(2, V@4))}
end
)
);
{c_b_array, V@5} ->
erlang:list_to_tuple(gleam@list:map(V@5, fun cbor_to_dynamic/1));
{c_b_bool, V@6} ->
gleam_stdlib:identity(V@6);
c_b_null ->
gleam@dynamic:nil();
c_b_undefined ->
gleam@dynamic:nil();
{c_b_binary, V@7} ->
gleam_stdlib:identity(V@7);
{c_b_tagged, Tag, Value} ->
erl_gbor:to_tagged(Tag, cbor_to_dynamic(Value))
end.
-file("src/gbor/decode.gleam", 310).
-spec decode_array_helper(bitstring(), integer(), list(gbor:c_b_o_r())) -> {ok,
{list(gbor:c_b_o_r()), bitstring()}} |
{error, cbor_decode_error()}.
decode_array_helper(Data, N, Acc) ->
case N of
0 ->
{ok, {Acc, Data}};
_ ->
gleam@result:'try'(
decode_helper(Data),
fun(_use0) ->
{V, Rest_data} = _use0,
decode_array_helper(Rest_data, N - 1, [V | Acc])
end
)
end.
-file("src/gbor/decode.gleam", 100).
-spec decode_helper(bitstring()) -> {ok, {gbor:c_b_o_r(), bitstring()}} |
{error, cbor_decode_error()}.
decode_helper(Data) ->
case Data of
<<0:3, Min:5, Rest/bitstring>> ->
decode_uint(Min, Rest);
<<1:3, Min@1:5, Rest@1/bitstring>> ->
decode_int(Min@1, Rest@1);
<<2:3, Min@2:5, Rest@2/bitstring>> ->
decode_bytes(Min@2, Rest@2);
<<3:3, Min@3:5, Rest@3/bitstring>> ->
decode_string(Min@3, Rest@3);
<<4:3, Min@4:5, Rest@4/bitstring>> ->
decode_array(Min@4, Rest@4);
<<5:3, Min@5:5, Rest@5/bitstring>> ->
decode_map(Min@5, Rest@5);
<<6:3, Min@6:5, Rest@6/bitstring>> ->
decode_tagged(Min@6, Rest@6);
<<7:3, Min@7:5, Rest@7/bitstring>> ->
decode_float_or_simple_value(Min@7, Rest@7);
<<N:3, _/bitstring>> ->
{error, {major_type_error, N}};
<<>> ->
{error,
{dynamic_decode_error,
gleam@dynamic@decode:decode_error(
<<"Expected data, but got none"/utf8>>,
gleam@dynamic:nil()
)}};
V ->
{error,
{unimplemented_error,
<<"Didn't know how to handle parsing from data: "/utf8,
(gleam@string:inspect(V))/binary>>}}
end.
-file("src/gbor/decode.gleam", 280).
-spec decode_array(integer(), bitstring()) -> {ok,
{gbor:c_b_o_r(), bitstring()}} |
{error, cbor_decode_error()}.
decode_array(Min, Data) ->
gleam@result:'try'(case {Min, Data} of
{24, <<N:8/integer-unsigned, Rest/bitstring>>} ->
{ok, {N, Rest}};
{25, <<N@1:16/integer-unsigned, Rest@1/bitstring>>} ->
{ok, {N@1, Rest@1}};
{26, <<N@2:32/integer-unsigned, Rest@2/bitstring>>} ->
{ok, {N@2, Rest@2}};
{27, <<N@3:64/integer-unsigned, Rest@3/bitstring>>} ->
{ok, {N@3, Rest@3}};
{N@4, <<Rest@4/bitstring>>} when N@4 < 24 ->
{ok, {N@4, Rest@4}};
{31, <<_/bitstring>>} ->
{error,
{unimplemented_error,
<<"Indeterminate lengths not supported yet."/utf8>>}};
{N@5, V} ->
Err = <<<<<<"Didn't know how to handle parsing an array from data: "/utf8,
(gleam@string:inspect(V))/binary>>/binary,
" with min: "/utf8>>/binary,
(gleam@string:inspect(N@5))/binary>>,
_pipe = gleam@dynamic@decode:decode_error(
Err,
gleam_stdlib:identity(V)
),
_pipe@1 = {dynamic_decode_error, _pipe},
{error, _pipe@1}
end, fun(_use0) ->
{N@6, Rest@5} = _use0,
gleam@result:'try'(
decode_array_helper(Rest@5, N@6, []),
fun(_use0@1) ->
{Values, Rest@6} = _use0@1,
{ok, {{c_b_array, lists:reverse(Values)}, Rest@6}}
end
)
end).
-file("src/gbor/decode.gleam", 86).
?DOC(
" Decode a CBOR value from a bit array\n"
" \n"
" This function is the main entry point for decoding CBOR data into Gleam types.\n"
" \n"
" It takes a bit array and returns a result containing the decoded CBOR value\n"
).
-spec from_bit_array(bitstring()) -> {ok, gbor:c_b_o_r()} |
{error, cbor_decode_error()}.
from_bit_array(Data) ->
case decode_helper(Data) of
{ok, {V, <<>>}} ->
{ok, V};
{ok, {_, Rest}} ->
{error,
{dynamic_decode_error,
gleam@dynamic@decode:decode_error(
<<"Expected data, but got more"/utf8>>,
gleam_stdlib:identity(Rest)
)}};
{error, E} ->
{error, E}
end.
-file("src/gbor/decode.gleam", 324).
-spec decode_map(integer(), bitstring()) -> {ok, {gbor:c_b_o_r(), bitstring()}} |
{error, cbor_decode_error()}.
decode_map(Min, Data) ->
gleam@result:'try'(case {Min, Data} of
{24, <<N:8/integer-unsigned, Rest/bitstring>>} ->
{ok, {N, Rest}};
{25, <<N@1:16/integer-unsigned, Rest@1/bitstring>>} ->
{ok, {N@1, Rest@1}};
{26, <<N@2:32/integer-unsigned, Rest@2/bitstring>>} ->
{ok, {N@2, Rest@2}};
{27, <<N@3:64/integer-unsigned, Rest@3/bitstring>>} ->
{ok, {N@3, Rest@3}};
{N@4, <<Rest@4/bitstring>>} when N@4 < 24 ->
{ok, {N@4, Rest@4}};
{31, <<_/bitstring>>} ->
{error,
{unimplemented_error,
<<"Indeterminate lengths not supported yet."/utf8>>}};
{N@5, V} ->
Err = <<<<<<"Didn't know how to handle parsing a map from data: "/utf8,
(gleam@string:inspect(V))/binary>>/binary,
" with min: "/utf8>>/binary,
(gleam@string:inspect(N@5))/binary>>,
_pipe = gleam@dynamic@decode:decode_error(
Err,
gleam_stdlib:identity(V)
),
_pipe@1 = {dynamic_decode_error, _pipe},
{error, _pipe@1}
end, fun(_use0) ->
{N@6, Rest@5} = _use0,
gleam@result:'try'(
decode_array_helper(Rest@5, N@6 * 2, []),
fun(_use0@1) ->
{Values, Rest@6} = _use0@1,
Map = begin
_pipe@2 = lists:reverse(Values),
_pipe@3 = gleam@list:sized_chunk(_pipe@2, 2),
_pipe@4 = gleam@list:try_map(
_pipe@3,
fun(X) -> case X of
[K, V@1] ->
{ok, {K, V@1}};
_ ->
{error,
{dynamic_decode_error,
gleam@dynamic@decode:decode_error(
<<"Expected even number of values"/utf8>>,
gleam@dynamic:nil()
)}}
end end
),
gleam@result:map(
_pipe@4,
fun(Field@0) -> {c_b_map, Field@0} end
)
end,
gleam@result:'try'(
Map,
fun(Map@1) -> {ok, {Map@1, Rest@6}} end
)
end
)
end).
-file("src/gbor/decode.gleam", 375).
-spec decode_tagged(integer(), bitstring()) -> {ok,
{gbor:c_b_o_r(), bitstring()}} |
{error, cbor_decode_error()}.
decode_tagged(Min, Data) ->
gleam@result:'try'(case {Min, Data} of
{24, <<Val:8/integer-unsigned, Rest/bitstring>>} ->
{ok, {Val, Rest}};
{25, <<Val@1:16/integer-unsigned, Rest@1/bitstring>>} ->
{ok, {Val@1, Rest@1}};
{26, <<Val@2:32/integer-unsigned, Rest@2/bitstring>>} ->
{ok, {Val@2, Rest@2}};
{27, <<Val@3:64/integer-unsigned, Rest@3/bitstring>>} ->
{ok, {Val@3, Rest@3}};
{Min@1, <<Rest@4/bitstring>>} when Min@1 =< 23 ->
{ok, {Min@1, Rest@4}};
{Min@2, <<_/bitstring>>} when (30 >= Min@2) andalso (Min@2 >= 28) ->
{error, reserved_error};
{Min@3, V} ->
Err = <<"Did not find a valid uint, min: "/utf8,
(gleam@string:inspect(Min@3))/binary>>,
_pipe = gleam@dynamic@decode:decode_error(
Err,
gleam_stdlib:identity(V)
),
_pipe@1 = {dynamic_decode_error, _pipe},
{error, _pipe@1}
end, fun(_use0) ->
{Tag_num, Rest@5} = _use0,
gleam@result:'try'(
decode_helper(Rest@5),
fun(_use0@1) ->
{Tag_value, Rest@6} = _use0@1,
{ok, {{c_b_tagged, Tag_num, Tag_value}, Rest@6}}
end
)
end).