GCC Code Coverage Report


Directory: Svc/DpCompressProc/
File: DpCompressProc.cpp
Date: 2026-09-03 21:17:35
Exec Total Coverage
Lines: 153 173 88.4%
Functions: 4 4 100.0%
Branches: 97 116 83.6%

Line Branch Exec Source
1 // ======================================================================
2 // \title DpCompressProc.cpp
3 // \author kubiak
4 // \brief cpp file for DpCompressProc component implementation class
5 // ======================================================================
6
7 #include <cstring>
8
9 #include "Svc/DpCompressProc/DpCompressProc.hpp"
10
11 #include <Fw/Dp/DpContainer.hpp>
12 #include <Fw/Prm/ParamValid.hpp>
13
14 namespace Svc {
15
16 // ----------------------------------------------------------------------
17 // Component construction and destruction
18 // ----------------------------------------------------------------------
19
20 1 DpCompressProc ::DpCompressProc(const char* const compName) : DpCompressProcComponentBase(compName) {}
21
22 2 DpCompressProc ::~DpCompressProc() {}
23
24 // ----------------------------------------------------------------------
25 // Handler implementations for typed input ports
26 // ----------------------------------------------------------------------
27
28 473868 void DpCompressProc::serializeCompressionHeader(Fw::LinearBufferBase& serializer,
29 const FwSizeStoreType compressed_payload_size,
30 const CompressionMetadata& metadata) {
31 473868 const FwDpIdType record_id = this->getIdBase() + RecordId::CompressionRecord;
32
33 Fw::SerializeStatus ok;
34
35 473868 ok = serializer.serializeFrom(record_id);
36 473868 FW_ASSERT(ok == Fw::FW_SERIALIZE_OK, ok);
37 473868 ok = serializer.serializeFrom(
38 473868 static_cast<FwSizeStoreType>(compressed_payload_size + CompressionMetadata::SERIALIZED_SIZE));
39 473868 FW_ASSERT(ok == Fw::FW_SERIALIZE_OK, ok);
40 473868 ok = serializer.serializeFrom(metadata);
41 473868 FW_ASSERT(ok == Fw::FW_SERIALIZE_OK, ok);
42 473868 }
43
44 10012 void DpCompressProc ::procRequest_handler(FwIndexType portNum, Fw::Buffer& fwBuffer) {
45
1/1
✓ Branch 2 taken 10012 times.
10012 Fw::ParamValid param_valid;
46
1/1
✓ Branch 3 taken 10012 times.
10012 Fw::Enabled en_compression = paramGet_ENABLE(param_valid);
47 10012 FW_ASSERT(FW_PARAM_OK(param_valid), param_valid);
48
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 10011 times.
10012 if (en_compression == Fw::Enabled::DISABLED) {
49 // Bypass compression
50 1 return;
51 }
52
53
2/3
✓ Branch 5 taken 10011 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 10011 times.
10011 if (!isConnected_compressChunk_OutputPort(0)) {
54 return;
55 }
56
57 // Check that the buffer is large enough to hold a data product packet
58
3/3
✓ Branch 4 taken 10011 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 10010 times.
10011 if (fwBuffer.getSize() < Fw::DpContainer::MIN_PACKET_SIZE) {
59
2/2
✓ Branch 8 taken 1 times.
✓ Branch 11 taken 1 times.
1 this->log_WARNING_HI_BufferTooSmallForPacket(fwBuffer.getSize(), Fw::DpContainer::MIN_PACKET_SIZE);
60 1 return;
61 }
62
63
1/1
✓ Branch 3 taken 10010 times.
10010 Fw::DpContainer container(0, fwBuffer);
64
1/1
✓ Branch 2 taken 10010 times.
10010 const Fw::SerializeStatus desStat = container.deserializeHeader();
65
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10010 times.
10010 if (desStat != Fw::FW_SERIALIZE_OK) {
66 // Cannot process a container with an invalid header. Give up
67 this->log_WARNING_HI_InvalidHeader(fwBuffer.getSize(), static_cast<U32>(desStat));
68 return;
69 }
70
71 // Record sizes are stored as FwSizeStoreType. Give up on any buffer
72 // whose contents could overflow a record size field
73
3/3
✓ Branch 4 taken 10010 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 10009 times.
10010 if (fwBuffer.getSize() >= std::numeric_limits<FwSizeStoreType>::max()) {
74
1/1
✓ Branch 9 taken 1 times.
1 this->log_WARNING_HI_ContainerTooLarge(container.getId(), container.getDataSize());
75 1 return;
76 }
77 // Consistency check: the header's data size must fit within the buffer
78
2/3
✓ Branch 6 taken 10009 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 10009 times.
10009 if (container.getDataSize() > fwBuffer.getSize() - Fw::DpContainer::MIN_PACKET_SIZE) {
79 this->log_WARNING_HI_BufferTooSmallForPacket(fwBuffer.getSize(),
80 Fw::DpContainer::MIN_PACKET_SIZE + container.getDataSize());
81 return;
82 }
83
84
1/1
✓ Branch 5 taken 10009 times.
10009 FwSizeType prm_chunk_size = paramGet_CHUNK_SIZE(param_valid);
85 10009 FW_ASSERT(FW_PARAM_OK(param_valid), param_valid);
86
87
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 10009 times.
10009 if (prm_chunk_size > container.getDataSize()) {
88 prm_chunk_size = container.getDataSize();
89 }
90 10009 const FwSizeType max_chunk_size = prm_chunk_size;
91 10009 FW_ASSERT(max_chunk_size <= std::numeric_limits<FwSizeStoreType>::max(),
92 static_cast<FwAssertArgType>(max_chunk_size));
93
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10009 times.
10009 if (max_chunk_size <= 0) {
94 // Chunk size is invalid. Give up
95 return;
96 }
97
98
1/1
✓ Branch 4 taken 10009 times.
20018 Fw::Buffer data_buffer(fwBuffer.getData() + Fw::DpContainer::DATA_OFFSET,
99
2/2
✓ Branch 4 taken 10009 times.
✓ Branch 9 taken 10009 times.
20018 fwBuffer.getSize() - Fw::DpContainer::MIN_PACKET_SIZE);
100 10009 FW_ASSERT(data_buffer.getSize() > 0, static_cast<FwAssertArgType>(data_buffer.getSize()));
101
102
1/1
✓ Branch 2 taken 10009 times.
10009 auto data_deser = data_buffer.getDeserializer();
103
1/1
✓ Branch 2 taken 10009 times.
10009 auto data_reser = data_buffer.getSerializer();
104
105 // Compression record serialized size
106 10009 const FwSizeType compression_header_size =
107 sizeof(FwDpIdType) + sizeof(FwSizeStoreType) + CompressionMetadata::SERIALIZED_SIZE;
108
109 // DpCompressProc processing state machine.
110 // Tracks the processing steps that need to occur for
111 // each chunk, whether is is compressed or not
112 enum {
113 // Initial state. First processing state will
114 // either be PRE_COMMIT or LAST_COMPRESSED depending
115 // on the compression of the first chunk.
116 INIT,
117
118 // Have not committed to returning a compressed
119 // data product. Stay in this state until at least
120 // one chunk is found to be compressible
121 PRE_COMMIT,
122
123 // The last chunk was compressed.
124 // The next chunk will need to have its own
125 // header prepended
126 LAST_COMPRESSED,
127
128 // The last chunk was uncompressed.
129 // If more uncompressed chunks are found then the
130 // size of the last uncompressed segment needs to
131 // grow to accommodate this segment
132 LAST_UNCOMPRESSED
133 } state;
134
135 10009 state = INIT;
136
137 10009 FwSizeType uncompressed_size = 0;
138 10009 U8* uncompressed_head = nullptr;
139 10009 Fw::SerializeStatus ser_stat = Fw::FW_SERIALIZE_OK;
140
141
3/3
✓ Branch 2 taken 515131 times.
✓ Branch 4 taken 505122 times.
✓ Branch 5 taken 10009 times.
515131 while (data_deser.getDeserializeSizeLeft() > 0) {
142 // Chunk size for this iteration
143
1/1
✓ Branch 2 taken 505122 times.
505122 const FwSizeType chunk_size_tmp = (data_deser.getDeserializeSizeLeft() < max_chunk_size)
144
1/3
✗ Branch 0 not taken.
✓ Branch 1 taken 505122 times.
✗ Branch 4 not taken.
505122 ? static_cast<FwSizeType>(data_deser.getDeserializeSizeLeft())
145 505122 : max_chunk_size;
146
147 // Ensure that FwSizeStoreType is large enough to hold the chunk_size
148 505122 FW_ASSERT(chunk_size_tmp > 0, static_cast<FwAssertArgType>(chunk_size_tmp));
149 505122 FW_ASSERT(chunk_size_tmp <= std::numeric_limits<FwSizeStoreType>::max(),
150 static_cast<FwAssertArgType>(chunk_size_tmp));
151 505122 const FwSizeStoreType chunk_size = static_cast<FwSizeStoreType>(chunk_size_tmp);
152 // a const U8 * and I need to mutable U8*
153
3/3
✓ Branch 2 taken 505122 times.
✓ Branch 6 taken 505122 times.
✓ Branch 10 taken 505122 times.
505122 U8* data_offset = data_buffer.getData() + (data_buffer.getSize() - data_deser.getDeserializeSizeLeft());
154
155
1/1
✓ Branch 2 taken 505122 times.
505122 Fw::Buffer compression_buffer(data_offset, chunk_size);
156
157 // Jump the deserializer to mark the data as read
158
1/1
✓ Branch 2 taken 505122 times.
505122 ser_stat = data_deser.deserializeSkip(chunk_size);
159 505122 FW_ASSERT(ser_stat == Fw::FW_SERIALIZE_OK, ser_stat);
160
161
1/1
✓ Branch 2 taken 505122 times.
505122 CompressionAlgorithm alg = CompressionAlgorithm::UNCOMPRESSED;
162 505122 FwSizeType min_compression = 0;
163 505122 FwSizeType compression_offset = 0;
164
165 // Only attempt compression on chunks larger than
166 // 4 times the header size. Sizes below 3 times the header
167 // size could trigger negative sizes in the min_compression
168 // calculation
169
1/2
✓ Branch 0 taken 505122 times.
✗ Branch 1 not taken.
505122 if (chunk_size > 4 * compression_header_size) {
170
3/4
✓ Branch 0 taken 10009 times.
✓ Branch 1 taken 3404 times.
✓ Branch 2 taken 491709 times.
✗ Branch 3 not taken.
505122 switch (state) {
171 10009 case INIT:
172 // Need room for two headers.
173 // 1. This compressed chunk
174 // 2. A potential uncompressed chunk that follows
175 // This is assuming that the initial chunk can be compressed
176 10009 min_compression = chunk_size - 2 * compression_header_size;
177
178 // Compression needs to occur in the buffer with enough
179 // space for the this header
180 10009 compression_offset = compression_header_size;
181 10009 break;
182 3404 case PRE_COMMIT:
183 // Need room for three headers.
184 // 1. The initial uncompressed chunk
185 // 2. This compressed chunk
186 // 3. A potential uncompressed chunk that follows
187 3404 min_compression = chunk_size - 3 * compression_header_size;
188
189 // Compression needs to occur in the buffer with enough
190 // space for the this header AND the initial uncompressed header
191 3404 compression_offset = 2 * compression_header_size;
192 3404 break;
193 491709 case LAST_UNCOMPRESSED:
194 case LAST_COMPRESSED:
195 // Need room for two headers.
196 // 1. The next compressed chunk
197 // 2. A potential uncompressed chunk
198 // Can take advantage of compression that has occurred
199 // before this point
200 // Note: Could keep track of the number of available spare bytes
201 // in the buffer, but that adds a complexity for very little benefit
202 491709 min_compression = chunk_size - (2 * compression_header_size);
203 // Compression needs to occur in the buffer with enough
204 // space for the this header
205 491709 compression_offset = compression_header_size;
206 491709 break;
207 default:
208 FW_ASSERT(false, state);
209 break;
210 }
211
212
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 505122 times.
505122 if (min_compression > chunk_size - compression_offset) {
213 min_compression = chunk_size - compression_offset;
214 }
215
216 505122 FW_ASSERT(min_compression <= chunk_size, static_cast<FwAssertArgType>(min_compression),
217 static_cast<FwAssertArgType>(chunk_size));
218
219 505122 FW_ASSERT(compression_offset <= chunk_size, static_cast<FwAssertArgType>(compression_offset),
220 static_cast<FwAssertArgType>(chunk_size));
221
222
2/2
✓ Branch 3 taken 505122 times.
✓ Branch 8 taken 505122 times.
505122 alg = compressChunk_out(0, compression_buffer, min_compression, compression_offset);
223
224
2/2
✓ Branch 2 taken 378727 times.
✓ Branch 3 taken 126395 times.
505122 if (alg != CompressionAlgorithm::UNCOMPRESSED) {
225 // The compressor writes up to min_compression bytes of compressed
226 // data after write_offset bytes of reserved space
227 378727 FW_ASSERT(compression_buffer.getSize() <= min_compression + compression_offset,
228 static_cast<FwAssertArgType>(compression_buffer.getSize()),
229 static_cast<FwAssertArgType>(min_compression),
230 static_cast<FwAssertArgType>(compression_offset));
231 }
232 }
233
234
2/2
✓ Branch 2 taken 378727 times.
✓ Branch 3 taken 126395 times.
505122 if (alg != CompressionAlgorithm::UNCOMPRESSED) {
235 // Data was compressed
236
237
1/1
✓ Branch 2 taken 378727 times.
378727 U8* const comp_data_ptr = compression_buffer.getData() + compression_offset;
238
239 const FwSizeType compressed_size_tmp =
240
1/1
✓ Branch 2 taken 378727 times.
378727 static_cast<FwSizeType>(compression_buffer.getSize()) - static_cast<FwSizeType>(compression_offset);
241 378727 FW_ASSERT(compressed_size_tmp <= std::numeric_limits<FwSizeStoreType>::max());
242 378727 FW_ASSERT(compressed_size_tmp < chunk_size);
243 378727 const FwSizeStoreType compressed_size = static_cast<FwSizeStoreType>(compressed_size_tmp);
244
245 // If the first chunk is compressible treat the state as
246 // if it was LAST_COMPRESSED. No need to perform the special
247 // move and header serialization in PRE_COMMIT
248
2/2
✓ Branch 0 taken 7436 times.
✓ Branch 1 taken 371291 times.
378727 if (state == INIT) {
249 7436 state = LAST_COMPRESSED;
250 }
251
252
2/2
✓ Branch 2 taken 378727 times.
✓ Branch 6 taken 378727 times.
378727 const FwSizeType deser_loc = data_deser.getSize() - data_deser.getDeserializeSizeLeft();
253
3/4
✓ Branch 0 taken 2543 times.
✓ Branch 1 taken 286011 times.
✓ Branch 2 taken 90173 times.
✗ Branch 3 not taken.
378727 switch (state) {
254 2543 case PRE_COMMIT:
255 // Case A.
256 // 1. Memmove uncompressed data so far by the size of compressed header
257 // 2. Prepend a header to the uncompressed data
258 // 3. Write a compressed header to the compressed data
259 // 4. Serialize compressed data
260
261 // Assert that the serialization operations in Case A will stay within deser_loc bounds
262 2543 FW_ASSERT(deser_loc >= (uncompressed_size + 2 * compression_header_size + compressed_size),
263 static_cast<FwAssertArgType>(deser_loc), static_cast<FwAssertArgType>(uncompressed_size),
264 static_cast<FwAssertArgType>(compressed_size));
265
4/6
✓ Branch 2 taken 2543 times.
✓ Branch 6 taken 2543 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2543 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 2543 times.
2543 (void)std::memmove(data_buffer.getData() + compression_header_size, data_buffer.getData(),
266 uncompressed_size);
267
268 // Serialize the header bytes to the front of the data
269
1/1
✓ Branch 4 taken 2543 times.
5086 serializeCompressionHeader(data_reser, static_cast<FwSizeStoreType>(uncompressed_size),
270
1/1
✓ Branch 2 taken 2543 times.
5086 CompressionMetadata(CompressionAlgorithm::UNCOMPRESSED));
271
272 // Move the serializer past the uncompressed chunk manually
273
1/1
✓ Branch 2 taken 2543 times.
2543 ser_stat = data_reser.serializeSkip(uncompressed_size);
274 2543 FW_ASSERT(ser_stat == Fw::FW_SERIALIZE_OK, ser_stat);
275
276
2/2
✓ Branch 7 taken 2543 times.
✓ Branch 11 taken 2543 times.
2543 serializeCompressionHeader(data_reser, compressed_size, CompressionMetadata(alg));
277
278
1/1
✓ Branch 2 taken 2543 times.
2543 ser_stat = data_reser.serializeFrom(comp_data_ptr, compressed_size, Fw::Serialization::OMIT_LENGTH);
279 2543 FW_ASSERT(ser_stat == Fw::FW_SERIALIZE_OK, ser_stat);
280 2543 break;
281 286011 case LAST_COMPRESSED:
282 // Case B
283 // 1. Write header at data_reser location
284 // 2. Serialize compressed data
285
2/2
✓ Branch 7 taken 286011 times.
✓ Branch 11 taken 286011 times.
286011 serializeCompressionHeader(data_reser, compressed_size, CompressionMetadata(alg));
286
287
1/1
✓ Branch 2 taken 286011 times.
286011 ser_stat = data_reser.serializeFrom(comp_data_ptr, compressed_size, Fw::Serialization::OMIT_LENGTH);
288 286011 FW_ASSERT(ser_stat == Fw::FW_SERIALIZE_OK, ser_stat);
289 286011 break;
290 90173 case LAST_UNCOMPRESSED:
291 // Case E
292 // 1. Write header for uncompressed data.
293 // data_reser has been kept at this location
294 // 2. Serialize uncompressed data
295 // 3. Write header for compressed data at data_reser location
296 // 4. Serialize compressed data
297
298 // Assert that the serialization operations in Case E will stay within deser_loc bounds
299 90173 FW_ASSERT(
300 deser_loc >=
301 (data_reser.getSize() + uncompressed_size + 2 * compression_header_size + compressed_size),
302 static_cast<FwAssertArgType>(deser_loc), static_cast<FwAssertArgType>(data_reser.getSize()),
303 static_cast<FwAssertArgType>(uncompressed_size), static_cast<FwAssertArgType>(compressed_size));
304
1/1
✓ Branch 4 taken 90173 times.
180346 serializeCompressionHeader(data_reser, static_cast<FwSizeStoreType>(uncompressed_size),
305
1/1
✓ Branch 2 taken 90173 times.
180346 CompressionMetadata(CompressionAlgorithm::UNCOMPRESSED));
306
307 90173 FW_ASSERT(uncompressed_head != nullptr);
308 ser_stat =
309
1/1
✓ Branch 2 taken 90173 times.
90173 data_reser.serializeFrom(uncompressed_head, uncompressed_size, Fw::Serialization::OMIT_LENGTH);
310 90173 FW_ASSERT(ser_stat == Fw::FW_SERIALIZE_OK, ser_stat);
311
312
2/2
✓ Branch 7 taken 90173 times.
✓ Branch 11 taken 90173 times.
90173 serializeCompressionHeader(data_reser, compressed_size, CompressionMetadata(alg));
313
314
1/1
✓ Branch 2 taken 90173 times.
90173 ser_stat = data_reser.serializeFrom(comp_data_ptr, compressed_size, Fw::Serialization::OMIT_LENGTH);
315 90173 FW_ASSERT(ser_stat == Fw::FW_SERIALIZE_OK, ser_stat);
316 90173 break;
317 default:
318 FW_ASSERT(false, state);
319 break;
320 }
321
322 // Last chunk was compressed. Remove history of uncompressed data
323 378727 uncompressed_size = 0;
324 378727 uncompressed_head = nullptr;
325 378727 state = LAST_COMPRESSED;
326 } else {
327 // Data was uncompressed
328
329 // If the first chunk is not compressible treat the state as
330 // if it was PRE_COMMIT
331
2/2
✓ Branch 0 taken 2573 times.
✓ Branch 1 taken 123822 times.
126395 if (state == INIT) {
332 2573 state = PRE_COMMIT;
333 }
334
335
3/4
✓ Branch 0 taken 3434 times.
✓ Branch 1 taken 92598 times.
✓ Branch 2 taken 30363 times.
✗ Branch 3 not taken.
126395 switch (state) {
336 3434 case PRE_COMMIT:
337 // No work.
338 // Keep looking for a compressible chunk in order to commit
339 // to a compressed structure
340 3434 state = PRE_COMMIT;
341 3434 break;
342 92598 case LAST_COMPRESSED:
343 // Case C - First incompressible chunk
344 //
345 // 1. Mark the location of the start of incompressible chunks
346
1/1
✓ Branch 2 taken 92598 times.
92598 uncompressed_head = compression_buffer.getData();
347
348 92598 state = LAST_UNCOMPRESSED;
349 92598 break;
350 30363 case LAST_UNCOMPRESSED:
351 // Case D - Continued sequence of incompressible chunks
352 // No work. Keep increasing track of uncompressed_size
353 30363 state = LAST_UNCOMPRESSED;
354 30363 break;
355 default:
356 FW_ASSERT(false, state);
357 break;
358 }
359 126395 uncompressed_size += chunk_size;
360 }
361
362 // Confirm that the serialized location has not jumped ahead of the deserialize location
363
1/1
✓ Branch 2 taken 505122 times.
505122 const FwSizeType ser_loc = data_reser.getSize();
364
2/2
✓ Branch 2 taken 505122 times.
✓ Branch 6 taken 505122 times.
505122 const FwSizeType deser_loc = static_cast<uintptr_t>(data_deser.getBuffAddrLeft() - data_buffer.getData());
365 505122 FW_ASSERT(ser_loc <= deser_loc, static_cast<FwAssertArgType>(ser_loc), static_cast<FwAssertArgType>(deser_loc));
366 505122 }
367
368
3/4
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 2425 times.
✓ Branch 2 taken 7554 times.
✗ Branch 3 not taken.
10009 switch (state) {
369 30 case PRE_COMMIT:
370 // Never compressed a chunk. Return the buffer
371 // unchanged
372
1/1
✓ Branch 9 taken 30 times.
30 log_ACTIVITY_LO_DidNotCompress(container.getId(), container.getDataSize());
373 30 break;
374 2425 case LAST_UNCOMPRESSED:
375 // Need to serialize the last bit of uncompressed data
376 // 1. Write header for uncompressed data.
377 // data_reser has been kept at this location
378 // 2. Serialize uncompressed data
379
1/1
✓ Branch 4 taken 2425 times.
4850 serializeCompressionHeader(data_reser, static_cast<FwSizeStoreType>(uncompressed_size),
380
1/1
✓ Branch 2 taken 2425 times.
4850 CompressionMetadata(CompressionAlgorithm::UNCOMPRESSED));
381
382 2425 FW_ASSERT(uncompressed_head != nullptr);
383
1/1
✓ Branch 2 taken 2425 times.
2425 ser_stat = data_reser.serializeFrom(uncompressed_head, uncompressed_size, Fw::Serialization::OMIT_LENGTH);
384 2425 FW_ASSERT(ser_stat == Fw::FW_SERIALIZE_OK, ser_stat);
385
386 /* FALLTHRU */
387 case LAST_COMPRESSED:
388 // Update buffer and header size to be consistent
389 // with the compression achieved
390
391 {
392
1/1
✓ Branch 2 taken 9979 times.
9979 FwSizeType comp_data_size = data_reser.getSize();
393
1/1
✓ Branch 9 taken 9979 times.
9979 log_DIAGNOSTIC_CompressionComplete(container.getId(), container.getDataSize(), comp_data_size);
394
395 9979 container.setDataSize(comp_data_size);
396
1/1
✓ Branch 2 taken 9979 times.
9979 container.serializeHeader();
397 }
398 9979 break;
399 default:
400 FW_ASSERT(false, state);
401 }
402 10016 }
403
404 } // namespace Svc
405