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 |