MDBX Containers
Loading...
Searching...
No Matches
utils.hpp
Go to the documentation of this file.
1#pragma once
2#ifndef _MDBX_CONTAINERS_UTILS_HPP_INCLUDED
3#define _MDBX_CONTAINERS_UTILS_HPP_INCLUDED
4
8
12
13namespace mdbxc {
14
18 void check_mdbx(int rc, const std::string& context) {
19 if (rc != MDBX_SUCCESS) {
20 throw MdbxException(context + ": (" + std::to_string(rc) + ") " + std::string(mdbx_strerror(rc)), rc);
21 }
22 }
23
24 // --- Traits ---
25
28 template <typename T>
29 struct has_to_bytes {
30 private:
31 template <typename U>
32 static auto check(U*) -> decltype(std::declval<const U>().to_bytes(), std::true_type());
33 template <typename>
34 static std::false_type check(...);
35 public:
36 static const bool value = decltype(check<T>(0))::value;
37 };
38
41 template <typename T>
43 private:
44 template <typename U>
45 static auto check(U*) -> decltype(U::from_bytes((const void*)0, size_t(0)), std::true_type());
46 template <typename>
47 static std::false_type check(...);
48 public:
49 static const bool value = decltype(check<T>(0))::value;
50 };
51
54 template <typename T>
56 private:
57 template <typename U>
58 static auto check(U*) -> decltype(typename U::value_type(), std::true_type());
59 template <typename>
60 static std::false_type check(...);
61 public:
62 static const bool value = decltype(check<T>(0))::value;
63 };
64
65//-----------------------------------------------------------------------------
66
70 template<typename T>
71 inline MDBX_db_flags_t get_mdbx_flags() {
72 return
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);
79 }
80
81//-----------------------------------------------------------------------------
82
87 template<typename T>
88 size_t get_key_size(const T& key) {
89 // std::string
90 if (std::is_same<T, std::string>::value) {
91 return key.size();
92 }
93
94 // 32-bit types
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);
100 }
101
102 // 64-bit types
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);
107 }
108
109 // byte vectors
110 if (
111# if __cplusplus >= 201703L
112 std::is_same<T, std::vector<std::byte> >::value ||
113# endif
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) {
117 return key.size();
118 }
119
120 // fallback
121 return sizeof(T);
122 }
123
124 // --- serialize_key overloads ---
125
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
132 serialize_key(const T& key) {
133 static_assert(sizeof(T) == 0, "Unsupported type for serialize_key");
134 MDBX_val val;
135 val.iov_base = nullptr;
136 val.iov_len = 0;
137 return val;
138 }
139
142 template<typename T>
143 typename std::enable_if<std::is_same<T, std::string>::value, MDBX_val>::type
144 serialize_key(const T& key) {
145 MDBX_val val;
146 val.iov_base = const_cast<char*>(key.data());
147 val.iov_len = key.size();
148 return val;
149 }
150
153 template<typename T>
154 typename std::enable_if<
155# if __cplusplus >= 201703L
156 std::is_same<T, std::vector<std::byte>>::value ||
157# endif
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
161 serialize_key(const T& key) {
162 MDBX_val val;
163 val.iov_base = const_cast<void*>(static_cast<const void*>(key.data()));
164 val.iov_len = key.size();
165 return val;
166 }
167
170 template<typename T>
171 typename std::enable_if<
172 std::is_integral<T>::value &&
173 (sizeof(T) <= 2), MDBX_val>::type
174 serialize_key(const T& key) {
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);
179 MDBX_val val;
180 val.iov_len = static_cast<size_t>(sizeof(uint32_t));
181 val.iov_base = static_cast<void*>(&storage);
182 return val;
183 }
184
187 template<typename T>
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
192 serialize_key(const T& key) {
193 static_assert(sizeof(uint32_t) == 4, "Expected 4-byte integer");
194 MDBX_val val;
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);
200# else
201 val.iov_base = const_cast<void*>(static_cast<const void*>(&key));
202# endif
203 val.iov_len = static_cast<size_t>(sizeof(uint32_t));
204 return val;
205 }
206
209 template<typename 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
214 serialize_key(const T& key) {
215 static_assert(sizeof(uint64_t) == 8, "Expected 8-byte integer");
216 MDBX_val val;
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);
222# else
223 val.iov_base = const_cast<void*>(static_cast<const void*>(&key));
224# endif
225 val.iov_len = sizeof(uint64_t);
226 return val;
227 }
228
231 template<typename 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
242 serialize_key(const T& key) {
243 MDBX_val val;
244 val.iov_base = const_cast<void*>(static_cast<const void*>(&key));
245 val.iov_len = sizeof(T);
246 return val;
247 }
248
252 template <size_t N>
253 inline MDBX_val serialize_key(const std::bitset<N>& data) {
254 const size_t num_bytes = (N + 7) / 8;
255 static thread_local std::array<uint8_t, (N + 7) / 8> buffer;
256 buffer.fill(0);
257 for (size_t i = 0; i < N; ++i) {
258 if (data[i]) buffer[i / 8] |= (1 << (i % 8));
259 }
260 MDBX_val val;
261 val.iov_base = static_cast<void*>(buffer.data());
262 val.iov_len = num_bytes;
263 return val;
264 }
265
266 // --- serialize_value overloads ---
267
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
280 serialize_value(const T& value) {
281 static_assert(sizeof(T) == 0, "Unsupported type for serialize_value");
282 MDBX_val val;
283 val.iov_base = nullptr;
284 val.iov_len = 0;
285 return val;
286 }
287
290 template<typename T>
291 typename std::enable_if<std::is_same<T, std::string>::value, MDBX_val>::type
292 serialize_value(const T& value) {
293 MDBX_val val;
294 val.iov_base = const_cast<char*>(value.data());
295 val.iov_len = value.size();
296 return val;
297 }
298
302 template <typename T>
303 typename std::enable_if<
305 std::is_trivially_copyable<typename T::value_type>::value &&
306 (
307 //std::is_same<T, std::vector<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
311 ),
312 MDBX_val>::type
313 serialize_value(const T& container) {
314 using Elem = typename T::value_type;
315 static thread_local std::vector<Elem> buffer;
316 buffer.assign(container.begin(), container.end());
317
318 MDBX_val val;
319 val.iov_base = static_cast<void*>(buffer.data());
320 val.iov_len = buffer.size() * sizeof(Elem);
321 return val;
322 }
323
326 template<typename T>
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,
331 MDBX_val>::type
332 serialize_value(const T& value) {
333 typedef typename T::value_type Elem;
334 MDBX_val val;
335 val.iov_base = const_cast<void*>(static_cast<const void*>(value.data()));
336 val.iov_len = value.size() * sizeof(Elem);
337 return val;
338 }
339
342 template<typename T>
343 typename std::enable_if<has_to_bytes<T>::value, MDBX_val>::type
344 serialize_value(const T& value) {
345 static thread_local std::vector<uint8_t> buffer;
346 buffer = value.to_bytes();
347 MDBX_val val;
348 val.iov_base = static_cast<void*>(buffer.data());
349 val.iov_len = buffer.size();
350 return val;
351 }
352
355 template<typename T>
356 typename std::enable_if<
358 std::is_trivially_copyable<T>::value,
359 MDBX_val>::type
360 serialize_value(const T& value) {
361 MDBX_val val;
362 val.iov_base = const_cast<void*>(static_cast<const void*>(&value));
363 val.iov_len = sizeof(T);
364 return val;
365 }
366
369 template<typename T>
370 typename std::enable_if<
372 std::is_same<typename T::value_type, std::string>::value,
373 MDBX_val>::type
374 serialize_value(const T& container) {
375 static thread_local std::vector<uint8_t> buffer;
376 buffer.clear();
377
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());
384 }
385
386 MDBX_val val;
387 val.iov_base = buffer.data();
388 val.iov_len = buffer.size();
389 return val;
390 }
391
392 // --- deserialize_value overloads ---
393
398 template<typename T>
399 typename std::enable_if<
401 !std::is_same<T, std::string>::value &&
402 !std::is_trivially_copyable<T>::value, T>::type
403 deserialize_value(const MDBX_val& val) {
404 static_assert(sizeof(T) == 0, "Unsupported type for deserialize_value");
405 T out;
406 return out;
407 }
408
411 template<typename T>
412 typename std::enable_if<std::is_same<T, std::string>::value, T>::type
413 deserialize_value(const MDBX_val& val) {
414 return std::string(static_cast<const char*>(val.iov_base), val.iov_len);
415 }
416
419 template<typename T>
420 typename std::enable_if<
421# if __cplusplus >= 201703L
422 std::is_same<T, std::vector<std::byte>>::value ||
423# endif
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
427 deserialize_value(const MDBX_val& val) {
428 const uint8_t* ptr = static_cast<const uint8_t*>(val.iov_base);
429 return T(ptr, ptr + val.iov_len);
430 }
431
434 template<typename T>
435 typename std::enable_if<
436# if __cplusplus >= 201703L
437 std::is_same<T, std::deque<std::byte>>::value ||
438# endif
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
442 deserialize_value(const MDBX_val& val) {
443 const uint8_t* ptr = static_cast<const uint8_t*>(val.iov_base);
444 return T(ptr, ptr + val.iov_len);
445 }
446
449 template<typename T>
450 typename std::enable_if<
451# if __cplusplus >= 201703L
452 std::is_same<T, std::list<std::byte>>::value ||
453# endif
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
457 deserialize_value(const MDBX_val& val) {
458 const uint8_t* ptr = static_cast<const uint8_t*>(val.iov_base);
459 return T(ptr, ptr + val.iov_len);
460 }
461
464 template<typename T>
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
469 deserialize_value(const MDBX_val& val) {
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);
476 }
477
480 template<typename T>
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
485 deserialize_value(const MDBX_val& val) {
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);
492 }
493
496 template<typename T>
497 typename std::enable_if<
498 !has_from_bytes<T>::value && std::is_trivially_copyable<T>::value, T>::type
499 deserialize_value(const MDBX_val& val) {
500 if (val.iov_len != sizeof(T))
501 throw std::runtime_error("deserialize_value: size mismatch");
502 T out;
503 std::memcpy(&out, val.iov_base, sizeof(T));
504 return out;
505 }
506
509 template<typename T>
510 typename std::enable_if<has_from_bytes<T>::value, T>::type
511 deserialize_value(const MDBX_val& val) {
512 return T::from_bytes(val.iov_base, val.iov_len);
513 }
514
517 template<typename T>
518 typename std::enable_if<
520 std::is_same<typename T::value_type, std::string>::value,
521 T>::type
522 deserialize_value(const MDBX_val& val) {
523 const uint8_t* ptr = static_cast<const uint8_t*>(val.iov_base);
524 const uint8_t* end = ptr + val.iov_len;
525
526 T result;
527 while (ptr + sizeof(uint32_t) <= end) {
528 uint32_t len;
529 std::memcpy(&len, ptr, sizeof(uint32_t));
530 ptr += sizeof(uint32_t);
531
532 if (ptr + len > end)
533 throw std::runtime_error("deserialize_value: corrupted data (length overflow)");
534
535 result.emplace_back(reinterpret_cast<const char*>(ptr), len);
536 ptr += len;
537 }
538
539 if (ptr != end)
540 throw std::runtime_error("deserialize_value: trailing data after deserialization");
541
542 return result;
543 }
544
547 template<typename T>
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,
551 T>::type
552 deserialize_value(const MDBX_val& val) {
553 const uint8_t* ptr = static_cast<const uint8_t*>(val.iov_base);
554 const uint8_t* end = ptr + val.iov_len;
555 T result;
556
557 while (ptr + sizeof(uint32_t) <= end) {
558 uint32_t len;
559 std::memcpy(&len, ptr, sizeof(uint32_t));
560 ptr += sizeof(uint32_t);
561
562 if (ptr + len > end)
563 throw std::runtime_error("deserialize_value: corrupted data (length overflow)");
564
565 result.insert(std::string(reinterpret_cast<const char*>(ptr), len));
566 ptr += len;
567 }
568
569 if (ptr != end)
570 throw std::runtime_error("deserialize_value: trailing data after deserialization");
571
572 return result;
573 }
574
575}; // namespace mdbxc
576
578
579#endif // _MDBX_CONTAINERS_UTILS_HPP_INCLUDED
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.
Definition utils.hpp:88
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.
Definition utils.hpp:280
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.
Definition utils.hpp:403
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.
Definition utils.hpp:132
MDBX_db_flags_t get_mdbx_flags()
Returns MDBX flags for a given key type.
Definition utils.hpp:71
void check_mdbx(int rc, const std::string &context)
Throws an MdbxException if MDBX return code indicates an error.
Definition utils.hpp:18
Trait to check if a type provides a static from_bytes() method.
Definition utils.hpp:42
static auto check(U *) -> decltype(U::from_bytes((const void *) 0, size_t(0)), std::true_type())
static std::false_type check(...)
static const bool value
Definition utils.hpp:49
Trait to check if a type provides a to_bytes() member.
Definition utils.hpp:29
static std::false_type check(...)
static auto check(U *) -> decltype(std::declval< const U >().to_bytes(), std::true_type())
static const bool value
Definition utils.hpp:36
Trait indicating that a container defines value_type.
Definition utils.hpp:55
static auto check(U *) -> decltype(typename U::value_type(), std::true_type())
static const bool value
Definition utils.hpp:62
static std::false_type check(...)