GCC Code Coverage Report


Directory: ./
File: Utils/Types/CircularBuffer.cpp
Date: 2026-09-23 21:11:01
Exec Total Coverage
Lines: 128 146 87.7%
Functions: 25 25 100.0%
Branches: 38 53 71.7%

Line Branch Exec Source
1 /*
2 * CircularBuffer.cpp:
3 *
4 * Buffer used to efficiently store data in ring data structure. Uses an externally supplied
5 * data store as the backing for this buffer. Thus it is dependent on receiving sole ownership
6 * of the supplied buffer.
7 *
8 * This implementation file contains the function definitions.
9 *
10 * Created on: Apr 4, 2019
11 * Author: lestarch
12 * Revised March 2022
13 * Author: bocchino
14 */
15 #include <Fw/FPrimeBasicTypes.hpp>
16 #include <Fw/Types/Assert.hpp>
17 #include <Utils/Types/CircularBuffer.hpp>
18
19 namespace Types {
20
21 1243 CircularBuffer ::CircularBuffer()
22 1243 : m_store(nullptr), m_store_size(0), m_head_idx(0), m_allocated_size(0), m_high_water_mark(0) {}
23
24 28 CircularBuffer ::CircularBuffer(U8* const buffer, const FwSizeType size)
25 28 : m_store(nullptr), m_store_size(0), m_head_idx(0), m_allocated_size(0), m_high_water_mark(0) {
26 28 setup(buffer, size);
27 28 }
28
29 1242 void CircularBuffer ::setup(U8* const buffer, const FwSizeType size) {
30 1242 FW_ASSERT(size > 0);
31 1242 FW_ASSERT(buffer != nullptr);
32 1242 FW_ASSERT(m_store == nullptr && m_store_size == 0); // Not already setup
33
34 // Initialize buffer data
35 1242 m_store = buffer;
36 1242 m_store_size = size;
37 1242 m_head_idx = 0;
38 1242 m_allocated_size = 0;
39 1242 m_high_water_mark = 0;
40 1242 }
41
42 20381 FwSizeType CircularBuffer ::get_allocated_size() const {
43 20381 return m_allocated_size;
44 }
45
46 15248 FwSizeType CircularBuffer ::get_free_size() const {
47 15248 FW_ASSERT(m_store != nullptr && m_store_size != 0); // setup method was called
48 15248 FW_ASSERT(m_allocated_size <= m_store_size, static_cast<FwAssertArgType>(m_allocated_size));
49 15248 return m_store_size - m_allocated_size;
50 }
51
52 1789791 FwSizeType CircularBuffer ::advance_idx(FwSizeType idx, FwSizeType amount) const {
53 1789791 FW_ASSERT(idx < m_store_size, static_cast<FwAssertArgType>(idx));
54
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1789791 times.
1789791 return (idx + amount) % m_store_size;
55 }
56
57 14324 Fw::SerializeStatus CircularBuffer ::serialize(const U8* const buffer, const FwSizeType size) {
58 14324 FW_ASSERT(m_store != nullptr && m_store_size != 0); // setup method was called
59 14324 FW_ASSERT(buffer != nullptr);
60 // Check there is sufficient space
61
2/2
✓ Branch 2 taken 228 times.
✓ Branch 3 taken 14096 times.
14324 if (size > get_free_size()) {
62 228 return Fw::FW_SERIALIZE_NO_ROOM_LEFT;
63 }
64 // Copy in all the supplied data
65 14096 FwSizeType idx = advance_idx(m_head_idx, m_allocated_size);
66
2/2
✓ Branch 0 taken 1139316 times.
✓ Branch 1 taken 14096 times.
1153412 for (FwSizeType i = 0; i < size; i++) {
67 1139316 FW_ASSERT(idx < m_store_size, static_cast<FwAssertArgType>(idx));
68 1139316 m_store[idx] = buffer[i];
69 1139316 idx = advance_idx(idx);
70 }
71 14096 m_allocated_size += size;
72 14096 FW_ASSERT(m_allocated_size <= this->get_capacity(), static_cast<FwAssertArgType>(m_allocated_size));
73
2/2
✓ Branch 4 taken 8225 times.
✓ Branch 5 taken 5871 times.
14096 m_high_water_mark = (m_high_water_mark > m_allocated_size) ? m_high_water_mark : m_allocated_size;
74 14096 return Fw::FW_SERIALIZE_OK;
75 }
76
77 template <typename T>
78 398 Fw::SerializeStatus CircularBuffer ::serialize_impl(const T& serializable, const FwSizeType size) {
79 398 FW_ASSERT(m_store != nullptr && m_store_size != 0); // setup method was called
80 // Check there is sufficient space
81
3/3
✓ Branch 2 taken 199 times.
✓ Branch 4 taken 14 times.
✓ Branch 5 taken 185 times.
398 if (size > get_free_size()) {
82 28 return Fw::FW_SERIALIZE_NO_ROOM_LEFT;
83 }
84
1/1
✓ Branch 6 taken 185 times.
370 const FwSizeType idx = advance_idx(m_head_idx, m_allocated_size);
85 // Wrapping slot: serialize into a stack staging buffer, then byte-copy in with wrap-around
86
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 185 times.
370 if ((idx + size) > m_store_size) {
87 ✗ FW_ASSERT(size <= STAGING_BUFFER_SIZE, static_cast<FwAssertArgType>(size),
88 static_cast<FwAssertArgType>(STAGING_BUFFER_SIZE));
89 ✗ U8 staging[STAGING_BUFFER_SIZE > 0 ? STAGING_BUFFER_SIZE : 1] = {};
90 ✗ Fw::ExternalSerializeBuffer stagingBuffer(staging, size);
91 ✗ const Fw::SerializeStatus stagingStatus = stagingBuffer.serializeFrom(serializable);
92 ✗ if (stagingStatus != Fw::FW_SERIALIZE_OK) {
93 ✗ return stagingStatus;
94 }
95 ✗ return this->serialize(staging, size);
96 ✗ }
97 // Linear slot: serialize directly into the store
98
1/1
✓ Branch 4 taken 185 times.
370 Fw::ExternalSerializeBuffer slot(&m_store[idx], size);
99
1/1
✓ Branch 2 taken 185 times.
370 const Fw::SerializeStatus status = slot.serializeFrom(serializable);
100
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 184 times.
370 if (status != Fw::FW_SERIALIZE_OK) {
101 2 return status;
102 }
103 368 m_allocated_size += size;
104 368 FW_ASSERT(m_allocated_size <= this->get_capacity(), static_cast<FwAssertArgType>(m_allocated_size));
105
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 184 times.
368 m_high_water_mark = (m_high_water_mark > m_allocated_size) ? m_high_water_mark : m_allocated_size;
106 368 return Fw::FW_SERIALIZE_OK;
107 370 }
108
109 99 Fw::SerializeStatus CircularBuffer ::serialize(const Fw::Serializable& serializable, const FwSizeType size) {
110 99 return this->serialize_impl(serializable, size);
111 }
112
113 100 Fw::SerializeStatus CircularBuffer ::serialize(const Fw::LinearBufferBase& buffer, const FwSizeType size) {
114 100 return this->serialize_impl(buffer, size);
115 }
116
117 44 Fw::SerializeStatus CircularBuffer ::peek(char& value, FwSizeType offset) const {
118 44 FW_ASSERT(m_store != nullptr && m_store_size != 0); // setup method was called
119 44 return peek(reinterpret_cast<U8&>(value), offset);
120 }
121
122 551704 Fw::SerializeStatus CircularBuffer ::peek(U8& value, FwSizeType offset) const {
123 551704 FW_ASSERT(m_store != nullptr && m_store_size != 0); // setup method was called
124 // Check there is sufficient data
125
2/2
✓ Branch 2 taken 64 times.
✓ Branch 3 taken 551640 times.
551704 if ((sizeof(U8) + offset) > m_allocated_size) {
126 64 return Fw::FW_DESERIALIZE_BUFFER_EMPTY;
127 }
128 551640 const FwSizeType idx = advance_idx(m_head_idx, offset);
129 551640 FW_ASSERT(idx < m_store_size, static_cast<FwAssertArgType>(idx));
130 551640 value = m_store[idx];
131 551640 return Fw::FW_SERIALIZE_OK;
132 }
133
134 59 Fw::SerializeStatus CircularBuffer ::peek(U32& value, FwSizeType offset) const {
135 59 FW_ASSERT(m_store != nullptr && m_store_size != 0); // setup method was called
136 // Check there is sufficient data
137
2/2
✓ Branch 2 taken 28 times.
✓ Branch 3 taken 31 times.
59 if ((sizeof(U32) + offset) > m_allocated_size) {
138 28 return Fw::FW_DESERIALIZE_BUFFER_EMPTY;
139 }
140 31 value = 0;
141 31 FwSizeType idx = advance_idx(m_head_idx, offset);
142
143 // Deserialize all the bytes from network format
144
2/2
✓ Branch 0 taken 124 times.
✓ Branch 1 taken 31 times.
155 for (FwSizeType i = 0; i < sizeof(U32); i++) {
145 124 FW_ASSERT(idx < m_store_size, static_cast<FwAssertArgType>(idx));
146 124 value = (value << 8) | static_cast<U32>(m_store[idx]);
147 124 idx = advance_idx(idx);
148 }
149 31 return Fw::FW_SERIALIZE_OK;
150 }
151
152 21493 Fw::SerializeStatus CircularBuffer ::peek(U8* buffer, FwSizeType size, FwSizeType offset) const {
153 21493 FW_ASSERT(m_store != nullptr && m_store_size != 0); // setup method was called
154 21493 FW_ASSERT(buffer != nullptr);
155 // Check there is sufficient data
156
2/2
✓ Branch 2 taken 7717 times.
✓ Branch 3 taken 13776 times.
21493 if ((size + offset) > m_allocated_size) {
157 7717 return Fw::FW_DESERIALIZE_BUFFER_EMPTY;
158 }
159 13776 FwSizeType idx = advance_idx(m_head_idx, offset);
160 // Deserialize all the bytes from network format
161
2/2
✓ Branch 0 taken 60646 times.
✓ Branch 1 taken 13776 times.
74422 for (FwSizeType i = 0; i < size; i++) {
162 60646 FW_ASSERT(idx < m_store_size, static_cast<FwAssertArgType>(idx));
163 60646 buffer[i] = m_store[idx];
164 60646 idx = advance_idx(idx);
165 }
166 13776 return Fw::FW_SERIALIZE_OK;
167 }
168
169 template <typename T>
170 352 Fw::SerializeStatus CircularBuffer ::peek_impl(T& serializable, FwSizeType size, FwSizeType offset) const {
171 352 FW_ASSERT(m_store != nullptr && m_store_size != 0); // setup method was called
172 // Check there is sufficient data
173
2/2
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 174 times.
352 if ((size + offset) > m_allocated_size) {
174 4 return Fw::FW_DESERIALIZE_BUFFER_EMPTY;
175 }
176
1/1
✓ Branch 4 taken 174 times.
348 const FwSizeType idx = advance_idx(m_head_idx, offset);
177 // Wrapping slot: byte-copy out with wrap-around into a stack staging buffer, then deserialize
178
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 174 times.
348 if ((idx + size) > m_store_size) {
179 ✗ FW_ASSERT(size <= STAGING_BUFFER_SIZE, static_cast<FwAssertArgType>(size),
180 static_cast<FwAssertArgType>(STAGING_BUFFER_SIZE));
181 ✗ U8 staging[STAGING_BUFFER_SIZE > 0 ? STAGING_BUFFER_SIZE : 1];
182 ✗ const Fw::SerializeStatus peekStatus = this->peek(staging, size, offset);
183 ✗ if (peekStatus != Fw::FW_SERIALIZE_OK) {
184 ✗ return peekStatus;
185 }
186 ✗ Fw::ExternalSerializeBuffer stagingBuffer(staging, size);
187 ✗ const Fw::SerializeStatus status = stagingBuffer.setBuffLen(size);
188 ✗ FW_ASSERT(status == Fw::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(status));
189 ✗ return stagingBuffer.deserializeTo(serializable);
190 ✗ }
191 // Linear slot: deserialize directly from the store
192
1/1
✓ Branch 4 taken 174 times.
348 Fw::ExternalSerializeBuffer slot(&m_store[idx], size);
193
1/1
✓ Branch 2 taken 174 times.
348 Fw::SerializeStatus status = slot.setBuffLen(size);
194 348 FW_ASSERT(status == Fw::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(status));
195
1/1
✓ Branch 2 taken 174 times.
348 return slot.deserializeTo(serializable);
196 348 }
197
198 88 Fw::SerializeStatus CircularBuffer ::peek(Fw::Serializable& serializable, FwSizeType size, FwSizeType offset) const {
199 88 return this->peek_impl(serializable, size, offset);
200 }
201
202 88 Fw::SerializeStatus CircularBuffer ::peek(Fw::LinearBufferBase& buffer, FwSizeType size, FwSizeType offset) const {
203 88 return this->peek_impl(buffer, size, offset);
204 }
205
206 9905 Fw::SerializeStatus CircularBuffer ::rotate(FwSizeType amount) {
207 9905 FW_ASSERT(m_store != nullptr && m_store_size != 0); // setup method was called
208 // Check there is sufficient data
209
2/2
✓ Branch 2 taken 102 times.
✓ Branch 3 taken 9803 times.
9905 if (amount > m_allocated_size) {
210 102 return Fw::FW_DESERIALIZE_BUFFER_EMPTY;
211 }
212 9803 m_head_idx = advance_idx(m_head_idx, amount);
213 9803 m_allocated_size -= amount;
214 9803 return Fw::FW_SERIALIZE_OK;
215 }
216
217 2643 Fw::SerializeStatus CircularBuffer ::trim(FwSizeType amount) {
218 2643 FW_ASSERT(m_store != nullptr && m_store_size != 0); // setup method was called
219 // Check there is sufficient data
220
2/2
✓ Branch 2 taken 146 times.
✓ Branch 3 taken 2497 times.
2643 if (amount > m_allocated_size) {
221 146 return Fw::FW_DESERIALIZE_BUFFER_EMPTY;
222 }
223 // Simply reduce the allocated size without moving the head
224 2497 m_allocated_size -= amount;
225 2497 return Fw::FW_SERIALIZE_OK;
226 }
227
228 16473 FwSizeType CircularBuffer ::get_capacity() const {
229 16473 return m_store_size;
230 }
231
232 10106 FwSizeType CircularBuffer ::get_high_water_mark() const {
233 10106 return m_high_water_mark;
234 }
235
236 1063 void CircularBuffer ::clear_high_water_mark() {
237 1063 m_high_water_mark = 0;
238 1063 }
239
240 1109 U8* CircularBuffer ::get_buffer() {
241 1109 return m_store;
242 }
243
244 } // End Namespace Types
245