Coverage Report

Created: 2026-10-28 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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_