Decoding encoding bencoded data with erlang
Jump to navigation
Jump to search
A simple codec of bencoded data written in Erlang
-module(torrent). %% API -export([decode/1, encode/1]). %% You are able to choose the dict implementation -define(DICT, dict). %%-define(DICT, orddict). %%==================================================================== %% API %%==================================================================== decode(Data) -> case catch dec(Data) of {'EXIT', _} -> {error, unparsed}; {Res, _} -> {ok, Res} end. encode(Struct) -> iolist_to_binary(enc(Struct)). %%==================================================================== %% Internal functions %%==================================================================== %%-------------------------------------------------------------------- %% Decoding %%-------------------------------------------------------------------- dec(<<$l, Tail/binary>>) -> dec_list(Tail, []); dec(<<$d, Tail/binary>>) -> dec_dict(Tail, ?DICT:new()); dec(<<$i, Tail/binary>>) -> dec_int(Tail, []); dec(Data) -> dec_string(Data, []). dec_int(<<$e, Tail/binary>>, Acc) -> {list_to_integer(lists:reverse(Acc)), Tail}; dec_int(<<X, Tail/binary>>, Acc) -> dec_int(Tail, [X|Acc]). dec_string(<<$:, Tail/binary>>, Acc) -> Int = list_to_integer(lists:reverse(Acc)), <<Str:Int/binary, Rest/binary>> = Tail, {Str, Rest}; dec_string(<<X, Tail/binary>>, Acc) -> dec_string(Tail, [X|Acc]). dec_list(<<$e, Tail/binary>>, Acc) -> {{list, lists:reverse(Acc)}, Tail}; dec_list(Data, Acc) -> {Res, Tail} = dec(Data), dec_list(Tail, [Res|Acc]). dec_dict(<<$e, Tail/binary>>, Acc) -> {{dict, Acc}, Tail}; dec_dict(Data, Acc) -> {Key, Tail1} = dec(Data), {Val, Tail2} = dec(Tail1), dec_dict(Tail2, ?DICT:store(Key, Val, Acc)). %%-------------------------------------------------------------------- %% Encoding %%-------------------------------------------------------------------- enc(Int) when is_integer(Int) -> IntBin = list_to_binary(integer_to_list(Int)), [$i, IntBin, $e]; enc(Str) when is_list(Str) -> enc(list_to_binary(Str)); enc(Str) when is_binary(Str) -> IntBin = list_to_binary(integer_to_list(size(Str))), [IntBin, $:, Str]; enc({list, List}) when is_list(List) -> [$l, [enc(Elem) || Elem <- List], $e]; enc({dict, Dict}) -> Data = lists:map( fun({Key, Val}) when is_list(Key) or is_binary(Key) -> [enc(Key), enc(Val)] end, lists:keysort(1, ?DICT:to_list(Dict))), [$d, Data, $e].