#ifndef __bencode_h_incl #define __bencode_h_incl #include #include #include #include #include #include #include #include namespace bencode { #ifndef BOOST_NO_INT64_T typedef boost::int64_t int_t; #else typedef int32_t int_t; #endif /// The field_t is the basic building block; you can use it to /// represent a scalar value, list or dictionary typedef boost::make_recursive_variant< int_t, double, std::string, std::vector, std::map >::type field_t; /// Some convenience typedefs typedef std::map dict; typedef std::vector vec_t; /// encoder - implements the bencode encoding format struct encoder : public boost::static_visitor<> { private: std::ostream& strm_; public: encoder (std::ostream& os) : strm_ (os) {} void operator() (const int_t& i) const { char buf[32]; #ifndef BOOST_NO_INT64_T int len = sprintf (buf, "i%llde", i); #else int len = sprintf (buf, "i%de", i); #endif strm_.write (buf, len); } void operator () (const double& d) const { char buf[64]; int len = sprintf (buf, "f%.15gf", d); strm_.write (buf, len); } void operator() (const std::string& s) const { char buf[16]; int len = sprintf (buf, "%d:", s.size()); strm_.write (buf, len); strm_.write (s.data (), s.size ()); } void operator() (const vec_t& v) const { strm_ << 'l'; std::for_each (v.begin(), v.end(), boost::apply_visitor (*this)); strm_ << 'e'; } void operator() (const dict& m) const { strm_ << 'd'; for (dict::const_iterator i = m.begin(); i != m.end(); ++i) { this->operator () (i->first); boost::apply_visitor (*this, i->second); } strm_ << 'e'; } }; /// decoder - decodes a bencode-format stream and places the result /// into a field_t. Usually you pass it a dict. struct decoder : public boost::static_visitor<> { /// decode a bencoded object from a stream into a dict static void decode (std::istream& is, dict& value) throw (std::runtime_error) { decode_dict (is, value); } /// decode a bencoded object from a stream into a field_t, /// changing it to the appropriate type static void decode (std::istream& is, field_t& value) throw (std::runtime_error) { char type = is.peek (); if (type == 'i') decode_int (is, value); else if (type == 'f') decode_double (is, value); else if (type == 'l') decode_list (is, value); else if (type == 'd') { value = dict (); decode_dict (is, *boost::get (&value)); } else decode_string (is, value); } private: static void decode_int (std::istream& is, field_t& value) { char buf[32]; if (is.get () != 'i' || !(is.get (buf, sizeof (buf), 'e')) || is.get () != 'e') { std::ostringstream os; os << "Error reading integer value at offset " << is.tellg (); throw std::runtime_error (os.str()); } char* end; #ifndef BOOST_NO_INT64_T # ifdef BOOST_MSVC value = _atoi64 (buf); end = buf + strlen (buf); # else value = strtoll (buf, &end, 0); # endif // !BOOST_MSVC #else value = strtol (buf, &end, 0); #endif // BOOST_NO_INT64_T if (*end != 0) { std::ostringstream os; os << "Found trailing garbage parsing '" << buf << "' as an integer at offset " << is.tellg (); throw std::runtime_error (os.str()); } } static void decode_double (std::istream& is, field_t& value) { char buf[64]; if (is.get () != 'f' || !is.get (buf, sizeof (buf), 'f') || is.get () != 'f') { std::ostringstream os; os << "Error reading double value at offset " << is.tellg(); throw std::runtime_error (os.str()); } char* end; value = strtod (buf, &end); if (*end != 0) { std::ostringstream os; os << "Found trailing garbage parsing '" << buf << "' as a double at offset " << is.tellg (); throw std::runtime_error (os.str()); } } static void decode_list (std::istream& is, field_t& value) { if (is.get () != 'l') { std::ostringstream os; os << "Error reading list begin tag at offset " << is.tellg(); throw std::runtime_error (os.str()); } value = vec_t (); vec_t* vec = boost::get (&value); field_t elem; while (is.peek () != 'e') { decode (is, elem); vec->push_back (elem); } is.get (); // closing 'e' } static void decode_string (std::istream& is, std::string& s) { char buf[32]; if (!is.get (buf, sizeof (buf), ':') || is.get () != ':') { std::ostringstream os; os << "Error reading string length at offset " << is.tellg (); throw std::runtime_error (os.str()); } char* end; long len = strtol (buf, &end, 0); if (*end != 0) { std::ostringstream os; os << "Found trailing garbage parsing '" << buf << "' as an integer at offset " << is.tellg (); throw std::runtime_error (os.str()); } s.resize (len); if (!is.read (const_cast (s.data()), len)) { std::ostringstream os; os << "Error reading string of length " << len << " at offset " << is.tellg () << "; last read was " << is.gcount () << " bytes"; throw std::runtime_error (os.str()); } } static void decode_string (std::istream& is, field_t& value) { value = std::string (); decode_string (is, boost::get (value)); } static void decode_dict (std::istream& is, dict& dict) { if (is.get () != 'd') { std::ostringstream os; os << "Error reading dictionary begin tag at offset " << is.tellg (); throw std::runtime_error (os.str()); } std::string k; field_t v; while (is.peek () != 'e') { decode_string (is, k); decode (is, v); dict.insert (make_pair (k, v)); } is.get (); // closing 'e' } }; struct printer : public boost::static_visitor<> { printer (std::ostream& os) : strm_ (os) {} void operator() (const int_t& i) const { strm_ << i << ' '; } void operator() (const double& d) const { strm_ << d << ' '; } void operator() (const std::string& s) const { strm_ << '\'' << s << "' "; } void operator() (const vec_t& v) const { strm_ << "[ "; std::for_each (v.begin(), v.end(), boost::apply_visitor (*this)); strm_ << " ] "; } void operator() (const dict& m) const { strm_ << "{ "; for (dict::const_iterator i = m.begin(); i != m.end(); ++i) { this->operator () (i->first); strm_ << "= "; boost::apply_visitor (*this, i->second); } strm_ << "} "; } private: std::ostream& strm_; }; } // namespace bencode #endif // macro guard