Handle HAVE_ALL and HAVE_NONE. Cleanup the BITFIELD message.
[etorrent.git] / lib / etorrent-1.0 / src / etorrent_bcoding.erl
blobe85d5c7db52362fdee1398c30a77a12b3485b8e3
1 %%%-------------------------------------------------------------------
2 %%% File : bcoding.erl
3 %%% Author : Jesper Louis Andersen <jlouis@succubus>
4 %%% License : See COPYING
5 %%% Description : Functions for handling bcoded values
6 %%%
7 %%% Created : 24 Jan 2007 by Jesper Louis Andersen <jlouis@succubus>
8 %%%-------------------------------------------------------------------
9 -module(etorrent_bcoding).
10 -author("Jesper Louis Andersen <jesper.louis.andersen@gmail.com>").
11 -vsn("1.0").
13 %% API
14 -export([encode/1, decode/1, search_dict/2, search_dict_default/3,
15 parse/1]).
17 %%====================================================================
18 %% API
19 %%====================================================================
20 %%--------------------------------------------------------------------
21 %% Function: encode/1
22 %% Description: Encode an erlang term into a String
23 %%--------------------------------------------------------------------
24 encode(BString) ->
25 case BString of
26 {string, String} -> encode_string(String);
27 {integer, Integer} -> encode_integer(Integer);
28 {list, Items} -> encode_list([encode(I) || I <- Items]);
29 {dict, Items} -> encode_dict(encode_dict_items(Items))
30 end.
32 %%--------------------------------------------------------------------
33 %% Function: decode/1
34 %% Description: Decode a string to an erlang term.
35 %%--------------------------------------------------------------------
36 decode(String) ->
37 {Res, []} = decode_b(String),
38 Res.
40 %%--------------------------------------------------------------------
41 %% Function: search_dict/1
42 %% Description: Search the dict for a key. Returns the value or crashes.
43 %%--------------------------------------------------------------------
44 search_dict(Key, {dict, Elems}) ->
45 case lists:keysearch(Key, 1, Elems) of
46 {value, {_, V}} ->
48 false ->
49 false
50 end.
52 search_dict_default(Key, Dict, Default) ->
53 case search_dict(Key, Dict) of
54 false ->
55 Default;
56 X ->
58 end.
60 %%--------------------------------------------------------------------
61 %% Function: parse/1
62 %% Description: Parse a file into a Torrent structure.
63 %%--------------------------------------------------------------------
64 parse(File) ->
65 {ok, IODev} = file:open(File, [read]),
66 Data = read_data(IODev),
67 ok = file:close(IODev),
68 decode(Data).
70 %%====================================================================
71 %% Internal functions
72 %%====================================================================
74 %% Encode a string.
75 encode_string(Str) ->
76 L = length(Str),
77 lists:concat([L, ':', Str]).
79 encode_integer(Int) ->
80 lists:concat(['i', Int, 'e']).
82 encode_list(Items) ->
83 lists:concat(["l", lists:concat(Items), "e"]).
85 encode_dict(Items) ->
86 lists:concat(["d", lists:concat(Items), "e"]).
88 encode_dict_items([]) ->
89 [];
90 encode_dict_items([{I1, I2} | Rest]) ->
91 I = encode(I1),
92 J = encode(I2),
93 [I, J | encode_dict_items(Rest)].
95 decode_b([]) ->
96 empty_string;
97 decode_b([H | Rest]) ->
98 case H of
99 $i ->
100 decode_integer(Rest);
101 $l ->
102 decode_list(Rest);
103 $d ->
104 decode_dict(Rest);
105 $e ->
106 {end_of_data, Rest};
107 S ->
108 %% This might fail, and so what ;)
109 attempt_string_decode([S|Rest])
110 end.
112 charPred(C) ->
113 fun(E) ->
114 E /= C end.
116 attempt_string_decode(String) ->
117 {Number, Data} = lists:splitwith(charPred($:), String),
118 {ParsedNumber, _} = string:to_integer(Number),
119 Rest1 = tl(Data),
120 {StrData, Rest} = lists:split(ParsedNumber, Rest1),
121 {{string, StrData}, Rest}.
123 decode_integer(String) ->
124 {IntegerPart, RestPart} = lists:splitwith(charPred($e), String),
125 {Int, _} = string:to_integer(IntegerPart),
126 {{integer, Int}, tl(RestPart)}.
128 decode_list(String) ->
129 {ItemTree, Rest} = decode_list_items(String, []),
130 {{list, ItemTree}, Rest}.
132 decode_list_items([], Accum) -> {lists:reverse(Accum), []};
133 decode_list_items(Items, Accum) ->
134 case decode_b(Items) of
135 {end_of_data, Rest} ->
136 {lists:reverse(Accum), Rest};
137 {I, Rest} -> decode_list_items(Rest, [I | Accum])
138 end.
140 decode_dict(String) ->
141 {Items, Rest} = decode_dict_items(String, []),
142 {{dict, lists:reverse(Items)}, Rest}.
144 decode_dict_items([], Accum) ->
145 {Accum, []};
146 decode_dict_items(String, Accum) ->
147 case decode_b(String) of
148 {end_of_data, Rest} ->
149 {Accum, Rest};
150 {Key, Rest1} -> {Value, Rest2} = decode_b(Rest1),
151 decode_dict_items(Rest2, [{Key, Value} | Accum])
152 end.
154 read_data(IODev) ->
155 eat_lines(IODev, []).
157 eat_lines(IODev, Accum) ->
158 case io:get_chars(IODev, ">", 8192) of
159 eof ->
160 lists:concat(lists:reverse(Accum));
161 String ->
162 eat_lines(IODev, [String | Accum])
163 end.