oxen-core/contrib/epee/include/epee/serialization/keyvalue_serialization_over...

339 lines
17 KiB
C++

// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
// 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 Andrey N. Sabelnikov 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 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.
//
#pragma once
#include <type_traits>
#include <set>
#include <unordered_set>
#include <list>
#include <vector>
#include <deque>
#include <array>
#include <optional>
#include <cstring>
#include "../span.h"
#include "../storages/portable_storage_base.h"
#undef OXEN_DEFAULT_LOG_CATEGORY
#define OXEN_DEFAULT_LOG_CATEGORY "serialization"
namespace epee
{
namespace
{
template <typename T>
constexpr bool is_std_vector = false;
template <typename... T>
constexpr bool is_std_vector<std::vector<T...>> = true;
template <typename T>
constexpr bool is_std_optional = false;
template <typename T>
constexpr bool is_std_optional<std::optional<T>> = true;
}
namespace serialization
{
template <typename T, typename SFINAE = void> constexpr bool is_basic_serializable = false;
template <typename T> constexpr bool is_basic_serializable<T, std::enable_if_t<std::is_integral<T>::value>> = true;
template <typename T> constexpr bool is_basic_serializable<const T> = is_basic_serializable<T>;
template <typename T> constexpr bool is_basic_serializable<std::optional<T>> = is_basic_serializable<T>;
template <> inline constexpr bool is_basic_serializable<std::string> = true;
template <> inline constexpr bool is_basic_serializable<double> = true;
template <> inline constexpr bool is_basic_serializable<storage_entry> = true;
template <typename T> constexpr bool is_serialize_stl_container = false;
template <typename T> constexpr bool is_serialize_stl_container<std::vector<T>> = true;
template <typename T> constexpr bool is_serialize_stl_container<std::deque<T>> = true;
template <typename T> constexpr bool is_serialize_stl_container<std::list<T>> = true;
template <typename T> constexpr bool is_serialize_stl_container<std::set<T>> = true;
template <typename T> constexpr bool is_serialize_stl_container<std::unordered_set<T>> = true;
template <typename T, size_t S> constexpr bool is_serialize_stl_container<std::array<T, S>> = true;
// static_asserts that the type is suitable for binary serialization: by default, this means it
// has no padding and is trivially copyable. Types that are safe but don't satisfy these
// requirements can specialize is_byte_spannable.
template <typename T>
constexpr void assert_blob_serializable() {
static_assert(is_byte_spannable<T>, "Type is not acceptable for blob serialization");
}
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
bool serialize_t_val(const t_type& d, t_storage& stg, section* parent_section, const char* pname)
{
return stg.set_value(pname, d, parent_section);
}
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
bool unserialize_t_val(t_type& d, t_storage& stg, section* parent_section, const char* pname)
{
return stg.get_value(pname, d, parent_section);
}
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
bool serialize_t_val_as_blob(const t_type& d, t_storage& stg, section* parent_section, const char* pname)
{
assert_blob_serializable<t_type>();
std::string blob((const char *)&d, sizeof(d));
return stg.set_value(pname, blob, parent_section);
}
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
bool unserialize_t_val_as_blob(t_type& d, t_storage& stg, section* parent_section, const char* pname)
{
assert_blob_serializable<t_type>();
std::string blob;
if(!stg.get_value(pname, blob, parent_section))
return false;
CHECK_AND_ASSERT_MES(blob.size() == sizeof(d), false, "unserialize_t_val_as_blob: size of " << typeid(t_type).name() << " = " << sizeof(t_type) << ", but stored blod size = " << blob.size() << ", value name = " << pname);
d = *(const t_type*)blob.data();
return true;
}
//-------------------------------------------------------------------------------------------------------------------
template<class serializible_type, class t_storage>
bool serialize_t_obj(const serializible_type& obj, t_storage& stg, section* parent_section, const char* pname)
{
section* child_section = stg.open_section(pname, parent_section, true);
CHECK_AND_ASSERT_MES(child_section, false, "serialize_t_obj: failed to open/create section " << pname);
return obj.store(stg, child_section);
}
//-------------------------------------------------------------------------------------------------------------------
template<class serializible_type, class t_storage>
bool unserialize_t_obj(serializible_type& obj, t_storage& stg, section* parent_section, const char* pname)
{
section* child_section = stg.open_section(pname, parent_section, false);
if(!child_section) return false;
return obj._load(stg, child_section);
}
//-------------------------------------------------------------------------------------------------------------------
template<class stl_container, class t_storage>
bool serialize_stl_container_t_val(const stl_container& container, t_storage& stg, section* parent_section, const char* pname)
{
using T = typename stl_container::value_type;
if(!container.size()) return true;
auto *arr = stg.template make_array_t<T>(pname, parent_section);
CHECK_AND_ASSERT_MES(arr, false, "failed to create array in storage");
for (auto& elem : container)
arr->push_back(elem);
return true;
}
//--------------------------------------------------------------------------------------------------------------------
template<class stl_container, class t_storage>
bool unserialize_stl_container_t_val(stl_container& container, t_storage& stg, section* parent_section, const char* pname)
{
using T = typename stl_container::value_type;
container.clear();
try {
for (auto [it, end] = stg.template converting_array_range<T>(pname, parent_section); it != end; ++it)
container.insert(container.end(), *it);
return true;
} catch (const std::out_of_range&) { // ignore silently
} catch (const std::exception& e) {
}
return false;
}
//--------------------------------------------------------------------------------------------------------------------
template<typename T, size_t Size, class t_storage>
bool unserialize_stl_container_t_val(std::array<T, Size>& array, t_storage& stg, section* parent_section, const char* pname)
{
static_assert(Size > 0, "cannot deserialize empty std::array");
size_t next_i = 0;
try {
for (auto [it, end] = stg.template converting_array_range<T>(pname, parent_section); it != end; ++it) {
CHECK_AND_ASSERT_MES(next_i < array.size(), false, "too many values to deserialize into fixed size std::array");
array[next_i++] = *it;
}
CHECK_AND_ASSERT_MES(next_i == array.size(), false, "not enough values to deserialize into fixed size std::array");
} catch (const std::out_of_range&) { return false; }
return true;
}
//--------------------------------------------------------------------------------------------------------------------
template<class stl_container, class t_storage>
bool serialize_stl_container_pod_val_as_blob(const stl_container& container, t_storage& stg, section* parent_section, const char* pname)
{
using T = typename stl_container::value_type;
assert_blob_serializable<T>();
if(!container.size()) return true;
std::string mb;
if constexpr (is_std_vector<stl_container>)
mb.append(reinterpret_cast<const char*>(container.data()), sizeof(T) * container.size());
else
{
mb.reserve(sizeof(T) * container.size());
for (const auto &v : container)
mb.append(reinterpret_cast<const char*>(&v), sizeof(T));
}
return stg.set_value(pname, mb, parent_section);
}
//--------------------------------------------------------------------------------------------------------------------
template<class stl_container, class t_storage>
bool unserialize_stl_container_pod_val_as_blob(stl_container& container, t_storage& stg, section* parent_section, const char* pname)
{
using T = typename stl_container::value_type;
assert_blob_serializable<T>();
container.clear();
std::string buff;
if (!stg.get_value(pname, buff, parent_section))
return false;
CHECK_AND_ASSERT_MES(buff.size() % sizeof(T) == 0,
false,
"size in blob " << buff.size() << " not have not zero modulo for sizeof(value_type) = " << sizeof(T) << ", type " << typeid(T).name());
if constexpr (is_std_vector<stl_container>)
{
container.resize(buff.size() / sizeof(T));
// The explicit cast to (void*) is to silence a compiler warning about non-trivial types;
// we've already verified the byte copy is okay with the assert_blob_serializable<T> above.
std::memcpy((void*) container.data(), buff.data(), buff.size());
}
else
{
// memcpy one element at a time because we have no alignment guarantee on buff's data
for (size_t i = 0; i < buff.size(); i += sizeof(T))
std::memcpy(&container.emplace_back(), buff.data() + i, sizeof(T));
}
return true;
}
//--------------------------------------------------------------------------------------------------------------------
template<class stl_container, class t_storage>
bool serialize_stl_container_t_obj(const stl_container& container, t_storage& stg, section* parent_section, const char* pname)
{
if (container.empty()) return true;
auto* sec_array = stg.template make_array_t<section>(pname, parent_section);
CHECK_AND_ASSERT_MES(sec_array, false, "failed to insert first section with section name " << pname);
for (auto& elem : container)
if (!elem.store(stg, &sec_array->emplace_back()))
return false;
return true;
}
//--------------------------------------------------------------------------------------------------------------------
template<class stl_container, class t_storage>
bool unserialize_stl_container_t_obj(stl_container& container, t_storage& stg, section* parent_section, const char* pname)
{
container.clear();
auto* arr = stg.template get_array<section>(pname, parent_section);
if (!arr) return false;
for (auto& child_section : *arr)
if (!container.emplace_back()._load(stg, &child_section))
return false;
return true;
}
//--------------------------------------------------------------------------------------------------------------------
template<typename T, size_t Size, class t_storage>
bool unserialize_stl_container_t_obj(std::array<T, Size>& out, t_storage& stg, section* parent_section, const char* pname)
{
static_assert(Size > 0, "cannot deserialize empty std::array");
auto* arr = stg.template get_array<section>(pname, parent_section);
if (!arr) return false;
CHECK_AND_ASSERT_MES(arr->size() != Size, false, "incorrect number of values to deserialize into fixed size std::array");
auto it = out.begin();
for (auto& child_section : *arr)
if (!(it++)->_load(stg, &child_section))
return false;
return true;
}
//--------------------------------------------------------------------------------------------------------------------
template <bool Serializing, typename T, typename Storage>
bool perform_serialize(T& d, Storage& stg, section* parent_section, const char* pname)
{
if constexpr (Serializing)
return kv_serialize(d, stg, parent_section, pname);
else
return kv_unserialize(d, stg, parent_section, pname);
}
template <bool Serializing, typename T, typename Storage>
bool perform_serialize_blob(T& d, Storage& stg, section* parent_section, const char* pname)
{
if constexpr (Serializing)
return serialize_t_val_as_blob(d, stg, parent_section, pname);
else
return unserialize_t_val_as_blob(d, stg, parent_section, pname);
}
template <bool Serializing, typename T, typename Storage>
bool perform_serialize_blob_container(T& d, Storage& stg, section* parent_section, const char* pname)
{
if constexpr (Serializing)
return serialize_stl_container_pod_val_as_blob(d, stg, parent_section, pname);
else
return unserialize_stl_container_pod_val_as_blob(d, stg, parent_section, pname);
}
template<class T, class Storage>
bool kv_serialize(const T& d, Storage& stg, section* parent_section, const char* pname)
{
if constexpr (is_std_optional<T>)
// Optional: only serialize if non-empty
return d ? kv_serialize(*d, stg, parent_section, pname) : false;
else if constexpr (!is_serialize_stl_container<T>)
{ // Non-container
if constexpr (is_basic_serializable<T>) // basic serializable or using portable storage:
return stg.set_value(pname, d, parent_section);
else // non-basic, non-portable serializable:
return serialize_t_obj(d, stg, parent_section, pname);
}
else if constexpr (is_basic_serializable<typename T::value_type>)
// stl container of basic or portable value type:
return serialize_stl_container_t_val(d, stg, parent_section, pname);
else
// stl containers (non-basic value type and non-portable storage), i.e. containers of custom
// serializable types.
return serialize_stl_container_t_obj(d, stg, parent_section, pname);
}
template<class T, class Storage>
bool kv_unserialize(T& d, Storage& stg, section* parent_section, const char* pname)
{
if constexpr (is_std_optional<T>) {
// Emplace a new value and try to deserialize into it
d = typename T::value_type{};
bool ret = kv_unserialize(*d, stg, parent_section, pname);
if (!ret) d.reset(); // Deserialization failed so clear the value
return ret;
}
else if constexpr (!is_serialize_stl_container<T>)
{ // Non-container
if constexpr (is_basic_serializable<T>) // basic serializable or using portable storage:
return stg.get_value(pname, d, parent_section);
else // non-basic, non-portable serializable:
return unserialize_t_obj(d, stg, parent_section, pname);
}
else if constexpr (is_basic_serializable<typename T::value_type>)
// stl container of basic or portable value type:
return unserialize_stl_container_t_val(d, stg, parent_section, pname);
else
// stl containers (non-basic value type and non-portable storage), i.e. containers of custom
// serializable types.
return unserialize_stl_container_t_obj(d, stg, parent_section, pname);
}
}
}