rpc.hpp 0.8.1
Simple RPC Header-Only Library
rpc_bitsery.hpp
Go to the documentation of this file.
1
36
37#pragma once
38
39#include "../rpc.hpp"
40
41#include <bitsery/bitsery.h>
42#include <bitsery/adapter/buffer.h>
43#include <bitsery/ext/std_tuple.h>
44#include <bitsery/traits/array.h>
45#include <bitsery/traits/string.h>
46#include <bitsery/traits/vector.h>
47
48#include <cassert>
49#include <vector>
50
51#if defined(RPC_HPP_ENABLE_SERVER_CACHE)
52# include <numeric>
53
54template<>
55struct std::hash<std::vector<uint8_t>>
56{
57 [[nodiscard]] inline size_t operator()(const std::vector<uint8_t>& vec) const noexcept
58 {
59 static constexpr auto seed_hash = [](size_t seed, size_t val) noexcept
60 {
61 static constexpr size_t magic_hash_val = 0x9E3779B9UL;
62 return seed ^ (val + magic_hash_val + (seed << 6) + (seed >> 2));
63 };
64
65 return std::accumulate(vec.begin(), vec.end(), vec.size(), seed_hash);
66 }
67};
68#endif
69
70namespace rpc_hpp
71{
72namespace adapters
73{
74 class bitsery_adapter;
75
76 template<>
78 {
79 using serial_t = std::vector<uint8_t>;
80 using bytes_t = std::vector<uint8_t>;
81 };
82
83 class bitsery_adapter : public detail::serial_adapter_base<bitsery_adapter>
84 {
85 public:
86 struct config
87 {
88#if defined(RPC_HPP_BITSERY_EXACT_SZ)
89 static constexpr bool use_exact_size = true;
90#else
91 static constexpr bool use_exact_size = false;
92#endif
93
94 static const uint64_t max_func_name_size;
95 static const uint64_t max_string_size;
96 static const uint64_t max_container_size;
97 };
98
99 [[nodiscard]] static std::vector<uint8_t> to_bytes(std::vector<uint8_t>&& serial_obj)
100 {
101 return std::move(serial_obj);
102 }
103
104 [[nodiscard]] static std::optional<std::vector<uint8_t>> from_bytes(
105 std::vector<uint8_t>&& bytes)
106 {
107 // TODO: Verify bitsery data somehow
108 return std::make_optional(std::move(bytes));
109 }
110
111 static std::vector<uint8_t> empty_object()
112 {
113 std::vector<uint8_t> buffer(sizeof(int) + 2);
114 bitsery::quickSerialization<output_adapter>(buffer, pack_helper<void>{});
115 return buffer;
116 }
117
118 template<typename R, typename... Args>
119 [[nodiscard]] static std::vector<uint8_t> serialize_pack(
120 const detail::packed_func<R, Args...>& pack)
121 {
122 const auto helper = to_helper(pack);
123 std::vector<uint8_t> buffer{};
124 buffer.reserve(64);
125
126 const auto bytes_written = bitsery::quickSerialization<output_adapter>(buffer, helper);
127 buffer.resize(bytes_written);
128 return buffer;
129 }
130
131 template<typename R, typename... Args>
132 [[nodiscard]] static detail::packed_func<R, Args...> deserialize_pack(
133 const std::vector<uint8_t>& serial_obj)
134 {
135 pack_helper<R, Args...> helper{};
136
137 if (const auto [error, _] = bitsery::quickDeserialization(
138 input_adapter{ serial_obj.begin(), serial_obj.size() }, helper);
139 error != bitsery::ReaderError::NoError)
140 {
141 switch (error)
142 {
143 case bitsery::ReaderError::ReadingError:
144 throw deserialization_error(
145 "Bitsery deserialization failed due to a reading error");
146
147 case bitsery::ReaderError::DataOverflow:
148 throw function_mismatch("Bitsery deserialization failed due to data "
149 "overflow (likely mismatched "
150 "function signature)");
151
152 case bitsery::ReaderError::InvalidData:
153 throw deserialization_error(
154 "Bitsery deserialization failed due to a invalid data");
155
156 case bitsery::ReaderError::InvalidPointer:
157 throw deserialization_error(
158 "Bitsery deserialization failed due to an invalid pointer");
159
160 case bitsery::ReaderError::NoError:
161 default:
162 throw deserialization_error(
163 "Bitsery deserialization failed due to extra data on the end");
164 }
165 }
166
167 return from_helper(helper);
168 }
169
170 [[nodiscard]] static std::string get_func_name(const std::vector<uint8_t>& serial_obj)
171 {
172 size_t index = sizeof(int);
173 const auto len = extract_length(serial_obj, index);
174
175 assert(index < serial_obj.size());
176 assert(index <= std::numeric_limits<ptrdiff_t>::max());
177
178 return { std::next(serial_obj.begin(), index),
179 std::next(serial_obj.begin(), index + len) };
180 }
181
182 [[nodiscard]] static rpc_exception extract_exception(const std::vector<uint8_t>& serial_obj)
183 {
184 const auto pack = deserialize_pack<void>(serial_obj);
185 return rpc_exception{ pack.get_err_mesg(), pack.get_except_type() };
186 }
187
188 static void set_exception(std::vector<uint8_t>& serial_obj, const rpc_exception& ex)
189 {
190 // copy except_type into buffer
191 const int ex_type = static_cast<int>(ex.get_type());
192 memcpy(&serial_obj[0], &ex_type, sizeof(int));
193 const std::string_view mesg = ex.what();
194 const auto new_err_len = static_cast<unsigned>(mesg.size());
195
196 size_t index = sizeof(int);
197 const auto name_len = extract_length(serial_obj, index);
198 const size_t name_sz_len = index;
199
200 index += name_len;
201
202 if (const auto err_len = extract_length(serial_obj, index); new_err_len != err_len)
203 {
204 if (err_len != 0)
205 {
206 assert(index < serial_obj.size());
207 assert(index <= std::numeric_limits<ptrdiff_t>::max());
208
209 serial_obj.erase(std::next(serial_obj.begin(), name_sz_len + name_len),
210 std::next(serial_obj.begin(), index + err_len));
211 }
212
213 index = name_sz_len + name_len;
214 write_length(serial_obj, new_err_len, index);
215 }
216
217 for (unsigned i = 0; i < new_err_len; ++i)
218 {
219 assert(index < serial_obj.size());
220 assert(index <= std::numeric_limits<ptrdiff_t>::max());
221
222 serial_obj.insert(
223 std::next(serial_obj.begin(), index++), static_cast<unsigned char>(mesg[i]));
224 }
225 }
226
227 private:
228 using bit_buffer = std::vector<uint8_t>;
229 using output_adapter = bitsery::OutputBufferAdapter<bit_buffer>;
230 using input_adapter = bitsery::InputBufferAdapter<bit_buffer>;
231
232 // NOTE: This jank can be replaced once C++20 is adopted
233 template<typename T,
234 int = std::is_arithmetic_v<T> + std::is_integral_v<T> * 2 + std::is_signed_v<T> * 3>
235 struct largest;
236
237 template<typename T>
238 struct largest<T, 0>
239 {
240 using type = T;
241 };
242
243 template<typename T>
244 struct largest<T, 3>
245 {
246 using type = uint64_t;
247 };
248
249 template<typename T>
250 struct largest<T, 4>
251 {
252 static_assert(!std::is_same_v<T, long double>,
253 "long double is not supported for RPC bitsery serialization!");
254 using type = double;
255 };
256
257 template<typename T>
258 struct largest<T, 6>
259 {
260 using type = int64_t;
261 };
262
263 template<typename T>
264 using largest_t = typename largest<T>::type;
265
266 template<typename R, typename... Args>
267 struct pack_helper
268 {
269 static_assert(!std::is_same_v<R, long double>,
270 "long double is not supported for RPC bitsery serialization!");
271
272#if defined(RPC_HPP_BITSERY_EXACT_SZ)
273 using args_t = std::tuple<std::remove_cv_t<std::remove_reference_t<Args>>...>;
274#else
275 using args_t =
276 std::tuple<largest_t<std::remove_cv_t<std::remove_reference_t<Args>>>...>;
277#endif
278
279 pack_helper() = default;
280
281 int except_type{};
282 std::string func_name{};
283 std::string err_mesg{};
284 R result{};
285 args_t args{};
286
287 template<typename S>
288 void serialize(S& s)
289 {
290 s.template value<sizeof(int)>(except_type);
291 s.text1b(func_name, config::max_func_name_size);
292 s.text1b(err_mesg, config::max_string_size);
293
294 if constexpr (std::is_arithmetic_v<R>)
295 {
296 s.template value<sizeof(R)>(result);
297 }
298 else if constexpr (rpc_hpp::detail::is_container_v<R>)
299 {
300 if constexpr (std::is_arithmetic_v<typename R::value_type>)
301 {
302 s.template container<sizeof(typename R::value_type)>(
303 result, config::max_container_size);
304 }
305 else
306 {
307 s.container(result, config::max_container_size);
308 }
309 }
310 else
311 {
312 s.object(result);
313 }
314
315 s.ext(args,
316 ::bitsery::ext::StdTuple{ [](S& s2, std::string& str)
317 { s2.text1b(str, config::max_string_size); },
318 // Fallback serializer for integers, floats, and enums
319 [](auto& s2, auto& val)
320 {
321 using T = std::remove_cv_t<std::remove_reference_t<decltype(val)>>;
322
323 if constexpr (std::is_arithmetic_v<T>)
324 {
325 if constexpr (config::use_exact_size)
326 {
327 s2.template value<sizeof(val)>(val);
328 }
329 else
330 {
331 s2.value8b(val);
332 }
333 }
334 else if constexpr (rpc_hpp::detail::is_container_v<T>)
335 {
336 if constexpr (std::is_arithmetic_v<typename T::value_type>)
337 {
338 s2.template container<sizeof(typename T::value_type)>(
339 val, config::max_container_size);
340 }
341 else
342 {
343 s2.container(val, config::max_container_size);
344 }
345 }
346 else
347 {
348 s2.object(val);
349 }
350 } });
351 }
352 };
353
354 template<typename... Args>
355 struct pack_helper<void, Args...>
356 {
357#if defined(RPC_HPP_BITSERY_EXACT_SZ)
358 using args_t = std::tuple<std::remove_cv_t<std::remove_reference_t<Args>>...>;
359#else
360 using args_t =
361 std::tuple<largest_t<std::remove_cv_t<std::remove_reference_t<Args>>>...>;
362#endif
363
364 pack_helper() = default;
365
366 int except_type{};
367 std::string func_name{};
368 std::string err_mesg{};
369 args_t args{};
370
371 template<typename S>
372 void serialize(S& s)
373 {
374 s.template value<sizeof(int)>(except_type);
375 s.text1b(func_name, config::max_func_name_size);
376 s.text1b(err_mesg, config::max_string_size);
377
378 s.ext(args,
379 bitsery::ext::StdTuple{ [](S& s2, std::string& str)
380 { s2.text1b(str, config::max_string_size); },
381 // Fallback serializer for integers, floats, and enums
382 [](auto& s2, auto& val)
383 {
384 using T = std::remove_cv_t<std::remove_reference_t<decltype(val)>>;
385
386 if constexpr (std::is_arithmetic_v<T>)
387 {
388 if constexpr (config::use_exact_size)
389 {
390 s2.template value<sizeof(val)>(val);
391 }
392 else
393 {
394 s2.value8b(val);
395 }
396 }
397 else if constexpr (rpc_hpp::detail::is_container_v<T>)
398 {
399 if constexpr (std::is_arithmetic_v<typename T::value_type>)
400 {
401 s2.template container<sizeof(typename T::value_type)>(
402 val, config::max_container_size);
403 }
404 else
405 {
406 s2.container(val, config::max_container_size);
407 }
408 }
409 else
410 {
411 s2.object(val);
412 }
413 } });
414 }
415 };
416
417 // nodiscard because a potentially expensive copy and allocation is being done
418 template<typename R, typename... Args>
419 [[nodiscard]] static pack_helper<R, Args...> to_helper(
420 const detail::packed_func<R, Args...>& pack)
421 {
422 pack_helper<R, Args...> helper{};
423 helper.func_name = pack.get_func_name();
424 helper.args = pack.get_args();
425
426 if (!pack)
427 {
428 helper.except_type = static_cast<int>(pack.get_except_type());
429 helper.err_mesg = pack.get_err_mesg();
430 return helper;
431 }
432
433 if constexpr (!std::is_void_v<R>)
434 {
435 helper.result = pack.get_result();
436 }
437
438 return helper;
439 }
440
441 // nodiscard because a potentially expensive copy and allocation is being done
442 template<typename R, typename... Args>
443 [[nodiscard]] static detail::packed_func<R, Args...> from_helper(
444 pack_helper<R, Args...> helper)
445 {
446 if constexpr (std::is_void_v<R>)
447 {
448 if (helper.except_type == 0)
449 {
450 return detail::packed_func<void, Args...>{ std::move(helper.func_name),
451 std::move(helper.args) };
452 }
453
454 detail::packed_func<void, Args...> pack{ std::move(helper.func_name),
455 std::move(helper.args) };
456
457 pack.set_exception(
458 std::move(helper.err_mesg), static_cast<exception_type>(helper.except_type));
459
460 return pack;
461 }
462 else
463 {
464 if (helper.err_mesg.empty())
465 {
466 return detail::packed_func<R, Args...>{ std::move(helper.func_name),
467 std::move(helper.result), std::move(helper.args) };
468 }
469
470 detail::packed_func<R, Args...> pack{ std::move(helper.func_name), std::nullopt,
471 std::move(helper.args) };
472
473 pack.set_exception(
474 std::move(helper.err_mesg), static_cast<exception_type>(helper.except_type));
475
476 return pack;
477 }
478 }
479
480 // Borrowed from Bitsery library for compatibility
481 static unsigned extract_length(const bit_buffer& bytes, size_t& index) noexcept
482 {
483 RPC_HPP_PRECONDITION(index < bytes.size());
484
485 const uint8_t hb = bytes[index++];
486
487 if (hb < 0x80U)
488 {
489 return hb;
490 }
491
492 assert(index < bytes.size());
493 const uint8_t lb = bytes[index++];
494
495 if ((hb & 0x40U) != 0U)
496 {
497 assert(index < bytes.size());
498 const uint16_t lw = *reinterpret_cast<const uint16_t*>(&bytes[index++]);
499 return ((((hb & 0x3FU) << 8) | lb) << 16) | lw;
500 }
501
502 return ((hb & 0x7FU) << 8) | lb;
503 }
504
505 // Borrowed from Bitsery library for compatibility
506 static void write_length(bit_buffer& bytes, size_t size, size_t& index)
507 {
508 RPC_HPP_PRECONDITION(size < 0x40000000U);
509
510 if (size < 0x80U)
511 {
512 assert(index < size);
513 assert(index <= std::numeric_limits<ptrdiff_t>::max());
514
515 bytes.insert(std::next(bytes.begin(), index++), static_cast<uint8_t>(size));
516 return;
517 }
518
519 if (size < 0x4000U)
520 {
521 bytes.insert(
522 std::next(bytes.begin(), index++), static_cast<uint8_t>((size >> 8) | 0x80U));
523
524 bytes.insert(std::next(bytes.begin(), index++), static_cast<uint8_t>(size));
525 return;
526 }
527
528 bytes.insert(
529 std::next(bytes.begin(), index++), static_cast<uint8_t>((size >> 24) | 0xC0U));
530
531 bytes.insert(std::next(bytes.begin(), index++), static_cast<uint8_t>(size >> 16));
532
533 bytes.insert(
534 std::next(bytes.begin(), index++), *reinterpret_cast<const uint8_t*>(&size));
535
536 bytes.insert(
537 std::next(bytes.begin(), index++), *reinterpret_cast<const uint8_t*>(&size) + 1);
538 }
539 };
540} // namespace adapters
541} // namespace rpc_hpp
Definition: rpc_bitsery.hpp:84
Top-level namespace for rpc.hpp classes and functions.
Definition: rpc.hpp:88
Definition: rpc_bitsery.hpp:87
Definition: rpc.hpp:254