GCC Code Coverage Report


Directory: ./
File: FpySequencerStack.cpp
Date: 2026-09-03 22:13:45
Exec Total Coverage
Lines: 0 83 0.0%
Functions: 0 26 0.0%
Branches: 0 8 0.0%

Line Branch Exec Source
1 #include <cstring>
2 #include "Svc/FpySequencer/FpySequencer.hpp"
3 namespace Svc {
4
5 template <typename T>
6 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 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 U8 valBytes[8] = {0};
12 // now move top of stack into byte array and shrink stack
13 (void)memcpy(valBytes, this->top() - sizeof(T), sizeof(T));
14 this->size -= static_cast<Fpy::StackSizeType>(sizeof(T));
15
16 // now do appropriate byteswap on byte array
17 if (sizeof(T) == 8) {
18 return static_cast<T>((static_cast<T>(valBytes[7]) << 0) | (static_cast<T>(valBytes[6]) << 8) |
19 (static_cast<T>(valBytes[5]) << 16) | (static_cast<T>(valBytes[4]) << 24) |
20 (static_cast<T>(valBytes[3]) << 32) | (static_cast<T>(valBytes[2]) << 40) |
21 (static_cast<T>(valBytes[1]) << 48) | (static_cast<T>(valBytes[0]) << 56));
22 } else if (sizeof(T) == 4) {
23 return static_cast<T>((static_cast<T>(valBytes[3]) << 0) | (static_cast<T>(valBytes[2]) << 8) |
24 (static_cast<T>(valBytes[1]) << 16) | (static_cast<T>(valBytes[0]) << 24));
25 } else if (sizeof(T) == 2) {
26 return static_cast<T>((static_cast<T>(valBytes[1]) << 0) | (static_cast<T>(valBytes[0]) << 8));
27 } else {
28 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 F32 FpySequencer::Stack::pop<F32>() {
43 U32 endianness = this->pop<U32>();
44 F32 val;
45 (void)memcpy(&val, &endianness, sizeof(val));
46 return val;
47 }
48
49 template <>
50 F64 FpySequencer::Stack::pop<F64>() {
51 U64 endianness = this->pop<U64>();
52 F64 val;
53 (void)memcpy(&val, &endianness, sizeof(val));
54 return val;
55 }
56
57 template <typename T>
58 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 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 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 UnsignedT valUnsigned = static_cast<UnsignedT>(val);
67 if (sizeof(T) == 8) {
68 valBytes[0] = static_cast<U8>(valUnsigned >> 56);
69 valBytes[1] = static_cast<U8>(valUnsigned >> 48);
70 valBytes[2] = static_cast<U8>(valUnsigned >> 40);
71 valBytes[3] = static_cast<U8>(valUnsigned >> 32);
72 valBytes[4] = static_cast<U8>(valUnsigned >> 24);
73 valBytes[5] = static_cast<U8>(valUnsigned >> 16);
74 valBytes[6] = static_cast<U8>(valUnsigned >> 8);
75 valBytes[7] = static_cast<U8>(valUnsigned >> 0);
76 } else if (sizeof(T) == 4) {
77 valBytes[0] = static_cast<U8>(valUnsigned >> 24);
78 valBytes[1] = static_cast<U8>(valUnsigned >> 16);
79 valBytes[2] = static_cast<U8>(valUnsigned >> 8);
80 valBytes[3] = static_cast<U8>(valUnsigned >> 0);
81 } else if (sizeof(T) == 2) {
82 valBytes[0] = static_cast<U8>(valUnsigned >> 8);
83 valBytes[1] = static_cast<U8>(valUnsigned >> 0);
84 } else {
85 valBytes[0] = static_cast<U8>(valUnsigned);
86 }
87 (void)memcpy(this->top(), valBytes, sizeof(T));
88 this->size += static_cast<Fpy::StackSizeType>(sizeof(T));
89 }
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 void FpySequencer::Stack::push<F32>(F32 val) {
102 U32 endianness;
103 (void)memcpy(&endianness, &val, sizeof(val));
104 this->push(endianness);
105 }
106
107 template <>
108 void FpySequencer::Stack::push<F64>(F64 val) {
109 U64 endianness;
110 (void)memcpy(&endianness, &val, sizeof(val));
111 this->push(endianness);
112 }
113
114 // pops a byte array from the top of the stack into the destination array
115 // does not convert endianness
116 void FpySequencer::Stack::pop(U8* dest, Fpy::StackSizeType destSize) {
117 FW_ASSERT(dest != nullptr);
118 FW_ASSERT(this->size >= destSize, static_cast<FwAssertArgType>(this->size), static_cast<FwAssertArgType>(destSize));
119 (void)memcpy(dest, this->top() - destSize, destSize);
120 this->size -= destSize;
121 }
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 void FpySequencer::Stack::push(const U8* src, Fpy::StackSizeType srcSize) {
127 FW_ASSERT(src != nullptr);
128 FW_ASSERT(this->size + srcSize <= Fpy::MAX_STACK_SIZE, static_cast<FwAssertArgType>(this->size),
129 static_cast<FwAssertArgType>(srcSize));
130 (void)memcpy(this->top(), src, srcSize);
131 this->size += srcSize;
132 }
133
134 // pushes zero bytes to the stack
135 void FpySequencer::Stack::pushZeroes(Fpy::StackSizeType byteCount) {
136 FW_ASSERT(this->size + byteCount <= Fpy::MAX_STACK_SIZE, static_cast<FwAssertArgType>(this->size),
137 static_cast<FwAssertArgType>(byteCount));
138 (void)memset(this->top(), 0, byteCount);
139 this->size += byteCount;
140 }
141
142 U8* FpySequencer::Stack::top() {
143 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 void FpySequencer::Stack::copy(Fpy::StackSizeType destOffset,
150 Fpy::StackSizeType srcOffset,
151 Fpy::StackSizeType copySize) {
152 FW_ASSERT(destOffset + copySize <= Fpy::MAX_STACK_SIZE, static_cast<FwAssertArgType>(destOffset),
153 static_cast<FwAssertArgType>(copySize));
154 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 FW_ASSERT(copySize == 0 || (destOffset + copySize <= srcOffset) || (srcOffset + copySize <= destOffset),
159 static_cast<FwAssertArgType>(destOffset), static_cast<FwAssertArgType>(srcOffset));
160 if (copySize > 0) {
161 (void)memcpy(this->bytes + destOffset, this->bytes + srcOffset, copySize);
162 }
163 }
164
165 // Moves data within the stack
166 // Asserts that both source and destination are within bounds
167 // Does not modify stack size
168 void FpySequencer::Stack::move(Fpy::StackSizeType destOffset,
169 Fpy::StackSizeType srcOffset,
170 Fpy::StackSizeType moveSize) {
171 FW_ASSERT(destOffset + moveSize <= Fpy::MAX_STACK_SIZE, static_cast<FwAssertArgType>(destOffset),
172 static_cast<FwAssertArgType>(moveSize));
173 FW_ASSERT(srcOffset + moveSize <= this->size, static_cast<FwAssertArgType>(srcOffset),
174 static_cast<FwAssertArgType>(moveSize));
175 if (moveSize > 0) {
176 (void)memmove(this->bytes + destOffset, this->bytes + srcOffset, moveSize);
177 }
178 }
179
180 } // namespace Svc
181