-module(beencode). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([encode/1, decode/1]). -export_type([b_value/0, b_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. -type b_value() :: {b_string, bitstring()} | {b_int, integer()} | {b_list, list(b_value())} | {b_dict, gleam@dict:dict(bitstring(), b_value())}. -type b_decode_error() :: expecting_eof | unexpected_eof | {unexpected_char, integer()} | {empty_number, integer()} | {negative_zero, integer()} | {leading_zero, integer()} | {invalid_dict_key, integer()} | {string_shorter_than_expected, integer()}. -file("/Users/giacomocavalieri/Documents/progetti/bvalue/src/beencode.gleam", 225). -spec decode_string(bitstring(), integer(), integer()) -> {ok, {b_value(), bitstring(), integer()}} | {error, b_decode_error()}. decode_string(String, Acc, Byte_index) -> case String of <<"0"/utf8, Rest/bitstring>> -> decode_string(Rest, Acc * 10, Byte_index + 1); <<"1"/utf8, Rest@1/bitstring>> -> decode_string(Rest@1, (Acc * 10) + 1, Byte_index + 1); <<"2"/utf8, Rest@2/bitstring>> -> decode_string(Rest@2, (Acc * 10) + 2, Byte_index + 1); <<"3"/utf8, Rest@3/bitstring>> -> decode_string(Rest@3, (Acc * 10) + 3, Byte_index + 1); <<"4"/utf8, Rest@4/bitstring>> -> decode_string(Rest@4, (Acc * 10) + 4, Byte_index + 1); <<"5"/utf8, Rest@5/bitstring>> -> decode_string(Rest@5, (Acc * 10) + 5, Byte_index + 1); <<"6"/utf8, Rest@6/bitstring>> -> decode_string(Rest@6, (Acc * 10) + 6, Byte_index + 1); <<"7"/utf8, Rest@7/bitstring>> -> decode_string(Rest@7, (Acc * 10) + 7, Byte_index + 1); <<"8"/utf8, Rest@8/bitstring>> -> decode_string(Rest@8, (Acc * 10) + 8, Byte_index + 1); <<"9"/utf8, Rest@9/bitstring>> -> decode_string(Rest@9, (Acc * 10) + 9, Byte_index + 1); <<":"/utf8, Rest@10/bitstring>> -> case Rest@10 of <> -> {ok, {{b_string, String@1}, Rest@11, (Byte_index + 1) + Acc}}; _ -> {error, {string_shorter_than_expected, Byte_index}} end; <<>> -> {error, unexpected_eof}; _ -> {error, {unexpected_char, Byte_index}} end. -file("/Users/giacomocavalieri/Documents/progetti/bvalue/src/beencode.gleam", 278). -spec decode_int(bitstring(), integer(), boolean(), integer()) -> {ok, {b_value(), bitstring(), integer()}} | {error, b_decode_error()}. decode_int(String, Acc, Negative, Byte_index) -> case String of <<"e"/utf8, Rest/bitstring>> when Negative -> {ok, {{b_int, - Acc}, Rest, Byte_index + 1}}; <<"e"/utf8, Rest@1/bitstring>> -> {ok, {{b_int, Acc}, Rest@1, Byte_index + 1}}; <<"0"/utf8, Rest@2/bitstring>> -> decode_int(Rest@2, Acc * 10, Negative, Byte_index + 1); <<"1"/utf8, Rest@3/bitstring>> -> decode_int(Rest@3, (Acc * 10) + 1, Negative, Byte_index + 1); <<"2"/utf8, Rest@4/bitstring>> -> decode_int(Rest@4, (Acc * 10) + 2, Negative, Byte_index + 1); <<"3"/utf8, Rest@5/bitstring>> -> decode_int(Rest@5, (Acc * 10) + 3, Negative, Byte_index + 1); <<"4"/utf8, Rest@6/bitstring>> -> decode_int(Rest@6, (Acc * 10) + 4, Negative, Byte_index + 1); <<"5"/utf8, Rest@7/bitstring>> -> decode_int(Rest@7, (Acc * 10) + 5, Negative, Byte_index + 1); <<"6"/utf8, Rest@8/bitstring>> -> decode_int(Rest@8, (Acc * 10) + 6, Negative, Byte_index + 1); <<"7"/utf8, Rest@9/bitstring>> -> decode_int(Rest@9, (Acc * 10) + 7, Negative, Byte_index + 1); <<"8"/utf8, Rest@10/bitstring>> -> decode_int(Rest@10, (Acc * 10) + 8, Negative, Byte_index + 1); <<"9"/utf8, Rest@11/bitstring>> -> decode_int(Rest@11, (Acc * 10) + 9, Negative, Byte_index + 1); <<>> -> {error, unexpected_eof}; _ -> {error, {unexpected_char, Byte_index}} end. -file("/Users/giacomocavalieri/Documents/progetti/bvalue/src/beencode.gleam", 400). ?DOC( " Encodes a `BValue` into a [bencoded](https://en.wikipedia.org/wiki/Bencode)\n" " `BitArray`.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " encode(BInt(1))\n" " // -> <<\"i1e\":utf8>>\n" "\n" " encode(BString(\"wibble\"))\n" " // -> <<\"6:wibble\":utf8>>\n" "\n" " encode(BList([BInt(1), BString(\"wibble\")]))\n" " // -> <<\"li1e6:wibblee\":utf8>>\n" "\n" " encode(BDict(dict.from_list([#(BString(\"wibble\"), BInt(1))])))\n" " // -> <<\"d6:wibblei1ee\">>\n" " ```\n" ). -spec encode(b_value()) -> bitstring(). encode(Value) -> case Value of {b_int, N} -> <<"i"/utf8, (erlang:integer_to_binary(N))/binary, "e"/utf8>>; {b_string, Value@1} -> Bytes = erlang:byte_size(Value@1), <<(erlang:integer_to_binary(Bytes))/binary, ":"/utf8, Value@1/bitstring>>; {b_list, Values} -> Encoded_values = gleam@list:map(Values, fun encode/1), <<"l"/utf8, (gleam_stdlib:bit_array_concat(Encoded_values))/bitstring, "e"/utf8>>; {b_dict, Dict} -> Values@1 = begin _pipe = maps:to_list(Dict), _pipe@1 = gleam@list:sort( _pipe, fun(One, Other) -> {One_key, _} = One, {Other_key, _} = Other, gleam@bit_array:compare(One_key, Other_key) end ), gleam@list:map( _pipe@1, fun(Pair) -> {Key, Value@2} = Pair, <<(encode({b_string, Key}))/bitstring, (encode(Value@2))/bitstring>> end ) end, <<"d"/utf8, (gleam_stdlib:bit_array_concat(Values@1))/bitstring, "e"/utf8>> end. -file("/Users/giacomocavalieri/Documents/progetti/bvalue/src/beencode.gleam", 323). -spec decode_list(bitstring(), list(b_value()), integer()) -> {ok, {b_value(), bitstring(), integer()}} | {error, b_decode_error()}. decode_list(String, Acc, Byte_index) -> case String of <<"e"/utf8, Rest/bitstring>> -> {ok, {{b_list, lists:reverse(Acc)}, Rest, Byte_index + 1}}; _ -> case decode_value(String, Byte_index) of {error, Error} -> {error, Error}; {ok, {Value, Rest@1, Byte_index@1}} -> decode_list(Rest@1, [Value | Acc], Byte_index@1) end end. -file("/Users/giacomocavalieri/Documents/progetti/bvalue/src/beencode.gleam", 146). -spec decode_value(bitstring(), integer()) -> {ok, {b_value(), bitstring(), integer()}} | {error, b_decode_error()}. decode_value(String, Byte_index) -> case String of <<"ie"/utf8, _/bitstring>> -> {error, {empty_number, Byte_index}}; <<"i0e"/utf8, Rest/bitstring>> -> {ok, {{b_int, 0}, Rest, Byte_index + 1}}; <<"i-0e"/utf8, _/bitstring>> -> {error, {negative_zero, Byte_index}}; <<"i0"/utf8, _/bitstring>> -> {error, {leading_zero, Byte_index}}; <<"i-0"/utf8, _/bitstring>> -> {error, {leading_zero, Byte_index}}; <<"i-"/utf8, Rest@1/bitstring>> -> decode_int(Rest@1, 0, true, Byte_index); <<"i"/utf8, Rest@2/bitstring>> -> decode_int(Rest@2, 0, false, Byte_index); <<"l"/utf8, Rest@3/bitstring>> -> decode_list(Rest@3, [], Byte_index); <<"d"/utf8, Rest@4/bitstring>> -> decode_dict(Rest@4, maps:new(), Byte_index); <<"0:"/utf8, Rest@5/bitstring>> -> {ok, {{b_string, <<>>}, Rest@5, Byte_index + 2}}; <<"0"/utf8, _/bitstring>> -> {error, {unexpected_char, Byte_index}}; <<"1"/utf8, _/bitstring>> -> decode_string(String, 0, Byte_index + 1); <<"2"/utf8, _/bitstring>> -> decode_string(String, 0, Byte_index + 1); <<"3"/utf8, _/bitstring>> -> decode_string(String, 0, Byte_index + 1); <<"4"/utf8, _/bitstring>> -> decode_string(String, 0, Byte_index + 1); <<"5"/utf8, _/bitstring>> -> decode_string(String, 0, Byte_index + 1); <<"6"/utf8, _/bitstring>> -> decode_string(String, 0, Byte_index + 1); <<"7"/utf8, _/bitstring>> -> decode_string(String, 0, Byte_index + 1); <<"8"/utf8, _/bitstring>> -> decode_string(String, 0, Byte_index + 1); <<"9"/utf8, _/bitstring>> -> decode_string(String, 0, Byte_index + 1); <<>> -> {error, unexpected_eof}; _ -> {error, {unexpected_char, Byte_index}} end. -file("/Users/giacomocavalieri/Documents/progetti/bvalue/src/beencode.gleam", 123). ?DOC( " Decodes a [bencoded](https://en.wikipedia.org/wiki/Bencode) `BitArray` into\n" " a `BValue`.\n" "\n" " > ⚠️ According to the bencode specification the keys of a dictionary should\n" " > always be sorted lexicographically. This decoder is a bit more permissive\n" " > and will successfully decode a dictionary even if its keys appear in a\n" " > different order.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " decode(<<\"i1e\":utf8>>)\n" " // -> BInt(1)\n" "\n" " decode(<<\"6:wibble\":utf8>>)\n" " // -> BString(\"wibble\")\n" "\n" " decode(<<\"li1e6:wibblee\":utf8>>)\n" " // -> BList([BInt(1), BString(\"wibble\")])\n" "\n" " decode(<<\"d6:wibblei1ee\">>)\n" " // -> BDict(dict.from_list([#(BString(\"wibble\"), BInt(1))]))\n" " ```\n" ). -spec decode(bitstring()) -> {ok, b_value()} | {error, b_decode_error()}. decode(Input) -> case decode_value(Input, 0) of {ok, {Value, <<>>, _}} -> {ok, Value}; {ok, {_, _, _}} -> {error, expecting_eof}; {error, Reason} -> {error, Reason} end. -file("/Users/giacomocavalieri/Documents/progetti/bvalue/src/beencode.gleam", 349). -spec decode_dict( bitstring(), gleam@dict:dict(bitstring(), b_value()), integer() ) -> {ok, {b_value(), bitstring(), integer()}} | {error, b_decode_error()}. decode_dict(String, Acc, Byte_index) -> case String of <<"e"/utf8, Rest/bitstring>> -> {ok, {{b_dict, Acc}, Rest, Byte_index + 1}}; _ -> case decode_value(String, Byte_index) of {error, Error} -> {error, Error}; {ok, {{b_string, Key}, Rest@1, Byte_index@1}} -> case decode_value(Rest@1, Byte_index@1) of {error, Error@1} -> {error, Error@1}; {ok, {Value, Rest@2, Byte_index@2}} -> decode_dict( Rest@2, gleam@dict:insert(Acc, Key, Value), Byte_index@2 ) end; {ok, {_, _, Byte_index@3}} -> {error, {invalid_dict_key, Byte_index@3}} end end.