/*

Copyright (c) 2015-2022, Arvid Norberg
Copyright (c) 2017, Steven Siloti
All rights reserved.

You may use, distribute and modify this code under the terms of the BSD license,
see LICENSE file.
*/

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

using namespace lt;

// test integer
TORRENT_TEST(integer)
{
	char b[] = "i12453e";
	error_code ec;
	bdecode_node e = bdecode(b, ec);
	TEST_CHECK(!ec);
	std::printf("%s\n", print_entry(e).c_str());
	TEST_CHECK(span<char>(b, sizeof(b) - 1) == e.data_section());
	TEST_EQUAL(e.type(), bdecode_node::int_t);
	TEST_EQUAL(e.int_value(), 12453);
}

TORRENT_TEST(construct_string)
{
	entry e(std::string("abc123"));
	TEST_EQUAL(e.string(), "abc123");
}

TORRENT_TEST(construct_string_literal)
{
	entry e("abc123");
	TEST_EQUAL(e.string(), "abc123");
}


TORRENT_TEST(construct_string_view)
{
	entry e(string_view("abc123"));
	TEST_EQUAL(e.string(), "abc123");
}

TORRENT_TEST(construct_integer)
{
	entry e(4);
	TEST_EQUAL(e.integer(), 4);
}

// test string
TORRENT_TEST(string)
{
	char b[] = "26:abcdefghijklmnopqrstuvwxyz";
	error_code ec;
	bdecode_node e = bdecode(b, ec);
	TEST_CHECK(!ec);
	std::printf("%s\n", print_entry(e).c_str());
	TEST_CHECK(span<char>(b, sizeof(b) - 1) == e.data_section());
	TEST_EQUAL(e.type(), bdecode_node::string_t);
	TEST_EQUAL(e.string_value(), std::string("abcdefghijklmnopqrstuvwxyz"));
	TEST_EQUAL(e.string_length(), 26);
}

// test string-prefix
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
	error_code ec;
	bdecode_node e = bdecode(test, ec);
	TEST_CHECK(!ec);
	std::printf("%d bytes string\n", e.string_length());
	TEST_CHECK(span<char const>(test) == e.data_section());
	TEST_EQUAL(e.type(), bdecode_node::string_t);
	TEST_EQUAL(e.string_length(), 1000000);
	TEST_EQUAL(e.string_ptr(), test.c_str() + 8);
}

// test list
TORRENT_TEST(list)
{
	char b[] = "li12453e3:aaae";
	error_code ec;
	bdecode_node e = bdecode(b, ec);
	TEST_CHECK(!ec);
	std::printf("%s\n", print_entry(e).c_str());
	TEST_CHECK(span<char>(b, sizeof(b) - 1) == e.data_section());
	TEST_EQUAL(e.type(), bdecode_node::list_t);
	TEST_EQUAL(e.list_size(), 2);
	TEST_EQUAL(e.list_at(0).type(), bdecode_node::int_t);
	TEST_EQUAL(e.list_at(1).type(), bdecode_node::string_t);
	TEST_EQUAL(e.list_at(0).int_value(), 12453);
	TEST_EQUAL(e.list_at(1).string_value(), std::string("aaa"));
	TEST_EQUAL(e.list_at(1).string_length(), 3);
	TEST_CHECK(span<char const>("3:aaa", 5) == e.list_at(1).data_section());
}

// test dict
TORRENT_TEST(dict)
{
	char b[] = "d1:ai12453e1:b3:aaa1:c3:bbb1:X10:0123456789e";
	error_code ec;
	bdecode_node e = bdecode(b, ec);
	TEST_CHECK(!ec);
	std::printf("%s\n", print_entry(e).c_str());
	TEST_CHECK(span<char>(b, sizeof(b) - 1) == e.data_section());
	TEST_EQUAL(e.type(), bdecode_node::dict_t);
	TEST_EQUAL(e.dict_size(), 4);
	TEST_EQUAL(e.dict_find("a").type(), bdecode_node::int_t);
	TEST_EQUAL(e.dict_find("a").int_value(), 12453);
	TEST_EQUAL(e.dict_find("b").type(), bdecode_node::string_t);
	TEST_EQUAL(e.dict_find("b").string_value(), std::string("aaa"));
	TEST_EQUAL(e.dict_find("b").string_length(), 3);
	TEST_EQUAL(e.dict_find("c").type(), bdecode_node::string_t);
	TEST_EQUAL(e.dict_find("c").string_value(), std::string("bbb"));
	TEST_EQUAL(e.dict_find("c").string_length(), 3);
	TEST_EQUAL(e.dict_find_string_value("X"), "0123456789");
	char error_string[200];
	TEST_CHECK(e.has_soft_error(error_string));
	TEST_EQUAL(std::string(error_string), std::string("unsorted dictionary key"));
}

// test dictionary with a key without a value
TORRENT_TEST(dict_key_novalue)
{
	char b[] = "d1:ai1e1:be";
	error_code ec;
	int pos;
	bdecode_node e = bdecode(b, ec, &pos);
	TEST_EQUAL(pos, 10);
	TEST_EQUAL(ec, error_code(bdecode_errors::expected_value));
	std::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";
	error_code ec;
	int pos;
	bdecode_node e = bdecode(b, ec, &pos);
	TEST_EQUAL(pos, 1);
	TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit));
	std::printf("%s\n", print_entry(e).c_str());
}

// dictionary key with \0
TORRENT_TEST(dict_null_key)
{
	char b[] = "d3:a\0bi1ee";
	error_code ec;
	bdecode_node e = bdecode(b, ec);
	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);
}

// soft error reported for dictionary with unordered keys
TORRENT_TEST(dict_unordered_keys)
{
	char error_string[200];
	{
		char b[] = "d2:abi1e2:aai2ee";
		error_code ec;
		bdecode_node e = bdecode(b, ec);
		TEST_CHECK(e.has_soft_error(error_string));
		TEST_EQUAL(std::string(error_string), std::string("unsorted dictionary key"));
	}
	{
		char b[] = "d2:bai1e2:aai2ee";
		error_code ec;
		bdecode_node e = bdecode(b, ec);
		TEST_CHECK(e.has_soft_error(error_string));
		TEST_EQUAL(std::string(error_string), std::string("unsorted dictionary key"));
	}
	{
		char b[] = "d2:aai1e1:ai2ee";
		error_code ec;
		bdecode_node e = bdecode(b, ec);
		TEST_CHECK(e.has_soft_error(error_string));
		TEST_EQUAL(std::string(error_string), std::string("unsorted dictionary key"));
	}
	{
		char b[] = "d1:ai1e2:aai2ee";
		error_code ec;
		bdecode_node e = bdecode(b, ec);
		TEST_CHECK(!e.has_soft_error(error_string));
		TEST_EQUAL(std::string(error_string), std::string("unsorted dictionary key"));
	}
	{
		char b[] = "d2:aai1e1:bi2ee";
		error_code ec;
		bdecode_node e = bdecode(b, ec);
		TEST_CHECK(!e.has_soft_error(error_string));
		TEST_EQUAL(std::string(error_string), std::string("unsorted dictionary key"));
	}
}

TORRENT_TEST(dict_duplicate_key)
{
	char b[] = "d2:aai1e2:aai2ee";
	error_code ec;
	bdecode_node e = bdecode(b, ec);
	char error_string[200];
	TEST_CHECK(e.has_soft_error(error_string));
	TEST_EQUAL(std::string(error_string), std::string("duplicate dictionary key"));
}

// premature e
TORRENT_TEST(premature_e)
{
	char b[] = "e";
	error_code ec;
	bdecode_node e = bdecode(b, ec);
	TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
	std::printf("%s\n", print_entry(e).c_str());
}

// test strings with negative length-prefix
TORRENT_TEST(negative_length_prefix)
{
	char b[] = "-10:foobar";
	error_code ec;
	int pos;
	bdecode_node e = bdecode(b, ec, &pos);
	TEST_EQUAL(pos, 0);
	TEST_EQUAL(ec, error_code(bdecode_errors::expected_value));
	std::printf("%s\n", print_entry(e).c_str());
}

// test strings with overflow length-prefix
TORRENT_TEST(overflow_length_prefix)
{
	char b[] = "18446744073709551615:foobar";
	error_code ec;
	int pos;
	bdecode_node e = bdecode(b, ec, &pos);
	TEST_EQUAL(pos, 19);
	TEST_EQUAL(ec,  error_code(bdecode_errors::overflow));
	std::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";
	error_code ec;
	int pos;
	bdecode_node e = bdecode(b, ec, &pos);
	TEST_EQUAL(pos, 8);
	TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
	std::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";
	error_code ec;
	int pos;
	// pretend that we have a large buffer like that
	bdecode_node e = bdecode({b, 999999999}, ec, &pos);
	TEST_EQUAL(pos, 0);
	TEST_EQUAL(ec, error_code(bdecode_errors::limit_exceeded));
	std::printf("%s\n", print_entry(e).c_str());
}

TORRENT_TEST(leading_zero_length_prefix)
{
	{
		char b[] = "06:foobar";
		error_code ec;
		int pos;
		bdecode_node e = bdecode(b, ec, &pos);
		char error_string[200];
		TEST_CHECK(e.has_soft_error(error_string));
		TEST_EQUAL(std::string(error_string), std::string("leading zero in string length"));
		std::printf("%s\n", print_entry(e).c_str());
	}
	{
		char b[] = "0:";
		error_code ec;
		int pos;
		bdecode_node e = bdecode(b, ec, &pos);
		char error_string[200];
		TEST_CHECK(!e.has_soft_error(error_string));
		std::printf("%s\n", print_entry(e).c_str());
	}
}

// test integer without any digits
TORRENT_TEST(nodigit_int)
{
	char b[] = "ie";
	error_code ec;
	int pos;
	bdecode_node e = bdecode(b, ec, &pos);
	TEST_EQUAL(pos, 1);
	TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit));
	std::printf("%s\n", print_entry(e).c_str());
}

// test integer with just a minus
TORRENT_TEST(minus_int)
{
	char b[] = "i-e";
	error_code ec;
	int pos;
	bdecode_node e = bdecode(b, ec, &pos);
	TEST_EQUAL(pos, 2);
	TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit));
	std::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";
	error_code ec;
	int pos;
	bdecode_node e = bdecode(b, ec, &pos);
	TEST_EQUAL(pos, 6);
	TEST_EQUAL(ec,  error_code(bdecode_errors::expected_digit));
	std::printf("%s\n", print_entry(e).c_str());
}

// test integers that don't fit in 64 bits
TORRENT_TEST(int_overflow)
{
	char b[] = "i18446744073709551615e";
	error_code ec;
	bdecode_node e = bdecode(b, ec);
	std::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";
	error_code ec;
	int pos;
	bdecode_node e = bdecode(b, ec, &pos);
	TEST_EQUAL(pos, 22);
	TEST_EQUAL(ec,  error_code(bdecode_errors::overflow));
	std::printf("%s\n", print_entry(e).c_str());
}

// test truncated negative integer
TORRENT_TEST(int_truncated)
{
	char b[] = "i-";
	error_code ec;
	int pos;
	bdecode_node e = bdecode({b, 2}, ec, &pos);
	TEST_EQUAL(pos, 2);
	TEST_EQUAL(ec,  error_code(bdecode_errors::unexpected_eof));
	std::printf("%s\n", print_entry(e).c_str());
}

TORRENT_TEST(int_leading_zero)
{
	{
		char b[] = "i01e";
		error_code ec;
		bdecode_node e = bdecode(b, ec);
		char error_string[200];
		TEST_CHECK(e.has_soft_error(error_string));
		TEST_EQUAL(std::string(error_string), std::string("leading zero in integer"));
		std::printf("%s\n", print_entry(e).c_str());
	}
	{
		char b[] = "i0e";
		error_code ec;
		bdecode_node e = bdecode(b, ec);
		char error_string[200];
		TEST_CHECK(!e.has_soft_error(error_string));
		std::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"));
	ec.assign(5434, bdecode_category());
	TEST_EQUAL(ec.message(), "Unknown error");
}

// test integers that just exactly fit in 64 bits
TORRENT_TEST(64bit_int)
{
	char b[] = "i9223372036854775807e";
	error_code ec;
	bdecode_node e = bdecode(b, ec);
	std::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";
	error_code ec;
	bdecode_node e = bdecode(b, ec);
	std::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";
	error_code ec;
	int pos = 0;
	bdecode_node e = bdecode(b, ec, &pos);
	TEST_EQUAL(pos, 9);
	TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit));
	std::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};

	std::printf("%s\n", buf);
	error_code ec;
	bdecode_node e = bdecode({reinterpret_cast<char const*>(buf), sizeof(buf)}, ec);
	TEST_CHECK(ec);
}

// 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

	error_code ec;
	bdecode_node e = bdecode({b, sizeof(b)}, ec, nullptr, 100);
	TEST_EQUAL(ec, error_code(bdecode_errors::depth_exceeded));
}

// test the item limit
TORRENT_TEST(item_limit)
{
	char b[10240];
	b[0] = 'l';
	std::ptrdiff_t i = 1;
	for (i = 1; i < 10239; i += 2)
		memcpy(&b[i], "0:", 2);
	b[i] = 'e';

	error_code ec;
	bdecode_node e = bdecode({b, i + 1}, ec, nullptr, 1000, 1000);
	TEST_EQUAL(ec, error_code(bdecode_errors::limit_exceeded));
}

// test unexpected EOF
TORRENT_TEST(unexpected_eof)
{
	char b[] = "l2:.."; // expected terminating 'e'

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

// test unexpected EOF in string length
TORRENT_TEST(unexpected_eof2)
{
	char b[] = "l2:..0"; // expected ':' delimiter instead of EOF

	error_code ec;
	int pos;
	bdecode_node e = bdecode({b, 6}, ec, &pos);
	TEST_EQUAL(pos, 6);
	TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
	std::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)

	error_code ec;
	int pos;
	bdecode_node e = bdecode(b, ec, &pos);
	TEST_EQUAL(pos, 1);
	TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit));
	std::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";

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

// test unexpected EOF while parsing dict key
TORRENT_TEST(unexpected_eof_dict_key2)
{
	char b[] = "d1000:";

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

// test expected string while parsing dict key
TORRENT_TEST(expected_string_dict_key2)
{
	char b[] = "df00:";

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

// test unexpected EOF while parsing int
TORRENT_TEST(unexpected_eof_int)
{
	char b[] = "i";

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

// test unexpected EOF while parsing int
TORRENT_TEST(unexpected_eof_int2)
{
	char b[] = "i10";

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


// test expected colon
TORRENT_TEST(expected_colon_dict)
{
	char b[] = "d1000";

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

// test empty string
TORRENT_TEST(empty_string)
{
	error_code ec;
	bdecode_node e = bdecode({}, ec, nullptr);
	TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
	std::printf("%s\n", print_entry(e).c_str());
}

// test partial string
TORRENT_TEST(partial_string)
{
	char b[] = "100:..";

	error_code ec;
	int pos;
	bdecode_node e = bdecode(b, ec, &pos);
	TEST_EQUAL(pos, 3);
	TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
	std::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];
		std::snprintf(tmp, sizeof(tmp), "i%de", i);
		buf += tmp;
	}
	buf += "e";

	error_code ec;
	bdecode_node e = bdecode(buf, ec);
	TEST_CHECK(!ec);
	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];
		std::snprintf(tmp, sizeof(tmp), "4:%04di%de", i, i);
		buf += tmp;
	}
	buf += "e";

	std::printf("%s\n", buf.c_str());
	error_code ec;
	bdecode_node e = bdecode(buf, ec);
	TEST_CHECK(!ec);
	TEST_EQUAL(e.type(), bdecode_node::dict_t);
	TEST_EQUAL(e.dict_size(), 1000);
	for (int i = 0; i < 1000; ++i)
	{
		char tmp[30];
		std::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";

	error_code ec;
	bdecode_node e = bdecode(b, ec);
	TEST_CHECK(!ec);

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

	error_code ec;
	bdecode_node e = bdecode(b, ec);
	TEST_CHECK(!ec);

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

	error_code ec;
	bdecode_node e = bdecode({b, 0x3fffffff}, ec);
	TEST_EQUAL(ec, error_code(bdecode_errors::limit_exceeded));
	std::printf("%s\n", print_entry(e).c_str());
}

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

// test invalid digit
TORRENT_TEST(invalid_digit)
{
	char b[] = "0o";
	std::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:";
	std::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)
{
	string_view const b[] = {
		"d1:a1919191010:11111"_sv,
		"d2143289344:a4:aaaae"_sv,
		"d214328934114:a4:aaaae"_sv,
		"d9205357638345293824:a4:aaaae"_sv,
		"d1:a9205357638345293824:11111"_sv
	};

	for (auto const& buf : b)
	{
		error_code ec;
		bdecode_node e = bdecode(buf, ec);
		TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
	}
}


TORRENT_TEST(expected_colon_string)
{
	char b[] = "928";
	std::int64_t val = 0;
	bdecode_errors::error_code_enum ec = bdecode_errors::no_error;
	char const* e = parse_int(b, b + sizeof(b)-1, ':', val, ec);
	TEST_EQUAL(ec, bdecode_errors::no_error);
	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";
	error_code ec;
	bdecode_node e = bdecode(b, ec);
	TEST_CHECK(!ec);
	std::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";
	error_code ec;
	bdecode_node e = bdecode(b, ec);
	TEST_CHECK(!ec);
	std::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";
	error_code ec;
	bdecode_node e = bdecode(b, ec);
	TEST_CHECK(!ec);
	std::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";
	error_code ec;
	bdecode_node e = bdecode(b, ec);
	TEST_CHECK(!ec);
	std::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";
	error_code ec;
	bdecode_node e = bdecode(b, ec);
	TEST_CHECK(!ec);
	std::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";
	error_code ec;
	bdecode_node e = bdecode(b, ec);
	TEST_CHECK(!ec);
	std::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";

	error_code ec;

	bdecode_node e1 = bdecode({b1, sizeof(b1)-1}, ec);
	TEST_CHECK(!ec);
	bdecode_node e2 = bdecode({b2, sizeof(b2)-1}, ec);
	TEST_CHECK(!ec);

	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);
	std::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);
	std::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);
	std::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";

	error_code ec;

	bdecode_node e1 = bdecode({b1, sizeof(b1)-1}, ec);
	TEST_CHECK(!ec);

	bdecode_node 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);
	std::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);
	std::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);
	std::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";


	error_code ec;

	bdecode_node e1_root = bdecode({b1, sizeof(b1)-1}, ec);
	TEST_CHECK(!ec);
	bdecode_node e2_root = bdecode({b2, sizeof(b2)-1}, ec);
	TEST_CHECK(!ec);

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


	error_code ec;

	bdecode_node e1_root = bdecode({b1, sizeof(b1)-1}, ec);
	TEST_CHECK(!ec);
	bdecode_node e2 = bdecode({b2, sizeof(b2)-1}, ec);
	TEST_CHECK(!ec);

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

	error_code ec;
	bdecode_node e = bdecode({b1, sizeof(b1)-1}, ec);
	std::printf("%s\n", print_entry(e).c_str());
	TEST_CHECK(!ec);
	TEST_EQUAL(e.type(), bdecode_node::dict_t);
	TEST_EQUAL(e.dict_size(), 4);
	TEST_EQUAL(e.dict_at(1).first, "b");

	e = bdecode({b2, sizeof(b2)-1}, ec);
	std::printf("%s\n", print_entry(e).c_str());
	TEST_CHECK(!ec);
	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";

	error_code ec;
	bdecode_node e1 = bdecode({b1, sizeof(b1)-1}, ec);
	TEST_CHECK(!ec);
	TEST_EQUAL(e1.type(), bdecode_node::dict_t);
	std::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";

	error_code ec;
	bdecode_node e1 = bdecode({b1, sizeof(b1)-1}, ec);
	TEST_CHECK(!ec);

	TEST_EQUAL(e1.type(), bdecode_node::dict_t);
	std::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 failed
TORRENT_TEST(partial_parse)
{
	char b[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1-eee";

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

	std::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";

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

	std::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";

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

	std::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";

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

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

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

TORRENT_TEST(partial_parse_string)
{
	// it's important to not have a null terminator here
	// to allow address sanitizer to trigger in case the decoder reads past the
	// end
	char b[] = { '5', '5'};

	error_code ec;
	int pos;
	bdecode_node e = bdecode({b, sizeof(b)}, ec, &pos);
	TEST_CHECK(ec);
	TEST_EQUAL(pos, 2);
}

// 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";

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

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

	e.switch_underlying_buffer(b2);

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

	TEST_EQUAL(string1, string2);
}

TORRENT_TEST(long_string_99999999)
{
	std::string input;
	input += "99999999:";
	input.resize(9 + 99999999, '_');

	error_code ec;
	int pos;
	bdecode_node e = bdecode(input, ec, &pos);
	TEST_EQUAL(e.type(), bdecode_node::string_t);
	TEST_EQUAL(e.string_value(), input.substr(9));
}

TORRENT_TEST(long_string_100000000)
{
	std::string input;
	input += "100000000:";
	input.resize(10 + 100000000, '_');

	error_code ec;
	int pos;
	bdecode_node e = bdecode(input, ec, &pos);
	TEST_EQUAL(e.type(), bdecode_node::string_t);
	TEST_EQUAL(e.string_value(), input.substr(10));
}

TORRENT_TEST(data_offset)
{
	char const b[] = "li1e3:fooli1ei2eed1:xi1eee";
	error_code ec;
	bdecode_node e = bdecode(b, ec);
	TEST_CHECK(!ec);
	std::printf("%s\n", print_entry(e).c_str());

	TEST_EQUAL(e.data_offset(), 0);
	TEST_EQUAL(e.list_at(0).data_offset(), 1);
	TEST_EQUAL(e.list_at(1).data_offset(), 4);
	TEST_EQUAL(e.list_at(2).data_offset(), 9);
	TEST_EQUAL(e.list_at(3).data_offset(), 17);
}

TORRENT_TEST(string_offset)
{
	char const b[] = "l3:foo3:bare";
	error_code ec;
	bdecode_node e = bdecode(b, ec);
	TEST_CHECK(!ec);
	std::printf("%s\n", print_entry(e).c_str());

	TEST_EQUAL(e.list_at(0).string_offset(), 3);
	TEST_EQUAL(e.list_at(1).string_offset(), 8);
}

TORRENT_TEST(dict_at_node)
{
	char const b[] = "d3:foo3:bar4:test4:teste";
	error_code ec;
	bdecode_node e = bdecode(b, ec);
	TEST_CHECK(!ec);
	std::printf("%s\n", print_entry(e).c_str());

	TEST_EQUAL(e.dict_at_node(0).first.string_offset(), 3);
	TEST_EQUAL(e.dict_at_node(0).second.string_offset(), 8);
	TEST_EQUAL(e.dict_at_node(1).first.string_offset(), 13);
	TEST_EQUAL(e.dict_at_node(1).second.string_offset(), 19);
}
