From fbb8aca2931e2981bd47f30f324c175ecdd419f0 Mon Sep 17 00:00:00 2001 From: rajRishi22 Date: Mon, 25 Nov 2024 12:48:10 +0530 Subject: [PATCH 01/10] Improve performance of rz_bv_copy_nbits --- librz/util/bitvector.c | 64 +++++++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/librz/util/bitvector.c b/librz/util/bitvector.c index b9ba66fec29..c5e0096f4c8 100644 --- a/librz/util/bitvector.c +++ b/librz/util/bitvector.c @@ -209,26 +209,64 @@ RZ_API ut32 rz_bv_copy(RZ_NONNULL const RzBitVector *src, RZ_NONNULL RzBitVector * \param nbit ut32, control the size of copy (in bits) * \return copied_size ut32, Actual copied size */ +#include // For CHAR_BIT + RZ_API ut32 rz_bv_copy_nbits(RZ_NONNULL const RzBitVector *src, ut32 src_start_pos, RZ_NONNULL RzBitVector *dst, ut32 dst_start_pos, ut32 nbit) { - rz_return_val_if_fail(src && dst, 0); + rz_return_val_if_fail(src && dst, 0); - ut32 max_nbit = RZ_MIN((src->len - src_start_pos), - (dst->len - dst_start_pos)); + // Determine the chunk size (word size) dynamically + const ut32 chunk_size = sizeof(unsigned long) * CHAR_BIT; // Word size in bits + ut32 max_nbit = RZ_MIN((src->len - src_start_pos), (dst->len - dst_start_pos)); - // prevent overflow - if (max_nbit < nbit) { - return 0; - } + if (max_nbit < nbit) { + return 0; + } - // normal case here - for (ut32 i = 0; i < nbit; ++i) { - bool c = rz_bv_get(src, src_start_pos + i); - rz_bv_set(dst, dst_start_pos + i, c); - } + ut32 bits_copied = 0; + + // Handle unaligned prefix + while ((src_start_pos % chunk_size != 0 || dst_start_pos % chunk_size != 0) && nbit > 0) { + bool bit = rz_bv_get(src, src_start_pos++); + rz_bv_set(dst, dst_start_pos++, bit); + --nbit; + ++bits_copied; + } + + // Process aligned chunks + while (nbit >= chunk_size) { + // Get chunks from the source and destination + unsigned long src_chunk = rz_bv_get_chunk(src, src_start_pos / chunk_size); + unsigned long dst_chunk = rz_bv_get_chunk(dst, dst_start_pos / chunk_size); - return nbit; + // Create a mask for the bits to copy + unsigned long mask = (1UL << chunk_size) - 1; + if (nbit < chunk_size) { + mask = (1UL << nbit) - 1; + } + + // Merge chunks using the optimized approach + unsigned long result = dst_chunk ^ ((dst_chunk ^ src_chunk) & mask); + rz_bv_set_chunk(dst, dst_start_pos / chunk_size, result); + + src_start_pos += chunk_size; + dst_start_pos += chunk_size; + nbit -= chunk_size; + bits_copied += chunk_size; + } + + // Handle remaining unaligned suffix bits + while (nbit > 0) { + bool bit = rz_bv_get(src, src_start_pos++); + rz_bv_set(dst, dst_start_pos++, bit); + --nbit; + ++bits_copied; + } + + return bits_copied; } + + /** * Return a new bitvector prepended with bv with n zero bits * \param bv RzBitVector, pointer to bitvector instance From 4852cf92cee224dd06db137f860c1663f118f0e0 Mon Sep 17 00:00:00 2001 From: rajRishi22 Date: Mon, 25 Nov 2024 12:53:30 +0530 Subject: [PATCH 02/10] Added limits.h as header --- librz/util/bitvector.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librz/util/bitvector.c b/librz/util/bitvector.c index c5e0096f4c8..be9240eeab9 100644 --- a/librz/util/bitvector.c +++ b/librz/util/bitvector.c @@ -4,6 +4,7 @@ #include "rz_util.h" #include #include +#include #define NELEM(N, ELEMPER) ((N + (ELEMPER)-1) / (ELEMPER)) #define BV_ELEM_SIZE 8U @@ -209,7 +210,6 @@ RZ_API ut32 rz_bv_copy(RZ_NONNULL const RzBitVector *src, RZ_NONNULL RzBitVector * \param nbit ut32, control the size of copy (in bits) * \return copied_size ut32, Actual copied size */ -#include // For CHAR_BIT RZ_API ut32 rz_bv_copy_nbits(RZ_NONNULL const RzBitVector *src, ut32 src_start_pos, RZ_NONNULL RzBitVector *dst, ut32 dst_start_pos, ut32 nbit) { rz_return_val_if_fail(src && dst, 0); From 27010283af41de1c46ac891dfeb5450a1cb84eba Mon Sep 17 00:00:00 2001 From: rajRishi22 Date: Mon, 25 Nov 2024 14:24:03 +0530 Subject: [PATCH 03/10] Improved performance of rz_bv_set_range --- librz/util/bitvector.c | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/librz/util/bitvector.c b/librz/util/bitvector.c index be9240eeab9..95dfcbc4952 100644 --- a/librz/util/bitvector.c +++ b/librz/util/bitvector.c @@ -1518,18 +1518,43 @@ RZ_API ut64 rz_bv_to_ut64(RZ_NONNULL const RzBitVector *x) { * \return return true if success, else return false */ RZ_API bool rz_bv_set_range(RZ_NONNULL RzBitVector *bv, ut32 pos_start, ut32 pos_end, bool b) { - rz_return_val_if_fail(bv, false); - if (pos_start > bv->len - 1 || pos_end > bv->len - 1) { - return false; - } + rz_return_val_if_fail(bv, false); - for (ut32 i = pos_start; i <= pos_end; ++i) { - rz_bv_set(bv, i, b); - } + if (pos_start > bv->len - 1 || pos_end > bv->len - 1 || pos_start > pos_end) { + return false; + } - return true; + // Determine the chunk size dynamically + const ut32 chunk_size = sizeof(unsigned long) * CHAR_BIT; + + // Handle unaligned prefix bits + while (pos_start < pos_end && pos_start % chunk_size != 0) { + rz_bv_set(bv, pos_start++, b); + } + + // Process aligned chunks + if (pos_start < pos_end) { + ut32 chunk_start = pos_start / chunk_size; + ut32 chunk_end = pos_end / chunk_size; + + unsigned long fill_value = b ? ~0UL : 0UL; + + for (ut32 i = chunk_start; i < chunk_end; ++i) { + rz_bv_set_chunk(bv, i, fill_value); + } + + pos_start = chunk_end * chunk_size; + } + + // Handle remaining unaligned suffix bits + while (pos_start <= pos_end) { + rz_bv_set(bv, pos_start++, b); + } + + return true; } + /** * check if bitvector's bits are all set to bit 1 * \param x RzBitVector From c84078d5c739429364e0007283b33eb08e8a80c5 Mon Sep 17 00:00:00 2001 From: rajRishi22 Date: Mon, 25 Nov 2024 20:29:57 +0530 Subject: [PATCH 04/10] Formatted code using clang-format-16 --- librz/util/bitvector.c | 105 ++++++++++++++++++++++++++++++++--------- 1 file changed, 84 insertions(+), 21 deletions(-) diff --git a/librz/util/bitvector.c b/librz/util/bitvector.c index b9ba66fec29..95dfcbc4952 100644 --- a/librz/util/bitvector.c +++ b/librz/util/bitvector.c @@ -4,6 +4,7 @@ #include "rz_util.h" #include #include +#include #define NELEM(N, ELEMPER) ((N + (ELEMPER)-1) / (ELEMPER)) #define BV_ELEM_SIZE 8U @@ -209,26 +210,63 @@ RZ_API ut32 rz_bv_copy(RZ_NONNULL const RzBitVector *src, RZ_NONNULL RzBitVector * \param nbit ut32, control the size of copy (in bits) * \return copied_size ut32, Actual copied size */ + RZ_API ut32 rz_bv_copy_nbits(RZ_NONNULL const RzBitVector *src, ut32 src_start_pos, RZ_NONNULL RzBitVector *dst, ut32 dst_start_pos, ut32 nbit) { - rz_return_val_if_fail(src && dst, 0); + rz_return_val_if_fail(src && dst, 0); - ut32 max_nbit = RZ_MIN((src->len - src_start_pos), - (dst->len - dst_start_pos)); + // Determine the chunk size (word size) dynamically + const ut32 chunk_size = sizeof(unsigned long) * CHAR_BIT; // Word size in bits + ut32 max_nbit = RZ_MIN((src->len - src_start_pos), (dst->len - dst_start_pos)); - // prevent overflow - if (max_nbit < nbit) { - return 0; - } + if (max_nbit < nbit) { + return 0; + } - // normal case here - for (ut32 i = 0; i < nbit; ++i) { - bool c = rz_bv_get(src, src_start_pos + i); - rz_bv_set(dst, dst_start_pos + i, c); - } + ut32 bits_copied = 0; + + // Handle unaligned prefix + while ((src_start_pos % chunk_size != 0 || dst_start_pos % chunk_size != 0) && nbit > 0) { + bool bit = rz_bv_get(src, src_start_pos++); + rz_bv_set(dst, dst_start_pos++, bit); + --nbit; + ++bits_copied; + } + + // Process aligned chunks + while (nbit >= chunk_size) { + // Get chunks from the source and destination + unsigned long src_chunk = rz_bv_get_chunk(src, src_start_pos / chunk_size); + unsigned long dst_chunk = rz_bv_get_chunk(dst, dst_start_pos / chunk_size); - return nbit; + // Create a mask for the bits to copy + unsigned long mask = (1UL << chunk_size) - 1; + if (nbit < chunk_size) { + mask = (1UL << nbit) - 1; + } + + // Merge chunks using the optimized approach + unsigned long result = dst_chunk ^ ((dst_chunk ^ src_chunk) & mask); + rz_bv_set_chunk(dst, dst_start_pos / chunk_size, result); + + src_start_pos += chunk_size; + dst_start_pos += chunk_size; + nbit -= chunk_size; + bits_copied += chunk_size; + } + + // Handle remaining unaligned suffix bits + while (nbit > 0) { + bool bit = rz_bv_get(src, src_start_pos++); + rz_bv_set(dst, dst_start_pos++, bit); + --nbit; + ++bits_copied; + } + + return bits_copied; } + + /** * Return a new bitvector prepended with bv with n zero bits * \param bv RzBitVector, pointer to bitvector instance @@ -1480,18 +1518,43 @@ RZ_API ut64 rz_bv_to_ut64(RZ_NONNULL const RzBitVector *x) { * \return return true if success, else return false */ RZ_API bool rz_bv_set_range(RZ_NONNULL RzBitVector *bv, ut32 pos_start, ut32 pos_end, bool b) { - rz_return_val_if_fail(bv, false); - if (pos_start > bv->len - 1 || pos_end > bv->len - 1) { - return false; - } + rz_return_val_if_fail(bv, false); - for (ut32 i = pos_start; i <= pos_end; ++i) { - rz_bv_set(bv, i, b); - } + if (pos_start > bv->len - 1 || pos_end > bv->len - 1 || pos_start > pos_end) { + return false; + } - return true; + // Determine the chunk size dynamically + const ut32 chunk_size = sizeof(unsigned long) * CHAR_BIT; + + // Handle unaligned prefix bits + while (pos_start < pos_end && pos_start % chunk_size != 0) { + rz_bv_set(bv, pos_start++, b); + } + + // Process aligned chunks + if (pos_start < pos_end) { + ut32 chunk_start = pos_start / chunk_size; + ut32 chunk_end = pos_end / chunk_size; + + unsigned long fill_value = b ? ~0UL : 0UL; + + for (ut32 i = chunk_start; i < chunk_end; ++i) { + rz_bv_set_chunk(bv, i, fill_value); + } + + pos_start = chunk_end * chunk_size; + } + + // Handle remaining unaligned suffix bits + while (pos_start <= pos_end) { + rz_bv_set(bv, pos_start++, b); + } + + return true; } + /** * check if bitvector's bits are all set to bit 1 * \param x RzBitVector From cde2cfff27ff284de3514723ea513a9eb1b3ce23 Mon Sep 17 00:00:00 2001 From: rajRishi22 Date: Mon, 25 Nov 2024 23:34:04 +0530 Subject: [PATCH 05/10] Resolved issues from 1st review --- librz/util/bitvector.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/librz/util/bitvector.c b/librz/util/bitvector.c index 95dfcbc4952..a78bbfed1a3 100644 --- a/librz/util/bitvector.c +++ b/librz/util/bitvector.c @@ -2,12 +2,15 @@ // SPDX-License-Identifier: LGPL-3.0-only #include "rz_util.h" +#include #include #include -#include + #define NELEM(N, ELEMPER) ((N + (ELEMPER)-1) / (ELEMPER)) #define BV_ELEM_SIZE 8U +#define SIZE_OF_UNSIGNED_LONG (sizeof(unsigned long)) + // optimization for reversing 8 bits which uses 32 bits // https://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits @@ -215,22 +218,23 @@ RZ_API ut32 rz_bv_copy_nbits(RZ_NONNULL const RzBitVector *src, ut32 src_start_p rz_return_val_if_fail(src && dst, 0); // Determine the chunk size (word size) dynamically - const ut32 chunk_size = sizeof(unsigned long) * CHAR_BIT; // Word size in bits + const ut32 chunk_size = SIZE_OF_UNSIGNED_LONG * CHAR_BIT; // Word size in bits ut32 max_nbit = RZ_MIN((src->len - src_start_pos), (dst->len - dst_start_pos)); if (max_nbit < nbit) { return 0; } - ut32 bits_copied = 0; + ut32 nbit_original = nbit; // Handle unaligned prefix - while ((src_start_pos % chunk_size != 0 || dst_start_pos % chunk_size != 0) && nbit > 0) { + if(src_start_pos % chunk_size != 0 || dst_start_pos % chunk_size != 0){ + while (nbit > 0) { bool bit = rz_bv_get(src, src_start_pos++); rz_bv_set(dst, dst_start_pos++, bit); --nbit; - ++bits_copied; } + } // Process aligned chunks while (nbit >= chunk_size) { @@ -239,7 +243,7 @@ RZ_API ut32 rz_bv_copy_nbits(RZ_NONNULL const RzBitVector *src, ut32 src_start_p unsigned long dst_chunk = rz_bv_get_chunk(dst, dst_start_pos / chunk_size); // Create a mask for the bits to copy - unsigned long mask = (1UL << chunk_size) - 1; + unsigned long mask = UINT32_MAX; if (nbit < chunk_size) { mask = (1UL << nbit) - 1; } @@ -251,7 +255,6 @@ RZ_API ut32 rz_bv_copy_nbits(RZ_NONNULL const RzBitVector *src, ut32 src_start_p src_start_pos += chunk_size; dst_start_pos += chunk_size; nbit -= chunk_size; - bits_copied += chunk_size; } // Handle remaining unaligned suffix bits @@ -259,10 +262,9 @@ RZ_API ut32 rz_bv_copy_nbits(RZ_NONNULL const RzBitVector *src, ut32 src_start_p bool bit = rz_bv_get(src, src_start_pos++); rz_bv_set(dst, dst_start_pos++, bit); --nbit; - ++bits_copied; } - return bits_copied; + return nbit_original; } @@ -1525,7 +1527,7 @@ RZ_API bool rz_bv_set_range(RZ_NONNULL RzBitVector *bv, ut32 pos_start, ut32 pos } // Determine the chunk size dynamically - const ut32 chunk_size = sizeof(unsigned long) * CHAR_BIT; + const ut32 chunk_size = SIZE_OF_UNSIGNED_LONG * CHAR_BIT; // Handle unaligned prefix bits while (pos_start < pos_end && pos_start % chunk_size != 0) { From d0f3b82b56c31d3894464e74a13868d1ad18e2f4 Mon Sep 17 00:00:00 2001 From: rajRishi22 Date: Tue, 26 Nov 2024 01:06:19 +0530 Subject: [PATCH 06/10] Fixed edge cases and added reference as per review2 --- librz/util/bitvector.c | 1 - 1 file changed, 1 deletion(-) diff --git a/librz/util/bitvector.c b/librz/util/bitvector.c index a78bbfed1a3..d419ef788c8 100644 --- a/librz/util/bitvector.c +++ b/librz/util/bitvector.c @@ -263,7 +263,6 @@ RZ_API ut32 rz_bv_copy_nbits(RZ_NONNULL const RzBitVector *src, ut32 src_start_p rz_bv_set(dst, dst_start_pos++, bit); --nbit; } - return nbit_original; } From ff043dd45518aa764d465af03a477cec9390e001 Mon Sep 17 00:00:00 2001 From: rajRishi22 Date: Tue, 26 Nov 2024 01:17:26 +0530 Subject: [PATCH 07/10] Added testcases for rz_bv_copy function --- librz/util/bitvector.c | 137 ++++++++++++++++++------------------- test/unit/test_bitvector.c | 38 +++++++--- 2 files changed, 96 insertions(+), 79 deletions(-) diff --git a/librz/util/bitvector.c b/librz/util/bitvector.c index d419ef788c8..7e2b0bb334e 100644 --- a/librz/util/bitvector.c +++ b/librz/util/bitvector.c @@ -6,11 +6,9 @@ #include #include - #define NELEM(N, ELEMPER) ((N + (ELEMPER)-1) / (ELEMPER)) #define BV_ELEM_SIZE 8U -#define SIZE_OF_UNSIGNED_LONG (sizeof(unsigned long)) - +#define RZ_BV_CHUNK_SIZE (sizeof(unsigned long) * CHAR_BIT) // optimization for reversing 8 bits which uses 32 bits // https://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits @@ -141,7 +139,6 @@ RZ_API RZ_OWN char *rz_bv_as_hex_string(RZ_NONNULL const RzBitVector *bv, bool p if (!str) { return NULL; } - str[0] = '0'; str[1] = 'x'; ut32 j = 2; @@ -215,59 +212,60 @@ RZ_API ut32 rz_bv_copy(RZ_NONNULL const RzBitVector *src, RZ_NONNULL RzBitVector */ RZ_API ut32 rz_bv_copy_nbits(RZ_NONNULL const RzBitVector *src, ut32 src_start_pos, RZ_NONNULL RzBitVector *dst, ut32 dst_start_pos, ut32 nbit) { - rz_return_val_if_fail(src && dst, 0); + rz_return_val_if_fail(src && dst, 0); - // Determine the chunk size (word size) dynamically - const ut32 chunk_size = SIZE_OF_UNSIGNED_LONG * CHAR_BIT; // Word size in bits - ut32 max_nbit = RZ_MIN((src->len - src_start_pos), (dst->len - dst_start_pos)); + // Determine the chunk size (word size) dynamically + const ut32 RZ_BV_CHUNK_SIZE = SIZE_OF_UNSIGNED_LONG * CHAR_BIT; // Word size in bits + ut32 max_nbit = RZ_MIN((src->len - src_start_pos), (dst->len - dst_start_pos)); - if (max_nbit < nbit) { - return 0; - } + if (max_nbit < nbit) { + return 0; + } - ut32 nbit_original = nbit; + ut32 nbit_original = nbit; - // Handle unaligned prefix - if(src_start_pos % chunk_size != 0 || dst_start_pos % chunk_size != 0){ - while (nbit > 0) { - bool bit = rz_bv_get(src, src_start_pos++); - rz_bv_set(dst, dst_start_pos++, bit); - --nbit; - } + // Handle unaligned prefix + if (src_start_pos % RZ_BV_CHUNK_SIZE != 0 || dst_start_pos % RZ_BV_CHUNK_SIZE != 0) { + while (nbit > 0) { + bool bit = rz_bv_get(src, src_start_pos++); + rz_bv_set(dst, dst_start_pos++, bit); + --nbit; + } } - // Process aligned chunks - while (nbit >= chunk_size) { - // Get chunks from the source and destination - unsigned long src_chunk = rz_bv_get_chunk(src, src_start_pos / chunk_size); - unsigned long dst_chunk = rz_bv_get_chunk(dst, dst_start_pos / chunk_size); + // Process aligned chunks + while (nbit >= RZ_BV_CHUNK_SIZE) { + // Get chunks from the source and destination + unsigned long src_chunk = rz_bv_get_chunk(src, src_start_pos / RZ_BV_CHUNK_SIZE); + unsigned long dst_chunk = rz_bv_get_chunk(dst, dst_start_pos / RZ_BV_CHUNK_SIZE); - // Create a mask for the bits to copy + // Create a mask for the bits to copy unsigned long mask = UINT32_MAX; - if (nbit < chunk_size) { - mask = (1UL << nbit) - 1; - } + if (nbit < RZ_BV_CHUNK_SIZE) { + mask = (1UL << nbit) - 1; + } - // Merge chunks using the optimized approach - unsigned long result = dst_chunk ^ ((dst_chunk ^ src_chunk) & mask); - rz_bv_set_chunk(dst, dst_start_pos / chunk_size, result); + // Merge chunks using the optimized approach , reference : https://graphics.stanford.edu/~seander/bithacks.html#MaskedMerge + unsigned long result = dst_chunk ^ ((dst_chunk ^ src_chunk) & mask); + rz_bv_set_chunk(dst, dst_start_pos / RZ_BV_CHUNK_SIZE, result); - src_start_pos += chunk_size; - dst_start_pos += chunk_size; - nbit -= chunk_size; - } + src_start_pos += RZ_BV_CHUNK_SIZE; + dst_start_pos += RZ_BV_CHUNK_SIZE; + if (nbit >= RV_BV_CHUNK_SIZE) + nbit -= RZ_BV_CHUNK_SIZE; + else + break; + } - // Handle remaining unaligned suffix bits - while (nbit > 0) { - bool bit = rz_bv_get(src, src_start_pos++); - rz_bv_set(dst, dst_start_pos++, bit); - --nbit; - } - return nbit_original; + // Handle remaining unaligned suffix bits + while (nbit > 0) { + bool bit = rz_bv_get(src, src_start_pos++); + rz_bv_set(dst, dst_start_pos++, bit); + --nbit; + } + return nbit_original; } - - /** * Return a new bitvector prepended with bv with n zero bits * \param bv RzBitVector, pointer to bitvector instance @@ -1519,43 +1517,42 @@ RZ_API ut64 rz_bv_to_ut64(RZ_NONNULL const RzBitVector *x) { * \return return true if success, else return false */ RZ_API bool rz_bv_set_range(RZ_NONNULL RzBitVector *bv, ut32 pos_start, ut32 pos_end, bool b) { - rz_return_val_if_fail(bv, false); + rz_return_val_if_fail(bv, false); - if (pos_start > bv->len - 1 || pos_end > bv->len - 1 || pos_start > pos_end) { - return false; - } + if (pos_start > bv->len - 1 || pos_end > bv->len - 1 || pos_start > pos_end) { + return false; + } - // Determine the chunk size dynamically - const ut32 chunk_size = SIZE_OF_UNSIGNED_LONG * CHAR_BIT; + // Determine the chunk size dynamically + const ut32 RZ_BV_CHUNK_SIZE = SIZE_OF_UNSIGNED_LONG * CHAR_BIT; - // Handle unaligned prefix bits - while (pos_start < pos_end && pos_start % chunk_size != 0) { - rz_bv_set(bv, pos_start++, b); - } + // Handle unaligned prefix bits + while (pos_start < pos_end && pos_start % RZ_BV_CHUNK_SIZE != 0) { + rz_bv_set(bv, pos_start++, b); + } - // Process aligned chunks - if (pos_start < pos_end) { - ut32 chunk_start = pos_start / chunk_size; - ut32 chunk_end = pos_end / chunk_size; + // Process aligned chunks + if (pos_start < pos_end) { + ut32 chunk_start = pos_start / RZ_BV_CHUNK_SIZE; + ut32 chunk_end = pos_end / RZ_BV_CHUNK_SIZE; - unsigned long fill_value = b ? ~0UL : 0UL; + unsigned long fill_value = b ? ~0UL : 0UL; - for (ut32 i = chunk_start; i < chunk_end; ++i) { - rz_bv_set_chunk(bv, i, fill_value); - } + for (ut32 i = chunk_start; i < chunk_end; ++i) { + rz_bv_set_chunk(bv, i, fill_value); + } - pos_start = chunk_end * chunk_size; - } + pos_start = chunk_end * RZ_BV_CHUNK_SIZE; + } - // Handle remaining unaligned suffix bits - while (pos_start <= pos_end) { - rz_bv_set(bv, pos_start++, b); - } + // Handle remaining unaligned suffix bits + while (pos_start <= pos_end) { + rz_bv_set(bv, pos_start++, b); + } - return true; + return true; } - /** * check if bitvector's bits are all set to bit 1 * \param x RzBitVector diff --git a/test/unit/test_bitvector.c b/test/unit/test_bitvector.c index 57b916fd044..a2ca0b750f0 100644 --- a/test/unit/test_bitvector.c +++ b/test/unit/test_bitvector.c @@ -1123,23 +1123,43 @@ bool test_rz_bv_copy_nbits(void) { rz_bv_set(src, src->len - 1, true); rz_bv_set(src, src->len - 3, true); - /// copy part of bv to a new one with the same size + // Test 1: Copy part of src to a smaller destination RzBitVector *small = rz_bv_new(part_sz); actual_copy = rz_bv_copy_nbits(src, 0, small, 0, part_sz); - mu_assert_eq(actual_copy, part_sz, "copy part_sz to normal"); - mu_assert_streq_free(rz_bv_as_string(small), "11111111", "copy nbits small bv"); + mu_assert_eq(actual_copy, part_sz, "copy part_sz to small"); + mu_assert_streq_free(rz_bv_as_string(small), "11111111", "copy nbits to small bv"); - /// copy part of bv to a new one which has more spaces + // Test 2: Copy part of src to a destination of the same size RzBitVector *normal = rz_bv_new(size); actual_copy = rz_bv_copy_nbits(src, 0, normal, 0, part_sz); - mu_assert_eq(actual_copy, part_sz, "copy part_sz bits to normal"); - mu_assert_streq_free(rz_bv_as_string(normal), "00000000000011111111", "copy nbits normal length bv"); + mu_assert_eq(actual_copy, part_sz, "copy part_sz to normal"); + mu_assert_streq_free(rz_bv_as_string(normal), "00000000000011111111", "copy nbits to normal bv"); - /// copy part of bv to the medium + // Test 3: Copy src to a destination with offset RzBitVector *res = rz_bv_new(size); actual_copy = rz_bv_copy_nbits(src, 0, res, 8, part_sz); - mu_assert_eq(actual_copy, part_sz, "copy part_sz bits to medium"); - mu_assert_streq_free(rz_bv_as_string(res), "00001111111100000000", "copy nbits to medium"); + mu_assert_eq(actual_copy, part_sz, "copy part_sz to medium offset"); + mu_assert_streq_free(rz_bv_as_string(res), "00001111111100000000", "copy nbits with offset"); + + // Test 4: Copy non-zero bits from src to dst + RzBitVector *a = rz_bv_new_from_ut64(32, 0x12345678); + RzBitVector *b = rz_bv_new_from_ut64(32, 0x1986); + actual_copy = rz_bv_copy_nbits(b, 0, a, a->len - 11, 11); + mu_assert_eq(actual_copy, 11, "copy non-zero 11 bits"); + mu_assert_streq_free(rz_bv_as_hex_string(a, false), "0x30d45678", "non-zero bit copy"); + + // Test 5: Copy fails when nbit exceeds the range + RzBitVector *too_small = rz_bv_new(part_sz); + actual_copy = rz_bv_copy_nbits(src, 0, too_small, 0, part_sz + 2); + mu_assert_eq(actual_copy, 0, "copy overflow fails"); + mu_assert_true(rz_bv_is_zero_vector(too_small), "copy nothing on overflow"); + + // Additional Test 6: Copy with unaligned start positions + RzBitVector *unaligned_src = rz_bv_new_from_ut64(64, 0xFFFFFFFFFFFFFFFF); + RzBitVector *unaligned_dst = rz_bv_new(64); + actual_copy = rz_bv_copy_nbits(unaligned_src, 3, unaligned_dst, 5, 20); + mu_assert_eq(actual_copy, 20, "unaligned copy works"); + mu_assert_streq_free(rz_bv_as_string(unaligned_dst), "00000011111111111111110000000000", "unaligned copy result"); /// copy non-zero, copy last 11 bits of `b` to the head of `a` /// dst : a = 0001 0010 0011 ... From 79204585bb4a5c6d84740e6ed0a8abda9aa2cdcd Mon Sep 17 00:00:00 2001 From: rajRishi22 Date: Tue, 26 Nov 2024 01:40:43 +0530 Subject: [PATCH 08/10] Fixed undefined variables --- librz/util/bitvector.c | 1 + 1 file changed, 1 insertion(+) diff --git a/librz/util/bitvector.c b/librz/util/bitvector.c index 7e2b0bb334e..3ce09543114 100644 --- a/librz/util/bitvector.c +++ b/librz/util/bitvector.c @@ -9,6 +9,7 @@ #define NELEM(N, ELEMPER) ((N + (ELEMPER)-1) / (ELEMPER)) #define BV_ELEM_SIZE 8U #define RZ_BV_CHUNK_SIZE (sizeof(unsigned long) * CHAR_BIT) +#define SIZE_OF_UNSIGNED_LONG sizeof(unsigned long) // optimization for reversing 8 bits which uses 32 bits // https://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits From 4becce15deebf399e83e4ef439cbd88e0fc5efde Mon Sep 17 00:00:00 2001 From: rajRishi22 Date: Tue, 26 Nov 2024 01:56:14 +0530 Subject: [PATCH 09/10] Reverted back to old test cases --- test/unit/test_bitvector.c | 38 +++++++++----------------------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/test/unit/test_bitvector.c b/test/unit/test_bitvector.c index a2ca0b750f0..57b916fd044 100644 --- a/test/unit/test_bitvector.c +++ b/test/unit/test_bitvector.c @@ -1123,43 +1123,23 @@ bool test_rz_bv_copy_nbits(void) { rz_bv_set(src, src->len - 1, true); rz_bv_set(src, src->len - 3, true); - // Test 1: Copy part of src to a smaller destination + /// copy part of bv to a new one with the same size RzBitVector *small = rz_bv_new(part_sz); actual_copy = rz_bv_copy_nbits(src, 0, small, 0, part_sz); - mu_assert_eq(actual_copy, part_sz, "copy part_sz to small"); - mu_assert_streq_free(rz_bv_as_string(small), "11111111", "copy nbits to small bv"); + mu_assert_eq(actual_copy, part_sz, "copy part_sz to normal"); + mu_assert_streq_free(rz_bv_as_string(small), "11111111", "copy nbits small bv"); - // Test 2: Copy part of src to a destination of the same size + /// copy part of bv to a new one which has more spaces RzBitVector *normal = rz_bv_new(size); actual_copy = rz_bv_copy_nbits(src, 0, normal, 0, part_sz); - mu_assert_eq(actual_copy, part_sz, "copy part_sz to normal"); - mu_assert_streq_free(rz_bv_as_string(normal), "00000000000011111111", "copy nbits to normal bv"); + mu_assert_eq(actual_copy, part_sz, "copy part_sz bits to normal"); + mu_assert_streq_free(rz_bv_as_string(normal), "00000000000011111111", "copy nbits normal length bv"); - // Test 3: Copy src to a destination with offset + /// copy part of bv to the medium RzBitVector *res = rz_bv_new(size); actual_copy = rz_bv_copy_nbits(src, 0, res, 8, part_sz); - mu_assert_eq(actual_copy, part_sz, "copy part_sz to medium offset"); - mu_assert_streq_free(rz_bv_as_string(res), "00001111111100000000", "copy nbits with offset"); - - // Test 4: Copy non-zero bits from src to dst - RzBitVector *a = rz_bv_new_from_ut64(32, 0x12345678); - RzBitVector *b = rz_bv_new_from_ut64(32, 0x1986); - actual_copy = rz_bv_copy_nbits(b, 0, a, a->len - 11, 11); - mu_assert_eq(actual_copy, 11, "copy non-zero 11 bits"); - mu_assert_streq_free(rz_bv_as_hex_string(a, false), "0x30d45678", "non-zero bit copy"); - - // Test 5: Copy fails when nbit exceeds the range - RzBitVector *too_small = rz_bv_new(part_sz); - actual_copy = rz_bv_copy_nbits(src, 0, too_small, 0, part_sz + 2); - mu_assert_eq(actual_copy, 0, "copy overflow fails"); - mu_assert_true(rz_bv_is_zero_vector(too_small), "copy nothing on overflow"); - - // Additional Test 6: Copy with unaligned start positions - RzBitVector *unaligned_src = rz_bv_new_from_ut64(64, 0xFFFFFFFFFFFFFFFF); - RzBitVector *unaligned_dst = rz_bv_new(64); - actual_copy = rz_bv_copy_nbits(unaligned_src, 3, unaligned_dst, 5, 20); - mu_assert_eq(actual_copy, 20, "unaligned copy works"); - mu_assert_streq_free(rz_bv_as_string(unaligned_dst), "00000011111111111111110000000000", "unaligned copy result"); + mu_assert_eq(actual_copy, part_sz, "copy part_sz bits to medium"); + mu_assert_streq_free(rz_bv_as_string(res), "00001111111100000000", "copy nbits to medium"); /// copy non-zero, copy last 11 bits of `b` to the head of `a` /// dst : a = 0001 0010 0011 ... From 9270d47c9858b4ba5f3a439b129a31d69311af7c Mon Sep 17 00:00:00 2001 From: rajRishi22 Date: Tue, 26 Nov 2024 13:24:14 +0530 Subject: [PATCH 10/10] Added functions rv_bv_set_chunk and rv_bv_get_chunk and changed unsigned long to ut32 --- librz/util/bitvector.c | 97 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 85 insertions(+), 12 deletions(-) diff --git a/librz/util/bitvector.c b/librz/util/bitvector.c index 3ce09543114..6cae8ff7b33 100644 --- a/librz/util/bitvector.c +++ b/librz/util/bitvector.c @@ -8,8 +8,8 @@ #define NELEM(N, ELEMPER) ((N + (ELEMPER)-1) / (ELEMPER)) #define BV_ELEM_SIZE 8U -#define RZ_BV_CHUNK_SIZE (sizeof(unsigned long) * CHAR_BIT) -#define SIZE_OF_UNSIGNED_LONG sizeof(unsigned long) +#define RZ_BV_CHUNK_SIZE (sizeof(ut32) * CHAR_BIT) +#define SIZE_OF_UT32 sizeof(ut32) // optimization for reversing 8 bits which uses 32 bits // https://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits @@ -202,6 +202,79 @@ RZ_API ut32 rz_bv_copy(RZ_NONNULL const RzBitVector *src, RZ_NONNULL RzBitVector return dst->_elem_len; } +/** + * Get a 32-bit chunk from the specified position in the bit vector. + * \param bv RzBitVector, the bit vector from which to extract the chunk + * \param chunk_idx ut32, the index of the chunk to retrieve + * \return chunk ut32, the extracted 32-bit chunk + */ +RZ_API ut32 rz_bv_get_chunk(const RzBitVector *bv, ut32 chunk_idx) { + rz_return_val_if_fail(bv, 0); // Ensure the bit vector is not NULL + + // Calculate the starting position for the chunk + ut32 chunk_start_pos = chunk_idx * RZ_BV_CHUNK_SIZE; + ut32 word_idx = chunk_start_pos / 32; // Identify the starting word for the chunk + + ut32 chunk = 0; + ut32 bit_offset = chunk_start_pos % 32; // Offset within the word + + // Extract the 32-bit chunk, considering the word boundary + if (bit_offset == 0) { + // The chunk is aligned to a 32-bit boundary + chunk = bv->data[word_idx]; + } else { + // The chunk spans across two 32-bit words + ut32 first_word = bv->data[word_idx]; + ut32 second_word = bv->data[word_idx + 1]; + + // Shift the first word and mask the necessary bits + chunk = first_word >> bit_offset; + + // Mask the remaining bits from the second word + chunk |= (second_word << (32 - bit_offset)); + } + + return chunk; // Return the extracted 32-bit chunk +} + +/** + * Set a 32-bit chunk at the specified position in the bit vector. + * \param bv RzBitVector, the bit vector in which to set the chunk + * \param chunk_idx ut32, the index of the chunk to set + * \param chunk ut32, the 32-bit chunk to set + */ +RZ_API void rz_bv_set_chunk(RzBitVector *bv, ut32 chunk_idx, ut32 chunk) { + rz_return_if_fail(bv); // Ensure the bit vector is not NULL + + // Calculate the starting position for the chunk + ut32 chunk_start_pos = chunk_idx * RZ_BV_CHUNK_SIZE; + ut32 word_idx = chunk_start_pos / 32; // Identify the starting word for the chunk + + ut32 bit_offset = chunk_start_pos % 32; // Offset within the word + + // Set the 32-bit chunk, considering the word boundary + if (bit_offset == 0) { + // The chunk is aligned to a 32-bit boundary + bv->data[word_idx] = chunk; + } else { + // The chunk spans across two 32-bit words + ut32 first_word = bv->data[word_idx]; + ut32 second_word = bv->data[word_idx + 1]; + + // Clear the bits in the current chunk positions + first_word &= ~(0xFFFFFFFF >> bit_offset); // Clear the upper bits + second_word &= (0xFFFFFFFF >> (32 - bit_offset)); // Clear the lower bits + + // Combine the chunk into the words + first_word |= (chunk << bit_offset); + second_word |= (chunk >> (32 - bit_offset)); + + // Write the words back to the bit vector + bv->data[word_idx] = first_word; + bv->data[word_idx + 1] = second_word; + } +} + /** * Copy n bits from start position of source to start position of dest, return num of copied bits * \param src RzBitVector, data source @@ -216,7 +289,7 @@ RZ_API ut32 rz_bv_copy_nbits(RZ_NONNULL const RzBitVector *src, ut32 src_start_p rz_return_val_if_fail(src && dst, 0); // Determine the chunk size (word size) dynamically - const ut32 RZ_BV_CHUNK_SIZE = SIZE_OF_UNSIGNED_LONG * CHAR_BIT; // Word size in bits + const ut32 RZ_BV_CHUNK_SIZE = SIZE_OF_UT32 * CHAR_BIT; // Word size in bits ut32 max_nbit = RZ_MIN((src->len - src_start_pos), (dst->len - dst_start_pos)); if (max_nbit < nbit) { @@ -237,25 +310,25 @@ RZ_API ut32 rz_bv_copy_nbits(RZ_NONNULL const RzBitVector *src, ut32 src_start_p // Process aligned chunks while (nbit >= RZ_BV_CHUNK_SIZE) { // Get chunks from the source and destination - unsigned long src_chunk = rz_bv_get_chunk(src, src_start_pos / RZ_BV_CHUNK_SIZE); - unsigned long dst_chunk = rz_bv_get_chunk(dst, dst_start_pos / RZ_BV_CHUNK_SIZE); + ut32 src_chunk = rz_bv_get_chunk(src, src_start_pos / RZ_BV_CHUNK_SIZE); + ut32 dst_chunk = rz_bv_get_chunk(dst, dst_start_pos / RZ_BV_CHUNK_SIZE); // Create a mask for the bits to copy - unsigned long mask = UINT32_MAX; + ut32 mask = UT32_MAX; if (nbit < RZ_BV_CHUNK_SIZE) { mask = (1UL << nbit) - 1; } // Merge chunks using the optimized approach , reference : https://graphics.stanford.edu/~seander/bithacks.html#MaskedMerge - unsigned long result = dst_chunk ^ ((dst_chunk ^ src_chunk) & mask); + ut32 result = dst_chunk ^ ((dst_chunk ^ src_chunk) & mask); rz_bv_set_chunk(dst, dst_start_pos / RZ_BV_CHUNK_SIZE, result); src_start_pos += RZ_BV_CHUNK_SIZE; dst_start_pos += RZ_BV_CHUNK_SIZE; - if (nbit >= RV_BV_CHUNK_SIZE) - nbit -= RZ_BV_CHUNK_SIZE; - else + if (nbit < RV_BV_CHUNK_SIZE) { break; + } + nbit -= RZ_BV_CHUNK_SIZE; } // Handle remaining unaligned suffix bits @@ -1525,7 +1598,7 @@ RZ_API bool rz_bv_set_range(RZ_NONNULL RzBitVector *bv, ut32 pos_start, ut32 pos } // Determine the chunk size dynamically - const ut32 RZ_BV_CHUNK_SIZE = SIZE_OF_UNSIGNED_LONG * CHAR_BIT; + const ut32 RZ_BV_CHUNK_SIZE = SIZE_OF_UT32 * CHAR_BIT; // Handle unaligned prefix bits while (pos_start < pos_end && pos_start % RZ_BV_CHUNK_SIZE != 0) { @@ -1537,7 +1610,7 @@ RZ_API bool rz_bv_set_range(RZ_NONNULL RzBitVector *bv, ut32 pos_start, ut32 pos ut32 chunk_start = pos_start / RZ_BV_CHUNK_SIZE; ut32 chunk_end = pos_end / RZ_BV_CHUNK_SIZE; - unsigned long fill_value = b ? ~0UL : 0UL; + ut32 fill_value = b ? ~0UL : 0UL; for (ut32 i = chunk_start; i < chunk_end; ++i) { rz_bv_set_chunk(bv, i, fill_value);