3 Copyright (c) 2003, Arvid Norberg
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
10 * Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in
14 the documentation and/or other materials provided with the distribution.
15 * Neither the name of the author nor the names of its
16 contributors may be used to endorse or promote products derived
17 from this software without specific prior written permission.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 POSSIBILITY OF SUCH DAMAGE.
34 #ifndef TORRENT_BENCODE_HPP_INCLUDED
35 #define TORRENT_BENCODE_HPP_INCLUDED
40 * This file declares the following functions:
42 *----------------------------------
43 * template<class OutIt>
44 * void libtorrent::bencode(OutIt out, const libtorrent::entry& e);
46 * Encodes a message entry with bencoding into the output
47 * iterator given. The bencoding is described in the BitTorrent
48 * protocol description document OutIt must be an OutputIterator
49 * of type char. This may throw libtorrent::invalid_encoding if
50 * the entry contains invalid nodes (undefined_t for example).
52 *----------------------------------
53 * template<class InIt>
54 * libtorrent::entry libtorrent::bdecode(InIt start, InIt end);
56 * Decodes the buffer given by the start and end iterators
57 * and returns the decoded entry. InIt must be an InputIterator
58 * of type char. May throw libtorrent::invalid_encoding if
59 * the string is not correctly bencoded.
69 #pragma warning(push, 1)
72 #include <boost/lexical_cast.hpp>
73 #include <boost/static_assert.hpp>
79 #include "libtorrent/entry.hpp"
80 #include "libtorrent/config.hpp"
82 #include "libtorrent/assert.hpp"
91 #define for if (false) {} else for
98 struct TORRENT_EXPORT invalid_encoding
: std::exception
100 virtual const char* what() const throw() { return "invalid bencoding"; }
105 template <class OutIt
>
106 int write_string(OutIt
& out
, const std::string
& val
)
108 int ret
= val
.length();
109 std::string::const_iterator end
= val
.begin() + ret
;
110 for (std::string::const_iterator i
= val
.begin()
111 , end(val
.begin() + ret
); i
!= end
; ++i
)
116 TORRENT_EXPORT
char const* integer_to_str(char* buf
, int size
, entry::integer_type val
);
118 template <class OutIt
>
119 int write_integer(OutIt
& out
, entry::integer_type val
)
121 // the stack allocated buffer for keeping the
122 // decimal representation of the number can
123 // not hold number bigger than this:
124 BOOST_STATIC_ASSERT(sizeof(entry::integer_type
) <= 8);
127 for (char const* str
= integer_to_str(buf
, 21, val
);
137 template <class OutIt
>
138 void write_char(OutIt
& out
, char c
)
144 template <class InIt
>
145 std::string
read_until(InIt
& in
, InIt end
, char end_token
, bool& err
)
153 while (*in
!= end_token
)
167 void read_string(InIt
& in
, InIt end
, int len
, std::string
& str
, bool& err
)
169 TORRENT_ASSERT(len
>= 0);
170 for (int i
= 0; i
< len
; ++i
)
182 // returns the number of bytes written
183 template<class OutIt
>
184 int bencode_recursive(OutIt
& out
, const entry
& e
)
190 write_char(out
, 'i');
191 ret
+= write_integer(out
, e
.integer());
192 write_char(out
, 'e');
195 case entry::string_t
:
196 ret
+= write_integer(out
, e
.string().length());
197 write_char(out
, ':');
198 ret
+= write_string(out
, e
.string());
202 write_char(out
, 'l');
203 for (entry::list_type::const_iterator i
= e
.list().begin(); i
!= e
.list().end(); ++i
)
204 ret
+= bencode_recursive(out
, *i
);
205 write_char(out
, 'e');
208 case entry::dictionary_t
:
209 write_char(out
, 'd');
210 for (entry::dictionary_type::const_iterator i
= e
.dict().begin();
211 i
!= e
.dict().end(); ++i
)
214 ret
+= write_integer(out
, i
->first
.length());
215 write_char(out
, ':');
216 ret
+= write_string(out
, i
->first
);
218 ret
+= bencode_recursive(out
, i
->second
);
221 write_char(out
, 'e');
232 void bdecode_recursive(InIt
& in
, InIt end
, entry
& ret
, bool& err
, int depth
)
244 ret
.m_type_queried
= false;
251 // ----------------------------------------------
256 std::string val
= read_until(in
, end
, 'e', err
);
258 TORRENT_ASSERT(*in
== 'e');
260 ret
= entry(entry::int_t
);
261 ret
.integer() = boost::lexical_cast
<entry::integer_type
>(val
);
263 ret
.m_type_queried
= false;
267 // ----------------------------------------------
271 ret
= entry(entry::list_t
);
275 ret
.list().push_back(entry());
276 entry
& e
= ret
.list().back();
277 bdecode_recursive(in
, end
, e
, err
, depth
+ 1);
281 ret
.m_type_queried
= false;
289 ret
.m_type_queried
= false;
295 ret
.m_type_queried
= false;
297 TORRENT_ASSERT(*in
== 'e');
301 // ----------------------------------------------
305 ret
= entry(entry::dictionary_t
);
310 bdecode_recursive(in
, end
, key
, err
, depth
+ 1);
311 if (err
|| key
.type() != entry::string_t
)
314 ret
.m_type_queried
= false;
318 entry
& e
= ret
[key
.string()];
319 bdecode_recursive(in
, end
, e
, err
, depth
+ 1);
323 ret
.m_type_queried
= false;
331 ret
.m_type_queried
= false;
337 ret
.m_type_queried
= false;
339 TORRENT_ASSERT(*in
== 'e');
343 // ----------------------------------------------
346 if (isdigit((unsigned char)*in
))
348 std::string len_s
= read_until(in
, end
, ':', err
);
352 ret
.m_type_queried
= false;
356 TORRENT_ASSERT(*in
== ':');
358 int len
= std::atoi(len_s
.c_str());
359 ret
= entry(entry::string_t
);
360 read_string(in
, end
, len
, ret
.string(), err
);
364 ret
.m_type_queried
= false;
373 ret
.m_type_queried
= false;
378 ret
.m_type_queried
= false;
384 template<class OutIt
>
385 int bencode(OutIt out
, const entry
& e
)
387 return detail::bencode_recursive(out
, e
);
391 entry
bdecode(InIt start
, InIt end
)
395 detail::bdecode_recursive(start
, end
, e
, err
, 0);
396 TORRENT_ASSERT(e
.m_type_queried
== false);
397 if (err
) return entry();
402 entry
bdecode(InIt start
, InIt end
, int& len
)
407 detail::bdecode_recursive(start
, end
, e
, err
, 0);
408 len
= std::distance(s
, start
);
409 TORRENT_ASSERT(len
>= 0);
410 if (err
) return entry();
415 #endif // TORRENT_BENCODE_HPP_INCLUDED