2#ifndef _MDBX_CONTAINERS_UTILS_HPP_INCLUDED
3#define _MDBX_CONTAINERS_UTILS_HPP_INCLUDED
12#if __cplusplus >= 201703L
13# define MDBXC_NODISCARD [[nodiscard]]
15# define MDBXC_NODISCARD
24 if (rc != MDBX_SUCCESS) {
25 throw MdbxException(context +
": (" + std::to_string(rc) +
") " + std::string(mdbx_strerror(rc)), rc);
34 std::memcpy(&u, &f,
sizeof(uint32_t));
35 return (u & 0x80000000u) ? ~u : (u ^ 0x80000000u);
43 std::memcpy(&u, &d,
sizeof(uint64_t));
44 return (u & 0x8000000000000000ull) ? ~u : (u ^ 0x8000000000000000ull);
55 static auto check(U*) ->
decltype(std::declval<const U>().to_bytes(), std::true_type());
57 static std::false_type
check(...);
68 static auto check(U*) ->
decltype(U::from_bytes((
const void*)0,
size_t(0)), std::true_type());
70 static std::false_type
check(...);
81 static auto check(U*) ->
decltype(
typename U::value_type(), std::true_type());
83 static std::false_type
check(...);
96 std::is_same<T, int>::value || std::is_same<T, int32_t>::value ||
97 std::is_same<T, uint32_t>::value || std::is_same<T, int64_t>::value ||
98 std::is_same<T, uint64_t>::value || std::is_same<T, float>::value ||
99 std::is_same<T, double>::value || std::is_same<T, char>::value ||
100 std::is_same<T, unsigned char>::value
101 ? MDBX_INTEGERKEY :
static_cast<MDBX_db_flags_t
>(0);
113 if (std::is_same<T, std::string>::value) {
118 if (std::is_same<T, int>::value ||
119 std::is_same<T, int32_t>::value ||
120 std::is_same<T, uint32_t>::value ||
121 std::is_same<T, float>::value) {
122 return sizeof(uint32_t);
126 if (std::is_same<T, int64_t>::value ||
127 std::is_same<T, uint64_t>::value ||
128 std::is_same<T, double>::value) {
129 return sizeof(uint64_t);
134#
if __cplusplus >= 201703L
135 std::is_same<T, std::vector<std::byte> >::value ||
137 std::is_same<T, std::vector<uint8_t> >::value ||
138 std::is_same<T, std::vector<char> >::value ||
139 std::is_same<T, std::vector<unsigned char> >::value) {
176 v.iov_base = (n ?
const_cast<void*
>(p) :
nullptr);
185 if (n) std::memcpy(
bytes.data(), p, n);
187 v.iov_base = (
bytes.empty() ?
nullptr :
static_cast<void*
>(
bytes.data()));
188 v.iov_len =
bytes.size();
195 v.iov_base = (
bytes.empty() ?
nullptr :
const_cast<void*
>(
static_cast<const void*
>(
bytes.data())));
196 v.iov_len =
bytes.size();
203 std::memcpy(
small, p, n);
205 v.iov_base = (n ?
static_cast<void*
>(
small) :
nullptr);
214 if (n) std::memcpy(
bytes.data(), p, n);
228 template <
typename T>
229 typename std::enable_if<!has_to_bytes<T>::value && !std::is_same<T, std::string>::value && !std::is_trivially_copyable<T>::value, MDBX_val>::type
233 static_assert(
sizeof(T) == 0,
"Unsupported type for serialize_key");
235 val.iov_base =
nullptr;
243 typename std::enable_if<std::is_same<T, std::string>::value, MDBX_val>::type
252 typename std::enable_if<
253# if __cplusplus >= 201703L
254 std::is_same<T, std::vector<std::byte>>::value ||
256 std::is_same<T, std::vector<uint8_t>>::value ||
257 std::is_same<T, std::vector<char>>::value ||
258 std::is_same<T, std::vector<unsigned char>>::value, MDBX_val>::type
267 typename std::enable_if<
268 std::is_integral<T>::value &&
269 (
sizeof(T) <= 2), MDBX_val>::type
271 static_assert(
sizeof(uint32_t) == 4,
"Expected 4-byte wrapper");
272 uint32_t temp =
static_cast<uint32_t
>(key);
279 typename std::enable_if<
280 std::is_same<T, int32_t>::value ||
281 std::is_same<T, uint32_t>::value, MDBX_val>::type
283 static_assert(
sizeof(uint32_t) == 4,
"Expected 4-byte integer");
284# if MDBXC_SAFE_INTEGERKEY
295 typename std::enable_if<
296 std::is_same<T, float>::value, MDBX_val>::type
298 static_assert(
sizeof(uint32_t) == 4,
"Expected 4-byte integer");
306 typename std::enable_if<
307 std::is_same<T, int64_t>::value ||
308 std::is_same<T, uint64_t>::value, MDBX_val>::type
310 static_assert(
sizeof(uint64_t) == 8,
"Expected 8-byte integer");
311# if MDBXC_SAFE_INTEGERKEY
322 typename std::enable_if<
323 std::is_same<T, double>::value, MDBX_val>::type
325 static_assert(
sizeof(uint64_t) == 8,
"Expected 8-byte integer");
333 typename std::enable_if<
334 std::is_trivially_copyable<T>::value &&
335 !std::is_same<T, std::string>::value &&
336 !(std::is_integral<T>::value &&
sizeof(T) <= 2) &&
337 !std::is_same<T, int32_t>::value &&
338 !std::is_same<T, uint32_t>::value &&
339 !std::is_same<T, float>::value &&
340 !std::is_same<T, int64_t>::value &&
341 !std::is_same<T, uint64_t>::value &&
342 !std::is_same<T, double>::value, MDBX_val>::type
353 const size_t num_bytes = (N + 7) / 8;
354 std::array<uint8_t, (N + 7) / 8> buffer;
356 for (
size_t i = 0; i < N; ++i) {
357 if (data[i]) buffer[i / 8] |= (1 << (i % 8));
359 return sc.
view_copy(buffer.data(), num_bytes);
368 template <
typename T>
369 typename std::enable_if<
371 !std::is_same<T, std::vector<typename T::value_type>>::value &&
372 !std::is_trivially_copyable<typename T::value_type>::value &&
374 !std::is_same<T, std::string>::value &&
375 !std::is_trivially_copyable<T>::value, MDBX_val>::type
379 static_assert(
sizeof(T) == 0,
"Unsupported type for serialize_value");
381 val.iov_base =
nullptr;
389 typename std::enable_if<std::is_same<T, std::string>::value, MDBX_val>::type
398 template <
typename T>
399 typename std::enable_if<
401 std::is_trivially_copyable<typename T::value_type>::value &&
403 std::is_same<T, std::deque<typename T::value_type>>::value ||
404 std::is_same<T, std::list<typename T::value_type>>::value ||
405 std::is_same<T, std::set<typename T::value_type>>::value ||
406 std::is_same<T, std::unordered_set<typename T::value_type>>::value
410 using Elem =
typename T::value_type;
411 sc.
bytes.resize(container.size() *
sizeof(Elem));
412 auto* out =
reinterpret_cast<Elem*
>(sc.
bytes.data());
413 std::copy(container.begin(), container.end(), out);
420 typename std::enable_if<
422 std::is_same<T, std::vector<typename T::value_type>>::value &&
423 std::is_trivially_copyable<typename T::value_type>::value,
426 using Elem =
typename T::value_type;
427 sc.
bytes.resize(container.size() *
sizeof(Elem));
428 std::memcpy(sc.
bytes.data(),
437 typename std::enable_if<has_to_bytes<T>::value, MDBX_val>::type
439 sc.
bytes = value.to_bytes();
446 typename std::enable_if<
448 std::is_trivially_copyable<T>::value,
458 typename std::enable_if<
460 std::is_same<typename T::value_type, std::string>::value,
464 for (
const auto& str : container) {
465 uint32_t len =
static_cast<uint32_t
>(str.size());
467 reinterpret_cast<const uint8_t*
>(&len),
468 reinterpret_cast<const uint8_t*
>(&len) +
sizeof(uint32_t));
469 sc.
bytes.insert(sc.
bytes.end(), str.begin(), str.end());
481 typename std::enable_if<
484 !std::is_same<T, std::string>::value &&
485 !std::is_trivially_copyable<T>::value, T>::type
488 static_assert(
sizeof(T) == 0,
"Unsupported type for deserialize_value");
496 typename std::enable_if<std::is_same<T, std::string>::value, T>::type
498 return std::string(
static_cast<const char*
>(val.iov_base), val.iov_len);
504 typename std::enable_if<
505# if __cplusplus >= 201703L
506 std::is_same<T, std::vector<std::byte>>::value ||
508 std::is_same<T, std::vector<uint8_t>>::value ||
509 std::is_same<T, std::vector<char>>::value ||
510 std::is_same<T, std::vector<unsigned char>>::value, T>::type
512 const uint8_t* ptr =
static_cast<const uint8_t*
>(val.iov_base);
513 return T(ptr, ptr + val.iov_len);
519 typename std::enable_if<
520# if __cplusplus >= 201703L
521 std::is_same<T, std::deque<std::byte>>::value ||
523 std::is_same<T, std::deque<uint8_t>>::value ||
524 std::is_same<T, std::deque<char>>::value ||
525 std::is_same<T, std::deque<unsigned char>>::value, T>::type
527 const uint8_t* ptr =
static_cast<const uint8_t*
>(val.iov_base);
528 return T(ptr, ptr + val.iov_len);
534 typename std::enable_if<
535# if __cplusplus >= 201703L
536 std::is_same<T, std::list<std::byte>>::value ||
538 std::is_same<T, std::list<uint8_t>>::value ||
539 std::is_same<T, std::list<char>>::value ||
540 std::is_same<T, std::list<unsigned char>>::value, T>::type
542 const uint8_t* ptr =
static_cast<const uint8_t*
>(val.iov_base);
543 return T(ptr, ptr + val.iov_len);
549 typename std::enable_if<
551 std::is_same<T, std::vector<typename T::value_type>>::value &&
552 std::is_trivially_copyable<typename T::value_type>::value &&
553# if __cplusplus >= 201703L
554 !std::is_same<T, std::vector<std::byte>>::value &&
556 !std::is_same<T, std::vector<uint8_t>>::value &&
557 !std::is_same<T, std::vector<char>>::value &&
558 !std::is_same<T, std::vector<unsigned char>>::value, T>::type
560 typedef typename T::value_type Elem;
561 if (val.iov_len %
sizeof(Elem) != 0)
562 throw std::runtime_error(
"deserialize_value: size not aligned");
563 const size_t count = val.iov_len /
sizeof(Elem);
564 const Elem* data =
static_cast<const Elem*
>(val.iov_base);
565 return T(data, data + count);
571 typename std::enable_if<
572 (std::is_same<T, std::deque<typename T::value_type>>::value ||
573 std::is_same<T, std::list<typename T::value_type>>::value) &&
574 std::is_trivially_copyable<typename T::value_type>::value, T>::type
576 typedef typename T::value_type Elem;
577 if (val.iov_len %
sizeof(Elem) != 0) {
578 throw std::runtime_error(
"deserialize_value: size not aligned");
580 const size_t count = val.iov_len /
sizeof(Elem);
581 const Elem* data =
static_cast<const Elem*
>(val.iov_base);
582 return T(data, data + count);
588 typename std::enable_if<
590 std::is_same<T, std::set<typename T::value_type>>::value ||
591 std::is_same<T, std::unordered_set<typename T::value_type>>::value
593 std::is_trivially_copyable<typename T::value_type>::value,
596 typedef typename T::value_type Elem;
597 if (val.iov_len %
sizeof(Elem) != 0)
598 throw std::runtime_error(
"deserialize_value: size not aligned");
599 const size_t count = val.iov_len /
sizeof(Elem);
600 const Elem* data =
static_cast<const Elem*
>(val.iov_base);
601 return T(data, data + count);
607 typename std::enable_if<
610 if (val.iov_len !=
sizeof(T)) {
611 throw std::runtime_error(
"deserialize_value: size mismatch");
614 std::memcpy(&out, val.iov_base,
sizeof(T));
621 typename std::enable_if<has_from_bytes<T>::value, T>::type
623 return T::from_bytes(val.iov_base, val.iov_len);
629 typename std::enable_if<
631 std::is_same<typename T::value_type, std::string>::value,
634 const uint8_t* ptr =
static_cast<const uint8_t*
>(val.iov_base);
635 const uint8_t* end = ptr + val.iov_len;
638 while (ptr +
sizeof(uint32_t) <= end) {
640 std::memcpy(&len, ptr,
sizeof(uint32_t));
641 ptr +=
sizeof(uint32_t);
644 throw std::runtime_error(
"deserialize_value: corrupted data (length overflow)");
646 result.emplace_back(
reinterpret_cast<const char*
>(ptr), len);
651 throw std::runtime_error(
"deserialize_value: trailing data after deserialization");
659 typename std::enable_if<
660 std::is_same<T, std::set<std::string>>::value ||
661 std::is_same<T, std::unordered_set<std::string>>::value,
664 const uint8_t* ptr =
static_cast<const uint8_t*
>(val.iov_base);
665 const uint8_t* end = ptr + val.iov_len;
668 while (ptr +
sizeof(uint32_t) <= end) {
670 std::memcpy(&len, ptr,
sizeof(uint32_t));
671 ptr +=
sizeof(uint32_t);
674 throw std::runtime_error(
"deserialize_value: corrupted data (length overflow)");
676 result.insert(std::string(
reinterpret_cast<const char*
>(ptr), len));
681 throw std::runtime_error(
"deserialize_value: trailing data after deserialization");
690#undef MDBXC_NODISCARD
Represents a specific exception for MDBX-related errors.
size_t get_key_size(const T &key)
Returns the size in bytes of a given key type.
std::enable_if<!has_value_type< T >::value &&!std::is_same< T, std::vector< typenameT::value_type > >::value &&!std::is_trivially_copyable< typenameT::value_type >::value &&!has_to_bytes< T >::value &&!std::is_same< T, std::string >::value &&!std::is_trivially_copyable< T >::value, MDBX_val >::type serialize_value(const T &value, SerializeScratch &sc)
Serializes a general value into MDBX_val.
uint32_t sortable_key_from_float(float f)
Convert IEEE754 float to monotonic sortable unsigned int key.
std::enable_if<!has_value_type< T >::value &&!has_from_bytes< T >::value &&!std::is_same< T, std::string >::value &&!std::is_trivially_copyable< T >::value, T >::type deserialize_value(const MDBX_val &val)
Deserializes a value from MDBX_val into type T.
MDBX_db_flags_t get_mdbx_flags()
Returns MDBX flags for a given key type.
std::enable_if<!has_to_bytes< T >::value &&!std::is_same< T, std::string >::value &&!std::is_trivially_copyable< T >::value, MDBX_val >::type serialize_key(const T &key, SerializeScratch &sc)
Serializes a key into MDBX_val for database operations.
uint64_t sortable_key_from_double(double d)
Convert IEEE754 double to monotonic sortable unsigned int key.
void check_mdbx(int rc, const std::string &context)
Throws an MdbxException if MDBX return code indicates an error.
Per-call scratch buffer to produce MDBX_val without using thread_local.
MDBXC_NODISCARD MDBX_val view_small_copy(const void *p, size_t n) noexcept
Copy n bytes into the small inline buffer and return a view.
void clear() noexcept
Optionally clear and release capacity.
MDBXC_NODISCARD MDBX_val view_bytes() const noexcept
Return a view over current bytes (no copy).
unsigned char small[16]
Small inline buffer (16 bytes) aligned to 8 — good for 4/8-byte keys.
std::vector< uint8_t > bytes
Owned dynamic buffer for cases when data must be copied.
static MDBXC_NODISCARD MDBX_val view(const void *p, size_t n) noexcept
Zero-copy view over external memory (no ownership).
void assign_bytes(const void *p, size_t n)
Replace bytes content with a copy of p..p+n .
MDBXC_NODISCARD MDBX_val view_copy(const void *p, size_t n)
Copy n bytes from p into bytes and return a view.
Trait to check if a type provides a static from_bytes() method.
static auto check(U *) -> decltype(U::from_bytes((const void *) 0, size_t(0)), std::true_type())
static std::false_type check(...)
Trait to check if a type provides a to_bytes() member.
static std::false_type check(...)
static auto check(U *) -> decltype(std::declval< const U >().to_bytes(), std::true_type())
Trait indicating that a container defines value_type.
static auto check(U *) -> decltype(typename U::value_type(), std::true_type())
static std::false_type check(...)