GCC Code Coverage Report


Directory: ./
File: Svc/Ccsds/CfdpManager/Clist.cpp
Date: 2026-09-03 22:12:29
Exec Total Coverage
Lines: 0 95 0.0%
Functions: 0 8 0.0%
Branches: 0 40 0.0%

Line Branch Exec Source
1 // ======================================================================
2 // \title Clist.cpp
3 // \brief CFDP circular list definition source file
4 //
5 // This file is a port of the cf_clist.c file from the
6 // NASA Core Flight System (cFS) CFDP (CF) Application,
7 // version 3.0.0, adapted for use within the F-Prime (F') framework.
8 //
9 // This is a circular doubly-linked list implementation. It is used for
10 // multiple data structures in CFDP.
11 //
12 // ======================================================================
13 //
14 // NASA Docket No. GSC-18,447-1
15 //
16 // Copyright (c) 2019 United States Government as represented by the
17 // Administrator of the National Aeronautics and Space Administration.
18 // All Rights Reserved.
19 //
20 // Licensed under the Apache License, Version 2.0 (the "License"); you may
21 // not use this file except in compliance with the License. You may obtain
22 // a copy of the License at
23 //
24 // http://www.apache.org/licenses/LICENSE-2.0
25 //
26 // Unless required by applicable law or agreed to in writing, software
27 // distributed under the License is distributed on an "AS IS" BASIS,
28 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29 // See the License for the specific language governing permissions and
30 // limitations under the License.
31 //
32 // ======================================================================
33
34 #include <Svc/Ccsds/CfdpManager/Clist.hpp>
35 #include <config/CfdpCfg.hpp>
36
37 #include <Fw/Types/Assert.hpp>
38
39 namespace Svc {
40 namespace Ccsds {
41 namespace Cfdp {
42
43 void CfdpCListInitNode(CListNode* node) {
44 node->next = node;
45 node->prev = node;
46 }
47
48 void CfdpCListInsertFront(CListNode** head, CListNode* node) {
49 CListNode* last;
50
51 FW_ASSERT(head);
52 FW_ASSERT(node);
53 FW_ASSERT(node->next == node);
54 FW_ASSERT(node->prev == node);
55
56 if (*head) {
57 last = (*head)->prev;
58
59 node->next = *head;
60 node->prev = last;
61
62 last->next = node;
63 (*head)->prev = node;
64 }
65
66 *head = node;
67 }
68
69 void CfdpCListInsertBack(CListNode** head, CListNode* node) {
70 CListNode* last;
71
72 FW_ASSERT(head);
73 FW_ASSERT(node);
74 FW_ASSERT(node->next == node);
75 FW_ASSERT(node->prev == node);
76
77 if (!*head) {
78 *head = node;
79 } else {
80 last = (*head)->prev;
81
82 node->next = *head;
83 (*head)->prev = node;
84 node->prev = last;
85 last->next = node;
86 }
87 }
88
89 CListNode* CfdpCListPop(CListNode** head) {
90 CListNode* ret;
91
92 FW_ASSERT(head);
93
94 ret = *head;
95 if (ret) {
96 CfdpCListRemove(head, ret);
97 }
98
99 return ret;
100 }
101
102 void CfdpCListRemove(CListNode** head, CListNode* node) {
103 FW_ASSERT(head);
104 FW_ASSERT(node);
105 FW_ASSERT(*head);
106
107 if (node->next == node) {
108 /* only node in the list, so this one is easy */
109 FW_ASSERT(node == *head); /* sanity check */
110 *head = nullptr;
111 } else if (*head == node) {
112 /* removing the first node in the list, so make the second node in the list the first */
113 (*head)->prev->next = node->next;
114 *head = node->next;
115
116 (*head)->prev = node->prev;
117 } else {
118 node->next->prev = node->prev;
119 node->prev->next = node->next;
120 }
121
122 CfdpCListInitNode(node);
123 }
124
125 void CfdpCListInsertAfter(CListNode** head, CListNode* start, CListNode* after) {
126 /* calling insert_after with nothing to insert after (no head) makes no sense */
127 FW_ASSERT(head);
128 FW_ASSERT(*head);
129 FW_ASSERT(start);
130 FW_ASSERT(start != after);
131
132 /* knowing that head is not empty, and knowing that start is non-zero, this is an easy operation */
133 after->next = start->next;
134 start->next = after;
135 after->prev = start;
136 after->next->prev = after;
137 }
138
139 void CfdpCListTraverse(CListNode* start, CListFunc fn, void* context) {
140 CListNode* node = start;
141 CListNode* node_next;
142 bool last = false;
143 // Safety bound: maximum possible list size based on transaction pool configuration
144 // Prevents infinite loop if list becomes corrupted
145 constexpr U32 maxIterations = MaxSimultaneousRx + MaxCommandedPlaybackFilesPerChan +
146 (MaxCommandedPlaybackDirectoriesPerChan * NumTransactionsPerPlayback) +
147 (MaxPollingDirPerChan * NumTransactionsPerPlayback);
148
149 if (node) {
150 U32 i;
151 for (i = 0; i < maxIterations && !last; ++i) {
152 /* set node_next in case callback removes this node from the list */
153 node_next = node->next;
154 if (node_next == start) {
155 last = true;
156 }
157 if (!CfdpCListTraverseStatusIsContinue(fn(node, context))) {
158 break;
159 }
160 /* list traversal is robust against an item deleting itself during traversal,
161 * but there is a special case if that item is the starting node. Since this is
162 * a circular list, start is remembered so we know when to stop. Must set start
163 * to the next node in this case. */
164 if ((start == node) && (node->next != node_next)) {
165 start = node_next;
166 }
167 node = node_next;
168 }
169 FW_ASSERT(last || (i < maxIterations), static_cast<FwAssertArgType>(i));
170 }
171 }
172
173 void CfdpCListTraverseR(CListNode* end, CListFunc fn, void* context) {
174 if (end) {
175 CListNode* node = end->prev;
176 CListNode* node_next;
177 bool last = false;
178 // Safety bound: maximum possible list size based on transaction pool configuration
179 // Prevents infinite loop if list becomes corrupted
180 constexpr U32 maxIterations = MaxSimultaneousRx + MaxCommandedPlaybackFilesPerChan +
181 (MaxCommandedPlaybackDirectoriesPerChan * NumTransactionsPerPlayback) +
182 (MaxPollingDirPerChan * NumTransactionsPerPlayback);
183
184 if (node) {
185 end = node;
186 U32 i;
187
188 for (i = 0; i < maxIterations && !last; ++i) {
189 /* set node_next in case callback removes this node from the list */
190 node_next = node->prev;
191 if (node_next == end) {
192 last = true;
193 }
194
195 if (!CfdpCListTraverseStatusIsContinue(fn(node, context))) {
196 break;
197 }
198
199 /* list traversal is robust against an item deleting itself during traversal,
200 * but there is a special case if that item is the starting node. Since this is
201 * a circular list, "end" is remembered so we know when to stop. Must set "end"
202 * to the next node in this case. */
203 if ((end == node) && (node->prev != node_next)) {
204 end = node_next;
205 }
206 node = node_next;
207 }
208 FW_ASSERT(last || (i < maxIterations), static_cast<FwAssertArgType>(i));
209 }
210 }
211 }
212
213 } // namespace Cfdp
214 } // namespace Ccsds
215 } // namespace Svc
216