| | /*
################################################################################
# Описание бинарного протокола
################################################################################
По сети ходят пакеты вида
packet : = size payload
size - размер последовательности, в количестве элементов, может быть 0.
payload - поток байтов(blob)
payload состоит из последовательности сериализованных переменных разных типов :
Описание типов и порядок их сериализации
type : = id(uint64_t) data(blob)
data : =
IntegerType - uint64_t
FloatType - double
StringType - size(uint64_t) blob
VectorType - size(uint64_t) ...(сериализованные переменные)
Все данные передаются в little endian порядке байтов
Необходимо реализовать сущности IntegerType, FloatType, StringType, VectorType
Кроме того, реализовать вспомогательную сущность Any
Сделать объект Serialisator с указанным интерфейсом.
Конструкторы(ы) типов IntegerType, FloatType и StringType должны обеспечивать инициализацию, аналогичную инициализации типов uint64_t, double и std::string соответственно.
Конструктор(ы) типа VectorType должен позволять инициализировать его списком любых из перечисленных типов(IntegerType, FloatType, StringType, VectorType) переданных как по ссылке, так и по значению.
Все указанные сигнатуры должны быть реализованы.
Сигнатуры шаблонных конструкторов условны, их можно в конечной реализации делать на усмотрение разработчика, можно вообще убрать.
Vector::push должен быть именно шаблонной функцией. Принимает любой из типов: IntegerType, FloatType, StringType, VectorType.
Serialisator::push должен быть именно шаблонной функцией.Принимает любой из типов: IntegerType, FloatType, StringType, VectorType, Any
Реализация всех шаблонных функций, должна обеспечивать constraint requirements on template arguments, при этом, использование static_assert - запрещается.
Код в функции main не подлежит изменению. Можно только добавлять дополнительные проверки.
Архитектурные требования :
1. Стаедарт - c++17
2. Запрещаются виртуальные функции.
3. Запрещается множественное и виртуальное наследование.
4. Запрещается создание каких - либо объектов в куче, в том числе с использованием умных указателей.
Это требование не влечет за собой огранечений на использование std::vector, std::string и тп.
5. Запрещается любое дублирование кода, такие случаи должны быть строго обоснованы. Максимально использовать обобщающие сущности.
Например, если в каждой из реализаций XType будет свой IdType getId() - это будет считаться ошибкой.
6. Запрещается хранение value_type поля на уровне XType, оно должно быть вынесено в обобщающую сущность.
7. Никаких других ограничений не накладывается, в том числе на создание дополнительных обобщающих сущностей и хелперов.
8. XType должны реализовать сериализацию и десериализацию аналогичную Any.
Пример сериализации VectorType(StringType("qwerty"), IntegerType(100500))
{0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x71,0x77,0x65,0x72,0x74,0x79,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x88,
0x01,0x00,0x00,0x00,0x00,0x00}
*/
#include <iostream>
#include <vector>
#include <fstream>
#include <type_traits>
#include <variant>
#include <algorithm>
using Id = uint64_t;
using Buffer = std::vector<std::byte>;
enum class TypeId : Id {
Uint,
Float,
String,
Vector
};
class Any {
private:
using DataType = std::variant<uint64_t, double, std::string, std::vector<Any>>;
static constexpr TypeId idToType[] = {TypeId::Uint, TypeId::Float, TypeId::String, TypeId::Vector};
static constexpr size_t typeToId(TypeId type)
{
for(auto id = 0; id < std::size(idToType); id++)
{
if(idToType[id] == type)
{
return id;
}
}
return -1;
}
public:
template<typename ...Args, std::enable_if_t<std::is_constructible_v<DataType, Args...>, bool> = false>
Any(Args&& ... args) : m_place(std::forward<Args...>(args...))
{}
Any() = default;
void serialize(Buffer& _buff) const
{
auto append = [&_buff](auto && val, size_t size = sizeof(val)){
_buff.resize(_buff.size() + size);
auto * begin = reinterpret_cast<const std::byte *>(&val);
if constexpr (std::is_pointer_v<std::decay_t<decltype(val)>>)
{
begin = reinterpret_cast<const std::byte *>(val);
}
std::copy(begin, begin + size, _buff.end() - size);
};
auto type = getPayloadTypeId();
append(type);
switch (type)
{
case TypeId::Uint:
{
append(std::get<uint64_t>(m_place));
return;
}
case TypeId::Float:
{
append(std::get<double>(m_place));
return;
}
case TypeId::String:
{
const auto & str = std::get<std::string>(m_place);
uint64_t stringLength = str.size();
append(stringLength);
append(str.data(), stringLength);
return;
}
case TypeId::Vector:
{
const auto & vec = std::get<std::vector<Any>>(m_place);
uint64_t vectorLength = vec.size();
append(vectorLength);
for(const auto & v : vec)
{
v.serialize(_buff);
}
return;
}
default:
break;
}
return;
}
Buffer::const_iterator deserialize(Buffer::const_iterator _begin, Buffer::const_iterator _end)
{
// TODO: need to add actions on failed deseriasization
// All types takes at least 2 * sizeof(uint64_t) bytes
// (TypeId + value) or (TypeId + Length)
auto hasEnoughtSpace = [&_begin, &_end](auto size){return _end - _begin >= size;};
if(!hasEnoughtSpace(sizeof(Id) + sizeof(uint64_t)))
{
return _end;
}
auto convert = [&_begin](auto * val){
std::decay_t<decltype(*val)> result;
std::copy(_begin, _begin + sizeof(result), reinterpret_cast<std::byte *>(&result));
_begin += sizeof(result);
return result;
};
// TODO: replace with template lambda from C++ 20
auto type = convert((TypeId *)(nullptr));
switch (type)
{
case TypeId::Uint:
{
m_place.emplace<uint64_t>(convert((uint64_t *)(nullptr)));
break;
}
case TypeId::Float:
{
m_place.emplace<double>(convert((double *)(nullptr)));
break;
}
case TypeId::String:
{
auto stringLength = convert((uint64_t *)(nullptr));
if(!hasEnoughtSpace(stringLength))
{
return _end;
}
m_place.emplace<std::string>(reinterpret_cast<const char *>(&(*_begin)), stringLength);
_begin += stringLength;
break;
}
case TypeId::Vector:
{
auto vectorLength = convert((uint64_t *)(nullptr));
if(!hasEnoughtSpace(vectorLength))
{
return _end;
}
m_place.emplace<std::vector<Any>>(vectorLength);
for(auto & any : std::get<std::vector<Any>>(m_place))
{
_begin = any.deserialize(_begin, _end);
}
break;
}
default:
break;
}
return _begin;
}
TypeId getPayloadTypeId() const
{
return idToType[m_place.index()];
}
template<typename Type>
auto& getValue() const
{
return std::get<Type>(m_place);
}
template<TypeId kId>
auto& getValue() const
{
return std::get<typeToId(kId)>(m_place);
}
bool operator == (const Any& _o) const
{
return (_o.m_place == m_place);
}
private:
DataType m_place;
};
class IntegerType : public Any {
public:
//TODO: replace with pretty concepts (c++20) instead of enable if
template<typename Arg, std::enable_if_t<std::is_constructible_v<uint64_t, Arg>, bool> = false>
explicit IntegerType(Arg && arg) : Any{std::in_place_type_t<uint64_t>{}, std::forward<Arg>(arg)}
{
}
};
class FloatType : public Any {
public:
template<typename Arg, std::enable_if_t<std::is_constructible_v<double, Arg>, bool> = false>
explicit FloatType(Arg && arg) : Any(std::in_place_type_t<double>{}, std::forward<Arg>(arg))
{
}
};
class StringType : public Any {
public:
template<typename ...Args, std::enable_if_t<std::is_constructible_v<std::string, Args ...>, bool> = false>
explicit StringType(Args&& ... args) : Any(std::in_place_type_t<std::string>{}, std::forward<Args...>(args...))
{}
};
class VectorType;
template<typename T>
static constexpr bool IsValidType = std::is_same_v<IntegerType, std::decay_t<T>> ||
std::is_same_v<FloatType, std::decay_t<T>> ||
std::is_same_v<StringType, std::decay_t<T>> ||
std::is_same_v<VectorType, std::decay_t<T>> ||
std::is_same_v<Any, std::decay_t<T>>;
class VectorType : public Any {
public:
template<typename ...Args, std::enable_if_t<(IsValidType<Args> && ...), bool> = false>
VectorType(Args&& ...args) : Any(std::in_place_type_t<std::vector<Any>>{}, std::forward<Args...>(args...))
{
}
template<typename Arg, std::enable_if_t<(IsValidType<Arg>), bool> = false>
void push_back(Arg&& _val)
{
getValue<TypeId::Vector>().emplace_back(std::forward<Arg>(_val));
}
};
class Serializator {
public:
template<typename Arg, std::enable_if_t<(IsValidType<Arg>), bool> = false>
void push(Arg&& _val)
{
m_data.emplace_back(std::forward<Arg>(_val));
}
Buffer serialize() const
{
Buffer result(sizeof(uint64_t));
uint64_t vectorSize = m_data.size();
std::copy(reinterpret_cast<std::byte *>(&vectorSize), reinterpret_cast<std::byte *>(&vectorSize) + sizeof(vectorSize), result.begin());
for(auto & any : m_data)
{
any.serialize(result);
}
return result;
}
static std::vector<Any> deserialize(const Buffer& _val)
{
if(_val.size() < sizeof(uint64_t))
{
return {};
}
uint64_t size;
std::copy(_val.begin(), _val.begin() + sizeof(uint64_t), reinterpret_cast<std::byte *>(&size));
if(size == 0)
{
return {};
}
auto begin = _val.begin() + sizeof(uint64_t);
std::vector<Any> result(size);
for(auto & any : result)
{
begin = any.deserialize(begin, _val.end());
}
return result;
}
const std::vector<Any>& getStorage() const
{
return m_data;
}
private:
std::vector<Any> m_data;
};
int main() {
std::ifstream raw;
raw.open("raw.bin", std::ios_base::in | std::ios_base::binary);
if (!raw.is_open())
return 1;
raw.seekg(0, std::ios_base::end);
std::streamsize size = raw.tellg();
raw.seekg(0, std::ios_base::beg);
Buffer buff(size);
raw.read(reinterpret_cast<char*>(buff.data()), size);
auto res = Serializator::deserialize(buff);
Serializator s;
for (auto&& i : res)
s.push(i);
std::cout << (buff == s.serialize()) << '\n';
return 0;
}
|