Commit 752d3c52 authored by Lucas Romero's avatar Lucas Romero Committed by Anas Nashif
Browse files

lib: bitarray: add method to xor two bitarrays



This is part one of several changes to add more methods to the bitarray api
so that it can be used for broader usecases, specifically LoRaWAN forward
error correction.

Signed-off-by: default avatarLucas Romero <luqasn@gmail.com>
parent c5e56868
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -168,6 +168,22 @@ int sys_bitarray_test_and_clear_bit(sys_bitarray_t *bitarray, size_t bit, int *p
int sys_bitarray_alloc(sys_bitarray_t *bitarray, size_t num_bits,
		       size_t *offset);

/*
 * Calculates the bit-wise XOR of two bitarrays in a region.
 * The result is stored in the first bitarray passed in (@p dst).
 * Both bitarrays must be of the same size.
 *
 * @param dst      Bitarray struct
 * @param other    Bitarray struct
 * @param num_bits Number of bits in the region, must be larger than 0
 * @param offset   Starting bit location
 *
 * @retval 0       Operation successful
 * @retval -EINVAL Invalid argument (e.g. out-of-bounds access, mismatching bitarrays, trying to xor
 * 0 bits, etc.)
 */
int sys_bitarray_xor(sys_bitarray_t *dst, sys_bitarray_t *other, size_t num_bits, size_t offset);

/**
 * Count bits set in a bit array region
 *
+57 −0
Original line number Diff line number Diff line
@@ -259,6 +259,63 @@ out:
	return ret;
}

int sys_bitarray_xor(sys_bitarray_t *dst, sys_bitarray_t *other, size_t num_bits, size_t offset)
{
	k_spinlock_key_t key_dst, key_other;
	int ret;
	size_t idx;
	struct bundle_data bd;

	key_dst = k_spin_lock(&dst->lock);
	key_other = k_spin_lock(&other->lock);

	__ASSERT_NO_MSG(dst != NULL);
	__ASSERT_NO_MSG(dst->num_bits > 0);
	__ASSERT_NO_MSG(other != NULL);
	__ASSERT_NO_MSG(other->num_bits > 0);

	if (dst->num_bits != other->num_bits) {
		ret = -EINVAL;
		goto out;
	}

	if (num_bits == 0 || offset + num_bits > dst->num_bits) {
		ret = -EINVAL;
		goto out;
	}

	setup_bundle_data(other, &bd, offset, num_bits);

	if (bd.sidx == bd.eidx) {
		/* Start/end at same bundle */
		dst->bundles[bd.sidx] =
			((other->bundles[bd.sidx] ^ dst->bundles[bd.sidx]) & bd.smask) |
			(dst->bundles[bd.sidx] & ~bd.smask);
	} else {
		/* Start/end at different bundle.
		 * So xor the bits in start and end bundles according to their bitmasks
		 * separately. For in-between bundles,
		 * xor all bits.
		 */
		dst->bundles[bd.sidx] =
			((other->bundles[bd.sidx] ^ dst->bundles[bd.sidx]) & bd.smask) |
			(dst->bundles[bd.sidx] & ~bd.smask);
		dst->bundles[bd.eidx] =
			((other->bundles[bd.eidx] ^ dst->bundles[bd.eidx]) & bd.emask) |
			(dst->bundles[bd.eidx] & ~bd.emask);
		for (idx = bd.sidx + 1; idx < bd.eidx; idx++) {
			dst->bundles[idx] ^= other->bundles[idx];
		}
	}

	ret = 0;

out:
	k_spin_unlock(&other->lock, key_other);
	k_spin_unlock(&dst->lock, key_dst);
	return ret;
}

int sys_bitarray_set_bit(sys_bitarray_t *bitarray, size_t bit)
{
	k_spinlock_key_t key;
+143 −0
Original line number Diff line number Diff line
@@ -617,6 +617,149 @@ ZTEST(bitarray, test_bitarray_popcount_region)
		      ret);
}

ZTEST(bitarray, test_bitarray_xor)
{
	int ret;

	/* Bitarrays have embedded spinlocks and can't on the stack. */
	if (IS_ENABLED(CONFIG_KERNEL_COHERENCE)) {
		ztest_test_skip();
	}

	SYS_BITARRAY_DEFINE(ba, 128);
	SYS_BITARRAY_DEFINE(bb, 128);
	SYS_BITARRAY_DEFINE(bc, 129);

	printk("Testing bit array region xor spanning single bundle\n");

	/* Pre-populate the bits */
	ba.bundles[0] = 0x80001001;
	ba.bundles[1] = 0x10000008;
	ba.bundles[2] = 0xFFFFFFFF;
	ba.bundles[3] = 0x00000000;

	bb.bundles[0] = 0x80010001;
	bb.bundles[1] = 0x10000008;
	bb.bundles[2] = 0xFFFFFFFF;
	bb.bundles[3] = 0x00000000;

	ret = sys_bitarray_xor(&ba, &bb, 32, 0);
	zassert_equal(ret, 0, "sys_bitarray_xor() returned unexpected value: %d", ret);
	zassert_equal(ba.bundles[0], 0x00011000, "sys_bitarray_xor() result unexpected: %x",
		      ba.bundles[0]);
	zassert_equal(bb.bundles[0], 0x80010001, "sys_bitarray_xor() result unexpected: %x",
		      bb.bundles[0]);

	zassert_equal(ba.bundles[1], 0x10000008, "sys_bitarray_xor() result unexpected: %x",
		      ba.bundles[1]);
	zassert_equal(bb.bundles[1], 0x10000008, "sys_bitarray_xor() result unexpected: %x",
		      bb.bundles[1]);

	zassert_equal(ba.bundles[2], 0xFFFFFFFF, "sys_bitarray_xor() result unexpected: %x",
		      ba.bundles[2]);
	zassert_equal(bb.bundles[2], 0xFFFFFFFF, "sys_bitarray_xor() result unexpected: %x",
		      bb.bundles[2]);

	zassert_equal(ba.bundles[3], 0x00000000, "sys_bitarray_xor() result unexpected: %x",
		      ba.bundles[3]);
	zassert_equal(bb.bundles[3], 0x00000000, "sys_bitarray_xor() result unexpected: %x",
		      bb.bundles[3]);

	/* Pre-populate the bits */
	ba.bundles[0] = 0x80001001;
	ba.bundles[1] = 0x10000008;
	ba.bundles[2] = 0xFFFFFFFF;
	ba.bundles[3] = 0x00000000;

	bb.bundles[0] = 0x80010001;
	bb.bundles[1] = 0x10000008;
	bb.bundles[2] = 0xFFFFFFFF;
	bb.bundles[3] = 0x00000000;

	ret = sys_bitarray_xor(&ba, &bb, 16, 0);
	zassert_equal(ret, 0, "sys_bitarray_xor() returned unexpected value: %d", ret);
	zassert_equal(ba.bundles[0], 0x80001000, "sys_bitarray_xor() result unexpected: %x",
		      ba.bundles[0]);
	zassert_equal(bb.bundles[0], 0x80010001, "sys_bitarray_xor() result unexpected: %x",
		      bb.bundles[0]);

	/* Pre-populate the bits */
	ba.bundles[0] = 0x80001001;
	ba.bundles[1] = 0x10000008;
	ba.bundles[2] = 0xFFFFFFFF;
	ba.bundles[3] = 0x00000000;

	bb.bundles[0] = 0x80010001;
	bb.bundles[1] = 0x10000008;
	bb.bundles[2] = 0xFFFFFFFF;
	bb.bundles[3] = 0x00000000;

	ret = sys_bitarray_xor(&ba, &bb, 16, 16);
	zassert_equal(ret, 0, "sys_bitarray_xor() returned unexpected value: %d", ret);
	zassert_equal(ba.bundles[0], 0x00011001, "sys_bitarray_xor() result unexpected: %x",
		      ba.bundles[0]);
	zassert_equal(bb.bundles[0], 0x80010001, "sys_bitarray_xor() result unexpected: %x",
		      bb.bundles[0]);

	printk("Testing bit array region xor spanning multiple bundles\n");

	/* Pre-populate the bits */
	ba.bundles[0] = 0x00000000;
	ba.bundles[1] = 0xFFFFFFFF;
	ba.bundles[2] = 0xFFFFFFFF;
	ba.bundles[3] = 0xFFFFFFFF;

	bb.bundles[0] = 0x00000000;
	bb.bundles[1] = 0xFFFFFFFF;
	bb.bundles[2] = 0xFFFFFFFF;
	bb.bundles[3] = 0xFFFFFFFF;

	ret = sys_bitarray_xor(&ba, &bb, 32*3 - 2, 32 + 1);
	zassert_equal(ret, 0, "sys_bitarray_xor() returned unexpected value: %d", ret);
	zassert_equal(ba.bundles[0], 0x00000000, "sys_bitarray_xor() result unexpected: %x",
		      ba.bundles[0]);
	zassert_equal(ba.bundles[1], 0x00000001, "sys_bitarray_xor() result unexpected: %x",
		      ba.bundles[1]);
	zassert_equal(ba.bundles[2], 0x00000000, "sys_bitarray_xor() result unexpected: %x",
		      ba.bundles[2]);
	zassert_equal(ba.bundles[3], 0x80000000, "sys_bitarray_xor() result unexpected: %x",
		      ba.bundles[3]);

	printk("Testing error cases\n");
	/* Pre-populate the bits */
	ba.bundles[0] = 0x00000000;
	ba.bundles[1] = 0x00000000;
	ba.bundles[2] = 0x00000000;
	ba.bundles[3] = 0x00000000;

	bb.bundles[0] = 0x00000000;
	bb.bundles[1] = 0x00000000;
	bb.bundles[2] = 0x00000000;
	bb.bundles[3] = 0x00000000;

	bc.bundles[0] = 0x00000000;
	bc.bundles[1] = 0x00000000;
	bc.bundles[2] = 0x00000000;
	bc.bundles[3] = 0x00000000;
	bc.bundles[4] = 0x00000000;

	ret = sys_bitarray_xor(&ba, &bb, 32, 0);
	zassert_equal(ret, 0, "sys_bitarray_xor() returned unexpected value: %d", ret);
	ret = sys_bitarray_xor(&ba, &bc, 32, 0);
	zassert_equal(ret, -EINVAL, "sys_bitarray_xor() returned unexpected value: %d", ret);
	ret = sys_bitarray_xor(&bc, &ba, 32, 0);
	zassert_equal(ret, -EINVAL, "sys_bitarray_xor() returned unexpected value: %d", ret);

	ret = sys_bitarray_xor(&ba, &bb, 128, 0);
	zassert_equal(ret, 0, "sys_bitarray_xor() returned unexpected value: %d", ret);
	ret = sys_bitarray_xor(&ba, &bb, 128, 1);
	zassert_equal(ret, -EINVAL, "sys_bitarray_xor() returned unexpected value: %d", ret);
	ret = sys_bitarray_xor(&ba, &bb, 129, 0);
	zassert_equal(ret, -EINVAL, "sys_bitarray_xor() returned unexpected value: %d", ret);
	ret = sys_bitarray_xor(&ba, &bb, 0, 0);
	zassert_equal(ret, -EINVAL, "sys_bitarray_xor() returned unexpected value: %d", ret);
}

ZTEST(bitarray, test_bitarray_region_set_clear)
{
	int ret;