Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

1 Commit

Repository files navigation

Custom Bencoder

A small, single-header C++ serialization library for bencoded data.

The library supports serializing and deserializing bencoded data with a simple API. It utilizes smart pointers, STL containers, templates, and concepts for efficient memory management and type safety.

This was written to support OurTorrent done as a group project for Computer Networks course.

It follows the specifications in the standard.

Requirements

This library has no external dependencies and only requires a C++20 compiler.

It has been tested on GCC 10+ and Clang 10+. (Remember to compile with -std=c++20 flag as and when required depending on the compiler version.)

Installation

Since custom_bencoder is a single-file, header-only library, just copy include/custom_bencoder.h to your destination of choice either directly or after cloning the repository.

Usage

Four types can be created : bencode_integer, bencode_string, bencode_list, bencode_dict. The list and dict store unique pointers to their contained objects.

bencode_integer:

API:

  • Construct using default or parametrized (param = value) ctors
  • Assignment operator for assigning values
  • Default copy and move operations
  • get() : Returns the value stored by the variable.
  • encode() : Returns a string of bencoded data from stored value.
  • encode_n_dump(std::ostream) : Outputs the bencoded data on the passed output stream
  • decode(std::string, std::string::const_iterator) : Deserializes bencoded string (whose beginning is pointed to by the const_iterator) and extracts the integer data.
  • get_as_str() : Returns the stored integer value as a string.

struct bencoding::string_subs can be used for conveniently passing a string and its beginning iterator to the methods.

Example:

#include"custom_bencoder.h"intmain()
{
bencoding::bencode_integer bInt = 7;
bInt.encode_n_dump(std::cout); // prints i7e
std::cout << "\n";
bInt = 999;
std::cout << bInt.encode() << "\n"; // prints i999e
bencoding::string_subs sa("i56e"); // for convenience
bInt.decode(sa.str, sa.citer);
std::cout << bInt.get() << "\n"; // prints 56return0;
}

bencode_string:

API:

  • Default and parametrized ctors to construct bencode_string from std::string or char *.
  • Default copy and move operations.
  • Assignment operator to assign another std::string or char *.
  • Standard iterator interface of begin(), end(), cbegin() and cend().
  • size() to return bencode_string size.
  • get() : Returns the value stored by the variable.
  • encode() : Returns a string of bencoded data from stored value.
  • encode_n_dump(std::ostream) : Outputs the bencoded data on the passed output stream.
  • decode(std::string, std::string::const_iterator) : Deserializes bencoded string (whose beginning is pointed to by the const_iterator) and extracts the string data.
  • get_as_str() : Returns the stored string.

Example:

#include"custom_bencoder.h"intmain()
{
bencoding::bencode_string bs("hello");
std::cout << bs.encode() << "\n"; // prints 5:hello
bencoding::bencode_string b = "bye";
b.encode_n_dump(std::cout); // prints 3:bye
std::cout << "\n";
bs = "ok";
std::cout << bs.encode() << "\n"; // prints 2:ok
bencoding::string_subs sa(bs.encode());
b.decode(sa.str, sa.citer);
std::cout << b.get() << "\n"; // prints okreturn0;
}

bencode_list:

API:

  • Default ctor for constructing an empty list and a ctor which allocates a list of given size.
  • Default copy and move operations.
  • Standard iterator interface of begin(), end(), cbegin() and cend().
  • Subscript operator [] is overloaded and behaves similar to that of std::vector.
  • push_back() accepts any of the data types and inserts it to end of list.

Note : bencode_list and bencode_dict need to be passed as rvalues (std::move) since they store unique pointers. Check example for clarity.

  • pop_back() removes last element from list.
  • size() to return bencode_list size.
  • encode() : Returns a string of bencoded data from stored list of values.
  • encode_n_dump(std::ostream) : Outputs the bencoded data on the passed output stream.
  • decode(std::string, std::string::const_iterator) : Deserializes bencoded string (whose beginning is pointed to by the const_iterator) and extracts the data into the list. Any prior data is overwritten.
  • get_as_str() : Returns the stored list encoded as a string.

Examples:

intmain()
{
bencoding::bencode_list ls;
bencoding::bencode_integer a(8);
ls.push_back<bencoding::bencode_integer>(a);
ls.push_back<bencoding::bencode_integer>(bencoding::bencode_integer(856));
ls.push_back<bencoding::bencode_integer>(777);
ls.push_back<bencoding::bencode_string>("bye");
ls.encode_n_dump(std::cout); // prints li8ei856ei777e3:byeereturn0;
}
#include"custom_bencoder.h"intmain()
{
bencoding::bencode_list bl(5);
bl[0] = std::make_unique<bencoding::bencode_integer>(7);
std::cout << bl[0]->encode() << "\n"; // prints i7e
bl[1] = std::make_unique<bencoding::bencode_string>("hiaf");
bencoding::bencode_integer c = 999;
bl[2] = std::make_unique<bencoding::bencode_integer>(c);
std::cout << bl.encode() << "\n"; // prints li7e4:hiafi999ee
bencoding::bencode_string str("ok");
bl[2] = std::make_unique<bencoding::bencode_string>(str);
std::cout << bl.encode() << "\n"; // prints li7e4:hiaf2:okereturn0;
}
#include"custom_bencoder.h"intmain()
{
bencoding::bencode_list ls;
ls.push_back<bencoding::bencode_integer>(777);
ls.push_back<bencoding::bencode_string>("bye");
bencoding::bencode_dict bd;
bd.insert_or_assign<bencoding::bencode_string>("key", "value");
bencoding::bencode_list bl;
// bl.push_back<bencoding::bencode_list>(ls); // this is incorrect// bl.push_back<bencoding::bencode_dict>(bd); // this is incorrect// this is the right way for lists and dicts
bl.push_back<bencoding::bencode_list>(std::move(ls));
bl.push_back<bencoding::bencode_dict>(std::move(bd));
// You are NOT supposed to use ls or bd after moving them.
bl.encode_n_dump(std::cout); // prints lli777e3:byeed3:key5:valueeereturn0;
}

bencode_dict:

The dict will have bencode_string as keys arranged in lexicographical order and values as unique pointers pointing to respective data types.

API:

  • Default ctor for constructing an empty dict.
  • Standard iterator interface of begin(), end(), cbegin() and cend().
  • Subscript operator [] is overloaded and behaves similar to that of std::map.
  • insert_or_assign() accepts a key and any of the data types as value. If the key already exists then it is assigned the value otherwise the (key, value) pair is inserted into the dict.

Note : bencode_list and bencode_dict need to be passed as rvalues (std::move) since they store unique pointers. Check example for clarity.

  • insert accepts a key and a unique pointer to any of the data types as value. The (key, value) pair is inserted into the dict only if the dict does not already contain that key.

  • erase() has two overloads : One accepts a key and erases the corresponding (key, value) pair from the dict. Other accepts an iterator to the dict and erases the corresponding node.

  • find() accepts a key and returns an iterator pointing to the node containing (key, value) of the key.

  • size() to return bencode_dict size.

  • encode() : Returns a string of bencoded data from stored dict of keys and values.

  • encode_n_dump(std::ostream) : Outputs the bencoded data on the passed output stream.

  • decode(std::string, std::string::const_iterator) : Deserializes bencoded string (whose beginning is pointed to by the const_iterator) and extracts the data into the dict. Any prior data is overwritten.

  • get_as_str() : Returns the stored dict encoded as a string.

Examples:

#include"custom_bencoder.h"intmain()
{
bencoding::bencode_dict bd;
bd.insert_or_assign<bencoding::bencode_integer>("key1", 42);
std::cout << bd["key1"]->encode() << "\n"; // prints i42e
bd.insert("key2", std::make_unique<bencoding::bencode_string>("ok bye"));
bd["abc"] = std::make_unique<bencoding::bencode_integer>(-89);
std::cout << bd.encode() << "\n"; // prints d3:abci-89e4:key1i42e4:key26:ok byee
bd.erase(bd.find("key1"));
std::cout << bd.encode() << "\n"; // d3:abci-89e4:key26:ok byeereturn0;
}
#include"custom_bencoder.h"intmain()
{
bencoding::bencode_dict bd;
bd.insert_or_assign<bencoding::bencode_integer>("key1", 42);
bd.insert_or_assign<bencoding::bencode_string>("abc", "ok bye");
bencoding::bencode_list bl;
bl.push_back<bencoding::bencode_integer>(88);
bl.push_back<bencoding::bencode_string>("hello");
bencoding::bencode_dict new_dict;
new_dict.insert_or_assign<bencoding::bencode_list>("key_list", std::move(bl));
new_dict["key_dict"] = std::make_unique<bencoding::bencode_dict>(std::move(bd));
// bl and bd are NOT supposed to be used now since they have been moved.
std::cout << new_dict.encode() << "\n";
// prints d8:key_dictd3:abc6:ok bye4:key1i42ee8:key_listli88e5:helloeereturn0;
}

string_subs

It is a struct for easily passing arguments to decode() methods.

API:

  • Public data members : str : for accessing underlying data members and citer : a const_iterator to some position in the string.
  • Parametrized ctor which accepts a std::string or const char* and intializes str with the argument and citer to beginning of the string.
  • refresh() to reset citer's position back to beginning of string. Useful if required to parse a string again.

Example usage should be clear from preceding examples.

License

This library is licensed under the MIT License.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages