forked from such-gitea/wownero-lws
138 lines
4.4 KiB
C++
138 lines
4.4 KiB
C++
#pragma once
|
|
|
|
#include <utility>
|
|
|
|
#include "common/expect.h" // monero/src
|
|
#include "lmdb/error.h" // monero/src
|
|
#include "lmdb/table.h" // monero/src
|
|
#include "lmdb/util.h" // monero/src
|
|
#include "wire/msgpack.h"
|
|
|
|
namespace lmdb
|
|
{
|
|
//! Helper for grouping typical LMDB DBI options when key is fixed and value has msgpack component
|
|
template<typename K, typename V1, typename V2>
|
|
struct msgpack_table : table
|
|
{
|
|
using key_type = K;
|
|
using fixed_value_type = V1;
|
|
using msgpack_value_type = V2;
|
|
using value_type = std::pair<fixed_value_type, msgpack_value_type>;
|
|
|
|
constexpr explicit msgpack_table(const char* name, unsigned flags = 0, MDB_cmp_func value_cmp = nullptr) noexcept
|
|
: table{name, flags, &lmdb::less<lmdb::native_type<K>>, value_cmp}
|
|
{}
|
|
|
|
static expect<key_type> get_key(MDB_val key)
|
|
{
|
|
if (key.mv_size != sizeof(key_type))
|
|
return {lmdb::error(MDB_BAD_VALSIZE)};
|
|
|
|
key_type out;
|
|
std::memcpy(std::addressof(out), static_cast<char*>(key.mv_data), sizeof(out));
|
|
return out;
|
|
}
|
|
|
|
static expect<epee::byte_slice> make_value(const fixed_value_type& val1, const msgpack_value_type& val2)
|
|
{
|
|
epee::byte_stream initial;
|
|
initial.write({reinterpret_cast<const char*>(std::addressof(val1)), sizeof(val1)});
|
|
|
|
wire::msgpack_slice_writer dest{std::move(initial), true};
|
|
try
|
|
{
|
|
wire_write::bytes(dest, val2);
|
|
}
|
|
catch (const wire::exception& e)
|
|
{
|
|
return {e.code()};
|
|
}
|
|
return epee::byte_slice{dest.take_sink()};
|
|
}
|
|
|
|
/*!
|
|
\tparam U must be same as `V`; used for sanity checking.
|
|
\tparam F is the type within `U` that is being extracted.
|
|
\tparam offset to `F` within `U`.
|
|
|
|
\note If using `F` and `offset` to retrieve a specific field, use
|
|
`MONERO_FIELD` macro in `src/lmdb/util.h` which calculates the
|
|
offset automatically.
|
|
|
|
\return Value of type `F` at `offset` within `value` which has
|
|
type `U`.
|
|
*/
|
|
template<typename U, typename F = U, std::size_t offset = 0>
|
|
static expect<F> get_fixed_value(MDB_val value) noexcept
|
|
{
|
|
static_assert(std::is_same<U, V1>(), "bad MONERO_FIELD?");
|
|
static_assert(std::is_pod<F>(), "F must be POD");
|
|
static_assert(sizeof(F) + offset <= sizeof(U), "bad field type and/or offset");
|
|
|
|
if (value.mv_size < sizeof(U))
|
|
return {lmdb::error(MDB_BAD_VALSIZE)};
|
|
|
|
F out;
|
|
std::memcpy(std::addressof(out), static_cast<char*>(value.mv_data) + offset, sizeof(out));
|
|
return out;
|
|
}
|
|
|
|
static expect<value_type> get_value(MDB_val value) noexcept
|
|
{
|
|
if (value.mv_size < sizeof(fixed_value_type))
|
|
return {lmdb::error(MDB_BAD_VALSIZE)};
|
|
std::pair<fixed_value_type, msgpack_value_type> out;
|
|
std::memcpy(std::addressof(out.first), static_cast<const char*>(value.mv_data), sizeof(out.first));
|
|
|
|
auto msgpack_bytes = lmdb::to_byte_span(value);
|
|
msgpack_bytes.remove_prefix(sizeof(out.first));
|
|
|
|
const std::error_code error = wire::msgpack::from_bytes(epee::byte_slice{{msgpack_bytes}}, out.second);
|
|
if (error)
|
|
return error;
|
|
return out;
|
|
}
|
|
|
|
//! Easier than doing another iterator .. for now :/
|
|
static expect<std::vector<std::pair<key_type, std::vector<value_type>>>> get_all(MDB_cursor& cur)
|
|
{
|
|
MDB_val key{};
|
|
MDB_val value{};
|
|
int err = mdb_cursor_get(&cur, &key, &value, MDB_FIRST);
|
|
std::vector<std::pair<key_type, std::vector<value_type>>> out;
|
|
for ( ; /* for every key */ ; )
|
|
{
|
|
if (err)
|
|
{
|
|
if (err != MDB_NOTFOUND)
|
|
return {lmdb::error(err)};
|
|
break;
|
|
}
|
|
|
|
expect<key_type> next_key = get_key(key);
|
|
if (!next_key)
|
|
return next_key.error();
|
|
out.emplace_back(std::move(*next_key), std::vector<value_type>{});
|
|
|
|
for ( ; /* for every value at key */ ; )
|
|
{
|
|
if (err)
|
|
{
|
|
if (err != MDB_NOTFOUND)
|
|
return {lmdb::error(err)};
|
|
break;
|
|
}
|
|
expect<value_type> next_value = get_value(value);
|
|
if (!next_value)
|
|
return next_value.error();
|
|
out.back().second.push_back(std::move(*next_value));
|
|
err = mdb_cursor_get(&cur, &key, &value, MDB_NEXT_DUP);
|
|
}
|
|
|
|
err = mdb_cursor_get(&cur, &key, &value, MDB_NEXT_NODUP);
|
|
} // every key
|
|
return out;
|
|
}
|
|
};
|
|
} // lmdb
|