Skip to content

Commit e8db163

Browse files
authored
librz/util: add bit counting and address alignment calculation helper. (#4886)
* Add bit counting function. * Add address padding calculating function. * Add doxygen to rz_bits_count_X() * Rename rz_bits_count -> rz_bits_count_ones
1 parent 0e22407 commit e8db163

File tree

6 files changed

+133
-0
lines changed

6 files changed

+133
-0
lines changed

librz/include/rz_util/rz_bits.h

+22
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,28 @@ extern "C" {
1111
#include <rz_util/rz_assert.h>
1212
#include <rz_types_base.h>
1313

14+
/**
15+
* \brief Count number of 1s in the given value.
16+
* Reference: https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
17+
*
18+
* \param v The value to count the set bits in.
19+
*
20+
* \return Number of set bits in \p v.
21+
*/
22+
#define DEFINE_COUNT_ONES(T) \
23+
static inline size_t rz_bits_count_ones_##T(T v) { \
24+
v = v - ((v >> 1) & (T) ~(T)0 / 3); \
25+
v = (v & (T) ~(T)0 / 15 * 3) + ((v >> 2) & (T) ~(T)0 / 15 * 3); \
26+
v = (v + (v >> 4)) & (T) ~(T)0 / 255 * 15; \
27+
size_t c = (T)(v * ((T) ~(T)0 / 255)) >> (sizeof(T) - 1) * CHAR_BIT; \
28+
return c; \
29+
}
30+
31+
DEFINE_COUNT_ONES(ut64);
32+
DEFINE_COUNT_ONES(ut32);
33+
DEFINE_COUNT_ONES(ut16);
34+
DEFINE_COUNT_ONES(ut8);
35+
1436
/**
1537
* \brief Get the number of leading zeros of a 64-bit integer in binary representation.
1638
* \param x the 64-bit integer

librz/include/rz_util/rz_mem.h

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ RZ_API const ut8 *rz_mem_mem_aligned(const ut8 *haystack, int hlen, const ut8 *n
3636
RZ_API int rz_mem_count(const ut8 **addr);
3737
RZ_API bool rz_mem_is_printable(const ut8 *a, int la);
3838
RZ_API bool rz_mem_is_zero(const ut8 *b, int l);
39+
RZ_API ut64 rz_mem_align_padding(const ut64 address, ut64 alignment);
3940

4041
#ifdef __cplusplus
4142
}

librz/util/mem.c

+28
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,34 @@ RZ_API bool rz_mem_is_zero(const ut8 *b, int l) {
286286
return true;
287287
}
288288

289+
/**
290+
* \brief Calculates the required padding to align the given \p address to the
291+
* given \p alignment.
292+
*
293+
* This is only valid for architectures with a defined alignment of n^2.
294+
*
295+
* \param address The address to calculate the required padding for.
296+
* \param alignment The required alignment. It must be a power of 2 and greater than 0.
297+
*
298+
* \return The additional padding to align \p address. If \p alignment
299+
* is equal 0 or not a power of two it returns UT64_MAX.
300+
*
301+
* Examples:
302+
*
303+
* ut64 address = 0x59d;
304+
* ut64 padding = rz_mem_align_padding(address, 4);
305+
* assert(padding == 3);
306+
* assert(address + padding == 0x5a0);
307+
*/
308+
RZ_API ut64 rz_mem_align_padding(const ut64 address, ut64 alignment) {
309+
size_t c = rz_bits_count_ones_ut64(alignment);
310+
if (c != 1) {
311+
rz_warn_if_reached();
312+
return UT64_MAX;
313+
}
314+
return (alignment - (address % alignment)) % alignment;
315+
}
316+
289317
RZ_API void rz_mem_memzero(void *dst, size_t l) {
290318
#ifdef _MSC_VER
291319
RtlSecureZeroMemory(dst, l);

test/unit/meson.build

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ if get_option('enable_tests')
2727
'bin_lines',
2828
'bin_mach0',
2929
'bitvector',
30+
'bits',
3031
'buf',
3132
'cmd',
3233
'compare',
@@ -73,6 +74,7 @@ if get_option('enable_tests')
7374
'list',
7475
'log',
7576
'lzma',
77+
'mem',
7678
'ovf',
7779
'pj',
7880
'rbtree',

test/unit/test_bits.c

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// SPDX-FileCopyrightText: 2025 Rot127 <[email protected]>
2+
// SPDX-License-Identifier: LGPL-3.0-only
3+
4+
#include <rz_util.h>
5+
#include "minunit.h"
6+
7+
bool test_rz_bits_count(void) {
8+
mu_assert_eq(rz_bits_count_ones_ut64(0xffffffffffffffff), 64, "Bit count mismatch.");
9+
mu_assert_eq(rz_bits_count_ones_ut64(0), 0, "Bit count mismatch.");
10+
mu_assert_eq(rz_bits_count_ones_ut64(1), 1, "Bit count mismatch.");
11+
mu_assert_eq(rz_bits_count_ones_ut64(0x8000000000000000), 1, "Bit count mismatch.");
12+
mu_assert_eq(rz_bits_count_ones_ut64(0x7fffffffffffffff), 63, "Bit count mismatch.");
13+
mu_assert_eq(rz_bits_count_ones_ut64(0xfffffffffffffffe), 63, "Bit count mismatch.");
14+
mu_assert_eq(rz_bits_count_ones_ut64(0xffffffffffefffff), 63, "Bit count mismatch.");
15+
mu_assert_eq(rz_bits_count_ones_ut64(0x0fffffffffffffff), 60, "Bit count mismatch.");
16+
mu_assert_eq(rz_bits_count_ones_ut64(0xf0ffffffffffffff), 60, "Bit count mismatch.");
17+
mu_assert_eq(rz_bits_count_ones_ut64(0xffffffffff0fffff), 60, "Bit count mismatch.");
18+
mu_assert_eq(rz_bits_count_ones_ut64(0xffffffff00000000), 32, "Bit count mismatch.");
19+
mu_assert_eq(rz_bits_count_ones_ut64(0x00000000ffffffff), 32, "Bit count mismatch.");
20+
mu_assert_eq(rz_bits_count_ones_ut64(0x0000010000000000), 1, "Bit count mismatch.");
21+
mu_assert_eq(rz_bits_count_ones_ut64(0x0000000000001000), 1, "Bit count mismatch.");
22+
mu_assert_eq(rz_bits_count_ones_ut64(0x0100000100001000), 3, "Bit count mismatch.");
23+
mu_assert_eq(rz_bits_count_ones_ut64(0x0400000100002000), 3, "Bit count mismatch.");
24+
mu_assert_eq(rz_bits_count_ones_ut64(0x0400008100002000), 4, "Bit count mismatch.");
25+
mu_assert_eq(rz_bits_count_ones_ut64(0x0400008100002008), 5, "Bit count mismatch.");
26+
27+
for (size_t i = 0; i <= 0xff; i++) {
28+
size_t naive_count = 0;
29+
for (size_t k = 0; k < 8; k++) {
30+
naive_count += i & (1 << k) ? 1 : 0;
31+
}
32+
mu_assert_eq(rz_bits_count_ones_ut8(i), naive_count, "Bit count mismatch.");
33+
}
34+
35+
mu_end;
36+
}
37+
38+
bool all_tests() {
39+
mu_run_test(test_rz_bits_count);
40+
41+
return tests_passed != tests_run;
42+
}
43+
44+
mu_main(all_tests)

test/unit/test_mem.c

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// SPDX-FileCopyrightText: 2025 Rot127 <[email protected]>
2+
// SPDX-License-Identifier: LGPL-3.0-only
3+
4+
#include <rz_util.h>
5+
#include "minunit.h"
6+
7+
bool test_rz_mem_align_padding(void) {
8+
mu_assert_eq(rz_mem_align_padding(0x0, 0), UT64_MAX, "Error case failed.");
9+
mu_assert_eq(rz_mem_align_padding(0x0, 3), UT64_MAX, "Error case failed.");
10+
mu_assert_eq(rz_mem_align_padding(0x0, UT64_MAX), UT64_MAX, "Error case failed.");
11+
12+
mu_assert_eq(rz_mem_align_padding(0x0, 1), 0, "Padding mismatch.");
13+
mu_assert_eq(rz_mem_align_padding(0x1, 1), 0, "Padding mismatch.");
14+
mu_assert_eq(rz_mem_align_padding(0x800000, 1), 0, "Padding mismatch.");
15+
mu_assert_eq(rz_mem_align_padding(UT64_MAX, 1), 0, "Padding mismatch.");
16+
17+
mu_assert_eq(rz_mem_align_padding(0x0, 2), 0, "Padding mismatch.");
18+
mu_assert_eq(rz_mem_align_padding(0x1, 2), 1, "Padding mismatch.");
19+
mu_assert_eq(rz_mem_align_padding(0x0, 0x8000000000000000), 0, "Padding mismatch.");
20+
mu_assert_eq(rz_mem_align_padding(0x1, 0x8000000000000000), 0x7fffffffffffffff, "Padding mismatch.");
21+
22+
mu_assert_eq(rz_mem_align_padding(0x59d, 4), 3, "Padding mismatch.");
23+
mu_assert_eq(rz_mem_align_padding(0x7fffe, 8), 2, "Padding mismatch.");
24+
mu_assert_eq(rz_mem_align_padding(0x7fffe, 0x80000), 2, "Padding mismatch.");
25+
mu_assert_eq(rz_mem_align_padding(0x7fffe, 0x800000), 0x780002, "Padding mismatch.");
26+
27+
mu_end;
28+
}
29+
30+
bool all_tests() {
31+
mu_run_test(test_rz_mem_align_padding);
32+
33+
return tests_passed != tests_run;
34+
}
35+
36+
mu_main(all_tests)

0 commit comments

Comments
 (0)