librz/util: implement inplace rz_bv_div and rz_bv_mod. (#5742)

This commit is contained in:
Rot127 2026-01-07 04:26:06 +00:00 committed by GitHub
parent c2349a9663
commit a89bbe5cf2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 109 additions and 45 deletions

View file

@ -86,7 +86,9 @@ RZ_API bool rz_bv_sub_inplace(RZ_INOUT RZ_NONNULL RzBitVector *x, RZ_INOUT RZ_NO
RZ_API RZ_OWN RzBitVector *rz_bv_mul(RZ_NONNULL RzBitVector *x, RZ_NONNULL RzBitVector *y);
RZ_API bool rz_bv_mul_inplace(RZ_NONNULL RZ_INOUT RzBitVector *x, const RZ_NONNULL RzBitVector *y);
RZ_API RZ_OWN RzBitVector *rz_bv_div(RZ_NONNULL RzBitVector *x, RZ_NONNULL RzBitVector *y);
RZ_API bool rz_bv_div_inplace(RZ_NONNULL RZ_INOUT RzBitVector *x, const RZ_NONNULL RzBitVector *y);
RZ_API RZ_OWN RzBitVector *rz_bv_mod(RZ_NONNULL RzBitVector *x, RZ_NONNULL RzBitVector *y);
RZ_API bool rz_bv_mod_inplace(RZ_NONNULL RZ_INOUT RzBitVector *x, const RZ_NONNULL RzBitVector *y);
RZ_API RZ_OWN RzBitVector *rz_bv_sdiv(RZ_NONNULL RzBitVector *x, RZ_NONNULL RzBitVector *y);
RZ_API RZ_OWN RzBitVector *rz_bv_smod(RZ_NONNULL RzBitVector *x, RZ_NONNULL RzBitVector *y);

View file

@ -1173,7 +1173,7 @@ RZ_API RZ_OWN RzBitVector *rz_bv_mul(RZ_NONNULL RzBitVector *x, RZ_NONNULL RzBit
* if x == y return 0
* if x > y return positive (+1)
*/
int bv_unsigned_cmp(RZ_NONNULL RzBitVector *x, RZ_NONNULL RzBitVector *y) {
int bv_unsigned_cmp(const RZ_NONNULL RzBitVector *x, const RZ_NONNULL RzBitVector *y) {
rz_return_val_if_fail(x && y, 0);
if (x->len != y->len) {
@ -1197,6 +1197,75 @@ int bv_unsigned_cmp(RZ_NONNULL RzBitVector *x, RZ_NONNULL RzBitVector *y) {
return 0;
}
/**
* Result of x = (x / y) mod 2^length
* Both operands must have the same length.
* If \p y is a zero vector, the result defined as a vector of all ones.
*
* \param x dividend
* \param y divisor
* \return True in case of success, false otherwise.
*/
RZ_API bool rz_bv_div_inplace(RZ_NONNULL RZ_INOUT RzBitVector *x, const RZ_NONNULL RzBitVector *y) {
rz_return_val_if_fail(x && y && x->len == y->len, false);
if (rz_bv_is_zero_vector(y)) {
rz_bv_set_all(x, true);
return true;
}
if (x->len <= 64) {
rz_bv_set_from_ut64(x, rz_bv_to_ut64(x) / rz_bv_to_ut64(y));
return true;
}
int compare_result = bv_unsigned_cmp(x, y);
// dividend < divisor
// remainder = dividend, quotient = 0
if (compare_result < 0) {
rz_bv_set_from_ut64(x, 0);
return true;
}
// dividend == divisor
// remainder = 0, quotient = 1
if (compare_result == 0) {
rz_bv_set_from_ut64(x, 1);
return true;
}
// dividend > divisor
// do typical division by shift and subtract
RzBitVector dend;
rz_bv_init(&dend, x->len);
rz_bv_copy(x, &dend);
RzBitVector sor;
rz_bv_init(&sor, y->len);
rz_bv_copy(y, &sor);
// shift the divisor left to align both highest bits
ut32 sorlz = rz_bv_clz(&sor);
ut32 shift = sorlz - rz_bv_clz(&dend);
rz_bv_lshift(&sor, shift);
rz_bv_set_from_ut64(x, 0);
for (ut32 b = shift + 1; b; b--) {
if (rz_bv_ule(&sor, &dend)) {
rz_bv_set(x, b - 1, true);
// sub_inplace() negates sor_cpy
RzBitVector sor_cpy;
rz_bv_init(&sor_cpy, y->len);
rz_bv_copy(&sor, &sor_cpy);
rz_bv_sub_inplace(&dend, &sor_cpy, NULL);
rz_bv_fini(&sor_cpy);
}
rz_bv_rshift(&sor, 1);
}
rz_bv_fini(&dend);
rz_bv_fini(&sor);
return true;
}
/**
* Result of (x / y) mod 2^length
* Both operands must have the same length.
@ -1208,50 +1277,46 @@ int bv_unsigned_cmp(RZ_NONNULL RzBitVector *x, RZ_NONNULL RzBitVector *y) {
*/
RZ_API RZ_OWN RzBitVector *rz_bv_div(RZ_NONNULL RzBitVector *x, RZ_NONNULL RzBitVector *y) {
rz_return_val_if_fail(x && y && x->len == y->len, NULL);
RzBitVector *res = rz_bv_dup(x);
if (!rz_bv_div_inplace(res, y)) {
rz_bv_free(res);
return NULL;
}
return res;
}
/**
* Result of x = (x mod y) mod 2^length
* Both operands must have the same length.
* If \p y == 0, the result is \p x
*
* \param x dividend
* \param y divisor
* \return True in case of success, false otherwise.
*/
RZ_API bool rz_bv_mod_inplace(RZ_NONNULL RZ_INOUT RzBitVector *x, const RZ_NONNULL RzBitVector *y) {
rz_return_val_if_fail(x && y && x->len == y->len, false);
if (rz_bv_is_zero_vector(y)) {
RzBitVector *ret = rz_bv_new(y->len);
rz_bv_set_all(ret, true);
return ret;
return true;
}
RzBitVector remul;
rz_bv_init(&remul, rz_bv_len(x));
rz_bv_copy(x, &remul);
if (x->len <= 64) {
return rz_bv_new_from_ut64(x->len, rz_bv_to_ut64(x) / rz_bv_to_ut64(y));
if (!rz_bv_div_inplace(&remul, y)) {
rz_bv_fini(&remul);
return false;
}
int compare_result = bv_unsigned_cmp(x, y);
// dividend < divisor
// remainder = dividend, quotient = 0
if (compare_result < 0) {
return rz_bv_new(x->len);
if (!rz_bv_mul_inplace(&remul, y)) {
rz_bv_fini(&remul);
return false;
}
// dividend == divisor
// remainder = 0, quotient = 1
if (compare_result == 0) {
return rz_bv_new_one(rz_bv_len(x));
if (!rz_bv_sub_inplace(x, &remul, NULL)) {
rz_bv_fini(&remul);
return false;
}
// dividend > divisor
// do typical division by shift and subtract
RzBitVector *dend = rz_bv_dup(x);
RzBitVector *sor = rz_bv_dup(y);
// shift the divisor left to align both highest bits
ut32 sorlz = rz_bv_clz(sor);
ut32 shift = sorlz - rz_bv_clz(dend);
rz_bv_lshift(sor, shift);
RzBitVector *quot = rz_bv_new_zero(rz_bv_len(x));
for (ut32 b = shift + 1; b; b--) {
if (rz_bv_ule(sor, dend)) {
rz_bv_set(quot, b - 1, true);
RzBitVector *tmp = rz_bv_sub(dend, sor, NULL);
rz_bv_free(dend);
dend = tmp;
}
rz_bv_rshift(sor, 1);
}
rz_bv_free(dend);
rz_bv_free(sor);
return quot;
rz_bv_fini(&remul);
return true;
}
/**
@ -1265,14 +1330,11 @@ RZ_API RZ_OWN RzBitVector *rz_bv_div(RZ_NONNULL RzBitVector *x, RZ_NONNULL RzBit
*/
RZ_API RZ_OWN RzBitVector *rz_bv_mod(RZ_NONNULL RzBitVector *x, RZ_NONNULL RzBitVector *y) {
rz_return_val_if_fail(x && y && x->len == y->len, NULL);
if (rz_bv_is_zero_vector(y)) {
return rz_bv_dup(x);
RzBitVector *r = rz_bv_dup(x);
if (!rz_bv_mod_inplace(r, y)) {
rz_bv_free(r);
return NULL;
}
RzBitVector *quot = rz_bv_div(x, y);
RzBitVector *remul = rz_bv_mul(quot, y);
RzBitVector *r = rz_bv_sub(x, remul, NULL);
rz_bv_free(quot);
rz_bv_free(remul);
return r;
}