GCC Code Coverage Report


Directory: ./
File: firmware/console/binary_mlg_log/mlg_field.h
Date: 2025-10-03 00:57:22
Coverage Exec Excl Total
Lines: 99.8% 570 0 571
Functions: 100.0% 47 0 47
Branches: 85.7% 6 0 7
Decisions: 83.3% 5 - 6

Line Branch Decision Exec Source
1 #pragma once
2
3 #include <cstdint>
4 #include <cstring>
5
6 #include <rusefi/scaled_channel.h>
7
8 #include "writer.h"
9 #include "mlg_types.h"
10
11 // For unit tests we are manipulating with storage in runtime so consteval is not possible.
12 // In prod builds we have engine and configs as global instances so all addresses to read data from
13 // must be known compile-time. If consteval fails then some runtime logic made its way into LogField and that moves
14 // LogField instance into RAM which is correct from code perspective but incorrect intent-wise and consume code and RAM
15 // for no real reason.
16 #if defined(EFI_UNIT_TEST) && EFI_UNIT_TEST
17 #define LOG_FIELD_CONSTNESS_SPECIFIER_METHODS constexpr
18 #define LOG_FIELD_CONSTNESS_SPECIFIER_STORAGE const
19 #else
20 #define LOG_FIELD_CONSTNESS_SPECIFIER_METHODS consteval
21 #define LOG_FIELD_CONSTNESS_SPECIFIER_STORAGE const constinit
22 #endif
23
24 namespace MLG::Entries {
25 using namespace MLG;
26
27 class Field {
28 public:
29 // Scaled channels, memcpys data directly and describes format in header
30 template <typename TValue, int TMult, int TDiv>
31 234 LOG_FIELD_CONSTNESS_SPECIFIER_METHODS Field(const scaled_channel<TValue, TMult, TDiv>& toRead,
32 const char* name, const char* units, int8_t digits, const char* category = "none")
33 234 : m_multiplier(float(TDiv) / TMult)
34 234 , m_addr(toRead.getFirstByteAddr())
35 234 , m_type_id(static_cast<std::underlying_type_t<Types::Field::Scalar>>(Types::Field::resolveBuiltInNumberType<TValue>()))
36 234 , m_digits(digits)
37 234 , m_size(Types::Field::sizeForType<Types::Field::resolveBuiltInNumberType<TValue>()>())
38 234 , m_name(name)
39 234 , m_units(units)
40 234 , m_category(category)
41 234 , m_isBitField(false)
42 234 , m_bitsBlockOffset(0)
43 234 , m_bitNumber(0)
44 {
45 234 }
46
47 // Non-scaled channel, works for plain arithmetic types (int, float, uint8_t, etc)
48 template <typename TValue, typename = typename std::enable_if<std::is_arithmetic_v<TValue>>::type>
49 357 LOG_FIELD_CONSTNESS_SPECIFIER_METHODS Field(TValue& toRead,
50 const char* name, const char* units, int8_t digits, const char* category = "none")
51 357 : m_multiplier(1)
52 357 , m_addr(&toRead)
53 357 , m_type_id(static_cast<std::underlying_type_t<Types::Field::Scalar>>(Types::Field::resolveBuiltInNumberType<TValue>()))
54 357 , m_digits(digits)
55 357 , m_size(Types::Field::sizeForType<Types::Field::resolveBuiltInNumberType<TValue>()>())
56 357 , m_name(name)
57 357 , m_units(units)
58 357 , m_category(category)
59 357 , m_isBitField(false)
60 357 , m_bitsBlockOffset(0)
61 357 , m_bitNumber(0)
62 {
63 357 }
64
65 // Bit channel
66 template <typename TValue>
67 138 LOG_FIELD_CONSTNESS_SPECIFIER_METHODS Field(
68 TValue& toRead,
69 const uint32_t bitsBlockOffset,
70 const uint8_t bitNumber,
71 const char* name,
72 const char* units,
73 const char* category = "none"
74 138 ): m_multiplier(1)
75 138 , m_addr(&toRead)
76 138 , m_type_id(static_cast<std::underlying_type_t<Types::Field::Scalar>>(Types::Field::Scalar::U08))
77 138 , m_digits(0)
78 138 , m_size(1)
79 138 , m_name(name)
80 138 , m_units(units)
81 138 , m_category(category)
82 138 , m_isBitField(true)
83 138 , m_bitsBlockOffset(bitsBlockOffset)
84 138 , m_bitNumber(bitNumber)
85 {
86 138 }
87
88 727 constexpr size_t getSize() const { return m_size; }
89 380187558 constexpr const void* getAddr() const { return m_addr; }
90
91 // Write the header data describing this field.
92 // Returns the number of bytes written.
93 404940 size_t writeHeader(Writer& outBuffer) const {
94 404940 char buffer[Types::Field::DescriptorSize];
95
96 // Offset 0, length 1 = type
97 404940 buffer[0] = static_cast<char>(m_type_id);
98
99 // Offset 1, length 34 = name
100 404940 strncpy(&buffer[1], m_name, 34);
101
102 // Offset 35, length 10 = units
103 404940 strncpy(&buffer[35], m_units, 10);
104
105 // Offset 45, length 1 = Display style
106 // value 0 -> floating point number
107 404940 buffer[45] = 0;
108
109 // Offset 46, length 4 = Scale
110 404940 copyFloat(buffer + 46, m_multiplier);
111
112 // Offset 50, length 4 = shift before scaling (always 0)
113 404940 copyFloat(buffer + 50, 0);
114
115 // Offset 54, size 1 = digits to display (signed int)
116 404940 buffer[54] = m_digits;
117
118 // Offset 55, (optional) category string
119
1/2
✓ Branch 0 taken 404940 times.
✗ Branch 1 not taken.
1/2
✓ Decision 'true' taken 404940 times.
✗ Decision 'false' not taken.
404940 if (m_category) {
120 404940 size_t categoryLength = strlen(m_category);
121 404940 size_t lengthAfterCategory = 34 - categoryLength;
122 404940 memcpy(&buffer[55], m_category, categoryLength);
123 404940 memset(&buffer[55] + categoryLength, 0, lengthAfterCategory);
124 } else {
125 memset(&buffer[55], 0, 34);
126 }
127
128 // Total size = 89
129
1/1
✓ Branch 1 taken 404940 times.
404940 outBuffer.write(buffer, Types::Field::DescriptorSize);
130
131 404940 return Types::Field::DescriptorSize;
132 }
133
134 // Write the field's data to the buffer.
135 // Returns the number of bytes written.
136 380187562 size_t writeData(char* buffer, void *offset) const {
137
2/2
✓ Branch 0 taken 71644701 times.
✓ Branch 1 taken 308542861 times.
2/2
✓ Decision 'true' taken 71644701 times.
✓ Decision 'false' taken 308542861 times.
380187562 if (m_isBitField) {
138 71644701 const char* const bitsBlockAddr = static_cast<const char*>(m_addr) + m_bitsBlockOffset;
139 71644701 const char* const byteWithBitAddr = bitsBlockAddr + m_bitNumber / 8;
140 71644701 unsigned char byteWithBit = 0;
141 71644701 memcpy_swapend(&byteWithBit, byteWithBitAddr, m_size, offset);
142 71644701 const uint8_t bitNumberInByte = m_bitNumber % 8;
143 71644701 buffer[0] = static_cast<char>(static_cast<bool>(byteWithBit & (1 << bitNumberInByte)));
144 } else {
145 308542861 memcpy_swapend(buffer, m_addr, m_size, offset);
146 }
147
148 380187562 return m_size;
149 }
150
151 private:
152
153 380997442 static void memcpy_swapend(void* dest, const void* src, size_t const size, void *offset) {
154 380997442 const char* src2 = reinterpret_cast<const char*>(src);
155 380997442 char* dest2 = reinterpret_cast<char*>(dest);
156
2/2
✓ Branch 0 taken 811726411 times.
✓ Branch 1 taken 380997442 times.
2/2
✓ Decision 'true' taken 811726411 times.
✓ Decision 'false' taken 380997442 times.
1192723853 for (size_t i = 0; i < size; i++) {
157 // Endian swap - copy the end to the beginning
158 811726411 dest2[i] = src2[size - 1 - i + (uint64_t)offset];
159 }
160 380997442 }
161
162 809880 static void copyFloat(char* buffer, float value) {
163 809880 memcpy_swapend(buffer, &value, sizeof(float), nullptr);
164 809880 }
165
166 const float m_multiplier;
167 const void* const m_addr;
168 const uint8_t m_type_id;
169 const int8_t m_digits;
170 const uint8_t m_size;
171
172 const char* const m_name;
173 const char* const m_units;
174 const char* const m_category;
175
176 const bool m_isBitField;
177 const uint32_t m_bitsBlockOffset; // only for bit log fields
178 const uint8_t m_bitNumber; // only for bit log fields
179 };
180 }
181