/*

Copyright (c) 2015, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "test.hpp"
#include "libtorrent/bdecode.hpp"

using namespace libtorrent;

// test integer
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);
}

// test string
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_CHECK(section.second == sizeof(b) - 1);
	TEST_CHECK(e.type() == bdecode_node::string_t);
	TEST_CHECK(e.string_value() == std::string("abcdefghijklmnopqrstuvwxyz"));
	TEST_CHECK(e.string_length() == 26);
}

// test list
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);
}

// test dict
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");
}

// test dictionary with a key without a value
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());
}

// test dictionary with a key that's not a string
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());
}

// dictionary key with \0
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);
}

// premature e
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());
}

// test strings with negative length-prefix
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());
}

// test strings with overflow length-prefix
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());
}

// test strings with almost overflow (more than 8 digits)
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());
}

// test strings with overflow (more than 8 digits)
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());
}

// test integer without any digits
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());
}

// test integer with just a minus
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());
}

// test integer with a minus inserted in it
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());
}

// test integers that don't fit in 64 bits
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);
}

// test integers with more than 20 digits (overflow on parsing)
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());
}

// test truncated negative integer
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());
}

// bdecode_error
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");
}

// test integers that just exactly fit in 64 bits
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);
}

// test integers that just exactly fit in 64 bits
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);
}

// test integers that have invalid digits
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());
}

// test invalid encoding
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);
}

// test the depth limit
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()));
}

// test the item limit
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()));
}

// test unexpected EOF
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());
}

// test unexpected EOF (really expected terminator)
TORRENT_TEST(unepected_eof2)
{
	char b[] = "l2:..0"; // expected terminating 'e' instead of '0'

	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_colon));
	printf("%s\n", print_entry(e).c_str());
}

// test expected string
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());
}

// test unexpected EOF while parsing dict key
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());
}

// test unexpected EOF while parsing dict key
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());
}

// test expected string while parsing dict key
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());
}

// test unexpected EOF while parsing int
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());
}

// test unexpected EOF while parsing int
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());
}


// test expected colon
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());
}

// test empty string
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());
}

// test partial string
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());
}

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);
	}
}

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);
	}
}

// test dict_at
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);
}

// test string_ptr
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);
}

// test exceeding buffer size limit
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());
}

// test parse_int
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);
}

// test invalid digit
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);
}

// test parse_int overflow
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);
}

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));
	}
}


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);
}

// test dict_find_* functions
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);
}

// test list_*_at functions
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);
}

// test list_at in reverse order
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);
}


// test dict_find_* functions
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);
}

// print_entry

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 } ]");
}

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 } }");
}

// test swap()
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());
}

// test swap() (one node is the root of the other node)
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());
}

// test swap() (neither is a root and they don't share a root)
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);
}

// test swap() (one is a root and they don't share a root)
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);
}

// make sure it's safe to reuse bdecode_nodes after clear() is called
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);
}

// assignment/copy of root nodes
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");
}

// non-owning references
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
}

// test that a partial parse can be still be printed up to the
// point where it faild
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': {} } }");
}

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': {} }");
}

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 ] }");
}

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 ] }");
}

// test switch_underlying_buffer
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);
}

