GCC Code Coverage Report


Directory: ./
File: Os/File.cpp
Date: 2026-09-23 21:11:01
Exec Total Coverage
Lines: 182 211 86.3%
Functions: 25 27 92.6%
Branches: 88 129 68.2%

Line Branch Exec Source
1 // ======================================================================
2 // \title Os/File.cpp
3 // \brief common function implementation for Os::File
4 // ======================================================================
5 #include <Fw/Types/Assert.hpp>
6 #include <Fw/Types/StringUtils.hpp>
7 #include <Os/File.hpp>
8 #include <algorithm>
9 #include <config/FppConstantsAc.hpp>
10
11 namespace Os {
12
13
6/6
✓ Branch 14 taken 46137 times.
✓ Branch 20 taken 23622144 times.
✓ Branch 21 taken 46137 times.
✓ Branch 27 taken 738192 times.
✓ Branch 28 taken 46137 times.
✓ Branch 34 taken 46137 times.
24406473 File::File() : m_crc_buffer(), m_handle_storage(), m_delegate(*FileInterface::getDelegate(m_handle_storage)) {
14 46137 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileInterface*>(&this->m_handle_storage[0]));
15 46137 }
16
17 185314 File::~File() {
18 92656 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileInterface*>(&this->m_handle_storage[0]));
19
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 46328 times.
✓ Branch 5 taken 9012 times.
✓ Branch 6 taken 37316 times.
92656 if (this->m_mode != OPEN_NO_MODE) {
20 18024 this->close();
21 }
22 92656 m_delegate.~FileInterface();
23 92658 }
24
25 291 File::File(const File& other)
26
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 291 times.
291 : m_mode(other.m_mode),
27 291 m_hash(other.m_hash),
28
2/2
✓ Branch 4 taken 148992 times.
✓ Branch 5 taken 291 times.
149283 m_crc_buffer(),
29
2/2
✓ Branch 4 taken 4656 times.
✓ Branch 5 taken 291 times.
4947 m_handle_storage(),
30
1/1
✓ Branch 14 taken 291 times.
582 m_delegate(*FileInterface::getDelegate(m_handle_storage, &other.m_delegate)) {
31 291 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileInterface*>(&this->m_handle_storage[0]));
32 291 }
33
34 91 File& File::operator=(const File& other) {
35
1/2
✓ Branch 0 taken 91 times.
✗ Branch 1 not taken.
91 if (this != &other) {
36 // The delegate below is constructed over the storage of the existing one. Any file this
37 // object currently holds must be closed first or its handle is orphaned permanently.
38
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 91 times.
✓ Branch 5 taken 70 times.
✓ Branch 6 taken 21 times.
91 if (this->m_mode != OPEN_NO_MODE) {
39 70 this->close();
40 }
41 91 this->m_delegate.~FileInterface();
42
1/2
✗ Branch 5 not taken.
✓ Branch 6 taken 91 times.
91 this->m_mode = other.m_mode;
43 91 this->m_hash = other.m_hash;
44 91 (void)FileInterface::getDelegate(m_handle_storage, &other.m_delegate);
45 91 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileInterface*>(&this->m_handle_storage[0]));
46 }
47 91 return *this;
48 }
49
50 29851 File::Status File::open(const CHAR* filepath, File::Mode requested_mode) {
51 29851 return this->open(filepath, requested_mode, OverwriteType::NO_OVERWRITE);
52 }
53
54 33621 File::Status File::open(const CHAR* filepath, File::Mode requested_mode, File::OverwriteType overwrite) {
55 33621 FW_ASSERT(nullptr != filepath);
56 33621 return this->open(filepath, static_cast<FwSizeType>(FileNameStringSize + 1), requested_mode, overwrite);
57 }
58
59 ✗ File::Status File::open(const CHAR* filepath, FwSizeType length, File::Mode requested_mode) {
60 ✗ return this->open(filepath, length, requested_mode, OverwriteType::NO_OVERWRITE);
61 }
62
63 33655 File::Status File::open(const CHAR* filepath,
64 FwSizeType length,
65 File::Mode requested_mode,
66 File::OverwriteType overwrite) {
67 33655 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileInterface*>(&this->m_handle_storage[0]));
68 33655 FW_ASSERT(nullptr != filepath);
69 33655 const FwSizeType string_len = static_cast<FwSizeType>(Fw::StringUtils::string_length(filepath, length));
70 33655 FW_ASSERT(string_len < length, static_cast<FwAssertArgType>(string_len), static_cast<FwAssertArgType>(length));
71 33655 FW_ASSERT(File::Mode::OPEN_NO_MODE < requested_mode && File::Mode::MAX_OPEN_MODE > requested_mode);
72 33655 FW_ASSERT((0 <= this->m_mode) && (this->m_mode < Mode::MAX_OPEN_MODE));
73 33655 FW_ASSERT((0 <= overwrite) && (overwrite < OverwriteType::MAX_OVERWRITE_TYPE));
74 // Check for already opened file
75
2/2
✓ Branch 4 taken 79 times.
✓ Branch 5 taken 33576 times.
33655 if (this->isOpen()) {
76 79 return File::Status::INVALID_MODE;
77 }
78 33576 File::Status status = this->m_delegate.open(filepath, requested_mode, overwrite);
79
2/2
✓ Branch 0 taken 23503 times.
✓ Branch 1 taken 10073 times.
33576 if (status == File::Status::OP_OK) {
80 23503 this->m_mode = requested_mode;
81 // Reset any open CRC calculations
82 23503 this->m_hash.init();
83 }
84
85 33576 return status;
86 }
87
88 ✗ File::Status File::open(const Fw::ConstStringBase& path, File::Mode requested_mode) {
89 ✗ return this->open(path.toChar(), static_cast<FwSizeType>(path.getCapacity()), requested_mode,
90 ✗ OverwriteType::NO_OVERWRITE);
91 }
92
93 15 File::Status File::open(const Fw::ConstStringBase& path, File::Mode requested_mode, File::OverwriteType overwrite) {
94 15 return this->open(path.toChar(), static_cast<FwSizeType>(path.getCapacity()), requested_mode, overwrite);
95 }
96
97 23743 void File::close() {
98 23743 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileInterface*>(&this->m_handle_storage[0]));
99 23743 FW_ASSERT(this->m_mode < Mode::MAX_OPEN_MODE);
100 23743 FW_ASSERT((0 <= this->m_mode) && (this->m_mode < Mode::MAX_OPEN_MODE));
101 23743 this->m_delegate.close();
102 23743 this->m_mode = Mode::OPEN_NO_MODE;
103 23743 }
104
105 47889 bool File::isOpen() const {
106 47889 FW_ASSERT(&this->m_delegate == reinterpret_cast<const FileInterface*>(&this->m_handle_storage[0]));
107 47889 FW_ASSERT((0 <= this->m_mode) && (this->m_mode < Mode::MAX_OPEN_MODE));
108
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 47889 times.
47889 return this->m_mode != Mode::OPEN_NO_MODE;
109 }
110
111 10756 File::Status File::size(FwSizeType& size_result) {
112 10756 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileInterface*>(&this->m_handle_storage[0]));
113 10756 FW_ASSERT((0 <= this->m_mode) && (this->m_mode < Mode::MAX_OPEN_MODE));
114
2/4
✗ Branch 3 not taken.
✓ Branch 4 taken 10756 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 10756 times.
10756 if (OPEN_NO_MODE == this->m_mode) {
115 ✗ return File::Status::NOT_OPENED;
116 }
117 10756 return this->m_delegate.size(size_result);
118 }
119
120 4872 File::Status File::position(FwSizeType& position_result) {
121 4872 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileInterface*>(&this->m_handle_storage[0]));
122 4872 FW_ASSERT((0 <= this->m_mode) && (this->m_mode < Mode::MAX_OPEN_MODE));
123 // Check that the file is open before attempting operation
124
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 4872 times.
✓ Branch 5 taken 14 times.
✓ Branch 6 taken 4858 times.
4872 if (OPEN_NO_MODE == this->m_mode) {
125 14 return File::Status::NOT_OPENED;
126 }
127 4858 return this->m_delegate.position(position_result);
128 }
129
130 94 File::Status File::preallocate(FwSizeType offset, FwSizeType length) {
131 94 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileInterface*>(&this->m_handle_storage[0]));
132 94 FW_ASSERT((0 <= this->m_mode) && (this->m_mode < Mode::MAX_OPEN_MODE));
133 // Check that the file is open before attempting operation
134
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 94 times.
✓ Branch 5 taken 22 times.
✓ Branch 6 taken 72 times.
94 if (OPEN_NO_MODE == this->m_mode) {
135 22 return File::Status::NOT_OPENED;
136
2/4
✗ Branch 3 not taken.
✓ Branch 4 taken 72 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 72 times.
72 } else if (OPEN_READ == this->m_mode) {
137 ✗ return File::Status::INVALID_MODE;
138 }
139 72 return this->m_delegate.preallocate(offset, length);
140 }
141
142 256 File::Status File::seek(FwSignedSizeType offset, File::SeekType seekType) {
143 256 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileInterface*>(&this->m_handle_storage[0]));
144 256 FW_ASSERT((0 <= seekType) && (seekType < SeekType::MAX_SEEK_TYPE));
145 // Cannot do a seek with a negative offset in absolute mode
146 256 FW_ASSERT((seekType == File::SeekType::RELATIVE) || (offset >= 0));
147 256 FW_ASSERT((0 <= this->m_mode) && (this->m_mode < Mode::MAX_OPEN_MODE));
148 // Check that the file is open before attempting operation
149
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 256 times.
✓ Branch 5 taken 17 times.
✓ Branch 6 taken 239 times.
256 if (OPEN_NO_MODE == this->m_mode) {
150 17 return File::Status::NOT_OPENED;
151 }
152 239 return this->m_delegate.seek(offset, seekType);
153 }
154
155 42 File::Status File::seek_absolute(FwSizeType offset) {
156 42 Os::File::Status status = File::Status::OTHER_ERROR;
157 // If the offset can be represented by a signed value, then we can perform a single seek
158
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 if (static_cast<FwSizeType>(std::numeric_limits<FwSignedSizeType>::max()) >= offset) {
159 // Check that the bounding above is correct
160 42 FW_ASSERT(static_cast<FwSignedSizeType>(offset) >= 0);
161 42 status = this->seek(static_cast<FwSignedSizeType>(offset), File::SeekType::ABSOLUTE);
162 }
163 // Otherwise, a full seek to any value represented by FwSizeType can be performed
164 // by at most 3 seeks of a FwSignedSizeType. Two half seeks (rounded down) that are
165 // strictly bounded by std::numeric_limits<FwSignedSizeType>::max() and one seek of
166 // a possibile "odd" byte to ensure odds offsets do not introduce an off-by-one-error.
167 // Thus we perform 3 seeks to guarantee that we can reach any position.
168 else {
169 ✗ FwSignedSizeType half_offset = static_cast<FwSignedSizeType>(offset >> 1);
170 ✗ bool is_odd = (offset % 2) == 1;
171 ✗ status = this->seek(half_offset, File::SeekType::ABSOLUTE);
172 ✗ if (status == File::Status::OP_OK) {
173 ✗ status = this->seek(half_offset, File::SeekType::RELATIVE);
174 }
175 ✗ if (status == File::Status::OP_OK) {
176 ✗ status = this->seek((is_odd) ? 1 : 0, File::SeekType::RELATIVE);
177 }
178 }
179 42 return status;
180 }
181
182 79 File::Status File::flush() {
183 79 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileInterface*>(&this->m_handle_storage[0]));
184 79 FW_ASSERT(this->m_mode < Mode::MAX_OPEN_MODE);
185 // Check that the file is open before attempting operation
186
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 79 times.
✓ Branch 5 taken 22 times.
✓ Branch 6 taken 57 times.
79 if (OPEN_NO_MODE == this->m_mode) {
187 22 return File::Status::NOT_OPENED;
188
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 57 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 55 times.
57 } else if (OPEN_READ == this->m_mode) {
189 2 return File::Status::INVALID_MODE;
190 }
191 55 return this->m_delegate.flush();
192 }
193
194 9016 File::Status File::read(U8* buffer, FwSizeType& size) {
195 9016 return this->read(buffer, size, WaitType::WAIT);
196 }
197
198 254156 File::Status File::read(U8* buffer, FwSizeType& size, File::WaitType wait) {
199 254156 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileInterface*>(&this->m_handle_storage[0]));
200 254156 FW_ASSERT(buffer != nullptr);
201 254156 FW_ASSERT(this->m_mode < Mode::MAX_OPEN_MODE);
202 // Check that the file is open before attempting operation
203
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 254156 times.
✓ Branch 5 taken 20 times.
✓ Branch 6 taken 254136 times.
254156 if (OPEN_NO_MODE == this->m_mode) {
204 20 size = 0;
205 20 return File::Status::NOT_OPENED;
206
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 254136 times.
✓ Branch 5 taken 71 times.
✓ Branch 6 taken 254065 times.
254136 } else if (OPEN_READ != this->m_mode) {
207 71 size = 0;
208 71 return File::Status::INVALID_MODE;
209 }
210 254065 return this->m_delegate.read(buffer, size, wait);
211 }
212
213 8039 File::Status File::write(const U8* buffer, FwSizeType& size) {
214 8039 return this->write(buffer, size, WaitType::WAIT);
215 }
216
217 8976 File::Status File::write(const U8* buffer, FwSizeType& size, File::WaitType wait) {
218 8976 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileInterface*>(&this->m_handle_storage[0]));
219 8976 FW_ASSERT(buffer != nullptr);
220 8976 FW_ASSERT(this->m_mode < Mode::MAX_OPEN_MODE);
221 // Check that the file is open before attempting operation
222
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 8976 times.
✓ Branch 5 taken 40 times.
✓ Branch 6 taken 8936 times.
8976 if (OPEN_NO_MODE == this->m_mode) {
223 40 size = 0;
224 40 return File::Status::NOT_OPENED;
225
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 8936 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 8933 times.
8936 } else if (OPEN_READ == this->m_mode) {
226 3 size = 0;
227 3 return File::Status::INVALID_MODE;
228 }
229 8933 return this->m_delegate.write(buffer, size, wait);
230 }
231
232 2 FileHandle* File::getHandle() {
233 2 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileInterface*>(&this->m_handle_storage[0]));
234 2 return this->m_delegate.getHandle();
235 }
236
237 104 File::Status File::calculateCrc(U32& crc) {
238 104 File::Status status = File::Status::OP_OK;
239 104 FwSizeType size = FW_FILE_CHUNK_SIZE;
240 104 crc = 0;
241
1/2
✓ Branch 1 taken 109 times.
✗ Branch 2 not taken.
109 for (FwSizeType i = 0; i < std::numeric_limits<FwSizeType>::max(); i++) {
242
1/1
✓ Branch 4 taken 109 times.
109 status = this->incrementalCrc(size);
243 // Break on eof or error
244
4/4
✓ Branch 0 taken 83 times.
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 78 times.
109 if ((size != FW_FILE_CHUNK_SIZE) || (status != File::OP_OK)) {
245 break;
246 }
247 }
248 // When successful, finalize the CRC
249
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 80 times.
104 if (status == File::OP_OK) {
250
1/1
✓ Branch 4 taken 24 times.
24 status = this->finalizeCrc(crc);
251 }
252 104 return status;
253 }
254
255 211 File::Status File::incrementalCrc(FwSizeType& size) {
256 211 File::Status status = File::Status::OP_OK;
257 211 FW_ASSERT(size <= FW_FILE_CHUNK_SIZE, FwAssertArgType(size));
258
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 211 times.
✓ Branch 5 taken 28 times.
✓ Branch 6 taken 183 times.
211 if (OPEN_NO_MODE == this->m_mode) {
259 28 status = File::Status::NOT_OPENED;
260
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 183 times.
✓ Branch 5 taken 149 times.
✓ Branch 6 taken 34 times.
183 } else if (OPEN_READ != this->m_mode) {
261 149 status = File::Status::INVALID_MODE;
262 } else {
263 // Read data without waiting for additional data to be available
264 34 status = this->read(this->m_crc_buffer, size, File::WaitType::NO_WAIT);
265
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 2 times.
34 if (OP_OK == status) {
266 32 FW_ASSERT(size <= FW_FILE_CHUNK_SIZE, FwAssertArgType(size));
267 32 this->m_hash.update(this->m_crc_buffer, size);
268 }
269 }
270 211 return status;
271 }
272
273 103 File::Status File::finalizeCrc(U32& crc) {
274 103 File::Status status = File::Status::OP_OK;
275 103 this->m_hash.finalize(crc);
276 // Historically, the CRC calculation in File omitted the final 1's complement step. Utils::Hash performs that step
277 // and as such, we must undo it before returning the value to ensure backwards compatibility.
278 103 crc = ~crc;
279 103 this->m_hash.init();
280 103 return status;
281 }
282
283 42 File::Status File::readline(U8* buffer, FwSizeType& size, File::WaitType wait) {
284 42 const FwSizeType requested_size = size;
285 42 FW_ASSERT(&this->m_delegate == reinterpret_cast<FileInterface*>(&this->m_handle_storage[0]));
286 42 FW_ASSERT(buffer != nullptr);
287 42 FW_ASSERT(this->m_mode < Mode::MAX_OPEN_MODE);
288 // Check that the file is open before attempting operation
289
2/4
✗ Branch 3 not taken.
✓ Branch 4 taken 42 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 42 times.
42 if (OPEN_NO_MODE == this->m_mode) {
290 ✗ size = 0;
291 ✗ return File::Status::NOT_OPENED;
292
2/4
✗ Branch 3 not taken.
✓ Branch 4 taken 42 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 42 times.
42 } else if (OPEN_READ != this->m_mode) {
293 ✗ size = 0;
294 ✗ return File::Status::INVALID_MODE;
295 }
296 42 FwSizeType original_location = 0;
297
1/1
✓ Branch 4 taken 42 times.
42 File::Status status = this->position(original_location);
298
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 42 times.
42 if (status != Os::File::Status::OP_OK) {
299 ✗ size = 0;
300 ✗ (void)this->seek_absolute(original_location);
301 ✗ return status;
302 }
303 42 FwSizeType read = 0;
304 // Loop reading chunk by chunk
305
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 for (FwSizeType i = 0; i < size; i += read) {
306 // read in chunks to avoid large buffer allocations
307 42 FwSizeType current_chunk_size = std::min(size - i, static_cast<FwSizeType>(FW_FILE_CHUNK_SIZE));
308 42 read = current_chunk_size;
309
1/1
✓ Branch 5 taken 42 times.
42 status = this->read(buffer + i, read, wait);
310
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 42 times.
42 if (status != File::Status::OP_OK) {
311 // Contract: on error, seek back to the original location
312 ✗ size = 0;
313 ✗ (void)this->seek_absolute(original_location);
314 ✗ return status;
315 }
316 // EOF break out now
317
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 42 times.
42 if (read == 0) {
318 ✗ size = i;
319 ✗ return Os::File::Status::OP_OK;
320 }
321 // Loop from i to i + current_chunk_size looking for `\n`
322 42 const FwSizeType chunk_end = i + read;
323
1/2
✓ Branch 0 taken 1777 times.
✗ Branch 1 not taken.
1777 for (FwSizeType j = i; j < chunk_end; j++) {
324 // Newline seek back to after it, return the size read
325
2/2
✓ Branch 2 taken 42 times.
✓ Branch 3 taken 1735 times.
1777 if (buffer[j] == '\n') {
326 42 size = j + 1;
327 // Ensure that the computation worked and there is not overflow
328 42 FW_ASSERT(size <= requested_size);
329 42 FW_ASSERT(std::numeric_limits<FwSizeType>::max() - size >= original_location);
330
1/1
✓ Branch 4 taken 42 times.
42 (void)this->seek_absolute(original_location + j + 1);
331 42 return Os::File::Status::OP_OK;
332 }
333 }
334 }
335 // Failed to find newline within data available
336 // Contract: on error, seek back to the original location
337 ✗ size = 0;
338 ✗ (void)this->seek_absolute(original_location);
339 ✗ return Os::File::Status::OTHER_ERROR;
340 }
341 } // namespace Os
342