mruby-random: support bigint in rand method

To achieve this, the following changes were made:

- Exported `mrb_bint_size`, `mrb_bint_from_bytes`, and `mrb_bint_sign`
  functions from `mruby-bigint` to be used in other mrbgems.
- Modified `mruby-random` to use these new functions to handle Bigint
  arguments in the `rand` method.

Co-authored-by: Gemini <gemini@google.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2025-07-19 12:34:15 +09:00
parent 12d77d447b
commit 7b0ee01310
3 changed files with 48 additions and 0 deletions
+3
View File
@@ -258,6 +258,9 @@ void mrb_bint_copy(mrb_state *mrb, mrb_value x, mrb_value y);
size_t mrb_bint_memsize(mrb_value x);
mrb_value mrb_bint_hash(mrb_state *mrb, mrb_value x);
mrb_value mrb_bint_sqrt(mrb_state *mrb, mrb_value x);
mrb_int mrb_bint_size(mrb_state *mrb, mrb_value bint);
mrb_value mrb_bint_from_bytes(mrb_state *mrb, const uint8_t *bytes, mrb_int len);
mrb_int mrb_bint_sign(mrb_state *mrb, mrb_value bint);
#endif
#endif /* MRUBY_INTERNAL_H */
+28
View File
@@ -2756,6 +2756,34 @@ mrb_bint_sqrt(mrb_state *mrb, mrb_value x)
return bint_norm(mrb, bint_new(mrb, &z));
}
mrb_int
mrb_bint_sign(mrb_state *mrb, mrb_value bint)
{
return RBIGINT_SIGN(RBIGINT(bint));
}
mrb_int
mrb_bint_size(mrb_state *mrb, mrb_value bint)
{
mpz_t z;
bint_as_mpz(RBIGINT(bint), &z);
return z.sz * sizeof(mp_limb);
}
mrb_value
mrb_bint_from_bytes(mrb_state *mrb, const uint8_t *bytes, mrb_int len)
{
mpz_t z;
mpz_init(mrb, &z);
size_t limb_len = (len + sizeof(mp_limb) - 1) / sizeof(mp_limb);
mpz_realloc(mrb, &z, limb_len);
memcpy(z.p, bytes, len);
z.sn = (len > 0) ? 1 : 0;
z.sz = limb_len;
trim(&z);
return bint_norm(mrb, bint_new(mrb, &z));
}
mrb_value
mrb_bint_hash(mrb_state *mrb, mrb_value x)
{
+17
View File
@@ -13,6 +13,7 @@
#include <mruby/presym.h>
#include <mruby/range.h>
#include <mruby/string.h>
#include <mruby/internal.h>
#include <time.h>
@@ -210,6 +211,22 @@ random_rand_impl(mrb_state *mrb, rand_state *t, mrb_value self)
return random_range(mrb, t, arg);
}
#ifdef MRB_USE_BIGINT
if (mrb_bigint_p(arg)) {
if (mrb_bint_sign(mrb, arg) < 0) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "negative value as random limit");
}
mrb_int size = mrb_bint_size(mrb, arg);
mrb_value bytes = mrb_str_new(mrb, NULL, size);
uint8_t *p = (uint8_t*)RSTRING_PTR(bytes);
for (mrb_int i = 0; i < size; i++) {
p[i] = (uint8_t)rand_uint32(t);
}
mrb_value rand_bint = mrb_bint_from_bytes(mrb, p, size);
return mrb_bint_mod(mrb, rand_bint, arg);
}
#endif
range_error(mrb, arg);
}