mirror of https://github.com/oxen-io/oxen-core.git
256 lines
12 KiB
C++
256 lines
12 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 "portable_storage_base.h"
|
|
#include "portable_storage_to_bin.h"
|
|
#include "portable_storage_from_bin.h"
|
|
#include "portable_storage_to_json.h"
|
|
#include "portable_storage_from_json.h"
|
|
#include "portable_storage_val_converters.h"
|
|
#include "../span.h"
|
|
|
|
namespace epee
|
|
{
|
|
namespace serialization
|
|
{
|
|
/************************************************************************/
|
|
/* */
|
|
/************************************************************************/
|
|
class portable_storage
|
|
{
|
|
public:
|
|
portable_storage() = default;
|
|
virtual ~portable_storage() = default;
|
|
section* open_section(const std::string& section_name, section* parent_section, bool create_if_notexist = false);
|
|
template <typename T>
|
|
bool get_value(const std::string& value_name, T& val, section* parent_section);
|
|
bool get_value(const std::string& value_name, storage_entry& val, section* parent_section);
|
|
template <class T>
|
|
bool set_value(const std::string& value_name, const T& target, section* parent_section);
|
|
|
|
// Class for iterating through a type with automatic conversion to `T` when dereferencing.
|
|
template <typename T>
|
|
class converting_array_iterator {
|
|
array_entry& array;
|
|
size_t index = 0;
|
|
public:
|
|
using value_type = T;
|
|
using difference_type = std::ptrdiff_t;
|
|
using pointer = T*;
|
|
using reference = T;
|
|
using iterator_category = std::input_iterator_tag;
|
|
|
|
explicit converting_array_iterator(array_entry& array) : array{array} {}
|
|
converting_array_iterator(array_entry& array, bool end) : array{array} {
|
|
if (end)
|
|
index = var::visit([](auto& a) { return a.size(); }, array);
|
|
}
|
|
// Converting dereference operator. Returns the converted value. Note that this can throw
|
|
// if the requested conversion fails.
|
|
T operator*() const {
|
|
return var::visit([this](auto& a) { T val; convert_t(a[index], val); return val; }, array);
|
|
}
|
|
bool operator==(const converting_array_iterator& other) const { return &array == &other.array && index == other.index; }
|
|
bool operator!=(const converting_array_iterator& other) const { return !(*this == other); }
|
|
converting_array_iterator operator++(int) {
|
|
auto old = *this;
|
|
index++;
|
|
return old;
|
|
}
|
|
converting_array_iterator& operator++() {
|
|
++index;
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
// Returns an input iterator pair for an array with automatic value conversion from the stored
|
|
// type to T. Throws std::out_of_range if the member doesn't exist or std::bad_variant_access
|
|
// if the member isn't an array.
|
|
template <typename T>
|
|
std::pair<converting_array_iterator<T>, converting_array_iterator<T>>
|
|
converting_array_range(const std::string& value_name, section* parent_section)
|
|
{
|
|
if (!parent_section) parent_section = &m_root;
|
|
storage_entry* pentry = find_storage_entry(value_name, parent_section);
|
|
if (!pentry)
|
|
throw std::out_of_range{value_name + " does not exist"};
|
|
auto& ar_entry = var::get<array_entry>(*pentry);
|
|
return {converting_array_iterator<T>{ar_entry}, converting_array_iterator<T>{ar_entry, true}};
|
|
}
|
|
|
|
// Accesses an existing array value of the given type. If the given value does not exist or
|
|
// is not an array of the given type then this returns nullptr, otherwise returns a pointer to
|
|
// the array_t<T> (which is a std::vector<T>, or a std::deque<bool>). Note that, unlike
|
|
// array_range(), this does not convert (so, for example, you can't get uint64_t's if the
|
|
// stored values are uint32_t's).
|
|
template <typename T>
|
|
array_t<T>* get_array(const std::string& value_name, section* parent_section) {
|
|
if (!parent_section) parent_section = &m_root;
|
|
if (storage_entry* pentry = find_storage_entry(value_name, parent_section))
|
|
if (auto* ar_entry = std::get_if<array_entry>(pentry))
|
|
if (auto* array = std::get_if<array_t<T>>(ar_entry))
|
|
return array;
|
|
return nullptr;
|
|
}
|
|
|
|
/// Inserts a <T> array with the given name inside parent_section. If the element already
|
|
/// exists it is replaced (if not an array_t<T>) or cleared. Returns a pointer to the
|
|
/// stored array_entry (which holds an array_t<T>). Returns nullptr on error.
|
|
template <typename T>
|
|
array_entry* make_array(const std::string& value_name, section* parent_section);
|
|
|
|
/// Same as above, but returns the array_t<T>* rather than the array_entry.
|
|
template <typename T>
|
|
array_t<T>* make_array_t(const std::string& value_name, section* parent_section);
|
|
|
|
//------------------------------------------------------------------------
|
|
//delete entry (section, value or array)
|
|
bool delete_entry(const std::string& pentry_name, section* parent_section = nullptr);
|
|
|
|
//-------------------------------------------------------------------------------
|
|
bool store_to_binary(std::string& target);
|
|
bool load_from_binary(const epee::span<const uint8_t> target);
|
|
bool load_from_binary(std::string_view target) { return load_from_binary(epee::strspan<uint8_t>(target)); }
|
|
bool dump_as_json(std::string& targetObj, size_t indent = 0, bool insert_newlines = true);
|
|
bool load_from_json(std::string_view source);
|
|
|
|
/// Lets you store a pointer to some arbitrary context object; typically used to pass some
|
|
/// context to dependent child objects.
|
|
template <typename T> void set_context(const T* obj) { context_type = &typeid(T); context = obj; }
|
|
/// Clears a context pointer stored with set_context
|
|
void clear_context() { context_type = nullptr; context = nullptr; }
|
|
/// Retrieves context set by set_context(). Returns nullptr if the stored type doesn't match
|
|
/// `T`, or if no context pointer is stored at all.
|
|
template <typename T> const T* get_context() {
|
|
return (context && context_type && *context_type == typeid(T))
|
|
? static_cast<const T*>(context)
|
|
: nullptr;
|
|
}
|
|
|
|
private:
|
|
section m_root;
|
|
section* get_root_section() {return &m_root;}
|
|
storage_entry* find_storage_entry(const std::string& pentry_name, section* psection);
|
|
template<class entry_type>
|
|
storage_entry* insert_new_entry_get_storage_entry(const std::string& pentry_name, section* psection, const entry_type& entry);
|
|
|
|
section* insert_new_section(const std::string& pentry_name, section* psection);
|
|
|
|
const void* context = nullptr;
|
|
const std::type_info* context_type = nullptr;
|
|
|
|
#pragma pack(push)
|
|
#pragma pack(1)
|
|
struct storage_block_header
|
|
{
|
|
uint32_t m_signature_a;
|
|
uint32_t m_signature_b;
|
|
uint8_t m_ver;
|
|
};
|
|
#pragma pack(pop)
|
|
};
|
|
template <typename T>
|
|
bool portable_storage::get_value(const std::string& value_name, T& val, section* parent_section)
|
|
{
|
|
static_assert(variant_contains<T, storage_entry>);
|
|
//TRY_ENTRY();
|
|
if(!parent_section) parent_section = &m_root;
|
|
storage_entry* pentry = find_storage_entry(value_name, parent_section);
|
|
if(!pentry)
|
|
return false;
|
|
|
|
var::visit([&val](const auto& v) { convert_t(v, val); }, *pentry);
|
|
return true;
|
|
//CATCH_ENTRY("portable_storage::template<>get_value", false);
|
|
}
|
|
//---------------------------------------------------------------------------------------------------------------
|
|
template <typename T>
|
|
bool portable_storage::set_value(const std::string& value_name, const T& v, section* parent_section)
|
|
{
|
|
static_assert(variant_contains<T, storage_entry> || std::is_same_v<T, storage_entry>);
|
|
TRY_ENTRY();
|
|
if(!parent_section)
|
|
parent_section = &m_root;
|
|
storage_entry* pentry = find_storage_entry(value_name, parent_section);
|
|
if(!pentry)
|
|
{
|
|
pentry = insert_new_entry_get_storage_entry(value_name, parent_section, v);
|
|
if(!pentry)
|
|
return false;
|
|
return true;
|
|
}
|
|
*pentry = std::move(v);
|
|
return true;
|
|
CATCH_ENTRY("portable_storage::template<>set_value", false);
|
|
}
|
|
//---------------------------------------------------------------------------------------------------------------
|
|
template<class entry_type>
|
|
storage_entry* portable_storage::insert_new_entry_get_storage_entry(const std::string& pentry_name, section* psection, const entry_type& entry)
|
|
{
|
|
TRY_ENTRY();
|
|
CHECK_AND_ASSERT(psection, nullptr);
|
|
auto ins_res = psection->m_entries.emplace(pentry_name, entry);
|
|
return &ins_res.first->second;
|
|
CATCH_ENTRY("portable_storage::insert_new_entry_get_storage_entry", nullptr);
|
|
}
|
|
//---------------------------------------------------------------------------------------------------------------
|
|
template <typename T>
|
|
array_entry* portable_storage::make_array(const std::string& value_name, section* parent_section)
|
|
{
|
|
TRY_ENTRY();
|
|
if(!parent_section) parent_section = &m_root;
|
|
storage_entry* pentry = find_storage_entry(value_name, parent_section);
|
|
if(!pentry)
|
|
{
|
|
pentry = insert_new_entry_get_storage_entry(value_name, parent_section, array_entry(array_t<T>{}));
|
|
if (!pentry)
|
|
return nullptr;
|
|
}
|
|
if (!std::holds_alternative<array_entry>(*pentry))
|
|
*pentry = array_entry(std::in_place_type<array_t<T>>);
|
|
|
|
auto& arr = var::get<array_entry>(*pentry);
|
|
if (auto* arr_t = std::get_if<array_t<T>>(&arr))
|
|
arr_t->clear();
|
|
else
|
|
arr = array_t<T>{};
|
|
|
|
return &arr;
|
|
|
|
CATCH_ENTRY("portable_storage::make_array", nullptr);
|
|
}
|
|
template <typename T>
|
|
array_t<T>* portable_storage::make_array_t(const std::string& value_name, section* parent_section)
|
|
{
|
|
return std::get_if<array_t<T>>(make_array<T>(value_name, parent_section));
|
|
}
|
|
}
|
|
}
|