%%%------------------------------------------------------------------- %%% @author ratopi@abwesend.de %%% @copyright (C) 2016, Ralf Th. Pietsch %%% @doc %%% Use decode to decode a binary containing an bencoded string. %%% Use encode to generate a binary containing an bencoded string. %%% @end %%% Created : 29. Dez 2016 17:43 %%%------------------------------------------------------------------- -module(bencoding). -author("ratopi@abwesend.de"). %% API -export([encode/1, decode/1]). encode(<>) -> encode_string([], String); encode(N) when is_integer(N) -> encode_int(start, N); encode(L) when is_list(L) -> encode_list(start, L); encode(M) when is_map(M) -> encode_dictionary(start, M). decode(<>) when L >= $0, L =< $9 -> decode_string({len, L - $0}, Rest); decode(<<$i, Rest/binary>>) -> decode_int({positive, 0}, Rest); decode(<<$l, Rest/binary>>) -> decode_list([], Rest); decode(<<$d, Rest/binary>>) -> decode_dictionary(#{}, Rest). %% %% Internal Functions %% encode_string(Text, <<>>) -> {ok, list_to_binary(integer_to_list(length(Text)) ++ [$:] ++ lists:reverse(Text))}; encode_string(Text, <>) -> encode_string([Char | Text], Rest). encode_int(start, 0) -> encode_int({positive, "0"}, 0); encode_int(start, N) when N < 0 -> encode_int({negative, []}, -N); encode_int(start, N) -> encode_int({positive, []}, N); encode_int({negative, Text}, 0) -> { ok, list_to_binary( [$i, $- | lists:reverse([$e, Text])] ) }; encode_int({positive, Text}, 0) -> { ok, list_to_binary( [$i | lists:reverse([$e, Text])] ) }; encode_int({NegPos, Text}, N) -> Digit = N rem 10, encode_int({NegPos, [Digit + $0 | Text]}, N div 10). encode_list(start, L) -> encode_list([$l], L); encode_list(Output, []) -> {ok, list_to_binary(lists:reverse([$e | Output]))}; encode_list(Output, [H | T]) -> {ok, Element} = encode(H), encode_list([Element | Output], T). encode_dictionary(start, M) -> encode_dictionary({[$d], maps:keys(M)}, M); encode_dictionary({Output, []}, _) -> {ok, list_to_binary(lists:reverse([$e | Output]))}; encode_dictionary({Output, [Key | Keys]}, Map) -> {ok, EncodedKey} = encode(Key), {ok, EncodedValue} = encode(maps:get(Key, Map)), encode_dictionary( { [EncodedValue, EncodedKey, Output], Keys }, Map ). %% %% %% decode_int({positive, N}, <<$e, Rest/binary>>) -> {ok, N, Rest}; decode_int({negative, N}, <<$e, Rest/binary>>) -> {ok, -N, Rest}; decode_int({positive, 0}, <<$-, Rest/binary>>) -> decode_int({negative, 0}, Rest); decode_int({PosNeg, N}, <>) when L >= $0, L =< $9 -> decode_int({PosNeg, 10 * N + (L - $0)}, Rest). decode_list(List, <<$e, Rest/binary>>) -> {ok, lists:reverse(List), Rest}; decode_list(List, <>) -> {ok, Element, NextRest} = decode(Rest), decode_list([Element | List], NextRest). decode_dictionary(Dictionary, <<$e, Rest/binary>>) -> {ok, Dictionary, Rest}; decode_dictionary(Dictionary, <>) -> {ok, Key, NextRest} = decode(Rest), {ok, Value, NextNextRest} = decode(NextRest), decode_dictionary(maps:put(Key, Value, Dictionary), NextNextRest). decode_string({len, L}, <>) when N >= $0 andalso N =< $9 -> decode_string({len, (L * 10 + (N - $0))}, Rest); decode_string({len, L}, <<$:, Rest/binary>>) -> decode_string({text, [], L}, Rest); decode_string({text, Text, 0}, <>) -> {ok, lists:reverse(Text), Rest}; decode_string({text, Text, L}, <>) -> decode_string({text, [Char | Text], L - 1}, Rest).