/src/Fast-CDR/src/cpp/helpers/memory_helpers.hpp
Line | Count | Source |
1 | | // Copyright 2026 Proyectos y Sistemas de Mantenimiento SL (eProsima). |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | #ifndef _SRC_CPP_HELPERS_MEMORYHELPERS_HPP_ |
16 | | #define _SRC_CPP_HELPERS_MEMORYHELPERS_HPP_ |
17 | | |
18 | | #include <atomic> |
19 | | #include <type_traits> |
20 | | #include <utility> |
21 | | #include <cstring> |
22 | | |
23 | | namespace eprosima { |
24 | | namespace fastcdr { |
25 | | |
26 | | namespace internal { |
27 | | void use_char_pointer( |
28 | | char const volatile *const v); |
29 | | } // namespace internal |
30 | | |
31 | | #if (!defined(__GNUC__) && !defined(__clang__)) || defined(__pnacl__) || \ |
32 | | defined(__EMSCRIPTEN__) |
33 | | #define HAS_NO_INLINE_ASSEMBLY |
34 | | #endif // if (!defined(__GNUC__) && !defined(__clang__)) || defined(__pnacl__) || defined(__EMSCRIPTEN__) |
35 | | |
36 | | inline void clobber_memory() |
37 | 0 | { |
38 | 0 | std::atomic_signal_fence(std::memory_order_acq_rel); |
39 | 0 | } |
40 | | |
41 | | #ifndef HAS_NO_INLINE_ASSEMBLY |
42 | | #if !defined(__GNUC__) || defined(__llvm__) || defined(__INTEL_COMPILER) |
43 | | template<class Tp> |
44 | | inline void do_not_optimize( |
45 | | Tp& value) |
46 | 0 | { |
47 | 0 | #if defined(__clang__) |
48 | 0 | asm volatile ("" : "+r,m" (value) : : "memory"); |
49 | 0 | #else |
50 | 0 | asm volatile ("" : "+m,r" (value) : : "memory"); |
51 | 0 | #endif // if defined(__clang__) |
52 | 0 | } |
53 | | |
54 | | template<class Tp> |
55 | | inline void do_not_optimize( |
56 | | Tp&& value) |
57 | | { |
58 | | #if defined(__clang__) |
59 | | asm volatile ("" : "+r,m" (value) : : "memory"); |
60 | | #else |
61 | | asm volatile ("" : "+m,r" (value) : : "memory"); |
62 | | #endif // if defined(__clang__) |
63 | | } |
64 | | |
65 | | #elif (__GNUC__ >= 5) |
66 | | template<class Tp> |
67 | | inline typename std::enable_if<std::is_trivially_copyable<Tp>::value&& |
68 | | (sizeof(Tp) <= sizeof(Tp*))>::type |
69 | | do_not_optimize( |
70 | | Tp& value) |
71 | | { |
72 | | asm volatile ("" : "+m,r" (value) : : "memory"); |
73 | | } |
74 | | |
75 | | template<class Tp> |
76 | | inline typename std::enable_if<!std::is_trivially_copyable<Tp>::value || |
77 | | (sizeof(Tp) > sizeof(Tp*))>::type |
78 | | do_not_optimize( |
79 | | Tp& value) |
80 | | { |
81 | | asm volatile ("" : "+m" (value) : : "memory"); |
82 | | } |
83 | | |
84 | | template<class Tp> |
85 | | inline typename std::enable_if<std::is_trivially_copyable<Tp>::value&& |
86 | | (sizeof(Tp) <= sizeof(Tp*))>::type |
87 | | do_not_optimize( |
88 | | Tp&& value) |
89 | | { |
90 | | asm volatile ("" : "+m,r" (value) : : "memory"); |
91 | | } |
92 | | |
93 | | template<class Tp> |
94 | | inline typename std::enable_if<!std::is_trivially_copyable<Tp>::value || |
95 | | (sizeof(Tp) > sizeof(Tp*))>::type |
96 | | do_not_optimize( |
97 | | Tp&& value) |
98 | | { |
99 | | asm volatile ("" : "+m" (value) : : "memory"); |
100 | | } |
101 | | |
102 | | #endif // if !defined(__GNUC__) || defined(__llvm__) || defined(__INTEL_COMPILER) |
103 | | |
104 | | #elif defined(_MSC_VER) |
105 | | template<class Tp> |
106 | | inline void do_not_optimize( |
107 | | Tp& value) |
108 | | { |
109 | | internal::use_char_pointer(&reinterpret_cast<char const volatile&>(value)); |
110 | | clobber_memory(); |
111 | | } |
112 | | |
113 | | template<class Tp> |
114 | | inline void do_not_optimize( |
115 | | Tp&& value) |
116 | | { |
117 | | internal::use_char_pointer(&reinterpret_cast<char const volatile&>(value)); |
118 | | clobber_memory(); |
119 | | } |
120 | | |
121 | | #else |
122 | | template<class Tp> |
123 | | inline void do_not_optimize( |
124 | | Tp&& value) |
125 | | { |
126 | | internal::use_char_pointer(&reinterpret_cast<char const volatile&>(value)); |
127 | | } |
128 | | |
129 | | #endif // ifndef HAS_NO_INLINE_ASSEMBLY |
130 | | |
131 | | /*! |
132 | | * @brief Normalizes a boolean value to a canonical 0 or 1 representation. |
133 | | * |
134 | | * In release builds, the compiler may optimize away explicit normalization |
135 | | * of bool values (e.g. `if (b) { write 1; } else { write 0; }`), resulting |
136 | | * in the raw underlying byte being written directly to the serialization |
137 | | * buffer. If that byte contains garbage (e.g. from an uninitialized or |
138 | | * improperly assigned field), a non-canonical value ends up on the wire, |
139 | | * causing deserialization failures on the receiving side. |
140 | | * |
141 | | * This function forces normalization by reading the bool's raw byte via |
142 | | * @c memcpy and passing it through a compiler barrier (@c do_not_optimize) |
143 | | * preventing the optimization from eliding the check. |
144 | | * |
145 | | * @param b The boolean value to normalize. |
146 | | * @return @c true if the underlying byte is non-zero, @c false otherwise. |
147 | | */ |
148 | | inline bool normalize_bool( |
149 | | bool b) noexcept |
150 | 0 | { |
151 | 0 | uint8_t raw; |
152 | 0 | std::memcpy(&raw, &b, 1); |
153 | 0 | do_not_optimize(raw); |
154 | 0 | return static_cast<bool>(raw); |
155 | 0 | } |
156 | | |
157 | | } // namespace fastcdr |
158 | | } // namespace eprosima |
159 | | |
160 | | #endif // _SRC_CPP_HELPERS_MEMORYHELPERS_HPP_ |