VLink  2.1.0
A high-performance communication middleware
serializer-inl.h
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2026 by Thun Lu. All rights reserved.
3  * Author: Thun Lu <thun.lu@zohomail.cn>
4  * Repo: https://github.com/thun-res/vlink
5  * _ __ __ _ __
6  * | | / / / / (_) ____ / /__
7  * | | / / / / / / / __ \ / //_/
8  * | |/ / / /___ / / / / / / / ,<
9  * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10  *
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  * http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  */
23 
24 #pragma once
25 
26 #include <cstring>
27 #include <exception>
28 #include <limits>
29 #include <sstream>
30 #include <string>
31 #include <type_traits>
32 #include <utility>
33 
34 #include "../base/helpers.h"
35 #include "../base/logger.h"
36 #include "../base/name_detector.h"
37 #include "../base/traits.h"
38 #include "../serializer.h"
39 
40 #ifndef VLINK_DDS_IDL_PREFIX
41 #define VLINK_DDS_IDL_PREFIX "dds::"
42 #endif
43 
44 #if defined(VLINK_SUPPORT_CDR) || defined(VLINK_SUPPORT_ROS2)
45 #if __has_include(<fastcdr/Cdr.h>)
46 #include <fastcdr/Cdr.h>
47 #if FASTCDR_VERSION_MAJOR >= 2
48 #include <fastcdr/CdrSizeCalculator.hpp>
49 #endif
50 #ifndef VLINK_HAS_CDR
51 #define VLINK_HAS_CDR
52 #endif
53 #else
54 #warning "Fast CDR headers are required when CDR serialization support is enabled."
55 #endif
56 #endif
57 
58 #ifdef VLINK_SUPPORT_ROS2
59 #if defined(VLINK_HAS_CDR) && __has_include(<rosidl_typesupport_fastrtps_cpp/message_type_support.h>)
60 #include <rosidl_typesupport_fastrtps_cpp/message_type_support.h>
61 
62 #include <rosidl_runtime_cpp/traits.hpp>
63 #include <rosidl_typesupport_fastrtps_cpp/message_type_support_decl.hpp>
64 #ifndef VLINK_HAS_ROS2
65 #define VLINK_HAS_ROS2
66 #endif
67 #elif defined(VLINK_HAS_CDR)
68 #warning "ROS 2 message headers are required when ROS 2 message support is enabled."
69 #endif
70 #endif
71 
72 #if __has_include(<google/protobuf/message_lite.h>)
73 #ifndef VLINK_HAS_PROTOBUF
74 #define VLINK_HAS_PROTOBUF
75 #endif
76 #include <google/protobuf/message_lite.h>
77 #endif
78 
79 #if __has_include(<flatbuffers/flatbuffers.h>)
80 #ifndef VLINK_HAS_FLATBUFFERS
81 #define VLINK_HAS_FLATBUFFERS
82 #endif
83 #include <flatbuffers/flatbuffers.h>
84 #endif
85 
86 namespace vlink {
87 
88 namespace Serializer { // NOLINT(readability-identifier-naming)
89 
90 [[maybe_unused]] inline constexpr bool is_supported(Type type) noexcept { return type != kUnknownType; }
91 
92 #ifdef VLINK_HAS_ROS2
93 template <typename T>
94 inline constexpr bool is_ros2_msg_type() noexcept {
95  using RealType = typename Traits::RemoveSharedPtr<T>::Type;
96  return rosidl_generator_traits::is_message<RealType>::value;
97 }
98 
99 template <typename T>
100 inline const message_type_support_callbacks_t* get_ros2_msg_callbacks() noexcept {
101  const auto* type_support = rosidl_typesupport_fastrtps_cpp::get_message_type_support_handle<T>();
102  return type_support ? static_cast<const message_type_support_callbacks_t*>(type_support->data) : nullptr;
103 }
104 #endif
105 
106 template <typename T>
107 inline constexpr Type get_type_of() noexcept {
108  if constexpr (is_bytes_type<T>()) {
109  return kBytesType;
110  } else if constexpr (is_dynamic_type<T>()) {
111  return kDynamicType;
112 #ifdef VLINK_HAS_CDR
113  } else if constexpr (is_cdr_type<T>()) {
114  return kCdrType;
115 #endif
116 #ifdef VLINK_HAS_PROTOBUF
117  } else if constexpr (is_proto_type<T>()) {
118  return kProtoType;
119  } else if constexpr (is_proto_ptr_type<T>()) {
120  return kProtoPtrType;
121 #endif
122 #ifdef VLINK_HAS_FLATBUFFERS
123  } else if constexpr (is_flat_table_type<T>()) {
124  return kFlatTableType;
125  } else if constexpr (is_flat_ptr_type<T>()) {
126  return kFlatPtrType;
127  } else if constexpr (is_flat_builder_type<T>()) {
128  return kFlatBuilderType;
129 #endif
130  } else if constexpr (is_custom_type<T>()) {
131  return kCustomType;
132  } else if constexpr (is_string_type<T>()) {
133  return kStringType;
134  } else if constexpr (is_chars_type<T>()) {
135  return kCharsType;
136  } else if constexpr (is_standard_type<T>()) {
137  return kStandardType;
138  } else if constexpr (is_standard_ptr_type<T>()) {
139  return kStandardPtrType;
140  } else if constexpr (is_stream_type<T>()) {
141  return kStreamType;
142  } else {
143  return kUnknownType;
144  }
145 }
146 
147 template <Type TypeT, typename T>
148 inline static constexpr SchemaType get_schema_type() noexcept {
149  using RealType = typename Traits::RemoveSharedPtr<T>::Type;
150 
151  if constexpr (TypeT == kBytesType) {
152  return SchemaType::kRaw;
153  } else if constexpr (TypeT == kCustomType) {
154  if constexpr (VLINK_HAS_MEMBER(RealType, kZerocopyTypes)) {
155  if constexpr (RealType::kZerocopyTypes) {
156  return SchemaType::kZeroCopy;
157  } else {
158  return SchemaType::kRaw;
159  }
160  } else if constexpr (VLINK_HAS_MEMBER(RealType, get_schema_type())) {
161  return RealType::get_schema_type();
162  } else {
163  return SchemaType::kRaw;
164  }
165 #ifdef VLINK_HAS_PROTOBUF
166  } else if constexpr (TypeT == kProtoType || TypeT == kProtoPtrType) {
167  return SchemaType::kProtobuf;
168 #endif
169 #ifdef VLINK_HAS_FLATBUFFERS
170  } else if constexpr (TypeT == kFlatTableType || TypeT == kFlatPtrType || TypeT == kFlatBuilderType) {
172 #endif
173 #ifdef VLINK_HAS_CDR
174  } else if constexpr (TypeT == kCdrType) {
175  return SchemaType::kCdr;
176 #endif
177  } else {
178  return SchemaType::kRaw;
179  }
180 }
181 
182 template <typename T>
183 inline static constexpr SchemaType get_schema_type() noexcept {
184  constexpr auto kType = get_type_of<T>();
185 
186  return get_schema_type<kType, T>();
187 }
188 
189 template <Type TypeT, typename T>
190 inline std::string get_serialized_type() noexcept {
191  using RealType = typename Traits::RemoveSharedPtr<T>::Type;
192  using NamedType = std::remove_pointer_t<RealType>;
193 
194  if constexpr (TypeT == kBytesType) {
195  return "";
196  } else if constexpr (TypeT == kDynamicType) {
197  return "vlink::DynamicData";
198  } else if constexpr (TypeT == kCustomType && VLINK_HAS_MEMBER(RealType, get_serialized_type())) {
200 #ifdef VLINK_HAS_PROTOBUF
201  } else if constexpr (TypeT == kProtoType || TypeT == kProtoPtrType) {
202  return std::string(NamedType{}.GetTypeName());
203 #endif
204 #ifdef VLINK_HAS_FLATBUFFERS
205  } else if constexpr (TypeT == kFlatTableType) {
206  using TableType = typename RealType::TableType;
207  if constexpr (VLINK_HAS_MEMBER(TableType, GetFullyQualifiedName)) {
208  return TableType::GetFullyQualifiedName();
209  } else {
210  std::string name = NameDetector::get<TableType>().data();
211  Helpers::replace_string(name, "::", ".");
212  return name;
213  }
214  } else if constexpr (TypeT == kFlatBuilderType) {
215  using TableType = typename RealType::Table;
216  if constexpr (VLINK_HAS_MEMBER(TableType, GetFullyQualifiedName)) {
217  return TableType::GetFullyQualifiedName();
218  } else {
219  std::string name = NameDetector::get<TableType>().data();
220  Helpers::replace_string(name, "::", ".");
221  return name;
222  }
223  } else if constexpr (TypeT == kFlatPtrType) {
224  using TableType = std::remove_pointer_t<T>;
225  if constexpr (VLINK_HAS_MEMBER(TableType, GetFullyQualifiedName)) {
226  return TableType::GetFullyQualifiedName();
227  } else {
228  std::string name = NameDetector::get<TableType>().data();
229  Helpers::replace_string(name, "::", ".");
230  return name;
231  }
232 #endif
233 #ifdef VLINK_HAS_ROS2
234  } else if constexpr (TypeT == kCdrType && is_ros2_msg_type<RealType>()) {
235  const auto* callbacks = get_ros2_msg_callbacks<RealType>();
236 
237  if VUNLIKELY (!callbacks || !callbacks->message_namespace_ || !callbacks->message_name_) {
238  return "";
239  }
240 
241  std::string name(callbacks->message_namespace_);
242  name.append("::dds_::").append(callbacks->message_name_).append("_");
243 
244  return name;
245 #endif
246  } else if constexpr (TypeT == kStringType || TypeT == kCharsType) {
247  return "string";
248  } else if constexpr (NameDetector::is_support<NamedType>()) {
249  return NameDetector::get<NamedType>().data();
250  } else {
251  return "";
252  }
253 }
254 
255 template <typename T>
256 inline std::string get_serialized_type() noexcept {
257  constexpr auto kType = get_type_of<T>();
258 
259  return get_serialized_type<kType, T>();
260 }
261 
262 template <Type TypeT, typename T>
263 inline size_t get_serialized_size(const T& src) noexcept {
264  using RealType = typename Traits::RemoveSharedPtr<T>::Type;
265 
266  if constexpr (TypeT == kBytesType) {
267  return 0;
268  } else if constexpr (TypeT == kDynamicType) {
269  return 0;
270 #ifdef VLINK_HAS_CDR
271  } else if constexpr (TypeT == kCdrType) {
272  try {
273 #ifdef VLINK_HAS_ROS2
274  if constexpr (is_ros2_msg_type<RealType>()) {
275  const auto* callbacks = get_ros2_msg_callbacks<RealType>();
276 
277  if VUNLIKELY (!callbacks || !callbacks->get_serialized_size) {
278  return 0U;
279  }
280 
281  const size_t payload_size = callbacks->get_serialized_size(&deref(src));
282  return payload_size <= std::numeric_limits<uint32_t>::max() - 4U ? payload_size + 4U : 0U;
283  } else {
284 #endif
285  if constexpr (VLINK_HAS_MEMBER(RealType, getCdrSerializedSize(deref(src)))) {
286  const size_t payload_size = RealType::getCdrSerializedSize(deref(src));
287  return payload_size <= std::numeric_limits<uint32_t>::max() - 4U ? payload_size + 4U : 0U;
288 #if FASTCDR_VERSION_MAJOR >= 2
289  } else {
290  eprosima::fastcdr::CdrSizeCalculator calculator(eprosima::fastcdr::CdrVersion::XCDRv1);
291  size_t current_alignment = 0;
292  const size_t payload_size = calculator.calculate_serialized_size(deref(src), current_alignment);
293  return payload_size <= std::numeric_limits<uint32_t>::max() - 4U ? payload_size + 4U : 0U;
294 #else
295  } else {
296  return 0;
297 #endif
298  }
299 #ifdef VLINK_HAS_ROS2
300  }
301 #endif
302  } catch (const std::exception&) {
303  return 0;
304  }
305 #endif
306 #ifdef VLINK_HAS_PROTOBUF
307  } else if constexpr (TypeT == kProtoType) {
308  if constexpr (VLINK_HAS_MEMBER(RealType, ByteSizeLong())) {
309  return deref(src).ByteSizeLong();
310  } else {
311  return deref(src).ByteSize();
312  }
313  } else if constexpr (TypeT == kProtoPtrType) {
314  if constexpr (VLINK_HAS_MEMBER(std::remove_pointer_t<T>, ByteSizeLong())) {
315  return src->ByteSizeLong();
316  } else {
317  return src->ByteSize();
318  }
319 #endif
320  } else if constexpr (TypeT == kCustomType) {
321  if constexpr (VLINK_HAS_MEMBER(RealType, get_serialized_size())) {
322  return deref(src).get_serialized_size();
323  } else {
324  return 0;
325  }
326  } else {
327  return 0;
328  }
329 }
330 
331 template <typename T>
332 inline size_t get_serialized_size(const T& src) noexcept {
333  constexpr auto kType = get_type_of<T>();
334 
335  return get_serialized_size<kType>(src);
336 }
337 
338 template <Type TypeT, typename T>
339 inline bool serialize(const T& src, Bytes& des, [[maybe_unused]] TransportType transport,
340  [[maybe_unused]] uint8_t offset) {
341  using RealType = typename Traits::RemoveSharedPtr<T>::Type;
342 
343  if constexpr (TypeT == kBytesType) {
344  des = Bytes::deep_copy(src.data(), src.size(), offset);
345  } else if constexpr (TypeT == kDynamicType) {
346  using ReturnT = decltype(deref(src) >> des);
347 
348  if constexpr (std::is_convertible_v<ReturnT, bool>) {
349  if VUNLIKELY (!(deref(src) >> des)) {
350  VLOG_T("Serializer: Dynamic serialize failed.");
351  return false;
352  }
353  } else {
354  deref(src) >> des;
355  }
356 #ifdef VLINK_HAS_CDR
357  } else if constexpr (TypeT == kCdrType) {
358  const size_t target_size = get_serialized_size<TypeT>(src);
359 
360  if VUNLIKELY (target_size < 4U || target_size > std::numeric_limits<uint32_t>::max()) {
361  VLOG_W("Serializer: FastBuffer serialized size is invalid.");
362  return false;
363  }
364 
365  if (!des.is_loaned()) {
366  if VUNLIKELY (des.size() != target_size) {
367  des = Bytes::create(target_size, offset);
368  }
369  }
370 
371  if VUNLIKELY (!des.data() || des.size() != target_size) {
372  return false;
373  }
374 
375  try {
376  eprosima::fastcdr::FastBuffer buffer(reinterpret_cast<char*>(des.data()), des.size());
377 #if FASTCDR_VERSION_MAJOR >= 2
378  constexpr auto kCdrVersion = VLINK_HAS_MEMBER(RealType, serialize(std::declval<eprosima::fastcdr::Cdr&>()))
379  ? eprosima::fastcdr::CdrVersion::DDS_CDR
380  : eprosima::fastcdr::CdrVersion::XCDRv1;
381  eprosima::fastcdr::Cdr cdr(buffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN, kCdrVersion);
382  if constexpr (kCdrVersion == eprosima::fastcdr::CdrVersion::XCDRv1) {
383  cdr.set_encoding_flag(eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR);
384  }
385 #else
386  eprosima::fastcdr::Cdr cdr(buffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN, eprosima::fastcdr::Cdr::DDS_CDR);
387 #endif
388  cdr.serialize_encapsulation();
389 
390 #ifdef VLINK_HAS_ROS2
391  if constexpr (is_ros2_msg_type<RealType>()) {
392  const auto* callbacks = get_ros2_msg_callbacks<RealType>();
393 
394  if VUNLIKELY (!callbacks || !callbacks->cdr_serialize || !callbacks->cdr_serialize(&deref(src), cdr)) {
395  return false;
396  }
397  } else {
398 #endif
399  if constexpr (VLINK_HAS_MEMBER(RealType, serialize(cdr))) {
400  deref(src).serialize(cdr);
401 #if FASTCDR_VERSION_MAJOR >= 2
402  } else {
403  cdr << deref(src);
404 #endif
405  }
406 #ifdef VLINK_HAS_ROS2
407  }
408 #endif
409  } catch (const eprosima::fastcdr::exception::Exception& e) {
410  VLOG_T("Serializer: FastBuffer serialize failed: ", e.what(), ".");
411  return false;
412  } catch (const std::exception& e) {
413  VLOG_T("Serializer: CDR serialize failed: ", e.what(), ".");
414  return false;
415  }
416 #endif
417 #ifdef VLINK_HAS_PROTOBUF
418  } else if constexpr (TypeT == kProtoType) {
419  if (!des.is_loaned()) {
420  if constexpr (VLINK_HAS_MEMBER(RealType, ByteSizeLong())) {
421  size_t target_size = deref(src).ByteSizeLong();
422 
423  if VUNLIKELY (des.size() != target_size) {
424  des = Bytes::create(target_size, offset);
425  }
426  } else {
427  size_t target_size = deref(src).ByteSize();
428 
429  if VUNLIKELY (des.size() != target_size) {
430  des = Bytes::create(target_size, offset);
431  }
432  }
433  }
434 
435  if VUNLIKELY (!deref(src).SerializeToArray(des.data(), des.size())) {
436  VLOG_T("Serializer: Protobuf serialize failed.");
437  return false;
438  }
439  } else if constexpr (TypeT == kProtoPtrType) {
440  if (!des.is_loaned()) {
441  if constexpr (VLINK_HAS_MEMBER(std::remove_pointer_t<T>, ByteSizeLong())) {
442  size_t target_size = src->ByteSizeLong();
443 
444  if VUNLIKELY (des.size() != target_size) {
445  des = Bytes::create(target_size, offset);
446  }
447  } else {
448  size_t target_size = src->ByteSize();
449 
450  if VUNLIKELY (des.size() != target_size) {
451  des = Bytes::create(target_size, offset);
452  }
453  }
454  }
455 
456  if VUNLIKELY (!src->SerializeToArray(des.data(), des.size())) {
457  VLOG_T("Serializer: Protobuf ptr serialize failed.");
458  return false;
459  }
460 #endif
461 #ifdef VLINK_HAS_FLATBUFFERS
462  } else if constexpr (TypeT == kFlatTableType) {
463  flatbuffers::FlatBufferBuilder fbb;
464  fbb.Finish(RealType::TableType::Pack(fbb, &deref(src)));
465 
466  if (des.is_loaned() && des.size() >= fbb.GetSize() + offset) {
467  std::memcpy(des.data() + offset, fbb.GetBufferPointer(), fbb.GetSize());
468  } else {
469  des = Bytes::deep_copy(fbb.GetBufferPointer(), fbb.GetSize(), offset);
470  }
471  } else if constexpr (TypeT == kFlatBuilderType) {
472  if VUNLIKELY (des.is_loaned()) {
473  return false;
474  }
475 
476  auto& mutable_src = const_cast<T&>(src);
477  mutable_src.fbb_.Finish(mutable_src.Finish());
478  des = Bytes::deep_copy(mutable_src.fbb_.GetBufferPointer(), mutable_src.fbb_.GetSize(), offset);
479  } else if constexpr (TypeT == kFlatPtrType) {
480  static_assert(Traits::ExpectFalse<T>(), "Not support flat ptr type.");
481 #endif
482  } else if constexpr (TypeT == kCustomType) {
483  using ReturnT = decltype(const_cast<RealType&>(deref(src)) >> des);
484 
485  if constexpr (std::is_convertible_v<ReturnT, bool>) {
486  if VUNLIKELY (!(const_cast<RealType&>(deref(src)) >> des)) {
487  VLOG_T("Serializer: Custom serialize failed.");
488  return false;
489  }
490  } else {
491  const_cast<RealType&>(deref(src)) >> des;
492  }
493 
494  if (offset > 0) {
495  des = Bytes::deep_copy(des.data(), des.size(), offset);
496  }
497  } else if constexpr (TypeT == kStringType) {
498  des = Bytes::deep_copy(reinterpret_cast<const uint8_t*>(deref(src).data()), deref(src).size(), offset);
499  } else if constexpr (TypeT == kCharsType) {
500  size_t size = 0;
501 
502  if constexpr (std::is_array_v<T>) {
503  constexpr size_t kExtent = std::extent_v<T>;
504  const auto* terminator = static_cast<const char*>(std::memchr(src, '\0', kExtent));
505 
506  if VUNLIKELY (!terminator) {
507  VLOG_T("Serializer: Chars array is not null-terminated.");
508  return false;
509  }
510 
511  size = static_cast<size_t>(terminator - src);
512  } else {
513  if VUNLIKELY (!src) {
514  VLOG_T("Serializer: Chars source is null.");
515  return false;
516  }
517 
518  size = std::strlen(src);
519  }
520 
521  des = Bytes::deep_copy(reinterpret_cast<const uint8_t*>(src), size, offset);
522  } else if constexpr (TypeT == kStreamType) {
523  std::stringstream ss;
524  ss << deref(src);
525  const std::string str = ss.str();
526  des = Bytes::deep_copy(reinterpret_cast<const uint8_t*>(str.data()), str.size(), offset);
527  } else if constexpr (TypeT == kStandardType) {
528  const auto* src_ptr = reinterpret_cast<const uint8_t*>(&deref(src));
529  des = Bytes::deep_copy(src_ptr, sizeof(RealType), offset);
530  } else if constexpr (TypeT == kStandardPtrType) {
531  if VUNLIKELY (offset > 0) {
532  VLOG_T("Serializer: Standard ptr does not support offset.");
533  return false;
534  }
535 
536  const auto* src_ptr = reinterpret_cast<const uint8_t*>(src);
537  constexpr size_t kSize = sizeof(std::remove_pointer_t<T>);
538 
539  if (des.is_loaned() && des.size() >= kSize) {
540  std::memcpy(des.data(), src_ptr, kSize);
541  } else {
542  des = Bytes::shallow_copy(src_ptr, kSize);
543  }
544  } else {
545  static_assert(Traits::ExpectFalse<T>(), "Not support serialize.");
546  return false;
547  }
548 
549  return true;
550 }
551 
552 template <typename T>
553 inline bool serialize(const T& src, Bytes& des) {
554  constexpr auto kType = get_type_of<T>();
555 
556  return serialize<kType>(src, des, TransportType::kUnknown);
557 }
558 
559 template <Type TypeT, typename T, typename LoanCallbackT>
560 inline bool serialize_to_transport(const T& src, Bytes& des, TransportType transport, bool use_loan,
561  LoanCallbackT&& loan) {
562 #ifdef VLINK_HAS_FLATBUFFERS
563  if constexpr (TypeT == kFlatBuilderType) {
564  auto& mutable_src = const_cast<T&>(src);
565  mutable_src.fbb_.Finish(mutable_src.Finish());
566 
567  const size_t size = mutable_src.fbb_.GetSize();
568 
569  if (use_loan) {
570  des = std::forward<LoanCallbackT>(loan)(size);
571 
572  if VUNLIKELY (des.empty() || des.size() != size) {
573  return false;
574  }
575 
576  std::memcpy(des.data(), mutable_src.fbb_.GetBufferPointer(), size);
577  } else {
578  des = Bytes::shallow_copy(mutable_src.fbb_.GetBufferPointer(), size);
579  }
580 
581  return true;
582  } else {
583 #endif
584  if (use_loan) {
585  const size_t size = get_serialized_size<TypeT>(src);
586 
587  if (size == 0) {
588  des = Bytes{};
589  } else {
590  des = std::forward<LoanCallbackT>(loan)(size);
591 
592  if VUNLIKELY (des.empty() || des.size() != size) {
593  return false;
594  }
595  }
596  }
597 
598  const bool had_loan = des.is_loaned();
599  auto* loan_data = des.data();
600  const size_t loan_size = des.size();
601  const bool ret = serialize<TypeT>(src, des, transport);
602 
603  if VUNLIKELY (had_loan && (!des.is_loaned() || des.data() != loan_data || des.size() != loan_size)) {
604  des = Bytes::loan_internal(loan_data, loan_size);
605  return false;
606  }
607 
608  return ret;
609 #ifdef VLINK_HAS_FLATBUFFERS
610  }
611 #endif
612 }
613 
614 template <Type TypeT, typename T>
615 inline bool deserialize(const Bytes& src, T& des, [[maybe_unused]] TransportType transport) {
616  using RealType = typename Traits::RemoveSharedPtr<T>::Type;
617 
618  if constexpr (TypeT == kBytesType) {
619  des = src;
620  return true;
621  } else if constexpr (TypeT == kDynamicType) {
622  try {
623  using ReturnT = decltype(deref(des) << src);
624 
625  if constexpr (std::is_convertible_v<ReturnT, bool>) {
626  if VUNLIKELY (!(deref(des) << src)) {
627  VLOG_T("Serializer: Dynamic deserialize failed.");
628  return false;
629  }
630  } else {
631  deref(des) << src;
632  }
633  } catch (const std::exception& e) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE
634  VLOG_T("Serializer: Dynamic deserialize threw: ", e.what(), "."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
635  return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
636  }
637 #ifdef VLINK_HAS_CDR
638  } else if constexpr (TypeT == kCdrType) {
639  if VUNLIKELY (!src.data() || src.size() < 4U || src.size() > std::numeric_limits<uint32_t>::max()) {
640  VLOG_T("Serializer: FastBuffer payload has no CDR encapsulation.");
641  return false;
642  }
643 
644  try {
645  eprosima::fastcdr::FastBuffer buffer(reinterpret_cast<char*>(const_cast<uint8_t*>(src.data())), src.size());
646 #if FASTCDR_VERSION_MAJOR >= 2
647  eprosima::fastcdr::Cdr cdr(buffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN,
648  eprosima::fastcdr::CdrVersion::DDS_CDR);
649 #else
650  eprosima::fastcdr::Cdr cdr(buffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN, eprosima::fastcdr::Cdr::DDS_CDR);
651 #endif
652  cdr.read_encapsulation();
653 
654 #ifdef VLINK_HAS_ROS2
655  if constexpr (is_ros2_msg_type<RealType>()) {
656  const auto* callbacks = get_ros2_msg_callbacks<RealType>();
657 
658  if VUNLIKELY (!callbacks || !callbacks->cdr_deserialize || !callbacks->cdr_deserialize(cdr, &deref(des))) {
659  return false;
660  }
661  } else {
662 #endif
663  if constexpr (VLINK_HAS_MEMBER(RealType, deserialize(cdr))) {
664  deref(des).deserialize(cdr);
665 #if FASTCDR_VERSION_MAJOR >= 2
666  } else {
667  cdr >> deref(des);
668 #endif
669  }
670 #ifdef VLINK_HAS_ROS2
671  }
672 #endif
673  } catch (const eprosima::fastcdr::exception::Exception& e) {
674  VLOG_T("Serializer: FastBuffer deserialize failed: ", e.what(), ".");
675  return false;
676  } catch (const std::exception& e) {
677  VLOG_T("Serializer: CDR deserialize failed: ", e.what(), ".");
678  return false;
679  }
680 #endif
681 #ifdef VLINK_HAS_PROTOBUF
682  } else if constexpr (TypeT == kProtoType) {
683  if (!src.empty()) {
684  if VUNLIKELY (!deref(des).ParseFromArray(src.data(), src.size())) {
685  VLOG_T("Serializer: Protobuf deserialize failed.");
686  return false;
687  }
688  } else {
689  deref(des).Clear();
690  }
691  } else if constexpr (TypeT == kProtoPtrType) {
692  if (!src.empty()) {
693  if VUNLIKELY (!des->ParseFromArray(src.data(), src.size())) {
694  VLOG_T("Serializer: Protobuf ptr deserialize failed.");
695  return false;
696  }
697  } else {
698  des->Clear();
699  }
700 #endif
701 #ifdef VLINK_HAS_FLATBUFFERS
702  } else if constexpr (TypeT == kFlatTableType) {
703  deref(des) = RealType{};
704 
705  if VLIKELY (!src.empty()) {
706  flatbuffers::Verifier verifier(src.data(), src.size());
707 
708  if VUNLIKELY (!verifier.VerifyBuffer<typename RealType::TableType>(nullptr)) {
709  VLOG_T("Serializer: Flatbuffers table deserialize failed.");
710  return false;
711  }
712 
713  auto target = flatbuffers::GetRoot<typename RealType::TableType>(src.data());
714  target->UnPackTo(&deref(des));
715  }
716  } else if constexpr (TypeT == kFlatPtrType) {
717  flatbuffers::Verifier verifier(src.data(), src.size());
718 
719  if VUNLIKELY (!verifier.VerifyBuffer<std::remove_pointer_t<T>>(nullptr)) {
720  VLOG_T("Serializer: Flatbuffers ptr verify failed.");
721  return false;
722  }
723 
724  des = const_cast<T>(flatbuffers::GetRoot<std::remove_pointer_t<T>>(src.data()));
725 
726  if VUNLIKELY (!des) {
727  VLOG_T("Serializer: Flatbuffers ptr deserialize failed.");
728  return false;
729  }
730 #endif
731  } else if constexpr (TypeT == kCustomType) {
732  try {
733  using ReturnT = decltype(deref(des) << src);
734 
735  if constexpr (std::is_convertible_v<ReturnT, bool>) {
736  if VUNLIKELY (!(deref(des) << src)) {
737  VLOG_T("Serializer: Custom deserialize failed.");
738  return false;
739  }
740  } else {
741  deref(des) << src;
742  }
743  } catch (const std::exception& e) {
744  VLOG_T("Serializer: Custom deserialize threw: ", e.what(), ".");
745  return false;
746  }
747  } else if constexpr (TypeT == kStringType) {
748  if VLIKELY (!src.empty()) {
749  deref(des) = std::string(reinterpret_cast<const char*>(src.data()), src.size());
750  } else {
751  deref(des) = std::string();
752  }
753  } else if constexpr (TypeT == kCharsType) {
754  (void)src;
755  (void)des;
756  VLOG_E("Serializer: Chars deserialization is not supported; use std::string.");
757  return false;
758  } else if constexpr (TypeT == kStreamType) {
759  std::stringstream ss(std::string(reinterpret_cast<const char*>(src.data()), src.size()));
760 
761  ss >> deref(des);
762 
763  if VUNLIKELY (ss.fail()) {
764  VLOG_T("Serializer: Stream deserialize failed.");
765  return false;
766  }
767  } else if constexpr (TypeT == kStandardType) {
768  if VLIKELY (!src.empty() && src.size() == sizeof(RealType)) {
769  std::memcpy(&deref(des), src.data(), src.size());
770  } else {
771  VLOG_T("Serializer: Standard layout deserialize failed.");
772  return false;
773  }
774  } else if constexpr (TypeT == kStandardPtrType) {
775  if VLIKELY (!src.empty() && src.size() == sizeof(std::remove_pointer_t<T>)) {
776  des = reinterpret_cast<T>(const_cast<uint8_t*>(src.data()));
777  } else {
778  VLOG_T("Serializer: Standard ptr layout deserialize failed.");
779  return false;
780  }
781  } else {
782  static_assert(Traits::ExpectFalse<T>(), "Not support deserialize.");
783  return false;
784  }
785 
786  return true;
787 }
788 
789 template <typename T>
790 inline bool deserialize(const Bytes& src, T& des) {
791  constexpr auto kType = get_type_of<T>();
792  return deserialize<kType>(src, des, TransportType::kUnknown);
793 }
794 
795 template <typename SrcT, typename DesT>
796 inline bool convert(const SrcT& src, DesT& des) {
797  static_assert(is_bytes_type<SrcT>() || is_bytes_type<DesT>(), "SrcT or DesT must be Bytes.");
798 
799  if constexpr (is_bytes_type<SrcT>() && is_bytes_type<DesT>()) {
800  des.shallow_copy(src);
801  return true;
802  } else if constexpr (is_bytes_type<DesT>()) {
803  return serialize(src, des);
804  } else {
805  return deserialize(src, des);
806  }
807 }
808 
809 template <typename T>
810 inline constexpr auto& deref(const T& t) noexcept {
811  if constexpr (Traits::IsSharedPtr<T>()) {
812  return *const_cast<T&>(t).get();
813  } else {
814  return const_cast<T&>(t);
815  }
816 }
817 
818 template <typename T>
819 inline constexpr bool is_bytes_type() noexcept {
820  return std::is_same_v<T, Bytes>;
821 }
822 
823 template <typename T>
824 inline constexpr bool is_dynamic_type() noexcept {
825  using RealType = typename Traits::RemoveSharedPtr<T>::Type;
826  return VLINK_HAS_MEMBER(RealType, is_vlink_dynamic_data());
827 }
828 
829 template <typename T>
830 inline constexpr bool is_cdr_type() noexcept {
831 #ifdef VLINK_HAS_CDR
832  using RealType = typename Traits::RemoveSharedPtr<T>::Type;
833 
834 #ifdef VLINK_HAS_ROS2
835  if constexpr (is_ros2_msg_type<RealType>()) {
836  return true;
837  }
838 #endif
839 
840  return (VLINK_HAS_MEMBER(RealType, serialize(std::declval<eprosima::fastcdr::Cdr&>())) &&
841  VLINK_HAS_MEMBER(RealType, deserialize(std::declval<eprosima::fastcdr::Cdr&>()))) ||
842  Helpers::contains_substring(NameDetector::get<RealType>(), VLINK_DDS_IDL_PREFIX);
843 #else
844  return false;
845 #endif
846 }
847 
848 template <typename T>
849 inline constexpr bool is_proto_type() noexcept {
850 #ifdef VLINK_HAS_PROTOBUF
851  using RealType = typename Traits::RemoveSharedPtr<T>::Type;
852  return VLINK_HAS_MEMBER(RealType, SerializeToArray(0, 0)) && VLINK_HAS_MEMBER(RealType, ParseFromArray(0, 0));
853 #else
854  return false;
855 #endif
856 }
857 
858 template <typename T>
859 inline constexpr bool is_proto_ptr_type() noexcept {
860 #ifdef VLINK_HAS_PROTOBUF
861  return std::is_pointer_v<T> && VLINK_HAS_MEMBER(std::remove_pointer_t<T>, SerializeToArray(0, 0)) &&
862  VLINK_HAS_MEMBER(std::remove_pointer_t<T>, ParseFromArray(0, 0));
863 #else
864  return false;
865 #endif
866 }
867 
868 template <typename T>
869 inline constexpr bool is_flat_table_type() noexcept {
870 #ifdef VLINK_HAS_FLATBUFFERS
871  using RealType = typename Traits::RemoveSharedPtr<T>::Type;
872  return std::is_base_of_v<flatbuffers::NativeTable, RealType>;
873 #else
874  return false;
875 #endif
876 }
877 
878 template <typename T>
879 inline constexpr bool is_flat_builder_type() noexcept {
880 #ifdef VLINK_HAS_FLATBUFFERS
881  return VLINK_HAS_MEMBER(T, fbb_) && VLINK_HAS_MEMBER(T, Finish());
882 #else
883  return false;
884 #endif
885 }
886 
887 template <typename T>
888 inline constexpr bool is_flat_ptr_type() noexcept {
889 #ifdef VLINK_HAS_FLATBUFFERS
890  return std::is_pointer_v<T> && std::is_base_of_v<flatbuffers::Table, std::remove_pointer_t<T>>;
891 #else
892  return false;
893 #endif
894 }
895 
896 template <typename T>
897 inline constexpr bool is_custom_type() noexcept {
898  using RealType = typename Traits::RemoveSharedPtr<T>::Type;
900 }
901 
902 template <typename T>
903 inline constexpr bool is_string_type() noexcept {
904  using RealType = typename Traits::RemoveSharedPtr<T>::Type;
905  return std::is_same_v<RealType, std::string>;
906 }
907 
908 template <typename T>
909 inline constexpr bool is_chars_type() noexcept {
910  using DecayedType = std::decay_t<T>;
911  using CharType = std::remove_pointer_t<DecayedType>;
912 
913  return std::is_pointer_v<DecayedType> && std::is_same_v<std::remove_cv_t<CharType>, char> &&
914  !std::is_volatile_v<CharType>;
915 }
916 
917 template <typename T>
918 inline constexpr bool is_stream_type() noexcept {
919  using RealType = typename Traits::RemoveSharedPtr<T>::Type;
920  return !std::is_pointer_v<RealType> && Traits::Operatorable<std::stringstream, std::decay_t<RealType>>();
921 }
922 
923 template <typename T>
924 inline constexpr bool is_standard_type() noexcept {
925  using RealType = typename Traits::RemoveSharedPtr<T>::Type;
926  return !std::is_pointer_v<RealType> && std::is_trivial_v<RealType> && std::is_standard_layout_v<RealType>;
927 }
928 
929 template <typename T>
930 inline constexpr bool is_standard_ptr_type() noexcept {
931  return std::is_pointer_v<T> && std::is_trivial_v<std::remove_pointer_t<T>> &&
932  std::is_standard_layout_v<std::remove_pointer_t<T>>;
933 }
934 
935 } // namespace Serializer
936 
937 } // namespace vlink
#define VLOG_E(...)
Definition: logger.h:841
#define VLOG_W(...)
Definition: logger.h:839
#define VLOG_T(...)
Definition: logger.h:833
#define VUNLIKELY(...)
Short alias for VLINK_UNLIKELY.
Definition: macros.h:289
#define VLIKELY(...)
Short alias for VLINK_LIKELY.
Definition: macros.h:284
Compile-time codec detection and dispatch for VLink message payloads.
#define VLINK_HAS_MEMBER(T, member)
Macro Definitions.
Definition: traits.h:316