oxen-core/contrib/epee/include/epee/storages/portable_storage_to_bin.h

154 lines
5.9 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 "../pragma_comp_defs.h"
#include "../misc_language.h"
#include "portable_storage_base.h"
#include <boost/endian/conversion.hpp>
#include <lokimq/variant.h>
namespace epee
{
namespace serialization
{
namespace detail
{
template<class IntT>
void pack_varint(std::ostream& strm, uint8_t type_or, IntT v);
} // namespace detail
inline void pack_varint(std::ostream& strm, uint64_t val)
{
// the two least significant bits are used for size information
if (val < (1ULL << 6))
detail::pack_varint(strm, PORTABLE_RAW_SIZE_MARK_6BIT, static_cast<uint8_t>(val));
else if (val < (1ULL << 14))
detail::pack_varint(strm, PORTABLE_RAW_SIZE_MARK_14BIT, static_cast<uint16_t>(val));
else if (val < (1ULL << 30))
detail::pack_varint(strm, PORTABLE_RAW_SIZE_MARK_30BIT, static_cast<uint32_t>(val));
else if (val < (1ULL << 62))
detail::pack_varint(strm, PORTABLE_RAW_SIZE_MARK_62BIT, val);
else
ASSERT_MES_AND_THROW("failed to pack varint -- integer value too large: " << val << " >= 2^62");
}
inline void pack_entry_to_buff(std::ostream& strm, const std::string& v)
{
CHECK_AND_ASSERT_THROW_MES(v.size() < MAX_STRING_LEN_POSSIBLE, "string to store is too large: " << v.size());
pack_varint(strm, v.size());
if (!v.empty())
strm.write(v.data(), v.size());
}
template <typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
void pack_entry_to_buff(std::ostream& strm, T v)
{
if constexpr (sizeof(T) > 1)
boost::endian::native_to_little_inplace(v);
strm.write(reinterpret_cast<const char*>(&v), sizeof(v));
}
inline void pack_entry_to_buff(std::ostream& strm, double v)
{
static_assert(std::numeric_limits<double>::is_iec559 && sizeof(double) == 8 &&
(boost::endian::order::native == boost::endian::order::big || boost::endian::order::native == boost::endian::order::little));
char* buff = reinterpret_cast<char*>(&v);
if constexpr (boost::endian::order::native == boost::endian::order::big) {
size_t i = 8;
while (i) strm.put(buff[--i]);
} else {
strm.write(buff, 8);
}
}
void pack_entry_to_buff(std::ostream& strm, const storage_entry& se);
void pack_entry_to_buff(std::ostream& strm, const section& se);
inline void pack_entry_to_buff(std::ostream& strm, const array_entry& ae)
{
var::visit([&strm](const auto& arr) {
using T = typename std::remove_const_t<std::remove_reference_t<decltype(arr)>>::value_type;
constexpr uint8_t tag = SERIALIZE_FLAG_ARRAY | SERIALIZE_TYPE_TAG<T>;
strm.write(reinterpret_cast<const char*>(&tag), 1);
pack_varint(strm, arr.size());
for (auto& v : arr)
pack_entry_to_buff(strm, v);
}, ae);
}
inline void pack_entry_to_buff(std::ostream& strm, const storage_entry& se)
{
var::visit([&strm](const auto& v) {
using T = std::remove_const_t<std::remove_reference_t<decltype(v)>>;
if constexpr (!std::is_same_v<T, array_entry>) // array_entries get a combined flag+value instead.
strm.write(reinterpret_cast<const char*>(&SERIALIZE_TYPE_TAG<T>), 1);
pack_entry_to_buff(strm, v);
}, se);
}
inline void pack_entry_to_buff(std::ostream& strm, const section& sec)
{
typedef std::map<std::string, storage_entry>::value_type section_pair;
pack_varint(strm, sec.m_entries.size());
for(const section_pair& se: sec.m_entries)
{
CHECK_AND_ASSERT_THROW_MES(se.first.size() < std::numeric_limits<uint8_t>::max(), "storage_entry_name is too long: " << se.first.size() << ", val: " << se.first);
uint8_t len = static_cast<uint8_t>(se.first.size());
strm.write((const char*)&len, sizeof(len));
strm.write(se.first.data(), size_t(len));
pack_entry_to_buff(strm, se.second);
}
}
namespace detail
{
template<class IntT>
void pack_varint(std::ostream& strm, uint8_t type_or, IntT v)
{
// Left shift it and store the size tag in the bottom two bits. We're always guaranteed
// (below) to have enough space for the shift to not drop significant bits.
v <<= 2;
v |= type_or;
pack_entry_to_buff(strm, v);
}
} // namespace detail
}
}