Code Coverage Report


Directory: targets/
File: om-tree-library/source/om/tree/branch.c
Date: 2024-10-29 22:25:26
Exec Total Coverage
Lines: 103 103 100.0%
Functions: 13 13 100.0%
Branches: 40 40 100.0%

Line Branch Exec Source
1 /*!
2 @file
3 @brief The @ref Om_Tree_Branch type and function definitions.
4 */
5
6 #include <om/tree/branch.h>
7
8 #include <om/bit.h>
9 #include <om/memory.h>
10
11 #include <assert.h>
12 #include <limits.h>
13 #include <string.h>
14
15 /*!
16 @brief A tree branch.
17 */
18 struct Om_Tree_Branch {
19
20 /*!
21 @brief An array of children.
22 @details The functions in this file treat the child nodes as opaque values; it
23 is up to the caller to manage the children as necessary.
24 */
25 Om_Tree_Node thisChildArray[2U];
26
27 /*!
28 @brief The size of the byte array.
29 */
30 size_t thisByteArraySize;
31
32 /*!
33 @brief A byte array containing the bytes and bits that prefix each child.
34 @details If the bit count is non-zero, the last byte is a bit array with the
35 specified bit count, and all extra insignificant bits are set to 1.
36 */
37 char thisByteArray[];
38
39 };
40
41 /*!
42 @brief Allocates memory for the branch and sets the byte array size.
43 @param theByteArray
44 The byte array to copy into the branch.
45 @param theByteArraySize
46 The size of the byte array to copy into the branch.
47 @returns The resulting branch, or null on failure.
48 */
49 860 static Om_Tree_Branch * Om_Tree_Branch_Allocate(
50 char const * Om_Restrict const theByteArray,
51 size_t const theByteArraySize
52 ) {
53 assert(theByteArray);
54
55 860 Om_Tree_Branch * Om_Restrict const theBranch = Om_Memory_Allocate(
56 sizeof(*theBranch) + theByteArraySize
57 );
58
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 845 times.
860 if(!theBranch) {
59 15 return NULL;
60 }
61 845 theBranch->thisByteArraySize = theByteArraySize;
62 // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
63 845 memcpy(theBranch->thisByteArray, theByteArray, theByteArraySize);
64 845 return theBranch;
65 }
66
67 /*!
68 @brief Shrinks the branch byte array and updates the byte array size.
69 @param theBranch
70 The branch.
71 @param theByteArraySize
72 The size to shrink the byte array to.
73 @returns The resulting branch; never null.
74 */
75 56 static Om_Tree_Branch * Om_Tree_Branch_Shrink(
76 Om_Tree_Branch * theBranch,
77 size_t const theByteArraySize
78 ) {
79 assert(theBranch);
80 assert(theByteArraySize <= theBranch->thisByteArraySize);
81
82 {
83 56 void * const theReallocation = Om_Memory_Reallocate(
84 theBranch, sizeof(*theBranch) + theByteArraySize
85 );
86
2/2
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 2 times.
56 if(theReallocation) {
87 54 theBranch = theReallocation;
88 }
89 }
90 56 theBranch->thisByteArraySize = theByteArraySize;
91 // cppcheck-suppress deallocret
92 56 return theBranch;
93 }
94
95 /*!
96 @brief Grows the branch byte array and updates the byte array size.
97 @param theBranch
98 The branch.
99 @param theByteArraySize
100 The size to expand the byte array to.
101 @returns The resulting branch; null if allocation failed.
102 */
103 43 static Om_Tree_Branch * Om_Tree_Branch_Grow(
104 Om_Tree_Branch * theBranch,
105 size_t const theByteArraySize
106 ) {
107 assert(theBranch);
108 assert(theByteArraySize >= theBranch->thisByteArraySize);
109
110 43 theBranch = Om_Memory_Reallocate(
111 theBranch, sizeof(*theBranch) + theByteArraySize
112 );
113
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 40 times.
43 if(!theBranch) {
114 3 return NULL;
115 }
116 40 theBranch->thisByteArraySize = theByteArraySize;
117 40 return theBranch;
118 }
119
120 194 Om_Tree_Branch * Om_Tree_Branch_Create(
121 uint_fast8_t * Om_Restrict const theBranchBitCount,
122 size_t const theStringLength,
123 char const * Om_Restrict const theString
124 ) {
125 assert(theBranchBitCount);
126 assert(theString);
127 assert(theStringLength == strlen(theString));
128
129 // Include the null terminator as a proper byte, but with the last bit masked
130 // to account for the child index.
131 194 Om_Tree_Branch * Om_Restrict const theBranch = Om_Tree_Branch_Allocate(
132 theString, theStringLength + 1U
133 );
134
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 189 times.
194 if(!theBranch) {
135 5 return NULL;
136 }
137 189 theBranch->thisByteArray[theStringLength] = '\1';
138 189 *theBranchBitCount = (CHAR_BIT - 1U);
139 189 return theBranch;
140 }
141
142 523 Om_Tree_Branch * Om_Tree_Branch_CreateCopy(
143 Om_Tree_Branch const * Om_Restrict const theBranch
144 ) {
145 assert(theBranch);
146
147 523 Om_Tree_Branch * const theCopy = Om_Tree_Branch_Allocate(
148 523 theBranch->thisByteArray, theBranch->thisByteArraySize
149 );
150
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 516 times.
523 if(!theCopy) {
151 7 return NULL;
152 }
153 // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
154 516 memcpy(
155 516 theCopy->thisChildArray, theBranch->thisChildArray,
156 sizeof(theBranch->thisChildArray)
157 );
158 516 return theCopy;
159 }
160
161 143 Om_Tree_Branch * Om_Tree_Branch_Split(
162 Om_Tree_Branch * theBranch,
163 uint_fast8_t * const theBranchBitCount,
164 size_t const theNewBranchByteCount,
165 uint_fast8_t const theNewBranchBitCount,
166 Om_Tree_Branch * * const theChildBranch,
167 uint_fast8_t * const theChildBranchBitCount
168 ) {
169 assert(theBranch);
170 assert(theBranchBitCount && (*theBranchBitCount < CHAR_BIT));
171 assert(
172 theNewBranchByteCount <=
173 Om_Tree_Branch_GetByteCount(theBranch, *theBranchBitCount)
174 );
175 assert(theNewBranchBitCount < CHAR_BIT);
176 // Assert that if the new byte count is the same, that the new bit count is
177 // less than the old one.
178 assert(
179 (
180 Om_Tree_Branch_GetByteCount(theBranch, *theBranchBitCount) >
181 theNewBranchByteCount
182 ) || !*theBranchBitCount || (theNewBranchBitCount < *theBranchBitCount)
183 );
184 assert(theChildBranch);
185 assert(theChildBranchBitCount);
186
187 // Determine the new byte array size for the branch. If the critical bit is
188 // leftmost, do not duplicate the child branch byte in the branch.
189 143 size_t const theNewBranchByteArraySize = (
190 143 theNewBranchByteCount + !!theNewBranchBitCount
191 );
192
193 // Split the remainder off to a new child.
194
4/4
✓ Branch 0 taken 122 times.
✓ Branch 1 taken 21 times.
✓ Branch 2 taken 85 times.
✓ Branch 3 taken 37 times.
143 if(!theNewBranchByteCount && (theNewBranchBitCount < (CHAR_BIT - 1U))) {
195 // Optimization for byte index 0 and critical bit not rightmost (i.e. the
196 // byte is also in the child): the branch becomes the child.
197 85 *theChildBranch = theBranch;
198 85 *theChildBranchBitCount = *theBranchBitCount;
199 85 theBranch = Om_Tree_Branch_Allocate(
200 85 theBranch->thisByteArray, theNewBranchByteArraySize
201 );
202
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 84 times.
85 if(!theBranch) {
203 1 return NULL;
204 }
205 } else {
206 // Determine the remainder to copy to the new child. If the critical bit is
207 // rightmost, do not duplicate the branch byte in the child branch.
208 58 char const * const theByteArrayRemainder = theBranch->thisByteArray + (
209 58 theNewBranchByteCount + (theNewBranchBitCount == (CHAR_BIT - 1U))
210 );
211 58 size_t const theByteArrayRemainderSize = theBranch->thisByteArraySize - (
212 58 (size_t)(theByteArrayRemainder - theBranch->thisByteArray)
213 );
214
215 58 *theChildBranch = Om_Tree_Branch_Allocate(
216 theByteArrayRemainder, theByteArrayRemainderSize
217 );
218
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 56 times.
58 if(!*theChildBranch) {
219 2 return NULL;
220 }
221 // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
222 56 memcpy(
223 56 (*theChildBranch)->thisChildArray, theBranch->thisChildArray,
224 sizeof(theBranch->thisChildArray)
225 );
226
227 // If the child branch is empty, then either the bit count count is 0, or
228 // the critical byte was skipped.
229 assert(
230 theByteArrayRemainderSize ||
231 !*theBranchBitCount ||
232 (theNewBranchBitCount == (CHAR_BIT - 1U))
233 );
234 56 *theChildBranchBitCount = (uint_fast8_t)(
235 56 !!theByteArrayRemainderSize * *theBranchBitCount
236 );
237
238 56 theBranch = Om_Tree_Branch_Shrink(theBranch, theNewBranchByteArraySize);
239 assert(theBranch);
240 }
241
242 // Mask the new partial byte.
243
2/2
✓ Branch 0 taken 131 times.
✓ Branch 1 taken 9 times.
140 if(theNewBranchBitCount) {
244 assert(theNewBranchByteCount < theBranch->thisByteArraySize);
245 131 theBranch->thisByteArray[theNewBranchByteCount] = (char)(
246 131 (uint_fast8_t)theBranch->thisByteArray[theNewBranchByteCount] |
247 131 Om_Bit_MaskRemainder(theNewBranchBitCount)
248 );
249 }
250
251 // Update the branch bit count and return the new branch.
252 140 *theBranchBitCount = theNewBranchBitCount;
253 140 return theBranch;
254 }
255
256 119 Om_Tree_Branch * Om_Tree_Branch_Merge(
257 Om_Tree_Branch * theBranch,
258 uint_fast8_t * const theBranchBitCount,
259 uint_fast8_t const theChildOffset,
260 Om_Tree_Branch * const theChildBranch,
261 uint_fast8_t const theChildBranchBitCount
262 ) {
263 assert(theBranch);
264 assert(theBranchBitCount && *theBranchBitCount < CHAR_BIT);
265 assert(theChildBranch);
266 assert(theChildBranchBitCount < CHAR_BIT);
267
268 // Determine the branch byte array size that the child byte array size will be
269 // added to, excluding the critical byte if it is in both; this is the case if
270 // the bit count is not zero and is less than the maximum.
271 238 size_t const theBranchByteArraySize = theBranch->thisByteArraySize - (
272
4/4
✓ Branch 0 taken 111 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 85 times.
✓ Branch 3 taken 26 times.
119 *theBranchBitCount && (*theBranchBitCount < (CHAR_BIT - 1U))
273 );
274
275 // Merge the child into the branch.
276
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 43 times.
119 if(!theBranchByteArraySize) {
277 // Optimization for empty branch: the branch can be replaced with the child.
278 76 Om_Tree_Branch_Destroy(theBranch);
279 76 theBranch = theChildBranch;
280 } else {
281 // Expand the branch.
282 43 size_t const theChildBranchByteArraySize = (
283 theChildBranch->thisByteArraySize
284 );
285 43 theBranch = Om_Tree_Branch_Grow(
286 theBranch, theBranchByteArraySize + theChildBranchByteArraySize
287 );
288
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 40 times.
43 if(!theBranch) {
289 3 return NULL;
290 }
291
292 // If the bit count is the maximum, the critical byte only exists in the
293 // parent, but with the rightmost bit masked; unmask it. Because the bit is
294 // 1 due to masking. it only needs to be changed if the child index is 0.
295
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 16 times.
40 if(*theBranchBitCount == (CHAR_BIT - 1U)) {
296 assert(theBranchByteArraySize);
297 // The extra variables are to avoid "helpful" compile warnings.
298 24 char * const theByte = theBranch->thisByteArray + (
299 theBranchByteArraySize - 1U
300 );
301 assert((uint_fast8_t)*theByte & 1U);
302 24 char unsigned const theOtherChildOffset = !theChildOffset;
303 24 *theByte = (char)(
304 24 (uint_fast8_t)*theByte & (uint_fast8_t)(~theOtherChildOffset)
305 );
306 }
307
308 // Copy the child bytes and grandchildren, then destroy the child.
309 // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
310 40 memcpy(
311 40 theBranch->thisByteArray + theBranchByteArraySize,
312 40 theChildBranch->thisByteArray,
313 theChildBranchByteArraySize
314 );
315 // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
316 40 memcpy(
317 40 theBranch->thisChildArray, theChildBranch->thisChildArray,
318 sizeof(theChildBranch->thisChildArray)
319 );
320 40 Om_Tree_Branch_Destroy(theChildBranch);
321 }
322
323 // Update the branch bit count and return the new branch.
324 116 *theBranchBitCount = theChildBranchBitCount;
325 116 return theBranch;
326 }
327
328 3900 bool Om_Tree_Branch_HasLeafChild(
329 Om_Tree_Branch const * Om_Restrict const theBranch,
330 uint_fast8_t const theBranchBitCount
331 ) {
332 assert(theBranch);
333 assert(theBranchBitCount < CHAR_BIT);
334 assert(!theBranchBitCount || theBranch->thisByteArraySize);
335
336 return (
337
2/2
✓ Branch 0 taken 2346 times.
✓ Branch 1 taken 1554 times.
6246 (theBranchBitCount == (CHAR_BIT - 1U)) &&
338
2/2
✓ Branch 0 taken 2068 times.
✓ Branch 1 taken 278 times.
2346 (theBranch->thisByteArray[theBranch->thisByteArraySize - 1U] == '\1')
339 );
340 }
341
342 10292 Om_Tree_Node * Om_Tree_Branch_GetChildArray(
343 Om_Tree_Branch * Om_Restrict const theBranch
344 ) {
345 assert(theBranch);
346
347 10292 return theBranch->thisChildArray;
348 }
349
350 3707 char const * Om_Tree_Branch_GetByteArray(
351 Om_Tree_Branch const * Om_Restrict const theBranch
352 ) {
353 assert(theBranch);
354
355 3707 return theBranch->thisByteArray;
356 }
357
358 4343 size_t Om_Tree_Branch_GetByteCount(
359 Om_Tree_Branch const * Om_Restrict const theBranch,
360 uint_fast8_t const theBranchBitCount
361 ) {
362 assert(theBranch);
363 assert(theBranchBitCount < CHAR_BIT);
364 assert(!theBranchBitCount || theBranch->thisByteArraySize);
365
366 4343 return (theBranch->thisByteArraySize - !!theBranchBitCount);
367 }
368
369 524 bool Om_Tree_Branch_Match(
370 Om_Tree_Branch const * Om_Restrict const theBranch,
371 uint_fast8_t const theBranchBitCount,
372 Om_Tree_Branch const * Om_Restrict const theOtherBranch,
373 uint_fast8_t const theOtherBranchBitCount
374 ) {
375 return (
376 522 (theBranchBitCount == theOtherBranchBitCount) &&
377
4/4
✓ Branch 0 taken 522 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 516 times.
✓ Branch 3 taken 6 times.
1040 (theBranch->thisByteArraySize == theOtherBranch->thisByteArraySize) &&
378 (
379 516 0 == strncmp(
380 516 theBranch->thisByteArray, theOtherBranch->thisByteArray,
381
2/2
✓ Branch 0 taken 514 times.
✓ Branch 1 taken 2 times.
516 theBranch->thisByteArraySize
382 )
383 )
384 );
385 }
386
387 845 void Om_Tree_Branch_Destroy(
388 Om_Tree_Branch * Om_Restrict const theBranch
389 ) {
390 845 Om_Memory_Deallocate(theBranch);
391 845 }
392