Code Coverage Report


Directory: targets/
File: om-tree-library/source/om/tree/iteration.c
Date: 2024-10-29 22:25:26
Exec Total Coverage
Lines: 71 71 100.0%
Functions: 7 7 100.0%
Branches: 20 20 100.0%

Line Branch Exec Source
1 /*!
2 @file
3 @brief The Om_Tree_Iteration function definitions.
4 */
5
6 #include <om/tree/iteration.h>
7
8 #include <om/bit.h>
9 #include <om/memory.h>
10 #include <om/tree/branch_node.h>
11 #include <om/tree/node_stack.h>
12
13 #include <assert.h>
14 #include <limits.h>
15 #include <string.h>
16
17 /*!
18 @brief Descends to the first leaf in the non-null branch.
19 @param theNodeStack
20 The current node stack.
21 @param theByteStack
22 The current byte stack.
23 @param theBranchNode
24 A non-null branch node.
25 @returns A non-null pointer to the leaf.
26 */
27 110 static Om_Tree_LeafNode * Om_Tree_Iteration_Descend(
28 Om_Tree_NodeStack * const theNodeStack,
29 Om_ByteStack * const theByteStack,
30 Om_Tree_BranchNode theBranchNode
31 ) {
32 assert(theNodeStack);
33 assert(theByteStack);
34 assert(theBranchNode != (Om_Tree_BranchNode)NULL);
35
36 102 for(;; ) {
37 // Push the branch node onto the stack.
38 212 Om_Tree_NodeStack_Push(theNodeStack, theBranchNode);
39
40 // Get the branch and its bit count.
41 212 Om_Tree_Branch * const theBranch = Om_Tree_BranchNode_GetBranch(
42 theBranchNode
43 );
44 assert(theBranch);
45 212 uint_fast8_t const theBranchBitCount = Om_Tree_BranchNode_GetBranchBitCount(
46 theBranchNode
47 );
48
49 // Push complete branch bytes onto the string.
50 212 char const * const theBranchByteArray = Om_Tree_Branch_GetByteArray(
51 theBranch
52 );
53 212 size_t const theBranchByteCount = Om_Tree_Branch_GetByteCount(
54 theBranch, theBranchBitCount
55 );
56 212 Om_ByteStack_Append(theByteStack, theBranchByteCount, theBranchByteArray);
57
58 // If the bit count is the maximum, it means that the partial byte is not
59 // repeated in the child. Push it with its last bit set to 0.
60
2/2
✓ Branch 0 taken 126 times.
✓ Branch 1 taken 86 times.
212 if(theBranchBitCount == (CHAR_BIT - 1U)) {
61 126 char const theByte = (char)(
62 126 (uint_fast8_t)theBranchByteArray[theBranchByteCount] &
63 126 (uint_fast8_t)(~Om_Bit_MaskIndex(theBranchBitCount))
64 );
65 126 Om_ByteStack_Push(theByteStack, theByte);
66 }
67
68 // If the branch has a leaf child, return a pointer to it.
69
2/2
✓ Branch 1 taken 110 times.
✓ Branch 2 taken 102 times.
212 if(Om_Tree_Branch_HasLeafChild(theBranch, theBranchBitCount)) {
70 110 return Om_Tree_Branch_GetChildArray(theBranch);
71 }
72
73 // Set the branch node to child 0.
74 102 theBranchNode = Om_Tree_Branch_GetChildArray(theBranch)[0];
75 assert(theBranchNode != (Om_Tree_BranchNode)NULL);
76 }
77 }
78
79 9 bool Om_Tree_Iteration_Initialize(
80 Om_Tree_Iteration * const theIteration,
81 Om_Tree * const theTree
82 ) {
83 assert(theIteration);
84 assert(theTree);
85
86 9 theIteration->thisLeafNode = NULL;
87
88 // Create the node stack, even if the tree is empty, in order to obviate null
89 // checks. The depth cannot be greater than the leaf count.
90 9 theIteration->thisNodeStack = Om_Tree_NodeStack_Create(
91 Om_Tree_GetCount(theTree)
92 );
93
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 8 times.
9 if(!theIteration->thisNodeStack) {
94 1 return false;
95 }
96
97 // Allocate the string with the tree's maximum string length, plus one for
98 // the stored null terminator.
99 assert(theTree->thisMaximumStringLength < SIZE_MAX);
100 16 theIteration->thisByteStack = Om_ByteStack_Create(
101 8 theTree->thisMaximumStringLength + 1U
102 );
103
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
8 if(!theIteration->thisByteStack) {
104 1 Om_Tree_NodeStack_Destroy(theIteration->thisNodeStack);
105 1 return false;
106 }
107
108 // If the tree isn't empty, descend to the first leaf node and return true.
109
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 5 times.
7 if(Om_Tree_IsEmpty(theTree)) {
110 2 Om_ByteStack_Push(theIteration->thisByteStack, '\0');
111 } else {
112 5 theIteration->thisLeafNode = Om_Tree_Iteration_Descend(
113 theIteration->thisNodeStack,
114 theIteration->thisByteStack,
115 theTree->thisRootNode
116 );
117 }
118 7 return true;
119 }
120
121 // NOLINTNEXTLINE(readability-function-cognitive-complexity)
122 111 bool Om_Tree_Iteration_Advance(
123 Om_Tree_Iteration * const theIteration
124 ) {
125 assert(theIteration);
126 assert(theIteration->thisNodeStack);
127 assert(theIteration->thisByteStack);
128
129 // If the node stack is empty, iteration is finished.
130
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 110 times.
111 if(Om_Tree_NodeStack_IsEmpty(theIteration->thisNodeStack)) {
131 1 return false;
132 }
133
134 // Pop a branch node from the stack and get its branch and bit count.
135 110 Om_Tree_BranchNode theBranchNode = Om_Tree_NodeStack_Pop(
136 theIteration->thisNodeStack
137 );
138 assert(theBranchNode != (Om_Tree_BranchNode)NULL);
139 110 Om_Tree_Branch * theBranch = Om_Tree_BranchNode_GetBranch(theBranchNode);
140 assert(theBranch);
141 110 uint_fast8_t theBranchBitCount = Om_Tree_BranchNode_GetBranchBitCount(
142 theBranchNode
143 );
144
145 // Assert that the branch has the leaf child in the iteration.
146 assert(Om_Tree_Branch_HasLeafChild(theBranch, theBranchBitCount));
147 assert(theIteration->thisLeafNode);
148 assert(
149 *theIteration->thisLeafNode == Om_Tree_Branch_GetChildArray(theBranch)[0]
150 );
151
152 // Advance to the first non-null, non-iterated 1 child.
153 110 Om_Tree_BranchNode theChildBranchNode = (
154 110 Om_Tree_Branch_GetChildArray(theBranch)[1]
155 );
156
2/2
✓ Branch 0 taken 107 times.
✓ Branch 1 taken 3 times.
110 if(theChildBranchNode == (Om_Tree_BranchNode)NULL) {
157 // Ascend until finding a node with a 1 child to be iterated, or the end.
158 do {
159 // If the node stack is empty, iteration is finished.
160
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 207 times.
212 if(Om_Tree_NodeStack_IsEmpty(theIteration->thisNodeStack)) {
161 5 return false;
162 }
163
164 // Drop all complete node bytes from the end of the string, including the
165 // last one if the branch bit count is the maximum (refer to the
166 // corresponding "descend" logic).
167 207 size_t const theByteCount = (
168 207 theBranchBitCount == (CHAR_BIT - 1U)
169 207 ) + Om_Tree_Branch_GetByteCount(theBranch, theBranchBitCount);
170 assert(
171 Om_ByteStack_GetCount(theIteration->thisByteStack) >= theByteCount
172 );
173 207 Om_ByteStack_Drop(theIteration->thisByteStack, theByteCount);
174
175 // Pop a branch node from the stack and get its branch and bit count.
176 207 theChildBranchNode = theBranchNode;
177 207 theBranchNode = Om_Tree_NodeStack_Pop(theIteration->thisNodeStack);
178 assert(theBranchNode != (Om_Tree_BranchNode)NULL);
179 207 theBranch = Om_Tree_BranchNode_GetBranch(theBranchNode);
180 assert(theBranch);
181 207 theBranchBitCount = Om_Tree_BranchNode_GetBranchBitCount(theBranchNode);
182
2/2
✓ Branch 1 taken 105 times.
✓ Branch 2 taken 102 times.
207 } while(theChildBranchNode == Om_Tree_Branch_GetChildArray(theBranch)[1]);
183
184 // Advance the child branch node from child 0 to 1, which cannot be null.
185 assert(theChildBranchNode == Om_Tree_Branch_GetChildArray(theBranch)[0]);
186 102 theChildBranchNode = Om_Tree_Branch_GetChildArray(theBranch)[1];
187 assert(theChildBranchNode != (Om_Tree_BranchNode)NULL);
188
189 // If the parent bit count is not the maximum, skip further string changes.
190 assert(theBranchBitCount <= (CHAR_BIT - 1U));
191
2/2
✓ Branch 0 taken 86 times.
✓ Branch 1 taken 16 times.
102 if(theBranchBitCount != (CHAR_BIT - 1U)) {
192 86 goto Descend;
193 }
194 }
195 // The parent branch bit count is the maximum; advance the last string bit.
196 assert(theBranchBitCount == (CHAR_BIT - 1U));
197 19 *Om_ByteStack_Peek(theIteration->thisByteStack) = (char)(
198 19 (uint_fast8_t)*Om_ByteStack_Peek(theIteration->thisByteStack) | 1U
199 );
200
201 105 Descend:
202
203 // Push the parent back onto the stack and descend to the first leaf
204 // descendant of the child.
205 105 Om_Tree_NodeStack_Push(theIteration->thisNodeStack, theBranchNode);
206 105 theIteration->thisLeafNode = Om_Tree_Iteration_Descend(
207 theIteration->thisNodeStack, theIteration->thisByteStack, theChildBranchNode
208 );
209 105 return true;
210 }
211
212 11 char const * Om_Tree_Iteration_GetString(
213 Om_Tree_Iteration const * const theIteration
214 ) {
215 assert(theIteration);
216 assert(theIteration->thisByteStack);
217 assert(!Om_ByteStack_IsEmpty(theIteration->thisByteStack));
218
219 11 char const * const theString = Om_ByteStack_GetArray(
220 11 theIteration->thisByteStack
221 );
222 assert(theString);
223 assert(
224 strlen(theString) ==
225 (Om_ByteStack_GetCount(theIteration->thisByteStack) - 1U)
226 );
227 11 return theString;
228 }
229
230 1 size_t Om_Tree_Iteration_GetStringLength(
231 Om_Tree_Iteration const * const theIteration
232 ) {
233 assert(theIteration);
234 assert(theIteration->thisByteStack);
235 assert(!Om_ByteStack_IsEmpty(theIteration->thisByteStack));
236
237 1 return (Om_ByteStack_GetCount(theIteration->thisByteStack) - 1U);
238 }
239
240 12 Om_Tree_LeafNode * Om_Tree_Iteration_GetLeafNode(
241 Om_Tree_Iteration const * const theIteration
242 ) {
243 assert(theIteration);
244
245 12 return theIteration->thisLeafNode;
246 }
247
248 7 void Om_Tree_Iteration_Deinitialize(
249 Om_Tree_Iteration * const theIteration
250 ) {
251 assert(theIteration);
252
253 7 Om_Tree_NodeStack_Destroy(theIteration->thisNodeStack);
254 7 Om_ByteStack_Destroy(theIteration->thisByteStack);
255 7 }
256