GCC Code Coverage Report


Directory: ./
File: Svc/FpySequencer/FpySequencerStack.cpp
Date: 2026-09-03 21:13:48
Exec Total Coverage
Lines: 87 87 100.0%
Functions: 26 26 100.0%
Branches: 31 54 57.4%

Line Branch Exec Source
1 #include <cstring>
2 #include "Svc/FpySequencer/FpySequencer.hpp"
3 namespace Svc {
4
5 template <typename T>
6 1004 T FpySequencer::Stack::pop() {
7 static_assert(sizeof(T) == 8 || sizeof(T) == 4 || sizeof(T) == 2 || sizeof(T) == 1, "size must be 1, 2, 4, 8");
8 1004 FW_ASSERT(this->size >= sizeof(T), static_cast<FwAssertArgType>(this->size),
9 static_cast<FwAssertArgType>(sizeof(T)));
10 // first make a byte array which can definitely store our val
11 1004 U8 valBytes[8] = {0};
12 // now move top of stack into byte array and shrink stack
13 1004 (void)memcpy(valBytes, this->top() - sizeof(T), sizeof(T));
14 1004 this->size -= static_cast<Fpy::StackSizeType>(sizeof(T));
15
16 // now do appropriate byteswap on byte array
17 if (sizeof(T) == 8) {
18 398 return static_cast<T>((static_cast<T>(valBytes[7]) << 0) | (static_cast<T>(valBytes[6]) << 8) |
19 398 (static_cast<T>(valBytes[5]) << 16) | (static_cast<T>(valBytes[4]) << 24) |
20 398 (static_cast<T>(valBytes[3]) << 32) | (static_cast<T>(valBytes[2]) << 40) |
21
8/16
✗ Branch 0 not taken.
✓ Branch 1 taken 138 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 138 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 138 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 138 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 138 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 138 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 138 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 138 times.
674 (static_cast<T>(valBytes[1]) << 48) | (static_cast<T>(valBytes[0]) << 56));
22 } else if (sizeof(T) == 4) {
23 156 return static_cast<T>((static_cast<T>(valBytes[3]) << 0) | (static_cast<T>(valBytes[2]) << 8) |
24
4/8
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 19 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 19 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 19 times.
194 (static_cast<T>(valBytes[1]) << 16) | (static_cast<T>(valBytes[0]) << 24));
25 } else if (sizeof(T) == 2) {
26
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
24 return static_cast<T>((static_cast<T>(valBytes[1]) << 0) | (static_cast<T>(valBytes[0]) << 8));
27 } else {
28 112 return static_cast<T>(valBytes[0]);
29 }
30 }
31
32 template U8 FpySequencer::Stack::pop();
33 template U16 FpySequencer::Stack::pop();
34 template U32 FpySequencer::Stack::pop();
35 template U64 FpySequencer::Stack::pop();
36 template I8 FpySequencer::Stack::pop();
37 template I16 FpySequencer::Stack::pop();
38 template I32 FpySequencer::Stack::pop();
39 template I64 FpySequencer::Stack::pop();
40
41 template <>
42 3 F32 FpySequencer::Stack::pop<F32>() {
43
1/1
✓ Branch 3 taken 3 times.
3 U32 endianness = this->pop<U32>();
44 3 F32 val;
45 3 (void)memcpy(&val, &endianness, sizeof(val));
46 6 return val;
47 }
48
49 template <>
50 133 F64 FpySequencer::Stack::pop<F64>() {
51
1/1
✓ Branch 3 taken 133 times.
133 U64 endianness = this->pop<U64>();
52 133 F64 val;
53 133 (void)memcpy(&val, &endianness, sizeof(val));
54 266 return val;
55 }
56
57 template <typename T>
58 1940 void FpySequencer::Stack::push(T val) {
59 static_assert(sizeof(T) == 8 || sizeof(T) == 4 || sizeof(T) == 2 || sizeof(T) == 1, "size must be 1, 2, 4, 8");
60 1940 FW_ASSERT(this->size + sizeof(val) <= Fpy::MAX_STACK_SIZE, static_cast<FwAssertArgType>(this->size),
61 static_cast<FwAssertArgType>(sizeof(T)));
62 // first make a byte array which can definitely store our val
63 1940 U8 valBytes[8] = {0};
64 // convert val to unsigned to avoid undefined behavior for bitshifts of signed types
65 using UnsignedT = typename std::make_unsigned<T>::type;
66 1940 UnsignedT valUnsigned = static_cast<UnsignedT>(val);
67 if (sizeof(T) == 8) {
68 734 valBytes[0] = static_cast<U8>(valUnsigned >> 56);
69 734 valBytes[1] = static_cast<U8>(valUnsigned >> 48);
70 734 valBytes[2] = static_cast<U8>(valUnsigned >> 40);
71 734 valBytes[3] = static_cast<U8>(valUnsigned >> 32);
72 734 valBytes[4] = static_cast<U8>(valUnsigned >> 24);
73 734 valBytes[5] = static_cast<U8>(valUnsigned >> 16);
74 734 valBytes[6] = static_cast<U8>(valUnsigned >> 8);
75 734 valBytes[7] = static_cast<U8>(valUnsigned >> 0);
76 } else if (sizeof(T) == 4) {
77 204 valBytes[0] = static_cast<U8>(valUnsigned >> 24);
78 204 valBytes[1] = static_cast<U8>(valUnsigned >> 16);
79 204 valBytes[2] = static_cast<U8>(valUnsigned >> 8);
80 204 valBytes[3] = static_cast<U8>(valUnsigned >> 0);
81 } else if (sizeof(T) == 2) {
82 26 valBytes[0] = static_cast<U8>(valUnsigned >> 8);
83 26 valBytes[1] = static_cast<U8>(valUnsigned >> 0);
84 } else {
85 976 valBytes[0] = static_cast<U8>(valUnsigned);
86 }
87 1940 (void)memcpy(this->top(), valBytes, sizeof(T));
88 1940 this->size += static_cast<Fpy::StackSizeType>(sizeof(T));
89 1940 }
90
91 template void FpySequencer::Stack::push(U8);
92 template void FpySequencer::Stack::push(U16);
93 template void FpySequencer::Stack::push(U32);
94 template void FpySequencer::Stack::push(U64);
95 template void FpySequencer::Stack::push(I8);
96 template void FpySequencer::Stack::push(I16);
97 template void FpySequencer::Stack::push(I32);
98 template void FpySequencer::Stack::push(I64);
99
100 template <>
101 3 void FpySequencer::Stack::push<F32>(F32 val) {
102 3 U32 endianness;
103 3 (void)memcpy(&endianness, &val, sizeof(val));
104
1/1
✓ Branch 2 taken 3 times.
3 this->push(endianness);
105 3 }
106
107 template <>
108 145 void FpySequencer::Stack::push<F64>(F64 val) {
109 145 U64 endianness;
110 145 (void)memcpy(&endianness, &val, sizeof(val));
111
1/1
✓ Branch 2 taken 145 times.
145 this->push(endianness);
112 145 }
113
114 // pops a byte array from the top of the stack into the destination array
115 // does not convert endianness
116 10 void FpySequencer::Stack::pop(U8* dest, Fpy::StackSizeType destSize) {
117 10 FW_ASSERT(dest != nullptr);
118 10 FW_ASSERT(this->size >= destSize, static_cast<FwAssertArgType>(this->size), static_cast<FwAssertArgType>(destSize));
119
2/4
✗ Branch 3 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 10 times.
10 (void)memcpy(dest, this->top() - destSize, destSize);
120 10 this->size -= destSize;
121 10 }
122
123 // pushes a byte array to the top of the stack from the source array
124 // leaves the source array unmodified
125 // does not convert endianness
126 73 void FpySequencer::Stack::push(const U8* src, Fpy::StackSizeType srcSize) {
127 73 FW_ASSERT(src != nullptr);
128 73 FW_ASSERT(this->size + srcSize <= Fpy::MAX_STACK_SIZE, static_cast<FwAssertArgType>(this->size),
129 static_cast<FwAssertArgType>(srcSize));
130
2/4
✗ Branch 2 not taken.
✓ Branch 3 taken 73 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 73 times.
73 (void)memcpy(this->top(), src, srcSize);
131 73 this->size += srcSize;
132 73 }
133
134 // pushes zero bytes to the stack
135 8 void FpySequencer::Stack::pushZeroes(Fpy::StackSizeType byteCount) {
136 8 FW_ASSERT(this->size + byteCount <= Fpy::MAX_STACK_SIZE, static_cast<FwAssertArgType>(this->size),
137 static_cast<FwAssertArgType>(byteCount));
138
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 (void)memset(this->top(), 0, byteCount);
139 8 this->size += byteCount;
140 8 }
141
142 1578 U8* FpySequencer::Stack::top() {
143 1578 return &this->bytes[this->size];
144 }
145
146 // Copies data from one region of the stack to another
147 // Asserts that both regions are within bounds and do not overlap
148 // Does not modify stack size
149 27 void FpySequencer::Stack::copy(Fpy::StackSizeType destOffset,
150 Fpy::StackSizeType srcOffset,
151 Fpy::StackSizeType copySize) {
152 27 FW_ASSERT(destOffset + copySize <= Fpy::MAX_STACK_SIZE, static_cast<FwAssertArgType>(destOffset),
153 static_cast<FwAssertArgType>(copySize));
154 27 FW_ASSERT(srcOffset + copySize <= Fpy::MAX_STACK_SIZE, static_cast<FwAssertArgType>(srcOffset),
155 static_cast<FwAssertArgType>(copySize));
156 // Check for overlap: regions overlap if one starts before the other ends
157 // No overlap if: destEnd <= srcStart OR srcEnd <= destStart
158 27 FW_ASSERT(copySize == 0 || (destOffset + copySize <= srcOffset) || (srcOffset + copySize <= destOffset),
159 static_cast<FwAssertArgType>(destOffset), static_cast<FwAssertArgType>(srcOffset));
160
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 1 times.
27 if (copySize > 0) {
161
2/4
✗ Branch 4 not taken.
✓ Branch 5 taken 26 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 26 times.
26 (void)memcpy(this->bytes + destOffset, this->bytes + srcOffset, copySize);
162 }
163 27 }
164
165 // Moves data within the stack
166 // Asserts that both source and destination are within bounds
167 // Does not modify stack size
168 6 void FpySequencer::Stack::move(Fpy::StackSizeType destOffset,
169 Fpy::StackSizeType srcOffset,
170 Fpy::StackSizeType moveSize) {
171 6 FW_ASSERT(destOffset + moveSize <= Fpy::MAX_STACK_SIZE, static_cast<FwAssertArgType>(destOffset),
172 static_cast<FwAssertArgType>(moveSize));
173 6 FW_ASSERT(srcOffset + moveSize <= this->size, static_cast<FwAssertArgType>(srcOffset),
174 static_cast<FwAssertArgType>(moveSize));
175
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 if (moveSize > 0) {
176
2/4
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 5 times.
5 (void)memmove(this->bytes + destOffset, this->bytes + srcOffset, moveSize);
177 }
178 6 }
179
180 } // namespace Svc
181