From a77bc25a5f3338c2abb2625249d7a4671b8bbd6d Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Tue, 9 Jul 2024 10:59:26 +0900 Subject: [PATCH] mruby-sprintf: support dots expression of negative integer format For specifiers assume unsigned integers, negative numbers show dots (..) to indicate virtual infinite 1s at the MSB side of 2's compliment. --- mrbgems/mruby-bigint/core/bigint.c | 38 +++++++++++++++++++++++++++++ mrbgems/mruby-sprintf/src/sprintf.c | 11 +++++++++ 2 files changed, 49 insertions(+) diff --git a/mrbgems/mruby-bigint/core/bigint.c b/mrbgems/mruby-bigint/core/bigint.c index bb33495ef..667b4723f 100644 --- a/mrbgems/mruby-bigint/core/bigint.c +++ b/mrbgems/mruby-bigint/core/bigint.c @@ -1626,3 +1626,41 @@ mrb_bint_hash(mrb_state *mrb, mrb_value x) hash = mrb_byte_hash_step((uint8_t*)&b->mp.sn, sizeof(b->mp.sn), hash); return mrb_int_value(mrb, hash); } + +mrb_value +mrb_bint_2comp(mrb_state *mrb, mrb_value x, mrb_int base) +{ + struct RBigint *b = RBIGINT(x); + mrb_int i; + + for (i=b->mp.sz-1; i>=0; i--) { + if (b->mp.p[i] > 0) break; + } + +#ifdef __GNUC__ + mrb_int dbits = __builtin_ctz(base); +#else + mrb_int dbits; + switch (base) { + case 16: dbits = 4; break; + case 8: dbits = 3; break; + case 2: dbits = 1; break; + } +#endif + mrb_int topbit = DIG_SIZE * (i+1) - lzb(b->mp.p[i]); + mrb_int nbits = (topbit / dbits + 1) * dbits; + + mpz_t one; + mpz_t up; + struct RBigint *b2 = bint_new(mrb); + + /* rounding up to nearest power of 2 */ + mpz_init_set_int(mrb, &one, 1); + mpz_init(mrb, &up); + mpz_mul_2exp(mrb, &up, &one, nbits); + mpz_clear(mrb, &one); + mpz_add(mrb, &b2->mp, &up, &b->mp); + mpz_clear(mrb, &up); + + return mrb_obj_value(b2); +} diff --git a/mrbgems/mruby-sprintf/src/sprintf.c b/mrbgems/mruby-sprintf/src/sprintf.c index 3763e7c7f..3e511d54e 100644 --- a/mrbgems/mruby-sprintf/src/sprintf.c +++ b/mrbgems/mruby-sprintf/src/sprintf.c @@ -17,6 +17,8 @@ #define BITSPERDIG MRB_INT_BIT #define EXTENDSIGN(n, l) (((~0U << (n)) >> (((n)*(l)) % BITSPERDIG)) & ~(~0U << (n))) +mrb_value mrb_bint_2comp(mrb_state *mrb, mrb_value x, mrb_int base); + static char* remove_sign_bits(char *str, int base) { @@ -594,6 +596,13 @@ retry: #ifdef MRB_USE_BIGINT case MRB_TT_BIGINT: { + mrb_int n = (mrb_bint_cmp(mrb, val, mrb_fixnum_value(0))); + mrb_bool need_dots = ((flags & FPLUS) == 0) && (base == 16 || base == 8 || base == 2) && n < 0; + if (need_dots) { + val = mrb_bint_2comp(mrb, val, base); + dots = 1; + v = -1; + } mrb_value str = mrb_bint_to_s(mrb, val, base); s = RSTRING_PTR(str); len = RSTRING_LEN(str); @@ -644,7 +653,9 @@ retry: len = (int)size; } +#ifdef MRB_USE_BIGINT str_skip: +#endif switch (base) { case 16: fc = 'f'; break;