From: Yui NARUSE Date: 2012-02-23T09:27:25+09:00 Subject: [ruby-core:42814] [ruby-trunk - Feature #6065][Assigned] Allow Bignum marshalling/unmarshalling from C API Issue #6065 has been updated by Yui NARUSE. Status changed from Open to Assigned Assignee changed from Martin Bosslet to Kenta Murata I made such dump API before. This dump a bignum as a format for OpenSSL::BN. diff --git a/bignum.c b/bignum.c index 3145f24..9141dc8 100644 --- a/bignum.c +++ b/bignum.c @@ -405,6 +405,90 @@ rb_big_unpack(unsigned long *buf, long num_longs) } } +/* + * dump an absolute value of an integer as a big endian byte string. + */ +VALUE +rb_big_bindump(VALUE val) +{ + long clen; + char *str; + val = rb_to_int(val); + if (FIXNUM_P(val)) { + long v = FIX2LONG(val); + int i; + for (clen = SIZEOF_LONG; clen >= 0 && !((v >> ((clen-1)*CHAR_BIT)) & 0xFF); clen--); + str = ALLOC_N(char, clen+1); + for (i = clen; i; i--) { + str[clen - i] = (v >> ((i-1) * CHAR_BIT)) & 0xFF; + } + } + else { + long len = RBIGNUM_LEN(val); + BDIGIT *ds = BDIGITS(val); + BDIGIT v = ds[len-1]; + int i, j; + + for (i = SIZEOF_BDIGITS; i >= 0 && !((v >> ((i-1)*CHAR_BIT)) & 0xFF); i--); + clen = i + (len-1) * SIZEOF_BDIGITS; + str = ALLOCA_N(char, clen+1); + str[clen] = 0; + + for (j = 0; j < i; j++) { + str[j] = (char)((v >> ((i - j - 1)*CHAR_BIT)) & 0xFF); + } + + for (i = len - 2; i >= 0; i--) { + v = ds[i]; + for (j = 0; j < SIZEOF_BDIGITS; j++) { + str[clen - (i + 1) * SIZEOF_BDIGITS + j] = + (char)((v >> ((SIZEOF_BDIGITS-1-j)* CHAR_BIT)) & 0xFF); + } + } + } + return rb_str_new(str, clen); +} + +/* + * load an absolute value of an integer from a big endian byte string. + */ +VALUE +rb_big_binload(VALUE dummy, VALUE val) +{ + char *cstr = RSTRING_PTR(val); + long blen = RSTRING_LEN(val); + int i; + + if (blen < SIZEOF_BDIGITS || + (blen == SIZEOF_BDIGITS && !(cstr[0]>>(CHAR_BIT-2)))) { + long v = 0; + for (i = 0; i < blen; i++) { + v <<= CHAR_BIT; + v |= cstr[i]; + } + return LONG2NUM(v); + } + else { + long len = (blen + SIZEOF_BDIGITS - 1) / SIZEOF_BDIGITS; + VALUE big = bignew(len, 1); + BDIGIT *ds = BDIGITS(big); + int n = 0; + BDIGIT v = 0; + + for (i = 0; i < blen; i++) { + int j = i % SIZEOF_BDIGITS; + v |= cstr[blen - 1 - i] << (CHAR_BIT * j); + if (j == SIZEOF_BDIGITS - 1) { + ds[n] = v; + v = 0; + n += 1; + } + } + if (v) ds[n] = v; + return big; + } +} + #define QUAD_SIZE 8 #if SIZEOF_LONG_LONG == QUAD_SIZE && SIZEOF_BDIGITS*2 == SIZEOF_LONG_LONG @@ -3478,7 +3562,10 @@ Init_Bignum(void) { rb_cBignum = rb_define_class("Bignum", rb_cInteger); + rb_define_singleton_method(rb_cBignum, "binload", rb_big_binload, 1); + rb_define_method(rb_cBignum, "to_s", rb_big_to_s, -1); + rb_define_method(rb_cBignum, "bindump", rb_big_bindump, 0); rb_define_method(rb_cBignum, "coerce", rb_big_coerce, 1); rb_define_method(rb_cBignum, "-@", rb_big_uminus, 0); rb_define_method(rb_cBignum, "+", rb_big_plus, 1); ---------------------------------------- Feature #6065: Allow Bignum marshalling/unmarshalling from C API https://bugs.ruby-lang.org/issues/6065 Author: Martin Bosslet Status: Assigned Priority: Normal Assignee: Kenta Murata Category: core Target version: 2.0.0 Currently, there's no public C API to create a Bignum. There is rb_big_pack and rb_big_unpack that will do the job, but they are not portable. Could we offer public functionality that is independent of the internal representation for the task of marshaling/unmarshalling a Bignum to raw C data types? I'd like to propose something like - creating a bignum: VALUE rb_big_from_ary(unsigned long *longs, size_t num_longs, int signed) - retrieving a representation of a Bignum (longs are allocated): size_t rb_big_to_ary(VALUE big, unsigned long **longs, int *signed) For getting a representation, rb_big2str could also be used, but the above would simplify things when developing an extension that is in need of Bignum support. Names and signatures are of course open for discussion, the example should just serve as an indication of what I'm aiming at. To avoid ambiguity, it would have to be defined how the longs are ordered and how the signed flag is to be interpreted - I would suggest a very simple representation: Let "longs" be the representation of the absolute value of the bignum in little- or big-endian order, where each of the longs themselves should probably be in the same order, in order to eliminate ambivalence. Signed is either 0 or 1, so no two's complement or anything involved. I would volunteer to provide a patch for this if we would agree on something. -- http://bugs.ruby-lang.org/