2#ifndef _MDBX_CONTAINERS_UTILS_HPP_INCLUDED
3#define _MDBX_CONTAINERS_UTILS_HPP_INCLUDED
19 if (rc != MDBX_SUCCESS) {
20 throw MdbxException(context +
": (" + std::to_string(rc) +
") " + std::string(mdbx_strerror(rc)), rc);
32 static auto check(U*) ->
decltype(std::declval<const U>().to_bytes(), std::true_type());
34 static std::false_type
check(...);
45 static auto check(U*) ->
decltype(U::from_bytes((
const void*)0,
size_t(0)), std::true_type());
47 static std::false_type
check(...);
58 static auto check(U*) ->
decltype(
typename U::value_type(), std::true_type());
60 static std::false_type
check(...);
73 std::is_same<T, int>::value || std::is_same<T, int32_t>::value ||
74 std::is_same<T, uint32_t>::value || std::is_same<T, int64_t>::value ||
75 std::is_same<T, uint64_t>::value || std::is_same<T, float>::value ||
76 std::is_same<T, double>::value || std::is_same<T, char>::value ||
77 std::is_same<T, unsigned char>::value
78 ? MDBX_INTEGERKEY :
static_cast<MDBX_db_flags_t
>(0);
90 if (std::is_same<T, std::string>::value) {
95 if (std::is_same<T, int>::value ||
96 std::is_same<T, int32_t>::value ||
97 std::is_same<T, uint32_t>::value ||
98 std::is_same<T, float>::value) {
99 return sizeof(uint32_t);
103 if (std::is_same<T, int64_t>::value ||
104 std::is_same<T, uint64_t>::value ||
105 std::is_same<T, double>::value) {
106 return sizeof(uint64_t);
111#
if __cplusplus >= 201703L
112 std::is_same<T, std::vector<std::byte> >::value ||
114 std::is_same<T, std::vector<uint8_t> >::value ||
115 std::is_same<T, std::vector<char> >::value ||
116 std::is_same<T, std::vector<unsigned char> >::value) {
130 template <
typename T>
131 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
133 static_assert(
sizeof(T) == 0,
"Unsupported type for serialize_key");
135 val.iov_base =
nullptr;
143 typename std::enable_if<std::is_same<T, std::string>::value, MDBX_val>::type
146 val.iov_base =
const_cast<char*
>(key.data());
147 val.iov_len = key.size();
154 typename std::enable_if<
155# if __cplusplus >= 201703L
156 std::is_same<T, std::vector<std::byte>>::value ||
158 std::is_same<T, std::vector<uint8_t>>::value ||
159 std::is_same<T, std::vector<char>>::value ||
160 std::is_same<T, std::vector<unsigned char>>::value, MDBX_val>::type
163 val.iov_base =
const_cast<void*
>(
static_cast<const void*
>(key.data()));
164 val.iov_len = key.size();
171 typename std::enable_if<
172 std::is_integral<T>::value &&
173 (
sizeof(T) <= 2), MDBX_val>::type
175 static_assert(
sizeof(uint32_t) == 4,
"Expected 4-byte wrapper");
176 using storage_t =
typename std::aligned_storage<
sizeof(uint32_t),
alignof(uint32_t)>::type;
177 thread_local storage_t storage;
178 *
reinterpret_cast<uint32_t*
>(&storage) =
static_cast<uint32_t
>(key);
180 val.iov_len =
static_cast<size_t>(
sizeof(uint32_t));
181 val.iov_base =
static_cast<void*
>(&storage);
188 typename std::enable_if<
189 std::is_same<T, int32_t>::value ||
190 std::is_same<T, uint32_t>::value ||
191 std::is_same<T, float>::value, MDBX_val>::type
193 static_assert(
sizeof(uint32_t) == 4,
"Expected 4-byte integer");
195# if MDBXC_SAFE_INTEGERKEY
196 using storage_t =
typename std::aligned_storage<
sizeof(uint32_t),
alignof(uint32_t)>::type;
197 static thread_local storage_t buffer;
198 *
reinterpret_cast<uint32_t*
>(&buffer) = *
reinterpret_cast<const uint32_t*
>(&key);
199 val.iov_base =
static_cast<void*
>(&buffer);
201 val.iov_base =
const_cast<void*
>(
static_cast<const void*
>(&key));
203 val.iov_len =
static_cast<size_t>(
sizeof(uint32_t));
210 typename std::enable_if<
211 std::is_same<T, int64_t>::value ||
212 std::is_same<T, uint64_t>::value ||
213 std::is_same<T, double>::value, MDBX_val>::type
215 static_assert(
sizeof(uint64_t) == 8,
"Expected 8-byte integer");
217# if MDBXC_SAFE_INTEGERKEY
218 using storage_t =
typename std::aligned_storage<
sizeof(uint64_t),
alignof(uint64_t)>::type;
219 static thread_local storage_t buffer;
220 *
reinterpret_cast<uint64_t*
>(&buffer) = *
reinterpret_cast<const uint64_t*
>(&key);
221 val.iov_base =
static_cast<void*
>(&buffer);
223 val.iov_base =
const_cast<void*
>(
static_cast<const void*
>(&key));
225 val.iov_len =
sizeof(uint64_t);
232 typename std::enable_if<
233 std::is_trivially_copyable<T>::value &&
234 !std::is_same<T, std::string>::value &&
235 !(std::is_integral<T>::value &&
sizeof(T) <= 2) &&
236 !std::is_same<T, int32_t>::value &&
237 !std::is_same<T, uint32_t>::value &&
238 !std::is_same<T, float>::value &&
239 !std::is_same<T, int64_t>::value &&
240 !std::is_same<T, uint64_t>::value &&
241 !std::is_same<T, double>::value, MDBX_val>::type
244 val.iov_base =
const_cast<void*
>(
static_cast<const void*
>(&key));
245 val.iov_len =
sizeof(T);
254 const size_t num_bytes = (N + 7) / 8;
255 static thread_local std::array<uint8_t, (N + 7) / 8> buffer;
257 for (
size_t i = 0; i < N; ++i) {
258 if (data[i]) buffer[i / 8] |= (1 << (i % 8));
261 val.iov_base =
static_cast<void*
>(buffer.data());
262 val.iov_len = num_bytes;
272 template <
typename T>
273 typename std::enable_if<
275 !std::is_same<T, std::vector<typename T::value_type>>::value &&
276 !std::is_trivially_copyable<typename T::value_type>::value &&
278 !std::is_same<T, std::string>::value &&
279 !std::is_trivially_copyable<T>::value, MDBX_val>::type
281 static_assert(
sizeof(T) == 0,
"Unsupported type for serialize_value");
283 val.iov_base =
nullptr;
291 typename std::enable_if<std::is_same<T, std::string>::value, MDBX_val>::type
294 val.iov_base =
const_cast<char*
>(value.data());
295 val.iov_len = value.size();
302 template <
typename T>
303 typename std::enable_if<
305 std::is_trivially_copyable<typename T::value_type>::value &&
308 std::is_same<T, std::deque<typename T::value_type>>::value ||
309 std::is_same<T, std::list<typename T::value_type>>::value ||
310 std::is_same<T, std::set<typename T::value_type>>::value
314 using Elem =
typename T::value_type;
315 static thread_local std::vector<Elem> buffer;
316 buffer.assign(container.begin(), container.end());
319 val.iov_base =
static_cast<void*
>(buffer.data());
320 val.iov_len = buffer.size() *
sizeof(Elem);
327 typename std::enable_if<
329 std::is_same<T, std::vector<typename T::value_type>>::value &&
330 std::is_trivially_copyable<typename T::value_type>::value,
333 typedef typename T::value_type Elem;
335 val.iov_base =
const_cast<void*
>(
static_cast<const void*
>(value.data()));
336 val.iov_len = value.size() *
sizeof(Elem);
343 typename std::enable_if<has_to_bytes<T>::value, MDBX_val>::type
345 static thread_local std::vector<uint8_t> buffer;
346 buffer = value.to_bytes();
348 val.iov_base =
static_cast<void*
>(buffer.data());
349 val.iov_len = buffer.size();
356 typename std::enable_if<
358 std::is_trivially_copyable<T>::value,
362 val.iov_base =
const_cast<void*
>(
static_cast<const void*
>(&value));
363 val.iov_len =
sizeof(T);
370 typename std::enable_if<
372 std::is_same<typename T::value_type, std::string>::value,
375 static thread_local std::vector<uint8_t> buffer;
378 for (
const auto& str : container) {
379 uint32_t len =
static_cast<uint32_t
>(str.size());
380 buffer.insert(buffer.end(),
381 reinterpret_cast<const uint8_t*
>(&len),
382 reinterpret_cast<const uint8_t*
>(&len) +
sizeof(uint32_t));
383 buffer.insert(buffer.end(), str.begin(), str.end());
387 val.iov_base = buffer.data();
388 val.iov_len = buffer.size();
399 typename std::enable_if<
401 !std::is_same<T, std::string>::value &&
402 !std::is_trivially_copyable<T>::value, T>::type
404 static_assert(
sizeof(T) == 0,
"Unsupported type for deserialize_value");
412 typename std::enable_if<std::is_same<T, std::string>::value, T>::type
414 return std::string(
static_cast<const char*
>(val.iov_base), val.iov_len);
420 typename std::enable_if<
421# if __cplusplus >= 201703L
422 std::is_same<T, std::vector<std::byte>>::value ||
424 std::is_same<T, std::vector<uint8_t>>::value ||
425 std::is_same<T, std::vector<char>>::value ||
426 std::is_same<T, std::vector<unsigned char>>::value, T>::type
428 const uint8_t* ptr =
static_cast<const uint8_t*
>(val.iov_base);
429 return T(ptr, ptr + val.iov_len);
435 typename std::enable_if<
436# if __cplusplus >= 201703L
437 std::is_same<T, std::deque<std::byte>>::value ||
439 std::is_same<T, std::deque<uint8_t>>::value ||
440 std::is_same<T, std::deque<char>>::value ||
441 std::is_same<T, std::deque<unsigned char>>::value, T>::type
443 const uint8_t* ptr =
static_cast<const uint8_t*
>(val.iov_base);
444 return T(ptr, ptr + val.iov_len);
450 typename std::enable_if<
451# if __cplusplus >= 201703L
452 std::is_same<T, std::list<std::byte>>::value ||
454 std::is_same<T, std::list<uint8_t>>::value ||
455 std::is_same<T, std::list<char>>::value ||
456 std::is_same<T, std::list<unsigned char>>::value, T>::type
458 const uint8_t* ptr =
static_cast<const uint8_t*
>(val.iov_base);
459 return T(ptr, ptr + val.iov_len);
465 typename std::enable_if<
467 std::is_same<T, std::vector<typename T::value_type>>::value &&
468 std::is_trivially_copyable<typename T::value_type>::value, T>::type
470 typedef typename T::value_type Elem;
471 if (val.iov_len %
sizeof(Elem) != 0)
472 throw std::runtime_error(
"deserialize_value: size not aligned");
473 const size_t count = val.iov_len /
sizeof(Elem);
474 const Elem* data =
static_cast<const Elem*
>(val.iov_base);
475 return T(data, data + count);
481 typename std::enable_if<
482 (std::is_same<T, std::deque<typename T::value_type>>::value ||
483 std::is_same<T, std::list<typename T::value_type>>::value) &&
484 std::is_trivially_copyable<typename T::value_type>::value, T>::type
486 typedef typename T::value_type Elem;
487 if (val.iov_len %
sizeof(Elem) != 0)
488 throw std::runtime_error(
"deserialize_value: size not aligned");
489 const size_t count = val.iov_len /
sizeof(Elem);
490 const Elem* data =
static_cast<const Elem*
>(val.iov_base);
491 return T(data, data + count);
497 typename std::enable_if<
500 if (val.iov_len !=
sizeof(T))
501 throw std::runtime_error(
"deserialize_value: size mismatch");
503 std::memcpy(&out, val.iov_base,
sizeof(T));
510 typename std::enable_if<has_from_bytes<T>::value, T>::type
512 return T::from_bytes(val.iov_base, val.iov_len);
518 typename std::enable_if<
520 std::is_same<typename T::value_type, std::string>::value,
523 const uint8_t* ptr =
static_cast<const uint8_t*
>(val.iov_base);
524 const uint8_t* end = ptr + val.iov_len;
527 while (ptr +
sizeof(uint32_t) <= end) {
529 std::memcpy(&len, ptr,
sizeof(uint32_t));
530 ptr +=
sizeof(uint32_t);
533 throw std::runtime_error(
"deserialize_value: corrupted data (length overflow)");
535 result.emplace_back(
reinterpret_cast<const char*
>(ptr), len);
540 throw std::runtime_error(
"deserialize_value: trailing data after deserialization");
548 typename std::enable_if<
549 std::is_same<T, std::set<std::string>>::value ||
550 std::is_same<T, std::unordered_set<std::string>>::value,
553 const uint8_t* ptr =
static_cast<const uint8_t*
>(val.iov_base);
554 const uint8_t* end = ptr + val.iov_len;
557 while (ptr +
sizeof(uint32_t) <= end) {
559 std::memcpy(&len, ptr,
sizeof(uint32_t));
560 ptr +=
sizeof(uint32_t);
563 throw std::runtime_error(
"deserialize_value: corrupted data (length overflow)");
565 result.insert(std::string(
reinterpret_cast<const char*
>(ptr), len));
570 throw std::runtime_error(
"deserialize_value: trailing data after deserialization");
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)
Serializes a general value into MDBX_val.
std::enable_if<!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.
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)
Serializes a key into MDBX_val for database operations.
MDBX_db_flags_t get_mdbx_flags()
Returns MDBX flags for a given key type.
void check_mdbx(int rc, const std::string &context)
Throws an MdbxException if MDBX return code indicates an error.
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(...)