Enhance performance of rz_bv_copy_nbits (#5541)
* Add benchmark for rz_bv_copy_nbits * Improve performance for large to large and small to small bitvector copy * Fix bug with nbit=64 and simplify code * Add test for same bitvector copy * Move bit copy logic to separate function in rz_bits.h + improve comments * Support same vector copy for unaligned case * Expect non-null RzTable in bench utils and add comments * Test against reference implementation instead of hardcoded values
This commit is contained in:
parent
f18b55f9dc
commit
c186f1ec83
10 changed files with 555 additions and 1 deletions
|
|
@ -124,6 +124,36 @@ static inline int rz_bits_leading_zeros(ut64 x) {
|
|||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Copies a bit range from \p src to \p dst at specified positions
|
||||
* \param src 64-bit unsigned integer to copy bits from
|
||||
* \param src_pos bit position related to \p src
|
||||
* \param dst 64-bit unsigned integer to copy bits to
|
||||
* \param dst_pos bit position related to \p dst
|
||||
* \param size number of bits to copy (needs to be <= 64)
|
||||
* \return a new 64-bit unsigned integer with the specified bit range replaced
|
||||
*/
|
||||
static inline ut64 rz_bits_copy_ut64(ut64 src, ut8 src_pos, ut64 dst, ut8 dst_pos, ut8 size) {
|
||||
if (size >= 64) {
|
||||
return src;
|
||||
}
|
||||
|
||||
ut64 mask = ((1ull) << size) - 1;
|
||||
return (dst & ~(mask << dst_pos)) | (src >> src_pos & mask) << dst_pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Similar to rz_bits_copy_ut64() but for 8-bit unsigned integers
|
||||
*/
|
||||
static inline ut8 rz_bits_copy_ut8(ut8 src, ut8 src_pos, ut8 dst, ut8 dst_pos, ut8 size) {
|
||||
if (size >= 8) {
|
||||
return src;
|
||||
}
|
||||
|
||||
ut8 mask = ((1u) << size) - 1;
|
||||
return (dst & ~(mask << dst_pos)) | (src >> src_pos & mask) << dst_pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sign-extends a value from a specified bit-width to full width of type.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -200,6 +200,81 @@ RZ_API ut32 rz_bv_copy(RZ_NONNULL const RzBitVector *src, RZ_NONNULL RzBitVector
|
|||
return dst->_elem_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Optimized version of rz_bv_copy_nbits() for large bitvectors (more than 64 bits) with bit positions aligned to BV_ELEM_SIZE
|
||||
*/
|
||||
static ut32 rz_bv_copy_nbits_large_aligned(const RzBitVector *src, ut32 src_start_pos, RzBitVector *dst, ut32 dst_start_pos, ut32 nbit) {
|
||||
// Sanity check performed by caller
|
||||
ut8 start_bits = RZ_MIN((BV_ELEM_SIZE - dst_start_pos) % BV_ELEM_SIZE, nbit);
|
||||
ut8 trailing_bits = RZ_MIN((src_start_pos + nbit) % BV_ELEM_SIZE, nbit - start_bits);
|
||||
ut32 middle_bytes = (nbit - start_bits) / BV_ELEM_SIZE;
|
||||
ut32 src_byte = src_start_pos / BV_ELEM_SIZE;
|
||||
ut32 dst_byte = dst_start_pos / BV_ELEM_SIZE;
|
||||
|
||||
// Handle starting bits
|
||||
if (start_bits > 0) {
|
||||
ut8 src_offset = src_start_pos % BV_ELEM_SIZE;
|
||||
dst->bits.large_a[dst_byte] = rz_bits_copy_ut8(src->bits.large_a[src_byte], src_offset, dst->bits.large_a[dst_byte], src_offset, start_bits);
|
||||
src_byte++;
|
||||
dst_byte++;
|
||||
}
|
||||
|
||||
// Handle middle bytes
|
||||
if (middle_bytes > 0) {
|
||||
if (src->bits.large_a == dst->bits.large_a) {
|
||||
// Copy within the same vector
|
||||
memmove(&dst->bits.large_a[dst_byte], &src->bits.large_a[src_byte], middle_bytes);
|
||||
} else {
|
||||
memcpy(&dst->bits.large_a[dst_byte], &src->bits.large_a[src_byte], middle_bytes);
|
||||
}
|
||||
|
||||
src_byte += middle_bytes;
|
||||
dst_byte += middle_bytes;
|
||||
}
|
||||
|
||||
// Handle trailing bits
|
||||
if (trailing_bits > 0) {
|
||||
dst->bits.large_a[dst_byte] = rz_bits_copy_ut8(src->bits.large_a[src_byte], 0, dst->bits.large_a[dst_byte], 0, trailing_bits);
|
||||
}
|
||||
|
||||
return nbit;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Optimized version of rz_bv_copy_nbits() for large bitvectors (more than 64 bits) with unaligned bit positions
|
||||
*/
|
||||
static ut32 rz_bv_copy_nbits_large_nonaligned(const RzBitVector *src, ut32 src_start_pos, RzBitVector *dst, ut32 dst_start_pos, ut32 nbit) {
|
||||
// Sanity check performed by caller
|
||||
ut64 bits_remaining = nbit;
|
||||
|
||||
while (bits_remaining > 0) {
|
||||
ut32 src_offset = src_start_pos % BV_ELEM_SIZE;
|
||||
ut32 src_byte = src_start_pos / BV_ELEM_SIZE;
|
||||
ut32 dst_offset = dst_start_pos % BV_ELEM_SIZE;
|
||||
ut32 dst_byte = dst_start_pos / BV_ELEM_SIZE;
|
||||
ut8 bits_to_write = RZ_MIN(bits_remaining, BV_ELEM_SIZE - dst_offset);
|
||||
|
||||
ut16 buffer;
|
||||
if (src_byte < dst->_elem_len - 1 && src_offset + bits_to_write > BV_ELEM_SIZE) {
|
||||
// If the bit subset spans across byte boundary, then read two bytes
|
||||
buffer = src->bits.large_a[src_byte + 1] << BV_ELEM_SIZE | src->bits.large_a[src_byte];
|
||||
} else {
|
||||
// Otherwise 1 byte is enough
|
||||
buffer = src->bits.large_a[src_byte];
|
||||
}
|
||||
|
||||
// Extract bits from the buffer
|
||||
dst->bits.large_a[dst_byte] = rz_bits_copy_ut64(buffer, src_offset, dst->bits.large_a[dst_byte], dst_offset, bits_to_write);
|
||||
|
||||
// Move positions
|
||||
src_start_pos += bits_to_write;
|
||||
dst_start_pos += bits_to_write;
|
||||
bits_remaining -= bits_to_write;
|
||||
}
|
||||
|
||||
return nbit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy n bits from start position of source to start position of dest, return num of copied bits
|
||||
* \param src RzBitVector, data source
|
||||
|
|
@ -220,7 +295,33 @@ RZ_API ut32 rz_bv_copy_nbits(RZ_NONNULL const RzBitVector *src, ut32 src_start_p
|
|||
return 0;
|
||||
}
|
||||
|
||||
// normal case here
|
||||
if (src->len <= 64 && dst->len <= 64) {
|
||||
// Both src and dst are smaller than 64 bits
|
||||
dst->bits.small_u = rz_bits_copy_ut64(src->bits.small_u, src_start_pos, dst->bits.small_u, dst_start_pos, nbit);
|
||||
return nbit;
|
||||
}
|
||||
|
||||
if (src->len > 64 && dst->len > 64) {
|
||||
// Both src and dst are larger than 64 bits
|
||||
if (src_start_pos % BV_ELEM_SIZE == dst_start_pos % BV_ELEM_SIZE) {
|
||||
return rz_bv_copy_nbits_large_aligned(src, src_start_pos, dst, dst_start_pos, nbit);
|
||||
}
|
||||
|
||||
if (src->bits.large_a != dst->bits.large_a) {
|
||||
return rz_bv_copy_nbits_large_nonaligned(src, src_start_pos, dst, dst_start_pos, nbit);
|
||||
}
|
||||
|
||||
// Use a temporary bitvector for same-vector copies
|
||||
RzBitVector *temp = rz_bv_new(rz_bv_len(dst));
|
||||
rz_bv_copy(dst, temp);
|
||||
ut32 bits_copied = rz_bv_copy_nbits_large_nonaligned(src, src_start_pos, temp, dst_start_pos, nbit);
|
||||
rz_bv_copy(temp, dst);
|
||||
rz_bv_free(temp);
|
||||
return bits_copied;
|
||||
}
|
||||
|
||||
// Only one of the bitvectors is large
|
||||
// TODO: add specialized functions for large to small and small to large bit copy
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -44,5 +44,6 @@ option('install_sigdb', type: 'boolean', value: false, description: 'Downloads a
|
|||
option('debugger', type: 'boolean', value: true)
|
||||
|
||||
option('enable_tests', type: 'boolean', value: true, description: 'Build unit tests in test/unit')
|
||||
option('enable_benchmarks', type: 'boolean', value: false, description: 'Build micro-benchmarks in test/bench')
|
||||
option('enable_rz_test', type: 'boolean', value: true, description: 'Build rz-test executable for regression testing')
|
||||
option('regenerate_cmds', type: 'feature', value: 'auto', description: 'Regenerate the cmd_descs.[ch] files (requires PyYAML)')
|
||||
|
|
|
|||
100
test/bench/bench_bitvector.c
Normal file
100
test/bench/bench_bitvector.c
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
// SPDX-FileCopyrightText: 2025 Anton Angelov <anton.angelov@protonmail.com>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include "bench_utils.h"
|
||||
#include <rz_util.h>
|
||||
|
||||
/**
|
||||
* \file bench_bitvector.c
|
||||
* \brief Benchmark for `rz_bv_*` functions (currently `rz_bv_copy_nbits` only)
|
||||
*
|
||||
* Tests various copy scenarios to measure performance of optimized paths
|
||||
*/
|
||||
|
||||
static void bench_bv_copy_small_60_bit(RzTable *t_out) {
|
||||
RzBitVector *src = rz_bv_new_from_ut64(64, 0x1122334455667788ULL);
|
||||
RzBitVector *dst = rz_bv_new_from_ut64(64, 0);
|
||||
|
||||
RZ_BENCH_RUN("rz_bv_copy_nbits: small to small (60 bit)", t_out, 1000000, {
|
||||
rz_bv_copy_nbits(src, 2, dst, 2, 60);
|
||||
});
|
||||
|
||||
rz_bv_free(src);
|
||||
rz_bv_free(dst);
|
||||
}
|
||||
|
||||
static void bench_bv_copy_large_100_bit_aligned(RzTable *t_out) {
|
||||
RzBitVector *src = rz_bv_new_from_ut64(128, 0);
|
||||
RzBitVector *dst = rz_bv_new_from_ut64(128, 0);
|
||||
|
||||
for (int i = 0; i < src->len; i++) {
|
||||
rz_bv_set(src, i, i % 2 == 0 ? true : false);
|
||||
}
|
||||
|
||||
RZ_BENCH_RUN("rz_bv_copy_nbits: large to large (100 bit) aligned", t_out, 1000000, {
|
||||
rz_bv_copy_nbits(src, 0, dst, 0, 100);
|
||||
});
|
||||
|
||||
rz_bv_free(src);
|
||||
rz_bv_free(dst);
|
||||
}
|
||||
|
||||
static void bench_bv_copy_large_100_bit_unaligned(RzTable *t_out) {
|
||||
RzBitVector *src = rz_bv_new_from_ut64(128, 0);
|
||||
RzBitVector *dst = rz_bv_new_from_ut64(128, 0);
|
||||
|
||||
for (int i = 0; i < src->len; i++) {
|
||||
rz_bv_set(src, i, i % 2 == 0 ? true : false);
|
||||
}
|
||||
|
||||
RZ_BENCH_RUN("rz_bv_copy_nbits: large to large (100 bit) unaligned", t_out, 1000000, {
|
||||
rz_bv_copy_nbits(src, 2, dst, 3, 100);
|
||||
});
|
||||
|
||||
rz_bv_free(src);
|
||||
rz_bv_free(dst);
|
||||
}
|
||||
|
||||
static void bench_bv_copy_small_to_large_60_bit(RzTable *t_out) {
|
||||
RzBitVector *src = rz_bv_new_from_ut64(64, 0x1122334455667788ULL);
|
||||
RzBitVector *dst = rz_bv_new_from_ut64(128, 0);
|
||||
|
||||
RZ_BENCH_RUN("rz_bv_copy_nbits: small to large (60 bit)", t_out, 1000000, {
|
||||
rz_bv_copy_nbits(src, 2, dst, 3, 60);
|
||||
});
|
||||
|
||||
rz_bv_free(src);
|
||||
rz_bv_free(dst);
|
||||
}
|
||||
|
||||
static void bench_bv_copy_large_to_small_60_bit(RzTable *t_out) {
|
||||
RzBitVector *src = rz_bv_new_from_ut64(128, 0x1122334455667788ULL);
|
||||
RzBitVector *dst = rz_bv_new_from_ut64(64, 0);
|
||||
|
||||
RZ_BENCH_RUN("rz_bv_copy_nbits: large to small (60 bit)", t_out, 1000000, {
|
||||
rz_bv_copy_nbits(src, 2, dst, 3, 60);
|
||||
});
|
||||
|
||||
rz_bv_free(src);
|
||||
rz_bv_free(dst);
|
||||
}
|
||||
|
||||
int main(RzTable *t_out) {
|
||||
RzTable *t = rz_table_new();
|
||||
rz_table_set_columnsf(t, "snnnn", "Benchmark", "Iterations", "Total time [ms]", "Average time [us/op]", "Throughput [ops/sec]");
|
||||
|
||||
// Micro benchmarks
|
||||
bench_bv_copy_small_60_bit(t);
|
||||
bench_bv_copy_large_100_bit_aligned(t);
|
||||
bench_bv_copy_large_100_bit_unaligned(t);
|
||||
bench_bv_copy_large_to_small_60_bit(t);
|
||||
bench_bv_copy_small_to_large_60_bit(t);
|
||||
|
||||
// Print results
|
||||
const char *out = rz_table_tostring(t);
|
||||
printf("%s\n", out);
|
||||
|
||||
free(out);
|
||||
rz_table_free(t);
|
||||
return 0;
|
||||
}
|
||||
45
test/bench/bench_utils.c
Normal file
45
test/bench/bench_utils.c
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
// SPDX-FileCopyrightText: 2025 Anton Angelov <anton.angelov@protonmail.com>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include "bench_utils.h"
|
||||
#include <stdio.h>
|
||||
|
||||
/**
|
||||
* \brief Initialize benchmark context
|
||||
*/
|
||||
RZ_API void rz_bench_init(RZ_NONNULL RzBenchCtx *ctx, RZ_NONNULL const char *name, ut64 iterations) {
|
||||
rz_return_if_fail(ctx && name);
|
||||
ctx->name = name;
|
||||
ctx->iterations = iterations;
|
||||
ctx->start_time = 0;
|
||||
ctx->total_time = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Start timing a benchmark
|
||||
*/
|
||||
RZ_API void rz_bench_start(RZ_NONNULL RzBenchCtx *ctx) {
|
||||
rz_return_if_fail(ctx);
|
||||
ctx->start_time = rz_time_now_mono();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief End timing a benchmark
|
||||
*/
|
||||
RZ_API void rz_bench_end(RZ_NONNULL RzBenchCtx *ctx) {
|
||||
rz_return_if_fail(ctx);
|
||||
ctx->total_time = rz_time_now_mono() - ctx->start_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Print benchmark results
|
||||
*/
|
||||
RZ_API void rz_bench_report(RZ_NONNULL RzBenchCtx *ctx, RZ_NONNULL RzTable *t) {
|
||||
rz_return_if_fail(ctx && t);
|
||||
|
||||
double total_ms = ctx->total_time / 1000.0;
|
||||
double avg_us = ctx->iterations != 0 ? (double)ctx->total_time / ctx->iterations : 0;
|
||||
double ops_per_sec = ctx->total_time != 0 ? (ctx->iterations * 1000000.0) / ctx->total_time : 0;
|
||||
|
||||
rz_table_add_rowf(t, "sdfff", ctx->name, (int)ctx->iterations, total_ms, avg_us, ops_per_sec);
|
||||
}
|
||||
48
test/bench/bench_utils.h
Normal file
48
test/bench/bench_utils.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
// SPDX-FileCopyrightText: 2025 Anton Angelov <anton.angelov@protonmail.com>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#ifndef BENCH_UTILS_H
|
||||
#define BENCH_UTILS_H
|
||||
|
||||
#include <rz_types.h>
|
||||
#include <rz_util.h>
|
||||
|
||||
|
||||
/**
|
||||
* \brief Description of the state of a micro benchmark
|
||||
*/
|
||||
typedef struct rz_bench_ctx_t {
|
||||
const char *name; ///< name of a micro benchmark
|
||||
ut64 iterations; ///< number of iterations
|
||||
ut64 start_time; ///< start time of the benchmark in microseconds
|
||||
ut64 total_time; ///< total elapsed time of the benchmark in microseconds
|
||||
} RzBenchCtx;
|
||||
|
||||
RZ_API void rz_bench_init(RZ_NONNULL RzBenchCtx *ctx, RZ_NONNULL const char *name, ut64 iterations);
|
||||
RZ_API void rz_bench_start(RZ_NONNULL RzBenchCtx *ctx);
|
||||
RZ_API void rz_bench_end(RZ_NONNULL RzBenchCtx *ctx);
|
||||
RZ_API void rz_bench_report(RZ_NONNULL RzBenchCtx *ctx, RZ_NONNULL RzTable *t);
|
||||
|
||||
/**
|
||||
* \brief Run a benchmark with the given code block
|
||||
*
|
||||
* Example usage:
|
||||
* \code
|
||||
* RZ_BENCH_RUN("my_function", table, 1000000, {
|
||||
* my_function(data);
|
||||
* });
|
||||
* \endcode
|
||||
*/
|
||||
#define RZ_BENCH_RUN(name, table, iterations, code) \
|
||||
do { \
|
||||
RzBenchCtx ctx; \
|
||||
rz_bench_init(&ctx, name, iterations); \
|
||||
rz_bench_start(&ctx); \
|
||||
for (ut64 i = 0; i < iterations; i++) { \
|
||||
code; \
|
||||
} \
|
||||
rz_bench_end(&ctx); \
|
||||
rz_bench_report(&ctx, table); \
|
||||
} while (0)
|
||||
|
||||
#endif // BENCH_UTILS_H
|
||||
41
test/bench/meson.build
Normal file
41
test/bench/meson.build
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# SPDX-FileCopyrightText: 2025
|
||||
# SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
if get_option('enable_benchmarks')
|
||||
# Build utility library for benchmarks
|
||||
bench_utils = static_library('bench_utils',
|
||||
'bench_utils.c',
|
||||
include_directories: [platform_inc],
|
||||
dependencies: [rz_util_dep],
|
||||
)
|
||||
|
||||
bench_utils_dep = declare_dependency(
|
||||
link_with: bench_utils,
|
||||
include_directories: [include_directories('.')],
|
||||
)
|
||||
|
||||
# List of benchmarks to build
|
||||
benchmarks = [
|
||||
'bitvector',
|
||||
]
|
||||
|
||||
# Create benchmark executables
|
||||
foreach bench : benchmarks
|
||||
exe = executable('bench_@0@'.format(bench),
|
||||
'bench_@0@.c'.format(bench),
|
||||
include_directories: [platform_inc, '.'],
|
||||
dependencies: [
|
||||
bench_utils_dep,
|
||||
rz_util_dep,
|
||||
rz_core_dep,
|
||||
],
|
||||
install: false,
|
||||
implicit_include_directories: false,
|
||||
)
|
||||
|
||||
# Register with meson test system (run with: meson test --benchmark)
|
||||
benchmark(bench, exe, suite: 'bench', timeout: 120)
|
||||
endforeach
|
||||
|
||||
message('Benchmarks enabled - build with "meson compile" and run with "meson test --benchmark"')
|
||||
endif
|
||||
|
|
@ -1,2 +1,6 @@
|
|||
subdir('unit')
|
||||
subdir('integration')
|
||||
|
||||
if get_option('enable_benchmarks')
|
||||
subdir('bench')
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -59,10 +59,20 @@ bool test_rz_bits_spread(void) {
|
|||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_bits_copy(void) {
|
||||
mu_assert_eq(rz_bits_copy_ut64(0x1122334455667788, 24, 0x8877665544332211, 8, 16), 0x8877665544445511, "Incorrect bit copy");
|
||||
mu_assert_eq(rz_bits_copy_ut64(0x1122334455667788, 0, 0x0, 1, 63), 0x22446688aaccef10, "Incorrect bit copy");
|
||||
mu_assert_eq(rz_bits_copy_ut64(0x1122334455667788, 0, 0x8877665544332211, 0, 64), 0x1122334455667788, "Incorrect bit copy");
|
||||
mu_assert_eq(rz_bits_copy_ut8(0xAB, 0, 0xCD, 0, 8), 0xAB, "Incorrect bit copy");
|
||||
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool all_tests() {
|
||||
mu_run_test(test_rz_bits_count);
|
||||
mu_run_test(test_rz_bits_spread);
|
||||
mu_run_test(test_rz_bits_trailing_zero);
|
||||
mu_run_test(test_rz_bits_copy);
|
||||
|
||||
return tests_passed != tests_run;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1164,6 +1164,177 @@ bool test_rz_bv_copy_nbits(void) {
|
|||
rz_bv_free(too_small);
|
||||
rz_bv_free(a);
|
||||
rz_bv_free(b);
|
||||
|
||||
mu_end;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Reference implementation of rz_bv_copy_nbits() to test against
|
||||
*/
|
||||
static ut32 rz_bv_copy_nbits_ref(const RzBitVector *src, ut32 src_start_pos, RzBitVector *dst, ut32 dst_start_pos, ut32 nbit) {
|
||||
rz_return_val_if_fail(src && dst, 0);
|
||||
ut32 max_nbit = RZ_MIN((src->len - src_start_pos), (dst->len - dst_start_pos));
|
||||
|
||||
// prevent overflow
|
||||
if (max_nbit < nbit) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (ut32 i = 0; i < nbit; ++i) {
|
||||
rz_bv_set(dst, dst_start_pos + i, rz_bv_get(src, src_start_pos + i));
|
||||
}
|
||||
|
||||
return nbit;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Performs rz_bv_copy_nbits() with actual and reference implementation and compares results
|
||||
*/
|
||||
static const char *test_rz_bv_copy_nbits_against_ref(const RzBitVector *src, ut32 src_pos, RzBitVector *dst, ut32 dst_pos, ut32 nbit) {
|
||||
RzBitVector *src_copy = rz_bv_new(rz_bv_len(src));
|
||||
RzBitVector *dst_copy = rz_bv_new(rz_bv_len(dst));
|
||||
RzBitVector *dst_copy_ref = rz_bv_new(rz_bv_len(dst));
|
||||
const char *error = NULL;
|
||||
|
||||
rz_bv_copy(src, src_copy);
|
||||
rz_bv_copy(dst, dst_copy);
|
||||
rz_bv_copy(dst, dst_copy_ref);
|
||||
|
||||
if (rz_bv_copy_nbits(src_copy, src_pos, dst_copy, dst_pos, nbit) != nbit) {
|
||||
error = "rz_bv_copy_nbits() incorrect return";
|
||||
goto finally;
|
||||
}
|
||||
|
||||
if (rz_bv_copy_nbits_ref(src_copy, src_pos, dst_copy_ref, dst_pos, nbit) != nbit) {
|
||||
error = "rz_bv_copy_nbits_ref() incorrect result";
|
||||
goto finally;
|
||||
}
|
||||
|
||||
if (rz_bv_cmp(dst_copy, dst_copy_ref)) {
|
||||
error = "rz_bv_copy_nbits() result differs from reference";
|
||||
goto finally;
|
||||
}
|
||||
|
||||
// Test with inverted src/dst for extra certainty
|
||||
rz_bv_copy(dst, dst_copy);
|
||||
rz_bv_toggle_all(src_copy);
|
||||
rz_bv_toggle_all(dst_copy);
|
||||
|
||||
if (rz_bv_copy_nbits(src_copy, src_pos, dst_copy, dst_pos, nbit) != nbit) {
|
||||
error = "rz_bv_copy_nbits() incorrect result";
|
||||
goto finally;
|
||||
}
|
||||
|
||||
rz_bv_toggle_all(dst_copy);
|
||||
|
||||
if (rz_bv_cmp(dst_copy, dst_copy_ref)) {
|
||||
error = "rz_bv_copy_nbits() result differs from reference";
|
||||
goto finally;
|
||||
}
|
||||
|
||||
finally:
|
||||
rz_bv_free(src_copy);
|
||||
rz_bv_free(dst_copy);
|
||||
rz_bv_free(dst_copy_ref);
|
||||
return error;
|
||||
}
|
||||
|
||||
bool test_rz_bv_copy_nbits_small(void) {
|
||||
RzBitVector *a = rz_bv_new_from_ut64(64, 0x67452301);
|
||||
RzBitVector *b = rz_bv_new_from_ut64(64, 0x0);
|
||||
const char *error;
|
||||
|
||||
error = test_rz_bv_copy_nbits_against_ref(a, 1, b, 2, 62);
|
||||
mu_assert_null(error, error);
|
||||
|
||||
error = test_rz_bv_copy_nbits_against_ref(a, 0, b, 63, 1);
|
||||
mu_assert_null(error, error);
|
||||
|
||||
rz_bv_free(a);
|
||||
rz_bv_free(b);
|
||||
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_bv_copy_nbits_large_aligned(void) {
|
||||
RzBitVector *a = rz_bv_new(128);
|
||||
RzBitVector *b = rz_bv_new_from_ut64(128, 0x0);
|
||||
const char *error;
|
||||
|
||||
rz_bv_set_all(a, true);
|
||||
|
||||
/// copy same offset at same byte
|
||||
error = test_rz_bv_copy_nbits_against_ref(a, 5, b, 5, 3);
|
||||
mu_assert_null(error, error);
|
||||
|
||||
/// copy with front/end byte trailing bits, but no middle bytes
|
||||
error = test_rz_bv_copy_nbits_against_ref(a, 3, b, 3, 7);
|
||||
mu_assert_null(error, error);
|
||||
|
||||
/// copy with front and end trailing bits and middle bytes
|
||||
error = test_rz_bv_copy_nbits_against_ref(a, 3, b, 3, 16);
|
||||
mu_assert_null(error, error);
|
||||
|
||||
/// copy without front/trailing bits
|
||||
error = test_rz_bv_copy_nbits_against_ref(a, 8, b, 8, 32);
|
||||
mu_assert_null(error, error);
|
||||
|
||||
/// copy 1 bit
|
||||
error = test_rz_bv_copy_nbits_against_ref(a, 13, b, 13, 1);
|
||||
mu_assert_null(error, error);
|
||||
|
||||
/// copy all except 1 bit
|
||||
error = test_rz_bv_copy_nbits_against_ref(a, 1, b, 1, 127);
|
||||
mu_assert_null(error, error);
|
||||
|
||||
/// Copy bits within the same bitvector
|
||||
rz_bv_set_from_ut64(b, 0xAAAABBBBCCCCDDDD);
|
||||
ut32 actual_copy = rz_bv_copy_nbits(b, 8, b, 16, 32);
|
||||
mu_assert_eq(actual_copy, 32, "copy 32 bits");
|
||||
mu_assert_streq_free(rz_bv_as_hex_string(b, false), "0xaaaabbccccdddddd", "copy large aligned");
|
||||
|
||||
rz_bv_free(a);
|
||||
rz_bv_free(b);
|
||||
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_bv_copy_nbits_large_unaligned(void) {
|
||||
RzBitVector *a = rz_bv_new(128);
|
||||
RzBitVector *b = rz_bv_new(128);
|
||||
const char *error;
|
||||
|
||||
rz_bv_set_all(a, true);
|
||||
|
||||
/// copy different offset but same byte
|
||||
error = test_rz_bv_copy_nbits_against_ref(a, 5, b, 2, 20);
|
||||
mu_assert_null(error, error);
|
||||
|
||||
/// copy different offset and different byte
|
||||
error = test_rz_bv_copy_nbits_against_ref(a, 10, b, 20, 10);
|
||||
mu_assert_null(error, error);
|
||||
|
||||
/// copy at bit boundary
|
||||
error = test_rz_bv_copy_nbits_against_ref(a, 48, b, 1, 22);
|
||||
mu_assert_null(error, error);
|
||||
|
||||
/// copy 1 bit
|
||||
error = test_rz_bv_copy_nbits_against_ref(a, 55, b, 13, 1);
|
||||
mu_assert_null(error, error);
|
||||
|
||||
/// copy all except 1 bit
|
||||
error = test_rz_bv_copy_nbits_against_ref(a, 0, b, 1, 127);
|
||||
mu_assert_null(error, error);
|
||||
|
||||
/// Copy bits within the same bitvector
|
||||
rz_bv_set_from_ut64(b, 0xAAAABBBBCCCCDDDD);
|
||||
ut32 actual_copy = rz_bv_copy_nbits(b, 0, b, 2, 30);
|
||||
mu_assert_eq(actual_copy, 30, "copy 30 bits");
|
||||
mu_assert_streq_free(rz_bv_as_hex_string(b, false), "0xaaaabbbb33337775", "copy large aligned");
|
||||
|
||||
rz_bv_free(a);
|
||||
rz_bv_free(b);
|
||||
|
||||
mu_end;
|
||||
}
|
||||
|
||||
|
|
@ -1284,6 +1455,9 @@ bool all_tests() {
|
|||
mu_run_test(test_rz_bv_set_operations);
|
||||
mu_run_test(test_rz_bv_set_to_bytes_le);
|
||||
mu_run_test(test_rz_bv_copy_nbits);
|
||||
mu_run_test(test_rz_bv_copy_nbits_small);
|
||||
mu_run_test(test_rz_bv_copy_nbits_large_aligned);
|
||||
mu_run_test(test_rz_bv_copy_nbits_large_unaligned);
|
||||
mu_run_test(test_rz_bv_extra_operations);
|
||||
|
||||
return tests_passed != tests_run;
|
||||
|
|
|
|||
Loading…
Reference in a new issue