1 /*
2  * Copyright (c) 2021 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <errno.h>
8 #include <stddef.h>
9 #include <stdbool.h>
10 #include <stdio.h>
11 #include <zephyr/sys/bitarray.h>
12 #include <zephyr/sys/check.h>
13 #include <zephyr/sys/sys_io.h>
14 
15 /* Number of bits represented by one bundle */
16 #define bundle_bitness(ba)	(sizeof((ba)->bundles[0]) * 8)
17 
18 struct bundle_data {
19 	 /* Start and end index of bundles */
20 	size_t sidx, eidx;
21 
22 	 /* Offset inside start and end bundles */
23 	size_t soff, eoff;
24 
25 	 /* Masks for start/end bundles */
26 	uint32_t smask, emask;
27 };
28 
setup_bundle_data(sys_bitarray_t * bitarray,struct bundle_data * bd,size_t offset,size_t num_bits)29 static void setup_bundle_data(sys_bitarray_t *bitarray,
30 			      struct bundle_data *bd,
31 			      size_t offset, size_t num_bits)
32 {
33 	bd->sidx = offset / bundle_bitness(bitarray);
34 	bd->soff = offset % bundle_bitness(bitarray);
35 
36 	bd->eidx = (offset + num_bits - 1) / bundle_bitness(bitarray);
37 	bd->eoff = (offset + num_bits - 1) % bundle_bitness(bitarray);
38 
39 	bd->smask = ~(BIT(bd->soff) - 1);
40 	bd->emask = (BIT(bd->eoff) - 1) | BIT(bd->eoff);
41 
42 	if (bd->sidx == bd->eidx) {
43 		/* The region lies within the same bundle. So combine the masks. */
44 		bd->smask &= bd->emask;
45 	}
46 }
47 
48 /*
49  * Find out if the bits in a region is all set or all clear.
50  *
51  * @param[in]  bitarray  Bitarray struct
52  * @param[in]  offset    Starting bit location
53  * @param[in]  num_bits  Number of bits in the region
54  * @param[in]  match_set True if matching all set bits,
55  *                       False if matching all cleared bits
56  * @param[out] bd        Data related to matching which can be
57  *                       used later to find out where the region
58  *                       lies in the bitarray bundles.
59  * @param[out] mismatch  Offset to the mismatched bit.
60  *                       Can be NULL.
61  *
62  * @retval     true      If all bits are set or cleared
63  * @retval     false     Not all bits are set or cleared
64  */
match_region(sys_bitarray_t * bitarray,size_t offset,size_t num_bits,bool match_set,struct bundle_data * bd,size_t * mismatch)65 static bool match_region(sys_bitarray_t *bitarray, size_t offset,
66 			 size_t num_bits, bool match_set,
67 			 struct bundle_data *bd,
68 			 size_t *mismatch)
69 {
70 	size_t idx;
71 	uint32_t bundle;
72 	uint32_t mismatch_bundle;
73 	size_t mismatch_bundle_idx;
74 	size_t mismatch_bit_off;
75 
76 	setup_bundle_data(bitarray, bd, offset, num_bits);
77 
78 	if (bd->sidx == bd->eidx) {
79 		bundle = bitarray->bundles[bd->sidx];
80 		if (!match_set) {
81 			bundle = ~bundle;
82 		}
83 
84 		if ((bundle & bd->smask) != bd->smask) {
85 			/* Not matching to mask. */
86 			mismatch_bundle = ~bundle & bd->smask;
87 			mismatch_bundle_idx = bd->sidx;
88 			goto mismatch;
89 		} else {
90 			/* Matching to mask. */
91 			goto out;
92 		}
93 	}
94 
95 	/* Region lies in a number of bundles. Need to loop through them. */
96 
97 	/* Start of bundles */
98 	bundle = bitarray->bundles[bd->sidx];
99 	if (!match_set) {
100 		bundle = ~bundle;
101 	}
102 
103 	if ((bundle & bd->smask) != bd->smask) {
104 		/* Start bundle not matching to mask. */
105 		mismatch_bundle = ~bundle & bd->smask;
106 		mismatch_bundle_idx = bd->sidx;
107 		goto mismatch;
108 	}
109 
110 	/* End of bundles */
111 	bundle = bitarray->bundles[bd->eidx];
112 	if (!match_set) {
113 		bundle = ~bundle;
114 	}
115 
116 	if ((bundle & bd->emask) != bd->emask) {
117 		/* End bundle not matching to mask. */
118 		mismatch_bundle = ~bundle & bd->emask;
119 		mismatch_bundle_idx = bd->eidx;
120 		goto mismatch;
121 	}
122 
123 	/* In-between bundles */
124 	for (idx = bd->sidx + 1; idx < bd->eidx; idx++) {
125 		/* Note that this is opposite from above so that
126 		 * we are simply checking if bundle == 0.
127 		 */
128 		bundle = bitarray->bundles[idx];
129 		if (match_set) {
130 			bundle = ~bundle;
131 		}
132 
133 		if (bundle != 0U) {
134 			/* Bits in "between bundles" do not match */
135 			mismatch_bundle = bundle;
136 			mismatch_bundle_idx = idx;
137 			goto mismatch;
138 		}
139 	}
140 
141 out:
142 	/* All bits in region matched. */
143 	return true;
144 
145 mismatch:
146 	if (mismatch != NULL) {
147 		/* Must have at least 1 bit set to indicate
148 		 * where the mismatch is.
149 		 */
150 		__ASSERT_NO_MSG(mismatch_bundle != 0);
151 
152 		mismatch_bit_off = find_lsb_set(mismatch_bundle) - 1;
153 		mismatch_bit_off += mismatch_bundle_idx *
154 				    bundle_bitness(bitarray);
155 		*mismatch = (uint32_t)mismatch_bit_off;
156 	}
157 	return false;
158 }
159 
160 /*
161  * Set or clear a region of bits.
162  *
163  * @param bitarray Bitarray struct
164  * @param offset   Starting bit location
165  * @param num_bits Number of bits in the region
166  * @param to_set   True if to set all bits.
167  *                 False if to clear all bits.
168  * @param bd       Bundle data. Can reuse the output from
169  *                 match_region(). NULL if there is no
170  *                 prior call to match_region().
171  */
set_region(sys_bitarray_t * bitarray,size_t offset,size_t num_bits,bool to_set,struct bundle_data * bd)172 static void set_region(sys_bitarray_t *bitarray, size_t offset,
173 		       size_t num_bits, bool to_set,
174 		       struct bundle_data *bd)
175 {
176 	size_t idx;
177 	struct bundle_data bdata;
178 
179 	if (bd == NULL) {
180 		bd = &bdata;
181 		setup_bundle_data(bitarray, bd, offset, num_bits);
182 	}
183 
184 	if (bd->sidx == bd->eidx) {
185 		/* Start/end at same bundle */
186 		if (to_set) {
187 			bitarray->bundles[bd->sidx] |= bd->smask;
188 		} else {
189 			bitarray->bundles[bd->sidx] &= ~bd->smask;
190 		}
191 	} else {
192 		/* Start/end at different bundle.
193 		 * So set/clear the bits in start and end bundles
194 		 * separately. For in-between bundles,
195 		 * set/clear all bits.
196 		 */
197 		if (to_set) {
198 			bitarray->bundles[bd->sidx] |= bd->smask;
199 			bitarray->bundles[bd->eidx] |= bd->emask;
200 			for (idx = bd->sidx + 1; idx < bd->eidx; idx++) {
201 				bitarray->bundles[idx] = ~0U;
202 			}
203 		} else {
204 			bitarray->bundles[bd->sidx] &= ~bd->smask;
205 			bitarray->bundles[bd->eidx] &= ~bd->emask;
206 			for (idx = bd->sidx + 1; idx < bd->eidx; idx++) {
207 				bitarray->bundles[idx] = 0U;
208 			}
209 		}
210 	}
211 }
212 
sys_bitarray_popcount_region(sys_bitarray_t * bitarray,size_t num_bits,size_t offset,size_t * count)213 int sys_bitarray_popcount_region(sys_bitarray_t *bitarray, size_t num_bits, size_t offset,
214 				 size_t *count)
215 {
216 	k_spinlock_key_t key;
217 	size_t idx;
218 	struct bundle_data bd;
219 	int ret;
220 
221 	__ASSERT_NO_MSG(bitarray != NULL);
222 	__ASSERT_NO_MSG(bitarray->num_bits > 0);
223 
224 	key = k_spin_lock(&bitarray->lock);
225 
226 	if (num_bits == 0 || offset + num_bits > bitarray->num_bits) {
227 		ret = -EINVAL;
228 		goto out;
229 	}
230 
231 	CHECKIF(count == NULL) {
232 		ret = -EINVAL;
233 		goto out;
234 	}
235 
236 	setup_bundle_data(bitarray, &bd, offset, num_bits);
237 
238 	if (bd.sidx == bd.eidx) {
239 		/* Start/end at same bundle */
240 		*count = POPCOUNT(bitarray->bundles[bd.sidx] & bd.smask);
241 	} else {
242 		/* Start/end at different bundle.
243 		 * So count the bits in start and end bundles
244 		 * separately with correct mask applied. For in-between bundles,
245 		 * count all bits.
246 		 */
247 		*count = 0;
248 		*count += POPCOUNT(bitarray->bundles[bd.sidx] & bd.smask);
249 		*count += POPCOUNT(bitarray->bundles[bd.eidx] & bd.emask);
250 		for (idx = bd.sidx + 1; idx < bd.eidx; idx++) {
251 			*count += POPCOUNT(bitarray->bundles[idx]);
252 		}
253 	}
254 
255 	ret = 0;
256 
257 out:
258 	k_spin_unlock(&bitarray->lock, key);
259 	return ret;
260 }
261 
sys_bitarray_xor(sys_bitarray_t * dst,sys_bitarray_t * other,size_t num_bits,size_t offset)262 int sys_bitarray_xor(sys_bitarray_t *dst, sys_bitarray_t *other, size_t num_bits, size_t offset)
263 {
264 	k_spinlock_key_t key_dst, key_other;
265 	int ret;
266 	size_t idx;
267 	struct bundle_data bd;
268 
269 	__ASSERT_NO_MSG(dst != NULL);
270 	__ASSERT_NO_MSG(dst->num_bits > 0);
271 	__ASSERT_NO_MSG(other != NULL);
272 	__ASSERT_NO_MSG(other->num_bits > 0);
273 
274 	key_dst = k_spin_lock(&dst->lock);
275 	key_other = k_spin_lock(&other->lock);
276 
277 
278 	if (dst->num_bits != other->num_bits) {
279 		ret = -EINVAL;
280 		goto out;
281 	}
282 
283 	if (num_bits == 0 || offset + num_bits > dst->num_bits) {
284 		ret = -EINVAL;
285 		goto out;
286 	}
287 
288 	setup_bundle_data(other, &bd, offset, num_bits);
289 
290 	if (bd.sidx == bd.eidx) {
291 		/* Start/end at same bundle */
292 		dst->bundles[bd.sidx] =
293 			((other->bundles[bd.sidx] ^ dst->bundles[bd.sidx]) & bd.smask) |
294 			(dst->bundles[bd.sidx] & ~bd.smask);
295 	} else {
296 		/* Start/end at different bundle.
297 		 * So xor the bits in start and end bundles according to their bitmasks
298 		 * separately. For in-between bundles,
299 		 * xor all bits.
300 		 */
301 		dst->bundles[bd.sidx] =
302 			((other->bundles[bd.sidx] ^ dst->bundles[bd.sidx]) & bd.smask) |
303 			(dst->bundles[bd.sidx] & ~bd.smask);
304 		dst->bundles[bd.eidx] =
305 			((other->bundles[bd.eidx] ^ dst->bundles[bd.eidx]) & bd.emask) |
306 			(dst->bundles[bd.eidx] & ~bd.emask);
307 		for (idx = bd.sidx + 1; idx < bd.eidx; idx++) {
308 			dst->bundles[idx] ^= other->bundles[idx];
309 		}
310 	}
311 
312 	ret = 0;
313 
314 out:
315 	k_spin_unlock(&other->lock, key_other);
316 	k_spin_unlock(&dst->lock, key_dst);
317 	return ret;
318 }
319 
sys_bitarray_set_bit(sys_bitarray_t * bitarray,size_t bit)320 int sys_bitarray_set_bit(sys_bitarray_t *bitarray, size_t bit)
321 {
322 	k_spinlock_key_t key;
323 	int ret;
324 	size_t idx, off;
325 
326 	__ASSERT_NO_MSG(bitarray != NULL);
327 	__ASSERT_NO_MSG(bitarray->num_bits > 0);
328 
329 	key = k_spin_lock(&bitarray->lock);
330 
331 	if (bit >= bitarray->num_bits) {
332 		ret = -EINVAL;
333 		goto out;
334 	}
335 
336 	idx = bit / bundle_bitness(bitarray);
337 	off = bit % bundle_bitness(bitarray);
338 
339 	bitarray->bundles[idx] |= BIT(off);
340 
341 	ret = 0;
342 
343 out:
344 	k_spin_unlock(&bitarray->lock, key);
345 	return ret;
346 }
347 
sys_bitarray_clear_bit(sys_bitarray_t * bitarray,size_t bit)348 int sys_bitarray_clear_bit(sys_bitarray_t *bitarray, size_t bit)
349 {
350 	k_spinlock_key_t key;
351 	int ret;
352 	size_t idx, off;
353 
354 	__ASSERT_NO_MSG(bitarray != NULL);
355 	__ASSERT_NO_MSG(bitarray->num_bits > 0);
356 
357 	key = k_spin_lock(&bitarray->lock);
358 
359 	if (bit >= bitarray->num_bits) {
360 		ret = -EINVAL;
361 		goto out;
362 	}
363 
364 	idx = bit / bundle_bitness(bitarray);
365 	off = bit % bundle_bitness(bitarray);
366 
367 	bitarray->bundles[idx] &= ~BIT(off);
368 
369 	ret = 0;
370 
371 out:
372 	k_spin_unlock(&bitarray->lock, key);
373 	return ret;
374 }
375 
sys_bitarray_test_bit(sys_bitarray_t * bitarray,size_t bit,int * val)376 int sys_bitarray_test_bit(sys_bitarray_t *bitarray, size_t bit, int *val)
377 {
378 	k_spinlock_key_t key;
379 	int ret;
380 	size_t idx, off;
381 
382 	__ASSERT_NO_MSG(bitarray != NULL);
383 	__ASSERT_NO_MSG(bitarray->num_bits > 0);
384 
385 	key = k_spin_lock(&bitarray->lock);
386 
387 	CHECKIF(val == NULL) {
388 		ret = -EINVAL;
389 		goto out;
390 	}
391 
392 	if (bit >= bitarray->num_bits) {
393 		ret = -EINVAL;
394 		goto out;
395 	}
396 
397 	idx = bit / bundle_bitness(bitarray);
398 	off = bit % bundle_bitness(bitarray);
399 
400 	if ((bitarray->bundles[idx] & BIT(off)) != 0) {
401 		*val = 1;
402 	} else {
403 		*val = 0;
404 	}
405 
406 	ret = 0;
407 
408 out:
409 	k_spin_unlock(&bitarray->lock, key);
410 	return ret;
411 }
412 
sys_bitarray_test_and_set_bit(sys_bitarray_t * bitarray,size_t bit,int * prev_val)413 int sys_bitarray_test_and_set_bit(sys_bitarray_t *bitarray, size_t bit, int *prev_val)
414 {
415 	k_spinlock_key_t key;
416 	int ret;
417 	size_t idx, off;
418 
419 	__ASSERT_NO_MSG(bitarray != NULL);
420 	__ASSERT_NO_MSG(bitarray->num_bits > 0);
421 
422 	key = k_spin_lock(&bitarray->lock);
423 
424 	CHECKIF(prev_val == NULL) {
425 		ret = -EINVAL;
426 		goto out;
427 	}
428 
429 	if (bit >= bitarray->num_bits) {
430 		ret = -EINVAL;
431 		goto out;
432 	}
433 
434 	idx = bit / bundle_bitness(bitarray);
435 	off = bit % bundle_bitness(bitarray);
436 
437 	if ((bitarray->bundles[idx] & BIT(off)) != 0) {
438 		*prev_val = 1;
439 	} else {
440 		*prev_val = 0;
441 	}
442 
443 	bitarray->bundles[idx] |= BIT(off);
444 
445 	ret = 0;
446 
447 out:
448 	k_spin_unlock(&bitarray->lock, key);
449 	return ret;
450 }
451 
sys_bitarray_test_and_clear_bit(sys_bitarray_t * bitarray,size_t bit,int * prev_val)452 int sys_bitarray_test_and_clear_bit(sys_bitarray_t *bitarray, size_t bit, int *prev_val)
453 {
454 	k_spinlock_key_t key;
455 	int ret;
456 	size_t idx, off;
457 
458 	__ASSERT_NO_MSG(bitarray != NULL);
459 	__ASSERT_NO_MSG(bitarray->num_bits > 0);
460 
461 	key = k_spin_lock(&bitarray->lock);
462 
463 	CHECKIF(prev_val == NULL) {
464 		ret = -EINVAL;
465 		goto out;
466 	}
467 
468 	if (bit >= bitarray->num_bits) {
469 		ret = -EINVAL;
470 		goto out;
471 	}
472 
473 	idx = bit / bundle_bitness(bitarray);
474 	off = bit % bundle_bitness(bitarray);
475 
476 	if ((bitarray->bundles[idx] & BIT(off)) != 0) {
477 		*prev_val = 1;
478 	} else {
479 		*prev_val = 0;
480 	}
481 
482 	bitarray->bundles[idx] &= ~BIT(off);
483 
484 	ret = 0;
485 
486 out:
487 	k_spin_unlock(&bitarray->lock, key);
488 	return ret;
489 }
490 
sys_bitarray_alloc(sys_bitarray_t * bitarray,size_t num_bits,size_t * offset)491 int sys_bitarray_alloc(sys_bitarray_t *bitarray, size_t num_bits,
492 		       size_t *offset)
493 {
494 	k_spinlock_key_t key;
495 	uint32_t bit_idx;
496 	int ret;
497 	struct bundle_data bd;
498 	size_t off_start, off_end;
499 	size_t mismatch;
500 
501 	__ASSERT_NO_MSG(bitarray != NULL);
502 	__ASSERT_NO_MSG(bitarray->num_bits > 0);
503 
504 	key = k_spin_lock(&bitarray->lock);
505 
506 	CHECKIF(offset == NULL) {
507 		ret = -EINVAL;
508 		goto out;
509 	}
510 
511 	if ((num_bits == 0) || (num_bits > bitarray->num_bits)) {
512 		ret = -EINVAL;
513 		goto out;
514 	}
515 
516 	bit_idx = 0;
517 
518 	/* Find the first non-allocated bit by looking at bundles
519 	 * instead of individual bits.
520 	 *
521 	 * On RISC-V 64-bit, it complains about undefined reference to `ffs`.
522 	 * So don't use this on RISCV64.
523 	 */
524 	for (size_t idx = 0; idx < bitarray->num_bundles; idx++) {
525 		if (~bitarray->bundles[idx] == 0U) {
526 			/* bundle is all 1s => all allocated, skip */
527 			bit_idx += bundle_bitness(bitarray);
528 			continue;
529 		}
530 
531 		if (bitarray->bundles[idx] != 0U) {
532 			/* Find the first free bit in bundle if not all free */
533 			off_start = find_lsb_set(~bitarray->bundles[idx]) - 1;
534 			bit_idx += off_start;
535 		}
536 
537 		break;
538 	}
539 
540 	off_end = bitarray->num_bits - num_bits;
541 	ret = -ENOSPC;
542 	while (bit_idx <= off_end) {
543 		if (match_region(bitarray, bit_idx, num_bits, false,
544 				 &bd, &mismatch)) {
545 			set_region(bitarray, bit_idx, num_bits, true, &bd);
546 
547 			*offset = bit_idx;
548 			ret = 0;
549 			break;
550 		}
551 
552 		/* Fast-forward to the bit just after
553 		 * the mismatched bit.
554 		 */
555 		bit_idx = mismatch + 1;
556 	}
557 
558 out:
559 	k_spin_unlock(&bitarray->lock, key);
560 	return ret;
561 }
562 
sys_bitarray_find_nth_set(sys_bitarray_t * bitarray,size_t n,size_t num_bits,size_t offset,size_t * found_at)563 int sys_bitarray_find_nth_set(sys_bitarray_t *bitarray, size_t n, size_t num_bits, size_t offset,
564 			      size_t *found_at)
565 {
566 	k_spinlock_key_t key;
567 	size_t count, idx;
568 	uint32_t mask;
569 	struct bundle_data bd;
570 	int ret;
571 
572 	__ASSERT_NO_MSG(bitarray != NULL);
573 	__ASSERT_NO_MSG(bitarray->num_bits > 0);
574 
575 	key = k_spin_lock(&bitarray->lock);
576 
577 	if (n == 0 || num_bits == 0 || offset + num_bits > bitarray->num_bits) {
578 		ret = -EINVAL;
579 		goto out;
580 	}
581 
582 	ret = 1;
583 	mask = 0;
584 	setup_bundle_data(bitarray, &bd, offset, num_bits);
585 
586 	count = POPCOUNT(bitarray->bundles[bd.sidx] & bd.smask);
587 	/* If we already found more bits set than n, we found the target bundle */
588 	if (count >= n) {
589 		idx = bd.sidx;
590 		mask = bd.smask;
591 		goto found;
592 	}
593 	/* Keep looking if there are more bundles */
594 	if (bd.sidx != bd.eidx) {
595 		/* We are now only looking for the remaining bits */
596 		n -= count;
597 		/* First bundle was already checked, keep looking in middle (complete)
598 		 * bundles.
599 		 */
600 		for (idx = bd.sidx + 1; idx < bd.eidx; idx++) {
601 			count = POPCOUNT(bitarray->bundles[idx]);
602 			if (count >= n) {
603 				mask = ~(mask & 0);
604 				goto found;
605 			}
606 			n -= count;
607 		}
608 		/* Continue searching in last bundle */
609 		count = POPCOUNT(bitarray->bundles[bd.eidx] & bd.emask);
610 		if (count >= n) {
611 			idx = bd.eidx;
612 			mask = bd.emask;
613 			goto found;
614 		}
615 	}
616 
617 	goto out;
618 
619 found:
620 	/* The bit we are looking for must be in the current bundle idx.
621 	 * Find out the exact index of the bit.
622 	 */
623 	for (int j = 0; j <= bundle_bitness(bitarray) - 1; j++) {
624 		if (bitarray->bundles[idx] & mask & BIT(j)) {
625 			if (--n <= 0) {
626 				*found_at = idx * bundle_bitness(bitarray) + j;
627 				ret = 0;
628 				break;
629 			}
630 		}
631 	}
632 
633 out:
634 	k_spin_unlock(&bitarray->lock, key);
635 	return ret;
636 }
637 
sys_bitarray_free(sys_bitarray_t * bitarray,size_t num_bits,size_t offset)638 int sys_bitarray_free(sys_bitarray_t *bitarray, size_t num_bits,
639 		      size_t offset)
640 {
641 	k_spinlock_key_t key;
642 	int ret;
643 	size_t off_end = offset + num_bits - 1;
644 	struct bundle_data bd;
645 
646 	__ASSERT_NO_MSG(bitarray != NULL);
647 	__ASSERT_NO_MSG(bitarray->num_bits > 0);
648 
649 	key = k_spin_lock(&bitarray->lock);
650 
651 	if ((num_bits == 0)
652 	    || (num_bits > bitarray->num_bits)
653 	    || (offset >= bitarray->num_bits)
654 	    || (off_end >= bitarray->num_bits)) {
655 		ret = -EINVAL;
656 		goto out;
657 	}
658 
659 	/* Note that we need to make sure the bits in specified region
660 	 * (offset to offset + num_bits) are all allocated before we clear
661 	 * them.
662 	 */
663 	if (match_region(bitarray, offset, num_bits, true, &bd, NULL)) {
664 		set_region(bitarray, offset, num_bits, false, &bd);
665 		ret = 0;
666 	} else {
667 		ret = -EFAULT;
668 	}
669 
670 out:
671 	k_spin_unlock(&bitarray->lock, key);
672 	return ret;
673 }
674 
is_region_set_clear(sys_bitarray_t * bitarray,size_t num_bits,size_t offset,bool to_set)675 static bool is_region_set_clear(sys_bitarray_t *bitarray, size_t num_bits,
676 				size_t offset, bool to_set)
677 {
678 	bool ret;
679 	struct bundle_data bd;
680 	size_t off_end = offset + num_bits - 1;
681 	k_spinlock_key_t key = k_spin_lock(&bitarray->lock);
682 
683 	__ASSERT_NO_MSG(bitarray != NULL);
684 	__ASSERT_NO_MSG(bitarray->num_bits > 0);
685 
686 	if ((num_bits == 0)
687 	    || (num_bits > bitarray->num_bits)
688 	    || (offset >= bitarray->num_bits)
689 	    || (off_end >= bitarray->num_bits)) {
690 		ret = false;
691 		goto out;
692 	}
693 
694 	ret = match_region(bitarray, offset, num_bits, to_set, &bd, NULL);
695 
696 out:
697 	k_spin_unlock(&bitarray->lock, key);
698 	return ret;
699 }
700 
sys_bitarray_is_region_set(sys_bitarray_t * bitarray,size_t num_bits,size_t offset)701 bool sys_bitarray_is_region_set(sys_bitarray_t *bitarray, size_t num_bits,
702 				size_t offset)
703 {
704 	return is_region_set_clear(bitarray, num_bits, offset, true);
705 }
706 
sys_bitarray_is_region_cleared(sys_bitarray_t * bitarray,size_t num_bits,size_t offset)707 bool sys_bitarray_is_region_cleared(sys_bitarray_t *bitarray, size_t num_bits,
708 				    size_t offset)
709 {
710 	return is_region_set_clear(bitarray, num_bits, offset, false);
711 }
712 
set_clear_region(sys_bitarray_t * bitarray,size_t num_bits,size_t offset,bool to_set)713 static int set_clear_region(sys_bitarray_t *bitarray, size_t num_bits,
714 			    size_t offset, bool to_set)
715 {
716 	int ret;
717 	size_t off_end = offset + num_bits - 1;
718 	k_spinlock_key_t key = k_spin_lock(&bitarray->lock);
719 
720 	__ASSERT_NO_MSG(bitarray != NULL);
721 	__ASSERT_NO_MSG(bitarray->num_bits > 0);
722 
723 	if ((num_bits == 0)
724 	    || (num_bits > bitarray->num_bits)
725 	    || (offset >= bitarray->num_bits)
726 	    || (off_end >= bitarray->num_bits)) {
727 		ret = -EINVAL;
728 		goto out;
729 	}
730 
731 	set_region(bitarray, offset, num_bits, to_set, NULL);
732 	ret = 0;
733 
734 out:
735 	k_spin_unlock(&bitarray->lock, key);
736 	return ret;
737 }
738 
sys_bitarray_test_and_set_region(sys_bitarray_t * bitarray,size_t num_bits,size_t offset,bool to_set)739 int sys_bitarray_test_and_set_region(sys_bitarray_t *bitarray, size_t num_bits,
740 				     size_t offset, bool to_set)
741 {
742 	int ret;
743 	bool region_clear;
744 	struct bundle_data bd;
745 
746 	__ASSERT_NO_MSG(bitarray != NULL);
747 	__ASSERT_NO_MSG(bitarray->num_bits > 0);
748 
749 	size_t off_end = offset + num_bits - 1;
750 	k_spinlock_key_t key = k_spin_lock(&bitarray->lock);
751 
752 
753 	if ((num_bits == 0)
754 	    || (num_bits > bitarray->num_bits)
755 	    || (offset >= bitarray->num_bits)
756 	    || (off_end >= bitarray->num_bits)) {
757 		ret = -EINVAL;
758 		goto out;
759 	}
760 
761 	region_clear = match_region(bitarray, offset, num_bits, !to_set, &bd, NULL);
762 	if (region_clear) {
763 		set_region(bitarray, offset, num_bits, to_set, &bd);
764 		ret = 0;
765 	} else {
766 		ret = -EEXIST;
767 	}
768 
769 out:
770 	k_spin_unlock(&bitarray->lock, key);
771 	return ret;
772 }
773 
sys_bitarray_set_region(sys_bitarray_t * bitarray,size_t num_bits,size_t offset)774 int sys_bitarray_set_region(sys_bitarray_t *bitarray, size_t num_bits,
775 			    size_t offset)
776 {
777 	return set_clear_region(bitarray, num_bits, offset, true);
778 }
779 
sys_bitarray_clear_region(sys_bitarray_t * bitarray,size_t num_bits,size_t offset)780 int sys_bitarray_clear_region(sys_bitarray_t *bitarray, size_t num_bits,
781 			      size_t offset)
782 {
783 	return set_clear_region(bitarray, num_bits, offset, false);
784 }
785