Commit message: fix out-of-bounds read in bdecode

Fixes #2099
CVE: CVE-2017-9847
CWE: CWE-125
Vulnerability description: The bdecode function in bdecode.cpp in libtorrent 1.1.3 allows remote attackers to cause a denial of service (heap-based buffer over-read and application crash) via a crafted file.

Project: libtorrent

	void print_string(std::string& ret, char const* str, int len, bool single_line)
	{
		bool printable = true;
		for (int i = 0; i < len; ++i)
		{
			char c = str[i];
			if (c >= 32 && c < 127) continue;
			printable = false;
			break;
		}
		ret += "'";
		if (printable)
		{
			if (single_line && len > 30)
			{
				ret.append(str, 14);
				ret += "...";
				ret.append(str + len-14, 14);
			}
			else
				ret.append(str, len);
			ret += "'";
			return;
		}
		if (single_line && len > 20)
		{
			escape_string(ret, str, 9);
			ret += "...";
			escape_string(ret, str + len - 9, 9);
		}
		else
		{
			escape_string(ret, str, len);
		}
		ret += "'";
	}

Project: libtorrent

TORRENT_TEST(64bit_int)
{
	char b[] = "i9223372036854775807e";
	bdecode_node e;
	error_code ec;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec);
	TEST_CHECK(ret == 0);
	printf("%s\n", print_entry(e).c_str());
	TEST_CHECK(e.int_value() == 9223372036854775807LL);
}

Project: libtorrent

TORRENT_TEST(invalid_digit)
{
	char b[] = "0o";
	boost::int64_t val = 0;
	bdecode_errors::error_code_enum ec;
	char const* e = parse_int(b, b + sizeof(b)-1, 'e', val, ec);
	TEST_EQUAL(ec, bdecode_errors::expected_digit);
	TEST_EQUAL(e, b + 1);
}

Project: libtorrent

TORRENT_TEST(swap_disjoint)
{
	char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee";
	char b2[] = "li1e3:fooli1ei2eed1:xi1eee";

	bdecode_node e1_root;
	bdecode_node e2_root;

	error_code ec;

	int ret = bdecode(b1, b1 + sizeof(b1)-1, e1_root, ec);
	TEST_EQUAL(ret, 0);
	ret = bdecode(b2, b2 + sizeof(b2)-1, e2_root, ec);
	TEST_EQUAL(ret, 0);

	bdecode_node e1 = e1_root.dict_find("c").list_at(0);
	bdecode_node e2 = e2_root.list_at(1);

	std::string str1 = print_entry(e1);
	std::string str2 = print_entry(e2);
	TEST_EQUAL(e1.type(), bdecode_node::int_t);
	TEST_EQUAL(e2.type(), bdecode_node::string_t);

	e1.swap(e2);

	TEST_EQUAL(e1.type(), bdecode_node::string_t);
	TEST_EQUAL(e2.type(), bdecode_node::int_t);
	TEST_EQUAL(print_entry(e1), str2);
	TEST_EQUAL(print_entry(e2), str1);

	// swap back
	e1.swap(e2);

	TEST_EQUAL(e1.type(), bdecode_node::int_t);
	TEST_EQUAL(e2.type(), bdecode_node::string_t);
	TEST_EQUAL(print_entry(e1), str1);
	TEST_EQUAL(print_entry(e2), str2);
}

Project: libtorrent

TORRENT_TEST(expected_colon_string)
{
	char b[] = "928";
	boost::int64_t val = 0;
	bdecode_errors::error_code_enum ec;
	char const* e = parse_int(b, b + sizeof(b)-1, ':', val, ec);
	TEST_EQUAL(ec, bdecode_errors::expected_colon);
	TEST_EQUAL(e, b + 3);
}

Project: libtorrent

TORRENT_TEST(unexpected_eof_dict_key2)
{
	char b[] = "d1000:";

	bdecode_node e;
	error_code ec;
	int pos;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(pos, 5);
	TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
	printf("%s\n", print_entry(e).c_str());
}

Project: libtorrent

TORRENT_TEST(expected_string)
{
	char b[] = "di2ei0ee";
	// expected string (dict keys must be strings)

	bdecode_node e;
	error_code ec;
	int pos;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(pos, 1);
	TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit));
	printf("%s\n", print_entry(e).c_str());
}

Project: libtorrent

TORRENT_TEST(overflow_length_prefix2)
{
	char b[] = "199999999:foobar";
	bdecode_node e;
	error_code ec;
	int pos;
	// pretend that we have a large buffer like that
	int ret = bdecode(b, b + 999999999, e, ec, &pos);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(pos, 0);
	TEST_EQUAL(ec, error_code(bdecode_errors::limit_exceeded));
	printf("%s\n", print_entry(e).c_str());
}

Project: libtorrent

TORRENT_TEST(string_prefix1)
{
	// test edge-case of a string that's nearly too long
	std::string test;
	test.resize(1000000 + 8);
	memcpy(&test[0], "1000000:", 8);
	// test is a valid bencoded string, that's quite long
	bdecode_node e;
	error_code ec;
	int ret = bdecode(test.c_str(), test.c_str() + test.size(), e, ec);
	TEST_CHECK(ret == 0);
	printf("%d bytes string\n", e.string_length());
	std::pair<const char*, int> section = e.data_section();
	TEST_CHECK(std::memcmp(test.c_str(), section.first, section.second) == 0);
	TEST_EQUAL(section.second, int(test.size()));
	TEST_EQUAL(e.type(), bdecode_node::string_t);
	TEST_EQUAL(e.string_length(), 1000000);
	TEST_EQUAL(e.string_ptr(), test.c_str() + 8);
}

Project: libtorrent

TORRENT_TEST(exceed_buf_limit)
{
	char b[] = "l3:fooe";

	bdecode_node e;
	error_code ec;
	int ret = bdecode(b, b + 0x3fffffff, e, ec);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(ec, error_code(bdecode_errors::limit_exceeded));
	printf("%s\n", print_entry(e).c_str());
}

Project: libtorrent

TORRENT_TEST(empty_string)
{
	char b[] = "";

	bdecode_node e;
	error_code ec;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec, NULL);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
	printf("%s\n", print_entry(e).c_str());
}

Project: libtorrent

TORRENT_TEST(switch_buffer)
{
	char b1[] = "d1:ai1e1:b3:foo1:cli1e-i2ee1:dd1:xi1eee";
	char b2[] = "d1:ai1e1:b3:foo1:cli1e-i2ee1:dd1:xi1eee";

	bdecode_node e;
	error_code ec;
	int pos;
	int ret = bdecode(b1, b1 + sizeof(b1)-1, e, ec, &pos);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(pos, 22);
	TEST_EQUAL(e.type(), bdecode_node::dict_t);

	std::string string1 = print_entry(e);
	printf("%s\n", string1.c_str());

	e.switch_underlying_buffer(b2);

	std::string string2 = print_entry(e);
	printf("%s\n", string2.c_str());

	TEST_EQUAL(string1, string2);
}

Project: libtorrent

TORRENT_TEST(int_truncated)
{
	char b[] = "i-";
	bdecode_node e;
	error_code ec;
	int pos;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(pos, 2);
	TEST_EQUAL(ec,  error_code(bdecode_errors::unexpected_eof));
	printf("%s\n", print_entry(e).c_str());
}

Project: libtorrent

TORRENT_TEST(string)
{
	char b[] = "26:abcdefghijklmnopqrstuvwxyz";
	bdecode_node e;
	error_code ec;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec);
	TEST_CHECK(ret == 0);
	printf("%s\n", print_entry(e).c_str());
	std::pair<const char*, int> section = e.data_section();
	TEST_CHECK(std::memcmp(b, section.first, section.second) == 0);
	TEST_EQUAL(section.second, sizeof(b) - 1);
	TEST_EQUAL(e.type(), bdecode_node::string_t);
	TEST_EQUAL(e.string_value(), std::string("abcdefghijklmnopqrstuvwxyz"));
	TEST_EQUAL(e.string_length(), 26);
}

Project: libtorrent

	std::string bdecode_node::string_value() const
	{
		TORRENT_ASSERT(type() == string_t);
		bdecode_token const& t = m_root_tokens[m_token_idx];
		int size = m_root_tokens[m_token_idx + 1].offset - t.offset - t.start_offset();
		TORRENT_ASSERT(t.type == bdecode_token::string);

		return std::string(m_buffer + t.offset + t.start_offset(), size);
	}

Project: libtorrent

TORRENT_TEST(list_at_reverse)
{
	// int
	// string
	// list
	// dict
	char b[] = "li1e3:fooli1ei2eed1:xi1eee";
	bdecode_node e;
	error_code ec;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec);
	TEST_EQUAL(ret, 0);
	printf("%s\n", print_entry(e).c_str());

	TEST_EQUAL(e.type(), bdecode_node::list_t);

	TEST_EQUAL(e.list_at(3).type(), bdecode_node::dict_t);
	TEST_EQUAL(e.list_at(2).type(), bdecode_node::list_t);
	TEST_EQUAL(e.list_string_value_at(1), "foo");
	TEST_EQUAL(e.list_int_value_at(0), 1);

	TEST_EQUAL(e.list_size(), 4);
	TEST_EQUAL(e.list_size(), 4);
}

Project: libtorrent

TORRENT_TEST(expected_colon_dict)
{
	char b[] = "d1000";

	bdecode_node e;
	error_code ec;
	int pos;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(pos, 5);
	TEST_EQUAL(ec, error_code(bdecode_errors::expected_colon));
	printf("%s\n", print_entry(e).c_str());
}

Project: libtorrent

TORRENT_TEST(print_entry)
{
	char b[] = "li1e3:fooli1ei2eed1:xi1eee";
	bdecode_node e;
	error_code ec;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec);
	TEST_EQUAL(ret, 0);
	printf("%s\n", print_entry(e).c_str());

	TEST_EQUAL(print_entry(e), "[ 1, 'foo', [ 1, 2 ], { 'x': 1 } ]");
}

Project: libtorrent

TORRENT_TEST(invalid_encoding)
{
	unsigned char buf[] =
		{ 0x64, 0x31, 0x3a, 0x61, 0x64, 0x32, 0x3a, 0x69
		, 0x64, 0x32, 0x30, 0x3a, 0x2a, 0x21, 0x19, 0x89
		, 0x9f, 0xcd, 0x5f, 0xc9, 0xbc, 0x80, 0xc1, 0x76
		, 0xfe, 0xe0, 0xc6, 0x84, 0x2d, 0xf6, 0xfc, 0xb8
		, 0x39, 0x3a, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x68
		, 0x61, 0xae, 0x68, 0x32, 0x30, 0x3a, 0x14, 0x78
		, 0xd5, 0xb0, 0xdc, 0xf6, 0x82, 0x42, 0x32, 0xa0
		, 0xd6, 0x88, 0xeb, 0x48, 0x57, 0x01, 0x89, 0x40
		, 0x4e, 0xbc, 0x65, 0x31, 0x3a, 0x71, 0x39, 0x3a
		, 0x67, 0x65, 0x74, 0x5f, 0x70, 0x65, 0x65, 0x72
		, 0x78, 0xff, 0x3a, 0x74, 0x38, 0x3a, 0xaa, 0xd4
		, 0xa1, 0x88, 0x7a, 0x8d, 0xc3, 0xd6, 0x31, 0x3a
		, 0x79, 0x31, 0xae, 0x71, 0x65, 0};

	printf("%s\n", buf);
	bdecode_node e;
	error_code ec;
	int ret = bdecode((char*)buf, (char*)buf + sizeof(buf), e, ec);
	TEST_CHECK(ret == -1);
}

Project: libtorrent

TORRENT_TEST(dict)
{
	char b[] = "d1:ai12453e1:b3:aaa1:c3:bbb1:X10:0123456789e";
	bdecode_node e;
	error_code ec;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec);
	TEST_EQUAL(ret, 0);
	printf("%s\n", print_entry(e).c_str());
	std::pair<const char*, int> section = e.data_section();
	TEST_CHECK(std::memcmp(b, section.first, section.second) == 0);
	TEST_CHECK(section.second == sizeof(b) - 1);
	TEST_CHECK(e.type() == bdecode_node::dict_t);
	TEST_CHECK(e.dict_size() == 4);
	TEST_CHECK(e.dict_find("a").type() == bdecode_node::int_t);
	TEST_CHECK(e.dict_find("a").int_value() == 12453);
	TEST_CHECK(e.dict_find("b").type() == bdecode_node::string_t);
	TEST_CHECK(e.dict_find("b").string_value() == std::string("aaa"));
	TEST_CHECK(e.dict_find("b").string_length() == 3);
	TEST_CHECK(e.dict_find("c").type() == bdecode_node::string_t);
	TEST_CHECK(e.dict_find("c").string_value() == std::string("bbb"));
	TEST_CHECK(e.dict_find("c").string_length() == 3);
	TEST_CHECK(e.dict_find_string_value("X") == "0123456789");
}

Project: libtorrent

TORRENT_TEST(interior_minus_int)
{
	char b[] = "i35412-5633e";
	bdecode_node e;
	error_code ec;
	int pos;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(pos, 6);
	TEST_EQUAL(ec,  error_code(bdecode_errors::expected_digit));
	printf("%s\n", print_entry(e).c_str());
}

Project: libtorrent

TORRENT_TEST(minus_int)
{
	char b[] = "i-e";
	bdecode_node e;
	error_code ec;
	int pos;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(pos, 2);
	TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit));
	printf("%s\n", print_entry(e).c_str());
}

Project: libtorrent

TORRENT_TEST(negative_length_prefix)
{
	char b[] = "-10:foobar";
	bdecode_node e;
	error_code ec;
	int pos;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(pos, 0);
	TEST_EQUAL(ec, error_code(bdecode_errors::expected_value));
	printf("%s\n", print_entry(e).c_str());
}

Project: libtorrent

TORRENT_TEST(bdecode_error)
{
	error_code ec(bdecode_errors::overflow);
	TEST_EQUAL(ec.message(), "integer overflow");
	TEST_EQUAL(ec.category().name(), std::string("bdecode error"));
	ec.assign(5434, get_bdecode_category());
	TEST_EQUAL(ec.message(), "Unknown error");
}

Project: libtorrent

TORRENT_TEST(premature_e)
{
	char b[] = "e";
	bdecode_node e;
	error_code ec;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
	printf("%s\n", print_entry(e).c_str());
}

Project: libtorrent

TORRENT_TEST(int_overflow2)
{
	char b[] = "i184467440737095516154e";
	bdecode_node e;
	error_code ec;
	int pos;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(pos, 22);
	TEST_EQUAL(ec,  error_code(bdecode_errors::overflow));
	printf("%s\n", print_entry(e).c_str());
}

Project: libtorrent

TORRENT_TEST(dict_ints)
{
	std::string buf;
	buf += "d";
	for (int i = 0; i < 1000; ++i)
	{
		char tmp[30];
		snprintf(tmp, sizeof(tmp), "4:%04di%de", i, i);
		buf += tmp;
	}
	buf += "e";

	printf("%s\n", buf.c_str());
	bdecode_node e;
	error_code ec;
	int ret = bdecode((char*)&buf[0], (char*)&buf[0] + buf.size(), e, ec);
	TEST_EQUAL(ret, 0);
	TEST_EQUAL(e.type(), bdecode_node::dict_t);
	TEST_EQUAL(e.dict_size(), 1000);
	for (int i = 0; i < 1000; ++i)
	{
		char tmp[30];
		snprintf(tmp, sizeof(tmp), "%04d", i);
		TEST_EQUAL(e.dict_find_int_value(tmp), i);
	}
}

Project: libtorrent

TORRENT_TEST(dict_find_funs2)
{
	// a: int
	// b: string
	// c: list
	// d: dict
	char b[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee";
	bdecode_node e;
	error_code ec;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec);
	TEST_EQUAL(ret, 0);
	printf("%s\n", print_entry(e).c_str());

	TEST_EQUAL(e.type(), bdecode_node::dict_t);

	// try finding the last item in a dict (to skip all the other ones)
	TEST_EQUAL(e.dict_find("d").type(), bdecode_node::dict_t);
	TEST_EQUAL(e.dict_find(std::string("d")).type(), bdecode_node::dict_t);
}

Project: libtorrent

	std::string print_entry(bdecode_node const& e
		, bool single_line, int indent)
	{
		char indent_str[200];
		using std::memset;
		memset(indent_str, ' ', 200);
		indent_str[0] = ',';
		indent_str[1] = '\n';
		indent_str[199] = 0;
		if (indent < 197 && indent >= 0) indent_str[indent+2] = 0;
		std::string ret;
		switch (e.type())
		{
			case bdecode_node::none_t: return "none";
			case bdecode_node::int_t:
			{
				char str[100];
				snprintf(str, sizeof(str), "%" PRId64, e.int_value());
				return str;
			}
			case bdecode_node::string_t:
			{
				print_string(ret, e.string_ptr(), e.string_length(), single_line);
				return ret;
			}
			case bdecode_node::list_t:
			{
				ret += '[';
				bool one_liner = line_longer_than(e, 200) != -1 || single_line;

				if (!one_liner) ret += indent_str + 1;
				for (int i = 0; i < e.list_size(); ++i)
				{
					if (i == 0 && one_liner) ret += " ";
					ret += print_entry(e.list_at(i), single_line, indent + 2);
					if (i < e.list_size() - 1) ret += (one_liner?", ":indent_str);
					else ret += (one_liner?" ":indent_str+1);
				}
				ret += "]";
				return ret;
			}
			case bdecode_node::dict_t:
			{
				ret += "{";
				bool one_liner = line_longer_than(e, 200) != -1 || single_line;

				if (!one_liner) ret += indent_str+1;
				for (int i = 0; i < e.dict_size(); ++i)
				{
					if (i == 0 && one_liner) ret += " ";
					std::pair<std::string, bdecode_node> ent = e.dict_at(i);
					print_string(ret, ent.first.c_str(), ent.first.size(), true);
					ret += ": ";
					ret += print_entry(ent.second, single_line, indent + 2);
					if (i < e.dict_size() - 1) ret += (one_liner?", ":indent_str);
					else ret += (one_liner?" ":indent_str+1);
				}
				ret += "}";
				return ret;
			}
		}
		return ret;
	}

Project: libtorrent

	char const* parse_int(char const* start, char const* end, char delimiter
		, boost::int64_t& val, bdecode_errors::error_code_enum& ec)
	{
		while (start < end && *start != delimiter)
		{
			if (!numeric(*start))
			{
				ec = bdecode_errors::expected_digit;
				return start;
			}
			if (val > (std::numeric_limits<boost::int64_t>::max)() / 10)
			{
				ec = bdecode_errors::overflow;
				return start;
			}
			val *= 10;
			int digit = *start - '0';
			if (val > (std::numeric_limits<boost::int64_t>::max)() - digit)
			{
				ec = bdecode_errors::overflow;
				return start;
			}
			val += digit;
			++start;
		}
		if (*start != delimiter)
			ec = bdecode_errors::expected_colon;
		return start;
	}

Project: libtorrent

TORRENT_TEST(parse_int)
{
	char b[] = "1234567890e";
	boost::int64_t val = 0;
	bdecode_errors::error_code_enum ec;
	char const* e = parse_int(b, b + sizeof(b)-1, 'e', val, ec);
	TEST_EQUAL(val, 1234567890);
	TEST_EQUAL(e, b + sizeof(b) - 2);
}

Project: libtorrent

TORRENT_TEST(dict_find_funs)
{
	// a: int
	// b: string
	// c: list
	// d: dict
	char b[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee";
	bdecode_node e;
	error_code ec;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec);
	TEST_EQUAL(ret, 0);
	printf("%s\n", print_entry(e).c_str());

	TEST_EQUAL(e.type(), bdecode_node::dict_t);

	// dict_find_int* 

	TEST_EQUAL(e.dict_find_int_value("a"), 1);
	TEST_EQUAL(e.dict_find_int("a").type(), bdecode_node::int_t);
	TEST_EQUAL(e.dict_find_int_value("b", -10), -10);
	TEST_EQUAL(e.dict_find_int_value("x", -10), -10);
	TEST_EQUAL(e.dict_find_int("b").type(), bdecode_node::none_t);
	TEST_EQUAL(e.dict_find_int("x").type(), bdecode_node::none_t);

	// dict_find_string*

	TEST_EQUAL(e.dict_find_string_value("b"), "foo");
	TEST_EQUAL(e.dict_find_string("b").type(), bdecode_node::string_t);
	TEST_EQUAL(e.dict_find_string_value("c", "blah"), "blah");
	TEST_EQUAL(e.dict_find_string_value("x", "blah"), "blah");
	TEST_EQUAL(e.dict_find_string("c").type(), bdecode_node::none_t);
	TEST_EQUAL(e.dict_find_string("x").type(), bdecode_node::none_t);

	// dict_find_list

	TEST_CHECK(e.dict_find_list("c"));
	TEST_EQUAL(e.dict_find_list("c").list_size(), 2);
	TEST_EQUAL(e.dict_find_list("c").list_int_value_at(0), 1);
	TEST_EQUAL(e.dict_find_list("c").list_int_value_at(1), 2);
	TEST_CHECK(!e.dict_find_list("d"));

	// dict_find_dict

	TEST_CHECK(e.dict_find_dict("d"));
	TEST_EQUAL(e.dict_find_dict("d").dict_find_int_value("x"), 1);
	TEST_EQUAL(e.dict_find_dict("d").dict_find_int_value("y", -10), -10);
	TEST_CHECK(!e.dict_find_dict("c"));

	// variants taking std::string
	TEST_EQUAL(e.dict_find_dict(std::string("d")).dict_find_int_value("x"), 1);
	TEST_CHECK(!e.dict_find_dict(std::string("c")));
	TEST_CHECK(!e.dict_find_dict(std::string("x")));

	TEST_EQUAL(e.dict_size(), 4);
	TEST_EQUAL(e.dict_size(), 4);

	// dict_at

	TEST_EQUAL(e.dict_at(0).first, "a");
	TEST_EQUAL(e.dict_at(0).second.int_value(), 1);
	TEST_EQUAL(e.dict_at(1).first, "b");
	TEST_EQUAL(e.dict_at(1).second.string_value(), "foo");
	TEST_EQUAL(e.dict_at(2).first, "c");
	TEST_EQUAL(e.dict_at(2).second.type(), bdecode_node::list_t);
	TEST_EQUAL(e.dict_at(3).first, "d");
	TEST_EQUAL(e.dict_at(3).second.type(), bdecode_node::dict_t);
}

Project: libtorrent

	bdecode_node bdecode_node::dict_find_string(char const* key) const
	{
		bdecode_node ret = dict_find(key);
		if (ret.type() == bdecode_node::string_t)
			return ret;
		return bdecode_node();
	}

Project: libtorrent

TORRENT_TEST(dict_null_key)
{
	char b[] = "d3:a\0bi1ee";
	bdecode_node e;
	error_code ec;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec);
	TEST_CHECK(ret == 0);
	TEST_CHECK(e.dict_size() == 1);
	bdecode_node d = e.dict_find(std::string("a\0b", 3));
	TEST_EQUAL(d.type(), bdecode_node::int_t);
	TEST_EQUAL(d.int_value(), 1);
}

Project: libtorrent

TORRENT_TEST(parse_int_overflow)
{
	char b[] = "9223372036854775808:";
	boost::int64_t val = 0;
	bdecode_errors::error_code_enum ec;
	char const* e = parse_int(b, b + sizeof(b)-1, ':', val, ec);
	TEST_EQUAL(ec, bdecode_errors::overflow);
	TEST_EQUAL(e, b + 18);
}

Project: libtorrent

	bdecode_node bdecode_node::dict_find(char const* key) const
	{
		TORRENT_ASSERT(type() == dict_t);

		bdecode_token const* tokens = m_root_tokens;
	
		// this is the first item
		int token = m_token_idx + 1;

		while (tokens[token].type != bdecode_token::end)
		{
			bdecode_token const& t = tokens[token];
			TORRENT_ASSERT(t.type == bdecode_token::string);
			int size = m_root_tokens[token + 1].offset - t.offset - t.start_offset();
			if (string_equal(key, m_buffer + t.offset + t.start_offset(), size))
			{
				// skip key
				token += t.next_item;
				TORRENT_ASSERT(tokens[token].type != bdecode_token::end);
			
				return bdecode_node(tokens, m_buffer, m_buffer_size, token);
			}

			// skip key
			token += t.next_item;
			TORRENT_ASSERT(tokens[token].type != bdecode_token::end);

			// skip value
			token += tokens[token].next_item;
		}

		return bdecode_node();
	}

Project: libtorrent

TORRENT_TEST(item_limit)
{
	char b[10240];
	b[0] = 'l';
	int i = 1;
	for (i = 1; i < 10239; i += 2)
		memcpy(&b[i], "0:", 2);
	b[i] = 'e';

	bdecode_node e;
	error_code ec;
	int ret = bdecode(b, b + i + 1, e, ec, NULL, 1000, 1000);
	TEST_CHECK(ret != 0);
	TEST_EQUAL(ec, error_code(bdecode_errors::limit_exceeded
		, get_bdecode_category()));
}

Project: libtorrent

	bdecode_node bdecode_node::dict_find_dict(char const* key) const
	{
		bdecode_node ret = dict_find(key);
		if (ret.type() == bdecode_node::dict_t)
			return ret;
		return bdecode_node();
	}

Project: libtorrent

	const char* bdecode_error_category::name() const BOOST_SYSTEM_NOEXCEPT
	{
		return "bdecode error";
	}

Project: libtorrent

TORRENT_TEST(partial_string)
{
	char b[] = "100:..";

	bdecode_node e;
	error_code ec;
	int pos;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(pos, 3);
	TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
	printf("%s\n", print_entry(e).c_str());
}

Project: libtorrent

TORRENT_TEST(dict_key_novalue)
{
	char b[] = "d1:ai1e1:be";
	bdecode_node e;
	error_code ec;
	int pos;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(pos, 10);
	TEST_EQUAL(ec, error_code(bdecode_errors::expected_value));
	printf("%s\n", print_entry(e).c_str());
}

Project: libtorrent

TORRENT_TEST(clear)
{
	char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee";
	char b2[] = "li1ei2ee";

	bdecode_node e;
	error_code ec;
	int ret = bdecode(b1, b1 + sizeof(b1)-1, e, ec);
	printf("%s\n", print_entry(e).c_str());
	TEST_EQUAL(ret, 0);
	TEST_EQUAL(e.type(), bdecode_node::dict_t);
	TEST_EQUAL(e.dict_size(), 4);
	TEST_EQUAL(e.dict_at(1).first, "b");

	ret = bdecode(b2, b2 + sizeof(b2)-1, e, ec);
	printf("%s\n", print_entry(e).c_str());
	TEST_EQUAL(ret, 0);
	TEST_EQUAL(e.type(), bdecode_node::list_t);
	TEST_EQUAL(e.list_size(), 2);
	TEST_EQUAL(e.list_int_value_at(1), 2);
}

Project: libtorrent

	bdecode_node bdecode_node::dict_find_list(char const* key) const
	{
		bdecode_node ret = dict_find(key);
		if (ret.type() == bdecode_node::list_t)
			return ret;
		return bdecode_node();
	}

Project: libtorrent

TORRENT_TEST(dict_at)
{
	char b[] = "d3:fooi1e3:bari2ee";

	bdecode_node e;
	error_code ec;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec);
	TEST_EQUAL(ret, 0);

	TEST_EQUAL(e.type(), bdecode_node::dict_t);
	TEST_EQUAL(e.dict_size(), 2);
	TEST_EQUAL(e.dict_at(0).first, "foo");
	TEST_EQUAL(e.dict_at(0).second.type(), bdecode_node::int_t);
	TEST_EQUAL(e.dict_at(0).second.int_value(), 1);
	TEST_EQUAL(e.dict_at(1).first, "bar");
	TEST_EQUAL(e.dict_at(1).second.type(), bdecode_node::int_t);
	TEST_EQUAL(e.dict_at(1).second.int_value(), 2);
}

Project: libtorrent

	int bdecode_node::string_length() const
	{
		TORRENT_ASSERT(type() == string_t);
		bdecode_token const& t = m_root_tokens[m_token_idx];
		TORRENT_ASSERT(t.type == bdecode_token::string);
		return m_root_tokens[m_token_idx + 1].offset - t.offset - t.start_offset();
	}

Project: libtorrent

TORRENT_TEST(copy_root)
{
	char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee";

	bdecode_node e1;
	error_code ec;
	int ret = bdecode(b1, b1 + sizeof(b1)-1, e1, ec);
	TEST_EQUAL(ret, 0);
	TEST_EQUAL(e1.type(), bdecode_node::dict_t);
	printf("%s\n", print_entry(e1).c_str());

	bdecode_node e2(e1);
	bdecode_node e3;
	e3 = e1;

	e1.clear();

	TEST_EQUAL(e2.type(), bdecode_node::dict_t);
	TEST_EQUAL(e2.dict_size(), 4);
	TEST_EQUAL(e2.dict_at(1).first, "b");

	TEST_EQUAL(e3.type(), bdecode_node::dict_t);
	TEST_EQUAL(e3.dict_size(), 4);
	TEST_EQUAL(e3.dict_at(1).first, "b");
}

Project: libtorrent

TORRENT_TEST(nodigit_int)
{
	char b[] = "ie";
	bdecode_node e;
	error_code ec;
	int pos;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(pos, 1);
	TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit));
	printf("%s\n", print_entry(e).c_str());
}

Project: libtorrent

	std::string bdecode_node::list_string_value_at(int i
		, char const* default_val)
	{
		bdecode_node n = list_at(i);
		if (n.type() != bdecode_node::string_t) return default_val;
		return n.string_value();
	}

Project: libtorrent

TORRENT_TEST(partial_parse)
{
	char b[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1-eee";

	bdecode_node e;
	error_code ec;
	int pos;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(pos, 35);
	TEST_EQUAL(e.type(), bdecode_node::dict_t);

	printf("%s\n", print_entry(e).c_str());

	TEST_EQUAL(print_entry(e), "{ 'a': 1, 'b': 'foo', 'c': [ 1, 2 ], 'd': { 'x': {} } }");
}

Project: libtorrent

	boost::int64_t bdecode_node::dict_find_int_value(char const* key
		, boost::int64_t default_val) const
	{
		bdecode_node n = dict_find(key);
		if (n.type() != bdecode_node::int_t) return default_val;
		return n.int_value();
	}

Project: libtorrent

	bool string_equal(char const* str1, char const* str2, int len2)
	{
		while (len2 > 0)
		{
			if (*str1 != *str2) return false;
			if (*str1 == 0) return false;
			++str1;
			++str2;
			--len2;
		}
		return *str1 == 0;
	}

Project: libtorrent

TORRENT_TEST(list_at_funs)
{
	// int
	// string
	// list
	// dict
	char b[] = "li1e3:fooli1ei2eed1:xi1eee";
	bdecode_node e;
	error_code ec;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec);
	TEST_EQUAL(ret, 0);
	printf("%s\n", print_entry(e).c_str());

	TEST_EQUAL(e.type(), bdecode_node::list_t);

	TEST_EQUAL(e.list_int_value_at(0), 1);
	// make sure default values work
	TEST_EQUAL(e.list_int_value_at(1, -10), -10);

	TEST_EQUAL(e.list_string_value_at(1), "foo");
	// make sure default values work
	TEST_EQUAL(e.list_string_value_at(2, "blah"), "blah");

	TEST_EQUAL(e.list_at(2).type(), bdecode_node::list_t);
	TEST_EQUAL(e.list_at(2).list_size(), 2);
	TEST_EQUAL(e.list_at(2).list_int_value_at(0), 1);
	TEST_EQUAL(e.list_at(2).list_int_value_at(1), 2);

	TEST_EQUAL(e.list_at(3).type(), bdecode_node::dict_t);
	TEST_EQUAL(e.list_at(3).dict_size(), 1);
	TEST_EQUAL(e.list_at(3).dict_find_int_value("x"), 1);
	TEST_EQUAL(e.list_at(3).dict_find_int_value("y", -10), -10);

	TEST_EQUAL(e.list_size(), 4);
	TEST_EQUAL(e.list_size(), 4);
}

Project: libtorrent

TORRENT_TEST(depth_limit)
{
	char b[2048];
	for (int i = 0; i < 1024; ++i)
		b[i]= 'l';

	for (int i = 1024; i < 2048; ++i)
		b[i]= 'e';

	// 1024 levels nested lists

	bdecode_node e;
	error_code ec;
	int ret = bdecode(b, b + sizeof(b), e, ec, NULL, 100);
	TEST_CHECK(ret != 0);
	TEST_EQUAL(ec, error_code(bdecode_errors::depth_exceeded
		, get_bdecode_category()));
}

Project: libtorrent

	void escape_string(std::string& ret, char const* str, int len)
	{
		for (int i = 0; i < len; ++i)
		{
			if (str[i] >= 32 && str[i] < 127)
			{
				ret += str[i];
			}
			else
			{
				char tmp[5];
				snprintf(tmp, sizeof(tmp), "\\x%02x", boost::uint8_t(str[i]));
				ret += tmp;
			}
		}
	}

Project: libtorrent

	bdecode_node bdecode_node::dict_find_dict(std::string key) const
	{
		bdecode_node ret = dict_find(key);
		if (ret.type() == bdecode_node::dict_t)
			return ret;
		return bdecode_node();
	}

Project: libtorrent

		boost::system::error_code make_error_code(error_code_enum e)
		{
			return boost::system::error_code(e, bdecode_category());
		}

Project: libtorrent

TORRENT_TEST(64bit_int_negative)
{
	char b[] = "i-9223372036854775807e";
	bdecode_node e;
	error_code ec;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec);
	TEST_CHECK(ret == 0);
	printf("%s\n", print_entry(e).c_str());
	TEST_CHECK(e.int_value() == -9223372036854775807LL);
}

Project: libtorrent

	void bdecode_node::clear()
	{
		m_tokens.clear();
		m_root_tokens = NULL;
		m_token_idx = -1;
		m_size = -1;
		m_last_index = -1;
		m_last_token = -1;
	}

Project: libtorrent

	char const* bdecode_node::string_ptr() const
	{
		TORRENT_ASSERT(type() == string_t);
		bdecode_token const& t = m_root_tokens[m_token_idx];
		TORRENT_ASSERT(t.type == bdecode_token::string);
		return m_buffer + t.offset + t.start_offset();
	}

Project: libtorrent

TORRENT_TEST(unexpected_eof_int2)
{
	char b[] = "i10";

	bdecode_node e;
	error_code ec;
	int pos;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(pos, 3);
	TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
	printf("%s\n", print_entry(e).c_str());
}

Project: libtorrent

	bdecode_node::bdecode_node(bdecode_token const* tokens, char const* buf
		, int len, int idx)
		: m_root_tokens(tokens)
		, m_buffer(buf)
		, m_buffer_size(len)
		, m_token_idx(idx)
		, m_last_index(-1)
		, m_last_token(-1)
		, m_size(-1)
	{
		TORRENT_ASSERT(tokens != NULL);
		TORRENT_ASSERT(idx >= 0);
	}

Project: libtorrent

	boost::int64_t bdecode_node::list_int_value_at(int i
		, boost::int64_t default_val)
	{
		bdecode_node n = list_at(i);
		if (n.type() != bdecode_node::int_t) return default_val;
		return n.int_value();
	}

Project: libtorrent

TORRENT_TEST(partial_parse3)
{
	char b[] = "d1:ai1e1:b3:foo1:cli1ei2ee-1:dd1:xi1eee";

	bdecode_node e;
	error_code ec;
	int pos;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(pos, 26);
	TEST_EQUAL(e.type(), bdecode_node::dict_t);

	printf("%s\n", print_entry(e).c_str());

	TEST_EQUAL(print_entry(e), "{ 'a': 1, 'b': 'foo', 'c': [ 1, 2 ] }");
}

Project: libtorrent

TORRENT_TEST(swap_root_disjoint)
{
	char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee";
	char b2[] = "li1e3:fooli1ei2eed1:xi1eee";

	bdecode_node e1_root;
	bdecode_node e2;

	error_code ec;

	int ret = bdecode(b1, b1 + sizeof(b1)-1, e1_root, ec);
	TEST_EQUAL(ret, 0);
	ret = bdecode(b2, b2 + sizeof(b2)-1, e2, ec);
	TEST_EQUAL(ret, 0);

	bdecode_node e1 = e1_root.dict_find("d");

	std::string str1 = print_entry(e1);
	std::string str2 = print_entry(e2);
	TEST_EQUAL(e1.type(), bdecode_node::dict_t);
	TEST_EQUAL(e2.type(), bdecode_node::list_t);

	e1.swap(e2);

	TEST_EQUAL(e1.type(), bdecode_node::list_t);
	TEST_EQUAL(e2.type(), bdecode_node::dict_t);
	TEST_EQUAL(print_entry(e1), str2);
	TEST_EQUAL(print_entry(e2), str1);

	// swap back
	e1.swap(e2);

	TEST_EQUAL(e1.type(), bdecode_node::dict_t);
	TEST_EQUAL(e2.type(), bdecode_node::list_t);
	TEST_EQUAL(print_entry(e1), str1);
	TEST_EQUAL(print_entry(e2), str2);
}

Project: libtorrent

	int bdecode_node::dict_size() const
	{
		TORRENT_ASSERT(type() == dict_t);
		TORRENT_ASSERT(m_token_idx != -1);

		if (m_size != -1) return m_size;

		bdecode_token const* tokens = m_root_tokens;
		TORRENT_ASSERT(tokens[m_token_idx].type == bdecode_token::dict);

		// this is the first item
		int token = m_token_idx + 1;
		int ret = 0;

		if (m_last_index != -1)
		{
			ret = m_last_index * 2;
			token = m_last_token;
		}

		while (tokens[token].type != bdecode_token::end)
		{
			token += tokens[token].next_item;
			++ret;
		}

		// a dictionary must contain full key-value pairs. which means
		// the number of entries is divisible by 2
		TORRENT_ASSERT((ret % 2) == 0);

		// each item is one key and one value, so divide by 2
		ret /= 2;

		m_size = ret;

		return ret;
	}

Project: libtorrent

	int bdecode_node::list_size() const
	{
		TORRENT_ASSERT(type() == list_t);

		if (m_size != -1) return m_size;

		// make sure this is a list.
		bdecode_token const* tokens = m_root_tokens;
		TORRENT_ASSERT(tokens[m_token_idx].type == bdecode_token::list);

		// this is the first item
		int token = m_token_idx + 1;
		int ret = 0;
		
		// do we have a lookup cached?
		if (m_last_index != -1)
		{
			token = m_last_token;
			ret = m_last_index;
		}
		while (tokens[token].type != bdecode_token::end)
		{
			token += tokens[token].next_item;
			++ret;
		}

		m_size = ret;

		return ret;
	}

Project: libtorrent

	bdecode_node& bdecode_node::operator=(bdecode_node const& n)
	{
		m_tokens = n.m_tokens;
		m_root_tokens = n.m_root_tokens;
		m_buffer = n.m_buffer;
		m_buffer_size = n.m_buffer_size;
		m_token_idx = n.m_token_idx;
		m_last_index = n.m_last_index;
		m_last_token = n.m_last_token;
		m_size = n.m_size;
		if (!m_tokens.empty())
		{
			// if this is a root, make the token pointer
			// point to our storage
			m_root_tokens = &m_tokens[0];
		}
		return *this;
	}

Project: libtorrent

	bdecode_node::bdecode_node(bdecode_node const& n)
		: m_tokens(n.m_tokens)
		, m_root_tokens(n.m_root_tokens)
		, m_buffer(n.m_buffer)
		, m_buffer_size(n.m_buffer_size)
		, m_token_idx(n.m_token_idx)
		, m_last_index(n.m_last_index)
		, m_last_token(n.m_last_token)
		, m_size(n.m_size)
	{
		(*this) = n;
	}

Project: libtorrent

	boost::int64_t bdecode_node::int_value() const
	{
		TORRENT_ASSERT(type() == int_t);
		bdecode_token const& t = m_root_tokens[m_token_idx];
		int size = m_root_tokens[m_token_idx + 1].offset - t.offset;
		TORRENT_ASSERT(t.type == bdecode_token::integer);
	
		// +1 is to skip the 'i'
		char const* ptr = m_buffer + t.offset + 1;
		boost::int64_t val = 0;
		bool negative = false;
		if (*ptr == '-') negative = true;
		bdecode_errors::error_code_enum ec = bdecode_errors::no_error;
		parse_int(ptr + negative
			, ptr + size, 'e', val, ec);
		if (ec) return 0;
		if (negative) val = -val;
		return val;
	}

Project: libtorrent

	void bdecode_node::reserve(int tokens)
	{ m_tokens.reserve(tokens); }

Project: libtorrent

	boost::system::error_category& get_bdecode_category()
	{ return bdecode_category(); }

Project: libtorrent

TORRENT_TEST(unepected_eof)
{
	char b[] = "l2:.."; // expected terminating 'e'

	bdecode_node e;
	error_code ec;
	int pos;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(pos, 5);
	TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
	printf("%s\n", print_entry(e).c_str());
}

Project: libtorrent

TORRENT_TEST(int_overflow)
{
	char b[] = "i18446744073709551615e";
	bdecode_node e;
	error_code ec;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec);
	TEST_EQUAL(ret, 0);
	printf("%s\n", print_entry(e).c_str());
	// the lazy aspect makes this overflow when asking for
	// the value. turning it to zero.
	TEST_EQUAL(e.int_value(), 0);
}

Project: libtorrent

TORRENT_TEST(unexpected_eof_int)
{
	char b[] = "i";

	bdecode_node e;
	error_code ec;
	int pos;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(pos, 1);
	TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
	printf("%s\n", print_entry(e).c_str());
}

Project: libtorrent

	int line_longer_than(bdecode_node const& e, int limit)
	{
		int line_len = 0;
		switch (e.type())
		{
		case bdecode_node::list_t:
			line_len += 4;
			if (line_len > limit) return -1;
			for (int i = 0; i < e.list_size(); ++i)
			{
				int ret = line_longer_than(e.list_at(i), limit - line_len);
				if (ret == -1) return -1;
				line_len += ret + 2;
			}
			break;
		case bdecode_node::dict_t:
			line_len += 4;
			if (line_len > limit) return -1;
			for (int i = 0; i < e.dict_size(); ++i)
			{
				line_len += 4 + e.dict_at(i).first.size();
				if (line_len > limit) return -1;
				int ret = line_longer_than(e.dict_at(i).second, limit - line_len);
				if (ret == -1) return -1;
				line_len += ret + 1;
			}
			break;
		case bdecode_node::string_t:
			line_len += 3 + e.string_length();
			break;
		case bdecode_node::int_t:
		{
			boost::int64_t val = e.int_value();
			while (val > 0)
			{
				++line_len;
				val /= 10;
			}
			line_len += 2;
		}
		break;
		case bdecode_node::none_t:
			line_len += 4;
			break;
		}

		if (line_len > limit) return -1;
		return line_len;
	}

Project: libtorrent

TORRENT_TEST(print_entry2)
{
	char b[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee";
	bdecode_node e;
	error_code ec;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec);
	TEST_EQUAL(ret, 0);
	printf("%s\n", print_entry(e).c_str());

	TEST_EQUAL(print_entry(e), "{ 'a': 1, 'b': 'foo', 'c': [ 1, 2 ], 'd': { 'x': 1 } }");
}

Project: libtorrent

TORRENT_TEST(unepected_eof2)
{
	char b[] = "l2:..0"; // expected ':' delimiter instead of EOF

	bdecode_node e;
	error_code ec;
	int pos;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(pos, 6);
	TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
	printf("%s\n", print_entry(e).c_str());
}

Project: libtorrent

	int bdecode(char const* start, char const* end, bdecode_node& ret
		, error_code& ec, int* error_pos, int depth_limit, int token_limit)
	{
		ec.clear();
		ret.clear();

		if (end - start > bdecode_token::max_offset)
		{
			if (error_pos) *error_pos = 0;
			ec = bdecode_errors::limit_exceeded;
			return -1;
		}

		// this is the stack of bdecode_token indices, into m_tokens.
		// sp is the stack pointer, as index into the array, stack
		int sp = 0;
		stack_frame* stack = TORRENT_ALLOCA(stack_frame, depth_limit);

		char const* const orig_start = start;

		if (start == end)
			TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof);

		while (start <= end)
		{
			if (start >= end) TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof);

			if (sp >= depth_limit)
				TORRENT_FAIL_BDECODE(bdecode_errors::depth_exceeded);

			--token_limit;
			if (token_limit < 0)
				TORRENT_FAIL_BDECODE(bdecode_errors::limit_exceeded);

			// look for a new token
			const char t = *start;

			const int current_frame = sp;

			// if we're currently parsing a dictionary, assert that
			// every other node is a string.
			if (current_frame > 0
				&& ret.m_tokens[stack[current_frame-1].token].type == bdecode_token::dict)
			{
				if (stack[current_frame-1].state == 0)
				{
					// the current parent is a dict and we are parsing a key.
					// only allow a digit (for a string) or 'e' to terminate
					if (!numeric(t) && t != 'e')
						TORRENT_FAIL_BDECODE(bdecode_errors::expected_digit);
				}
			}

			switch (t)
			{
				case 'd':
					stack[sp++] = ret.m_tokens.size();
					// we push it into the stack so that we know where to fill
					// in the next_node field once we pop this node off the stack.
					// i.e. get to the node following the dictionary in the buffer
					ret.m_tokens.push_back(bdecode_token(start - orig_start
						, bdecode_token::dict));
					++start;
					break;
				case 'l':
					stack[sp++] = ret.m_tokens.size();
					// we push it into the stack so that we know where to fill
					// in the next_node field once we pop this node off the stack.
					// i.e. get to the node following the list in the buffer
					ret.m_tokens.push_back(bdecode_token(start - orig_start
						, bdecode_token::list));
					++start;
					break;
				case 'i':
				{
					char const* int_start = start;
					bdecode_errors::error_code_enum e = bdecode_errors::no_error;
					// +1 here to point to the first digit, rather than 'i'
					start = check_integer(start + 1, end, e);
					if (e)
					{
						// in order to gracefully terminate the tree,
						// make sure the end of the previous token is set correctly
						if (error_pos) *error_pos = start - orig_start;
						error_pos = NULL;
						start = int_start;
						TORRENT_FAIL_BDECODE(e);
					}
					ret.m_tokens.push_back(bdecode_token(int_start - orig_start
						, 1, bdecode_token::integer, 1));
					TORRENT_ASSERT(*start == 'e');

					// skip 'e'
					++start;
					break;
				}
				case 'e':
				{
					// this is the end of a list or dict
					if (sp == 0)
						TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof);

					if (sp > 0
						&& ret.m_tokens[stack[sp-1].token].type == bdecode_token::dict
						&& stack[sp-1].state == 1)
					{
						// this means we're parsing a dictionary and about to parse a
						// value associated with a key. Instead, we got a termination
						TORRENT_FAIL_BDECODE(bdecode_errors::expected_value);
					}

					// insert the end-of-sequence token
					ret.m_tokens.push_back(bdecode_token(start - orig_start, 1
						, bdecode_token::end));

					// and back-patch the start of this sequence with the offset
					// to the next token we'll insert
					int top = stack[sp-1].token;
					// subtract the token's own index, since this is a relative
					// offset
					if (ret.m_tokens.size() - top > bdecode_token::max_next_item)
						TORRENT_FAIL_BDECODE(bdecode_errors::limit_exceeded);

					ret.m_tokens[top].next_item = ret.m_tokens.size() - top;

					// and pop it from the stack.
					--sp;
					++start;
					break;
				}
				default:
				{
					// this is the case for strings. The start character is any
					// numeric digit
					if (!numeric(t))
						TORRENT_FAIL_BDECODE(bdecode_errors::expected_value);

					boost::int64_t len = t - '0';
					char const* str_start = start;
					++start;
					if (start >= end) TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof);
					bdecode_errors::error_code_enum e = bdecode_errors::no_error;
					start = parse_int(start, end, ':', len, e);
					if (e)
						TORRENT_FAIL_BDECODE(e);

					// remaining buffer size excluding ':'
					const ptrdiff_t buff_size = end - start - 1;
					if (len > buff_size)
						TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof);
					if (len < 0)
						TORRENT_FAIL_BDECODE(bdecode_errors::overflow);

					// skip ':'
					++start;
					if (start >= end) TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof);

					// the bdecode_token only has 8 bits to keep the header size
					// in. If it overflows, fail!
					if (start - str_start - 2 > detail::bdecode_token::max_header)
						TORRENT_FAIL_BDECODE(bdecode_errors::limit_exceeded);

					ret.m_tokens.push_back(bdecode_token(str_start - orig_start
						, 1, bdecode_token::string, start - str_start));
					start += len;
					break;
				}
			}

			if (current_frame > 0
				&& ret.m_tokens[stack[current_frame-1].token].type == bdecode_token::dict)
			{
				// the next item we parse is the opposite
				stack[current_frame-1].state = ~stack[current_frame-1].state;
			}

			// this terminates the top level node, we're done!
			if (sp == 0) break;
		}

done:

		// if parse failed, sp will be greater than 1
		// unwind the stack by inserting terminator to make whatever we have
		// so far valid
		while (sp > 0) {
			TORRENT_ASSERT(ec);
			--sp;

			// we may need to insert a dummy token to properly terminate the tree,
			// in case we just parsed a key to a dict and failed in the value
			if (ret.m_tokens[stack[sp].token].type == bdecode_token::dict
				&& stack[sp].state == 1)
			{
				// insert an empty dictionary as the value
				ret.m_tokens.push_back(bdecode_token(start - orig_start
					, 2, bdecode_token::dict));
				ret.m_tokens.push_back(bdecode_token(start - orig_start
					, bdecode_token::end));
			}

			int top = stack[sp].token;
			TORRENT_ASSERT(ret.m_tokens.size() - top <= bdecode_token::max_next_item);
			ret.m_tokens[top].next_item = ret.m_tokens.size() - top;
			ret.m_tokens.push_back(bdecode_token(start - orig_start, 1, bdecode_token::end));
		}

		ret.m_tokens.push_back(bdecode_token(start - orig_start, 0
			, bdecode_token::end));

		ret.m_token_idx = 0;
		ret.m_buffer = orig_start;
		ret.m_buffer_size = start - orig_start;
		ret.m_root_tokens = &ret.m_tokens[0];

		return ec ? -1 : 0;
	}

Project: libtorrent

	std::pair<std::string, bdecode_node> bdecode_node::dict_at(int i) const
	{
		TORRENT_ASSERT(type() == dict_t);
		TORRENT_ASSERT(m_token_idx != -1);
	
		bdecode_token const* tokens = m_root_tokens;
		TORRENT_ASSERT(tokens[m_token_idx].type == bdecode_token::dict);

		int token = m_token_idx + 1;
		int item = 0;

		// do we have a lookup cached?
		if (m_last_index <= i && m_last_index != -1)
		{
			token = m_last_token;
			item = m_last_index;
		}

		while (item < i)
		{
			TORRENT_ASSERT(tokens[token].type == bdecode_token::string);

			// skip the key
			token += tokens[token].next_item;
			TORRENT_ASSERT(tokens[token].type != bdecode_token::end);

			// skip the value
			token += tokens[token].next_item;

			++item;

			// index 'i' out of range
			TORRENT_ASSERT(tokens[token].type != bdecode_token::end);
		}

		// there's no point in caching the first item
		if (i > 0)
		{
			m_last_token = token;
			m_last_index = i;
		}

		int value_token = token + tokens[token].next_item;
		TORRENT_ASSERT(tokens[token].type != bdecode_token::end);

		return std::make_pair(
			bdecode_node(tokens, m_buffer, m_buffer_size, token).string_value()
			, bdecode_node(tokens, m_buffer, m_buffer_size, value_token));
	}

Project: libtorrent

TORRENT_TEST(list)
{
	char b[] = "li12453e3:aaae";
	bdecode_node e;
	error_code ec;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec);
	TEST_CHECK(ret == 0);
	printf("%s\n", print_entry(e).c_str());
	std::pair<const char*, int> section = e.data_section();
	TEST_CHECK(std::memcmp(b, section.first, section.second) == 0);
	TEST_CHECK(section.second == sizeof(b) - 1);
	TEST_CHECK(e.type() == bdecode_node::list_t);
	TEST_CHECK(e.list_size() == 2);
	TEST_CHECK(e.list_at(0).type() == bdecode_node::int_t);
	TEST_CHECK(e.list_at(1).type() == bdecode_node::string_t);
	TEST_CHECK(e.list_at(0).int_value() == 12453);
	TEST_CHECK(e.list_at(1).string_value() == std::string("aaa"));
	TEST_CHECK(e.list_at(1).string_length() == 3);
	section = e.list_at(1).data_section();
	TEST_CHECK(std::memcmp("3:aaa", section.first, section.second) == 0);
	TEST_CHECK(section.second == 5);
}

Project: libtorrent

TORRENT_TEST(string_ptr)
{
	char b[] = "l3:fooe";

	bdecode_node e;
	error_code ec;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec);
	TEST_EQUAL(ret, 0);

	TEST_EQUAL(e.type(), bdecode_node::list_t);
	TEST_EQUAL(e.list_size(), 1);
	TEST_EQUAL(e.list_at(0).type(), bdecode_node::string_t);
	TEST_EQUAL(e.list_at(0).string_ptr(), b + 3);
	TEST_EQUAL(e.list_at(0).string_length(), 3);
}

Project: libtorrent

	std::string bdecode_node::dict_find_string_value(char const* key
		, char const* default_value) const
	{
		bdecode_node n = dict_find(key);
		if (n.type() != bdecode_node::string_t) return default_value;
		return n.string_value();
	}

Project: libtorrent

	bdecode_node::bdecode_node()
		: m_root_tokens(0)
		, m_buffer(NULL)
		, m_buffer_size(0)
		, m_token_idx(-1)
		, m_last_index(-1)
		, m_last_token(-1)
		, m_size(-1)
	{}

Project: libtorrent

TORRENT_TEST(swap_root)
{
	char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee";

	bdecode_node e1;
	bdecode_node e2;

	error_code ec;

	int ret = bdecode(b1, b1 + sizeof(b1)-1, e1, ec);
	TEST_EQUAL(ret, 0);

	e2 = e1.dict_find("c").list_at(0);

	std::string str1 = print_entry(e1);
	std::string str2 = print_entry(e2);
	TEST_EQUAL(e1.type(), bdecode_node::dict_t);
	TEST_EQUAL(e2.type(), bdecode_node::int_t);
	printf("%s\n", print_entry(e1).c_str());

	e1.swap(e2);

	TEST_EQUAL(e1.type(), bdecode_node::int_t);
	TEST_EQUAL(e2.type(), bdecode_node::dict_t);
	TEST_EQUAL(print_entry(e1), str2);
	TEST_EQUAL(print_entry(e2), str1);
	printf("%s\n", print_entry(e1).c_str());

	// swap back
	e1.swap(e2);

	TEST_EQUAL(e1.type(), bdecode_node::dict_t);
	TEST_EQUAL(e2.type(), bdecode_node::int_t);
	TEST_EQUAL(print_entry(e1), str1);
	TEST_EQUAL(print_entry(e2), str2);
	printf("%s\n", print_entry(e1).c_str());
}

Project: libtorrent

	void bdecode_node::swap(bdecode_node& n)
	{
/*
		bool lhs_is_root = (m_root_tokens == &m_tokens);
		bool rhs_is_root = (n.m_root_tokens == &n.m_tokens);

		// swap is only defined between non-root nodes
		// and between root-nodes. They may not be mixed!
		// note that when swapping root nodes, all bdecode_node
		// entries that exist in those subtrees are invalidated!
		TORRENT_ASSERT(lhs_is_root == rhs_is_root);

		// if both are roots, m_root_tokens always point to
		// its own vector, and should not get swapped (the
		// underlying vectors are swapped already)
		if (!lhs_is_root && !rhs_is_root)
		{
			// if neither is a root, we just swap the pointers
			// to the token vectors, switching their roots
			std::swap(m_root_tokens, n.m_root_tokens);
		}
*/
		m_tokens.swap(n.m_tokens);
		std::swap(m_root_tokens, n.m_root_tokens);
		std::swap(m_buffer, n.m_buffer);
		std::swap(m_buffer_size, n.m_buffer_size);
		std::swap(m_token_idx, n.m_token_idx);
		std::swap(m_last_index, n.m_last_index);
		std::swap(m_last_token, n.m_last_token);
		std::swap(m_size, n.m_size);
	}

Project: libtorrent

	bdecode_node::operator bool() const
	{ return m_token_idx != -1; }

Project: libtorrent

	char const* check_integer(char const* start, char const* end
		, bdecode_errors::error_code_enum& e)
	{
		if (start == end)
		{
			e = bdecode_errors::unexpected_eof;
			return start;
		}

		if (*start == '-')
		{
			++start;
			if (start == end)
			{
				e = bdecode_errors::unexpected_eof;
				return start;
			}
		}

		int digits = 0;
		do
		{
			if (!numeric(*start))
			{
				e = bdecode_errors::expected_digit;
				break;
			}
			++start;
			++digits;

			if (start == end)
			{
				e = bdecode_errors::unexpected_eof;
				break;
			}
		}
		while (*start != 'e');

		if (digits > 20)
		{
			e = bdecode_errors::overflow;
		}

		return start;
	}

Project: libtorrent

TORRENT_TEST(overflow_length_prefix)
{
	char b[] = "18446744073709551615:foobar";
	bdecode_node e;
	error_code ec;
	int pos;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(pos, 19);
	TEST_EQUAL(ec,  error_code(bdecode_errors::overflow));
	printf("%s\n", print_entry(e).c_str());
}

Project: libtorrent

TORRENT_TEST(list_ints)
{
	std::string buf;
	buf += "l";
	for (int i = 0; i < 1000; ++i)
	{
		char tmp[20];
		snprintf(tmp, sizeof(tmp), "i%de", i);
		buf += tmp;
	}
	buf += "e";

	bdecode_node e;
	error_code ec;
	int ret = bdecode((char*)&buf[0], (char*)&buf[0] + buf.size(), e, ec);
	TEST_EQUAL(ret, 0);
	TEST_EQUAL(e.type(), bdecode_node::list_t);
	TEST_EQUAL(e.list_size(), 1000);
	for (int i = 0; i < 1000; ++i)
	{
		TEST_EQUAL(e.list_int_value_at(i), i);
	}
}

Project: libtorrent

TORRENT_TEST(dict_nonstring_key)
{
	char b[] = "di5e1:ae";
	bdecode_node e;
	error_code ec;
	int pos;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(pos, 1);
	TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit));
	printf("%s\n", print_entry(e).c_str());
}

Project: libtorrent

		stack_frame(int t): token(t), state(0) {}

Project: libtorrent

	bool numeric(char c) { return c >= '0' && c <= '9'; }

Project: libtorrent

	bdecode_node::type_t bdecode_node::type() const
	{
		if (m_token_idx == -1) return none_t;
		return static_cast<bdecode_node::type_t>(m_root_tokens[m_token_idx].type);
	}

Project: libtorrent

	bdecode_node bdecode_node::dict_find(std::string key) const
	{
		TORRENT_ASSERT(type() == dict_t);

		bdecode_token const* tokens = m_root_tokens;
	
		// this is the first item
		int token = m_token_idx + 1;

		while (tokens[token].type != bdecode_token::end)
		{
			bdecode_token const& t = tokens[token];
			TORRENT_ASSERT(t.type == bdecode_token::string);
			int size = m_root_tokens[token + 1].offset - t.offset - t.start_offset();
			if (int(key.size()) == size
				&& std::equal(key.c_str(), key.c_str() + size, m_buffer
					+ t.offset + t.start_offset()))
			{
				// skip key
				token += t.next_item;
				TORRENT_ASSERT(tokens[token].type != bdecode_token::end);
			
				return bdecode_node(tokens, m_buffer, m_buffer_size, token);
			}

			// skip key
			token += t.next_item;
			TORRENT_ASSERT(tokens[token].type != bdecode_token::end);

			// skip value
			token += tokens[token].next_item;
		}

		return bdecode_node();
	}

Project: libtorrent

	bdecode_node bdecode_node::non_owning() const
	{
		// if we're not a root, just return a copy of ourself
		if (m_tokens.empty()) return *this;

		// otherwise, return a reference to this node, but without
		// being an owning root node
		return bdecode_node(&m_tokens[0], m_buffer, m_buffer_size, m_token_idx);
	}

Project: libtorrent

	std::pair<char const*, int> bdecode_node::data_section() const
	{
		if (m_token_idx == -1) return std::make_pair(m_buffer, 0);

		TORRENT_ASSERT(m_token_idx != -1);
		bdecode_token const& t = m_root_tokens[m_token_idx];
		bdecode_token const& next = m_root_tokens[m_token_idx + t.next_item];
		return std::make_pair(m_buffer + t.offset, next.offset - t.offset);
	}

Project: libtorrent

TORRENT_TEST(unexpected_eof_dict_key)
{
	char b[] = "d1000:..e";

	bdecode_node e;
	error_code ec;
	int pos;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(pos, 5);
	TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
	printf("%s\n", print_entry(e).c_str());
}

Project: libtorrent

	void bdecode_node::switch_underlying_buffer(char const* buf)
	{
		TORRENT_ASSERT(!m_tokens.empty());
		if (m_tokens.empty()) return;

		m_buffer = buf;
	}

Project: libtorrent

TORRENT_TEST(partial_parse2)
{
	char b[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:d-d1:xi1eee";

	bdecode_node e;
	error_code ec;
	int pos;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(pos, 29);
	TEST_EQUAL(e.type(), bdecode_node::dict_t);

	printf("%s\n", print_entry(e).c_str());

	TEST_EQUAL(print_entry(e), "{ 'a': 1, 'b': 'foo', 'c': [ 1, 2 ], 'd': {} }");
}

Project: libtorrent

	std::string bdecode_error_category::message(int ev) const BOOST_SYSTEM_NOEXCEPT
	{
		static char const* msgs[] =
		{
			"no error",
			"expected digit in bencoded string",
			"expected colon in bencoded string",
			"unexpected end of file in bencoded string",
			"expected value (list, dict, int or string) in bencoded string",
			"bencoded nesting depth exceeded",
			"bencoded item count limit exceeded",
			"integer overflow",
		};
		if (ev < 0 || ev >= int(sizeof(msgs)/sizeof(msgs[0])))
			return "Unknown error";
		return msgs[ev];
	}

Project: libtorrent

TORRENT_TEST(parse_length_overflow)
{
	char const* b[] = {
		"d1:a1919191010:11111",
		"d2143289344:a4:aaaae",
		"d214328934114:a4:aaaae",
		"d9205357638345293824:a4:aaaae",
		"d1:a9205357638345293824:11111",
	};

	for (int i = 0; i < int(sizeof(b)/sizeof(b[0])); ++i)
	{
		error_code ec;
		bdecode_node e;
		int ret = bdecode(b[i], b[i] + strlen(b[i]), e, ec);
		TEST_EQUAL(ret, -1);
		TEST_CHECK(ec == error_code(bdecode_errors::unexpected_eof));
	}
}

Project: libtorrent

TORRENT_TEST(non_owning_refs)
{
	char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee";

	bdecode_node e1;
	error_code ec;
	int ret = bdecode(b1, b1 + sizeof(b1)-1, e1, ec);

	TEST_EQUAL(ret, 0);
	TEST_EQUAL(e1.type(), bdecode_node::dict_t);
	printf("%s\n", print_entry(e1).c_str());

	bdecode_node e2 = e1.non_owning();

	TEST_EQUAL(e2.type(), bdecode_node::dict_t);

	e1.clear();

	// e2 is invalid now
}

Project: libtorrent

	bdecode_node bdecode_node::dict_find_int(char const* key) const
	{
		bdecode_node ret = dict_find(key);
		if (ret.type() == bdecode_node::int_t)
			return ret;
		return bdecode_node();
	}

Project: libtorrent

TORRENT_TEST(int_invalid_digit)
{
	char b[] = "i92337203t854775807e";
	bdecode_node e;
	error_code ec;
	int pos = 0;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(pos, 9);
	TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit));
	printf("%s\n", print_entry(e).c_str());
}

Project: libtorrent

TORRENT_TEST(swap)
{
	char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee";
	char b2[] = "i1e";

	bdecode_node e1;
	bdecode_node e2;

	error_code ec;

	int ret = bdecode(b1, b1 + sizeof(b1)-1, e1, ec);
	TEST_EQUAL(ret, 0);
	ret = bdecode(b2, b2 + sizeof(b2)-1, e2, ec);
	TEST_EQUAL(ret, 0);

	std::string str1 = print_entry(e1);
	std::string str2 = print_entry(e2);
	TEST_EQUAL(e1.type(), bdecode_node::dict_t);
	TEST_EQUAL(e2.type(), bdecode_node::int_t);
	printf("%s\n", print_entry(e1).c_str());

	e1.swap(e2);

	TEST_EQUAL(e1.type(), bdecode_node::int_t);
	TEST_EQUAL(e2.type(), bdecode_node::dict_t);
	TEST_EQUAL(print_entry(e1), str2);
	TEST_EQUAL(print_entry(e2), str1);
	printf("%s\n", print_entry(e1).c_str());

	e1.swap(e2);

	TEST_EQUAL(e1.type(), bdecode_node::dict_t);
	TEST_EQUAL(e2.type(), bdecode_node::int_t);
	TEST_EQUAL(print_entry(e1), str1);
	TEST_EQUAL(print_entry(e2), str2);
	printf("%s\n", print_entry(e1).c_str());
}

Project: libtorrent

TORRENT_TEST(close_overflow_length_prefix)
{
	char b[] = "99999999:foobar";
	bdecode_node e;
	error_code ec;
	int pos;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(pos, 8);
	TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
	printf("%s\n", print_entry(e).c_str());
}

Project: libtorrent

	boost::system::error_category& bdecode_category()
	{
		static bdecode_error_category bdecode_category;
		return bdecode_category;
	}

Project: libtorrent

	bdecode_node bdecode_node::list_at(int i) const
	{
		TORRENT_ASSERT(type() == list_t);
		TORRENT_ASSERT(i >= 0);

		// make sure this is a list.
		bdecode_token const* tokens = m_root_tokens;

		// this is the first item
		int token = m_token_idx + 1;
		int item = 0;

		// do we have a lookup cached?
		if (m_last_index <= i && m_last_index != -1)
		{
			token = m_last_token;
			item = m_last_index;
		}

		while (item < i)
		{
			token += tokens[token].next_item;
			++item;

			// index 'i' out of range
			TORRENT_ASSERT(tokens[token].type != bdecode_token::end);
		}

		m_last_token = token;
		m_last_index = i;

		return bdecode_node(tokens, m_buffer, m_buffer_size, token);
	}

Project: libtorrent

TORRENT_TEST(expected_string_dict_key2)
{
	char b[] = "df00:";

	bdecode_node e;
	error_code ec;
	int pos;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(pos, 1);
	TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit));
	printf("%s\n", print_entry(e).c_str());
}

Project: libtorrent

TORRENT_TEST(partial_parse4)
{
	char b[] = "d1:ai1e1:b3:foo1:cli1e-i2ee1:dd1:xi1eee";

	bdecode_node e;
	error_code ec;
	int pos;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec, &pos);
	TEST_EQUAL(ret, -1);
	TEST_EQUAL(pos, 22);
	TEST_EQUAL(e.type(), bdecode_node::dict_t);

	printf("%s\n", print_entry(e).c_str());

	TEST_EQUAL(print_entry(e), "{ 'a': 1, 'b': 'foo', 'c': [ 1 ] }");
}

Project: libtorrent

TORRENT_TEST(integer)
{
	char b[] = "i12453e";
	bdecode_node e;
	error_code ec;
	int ret = bdecode(b, b + sizeof(b)-1, e, ec);
	TEST_CHECK(ret == 0);
	printf("%s\n", print_entry(e).c_str());
	std::pair<const char*, int> section = e.data_section();
	TEST_CHECK(std::memcmp(b, section.first, section.second) == 0);
	TEST_CHECK(section.second == sizeof(b) - 1);
	TEST_CHECK(e.type() == bdecode_node::int_t);
	TEST_CHECK(e.int_value() == 12453);
}