Skip to content

Instantly share code, notes, and snippets.

@mrkn
Created February 23, 2012 13:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mrkn/1892968 to your computer and use it in GitHub Desktop.
Save mrkn/1892968 to your computer and use it in GitHub Desktop.
diff --git a/bignum.c b/bignum.c
index 80db699..96df679 100644
--- a/bignum.c
+++ b/bignum.c
@@ -195,6 +195,50 @@ rb_big_clone(VALUE x)
return z;
}
+size_t
+rb_big_dump_to_cary(VALUE big, unsigned long** ary_ptr, int* sign)
+{
+ unsigned long* ary;
+ size_t ary_len, big_len;
+ BDIGIT const* pd;
+
+ if (sign != NULL) {
+ *sign = RBIGNUM_SIGN(big);
+ }
+
+ pd = BDIGITS(big);
+ big_len = RBIGNUM_LEN(big);
+ ary_len = (big_len * SIZEOF_BDIGITS) / SIZEOF_LONG;
+ if (ary_len == 0) ary_len = 1;
+
+ if (ary_ptr != NULL) {
+ ary = ALLOC_N(unsigned long, ary_len);
+ ary[ary_len - 1] = 0;
+ MEMCPY(ary, pd, BDIGIT, big_len);
+ *ary_ptr = ary;
+ }
+
+ return ary_len;
+}
+
+VALUE
+rb_big_load_from_cary(unsigned long const* ary, size_t ary_len, int sign)
+{
+ size_t big_len;
+ VALUE big;
+
+ if (ary == NULL || ary_len <= 0) {
+ return Qnil;
+ }
+
+ big_len = (ary_len * SIZEOF_LONG) / SIZEOF_BDIGITS;
+ big = rb_big_new(big_len, sign);
+ MEMCPY(BDIGITS(big), ary, BDIGIT, big_len);
+ rb_big_norm(big);
+
+ return big;
+}
+
/* modify a bignum by 2's complement */
static void
get2comp(VALUE x)
diff --git a/ext/-test-/bignum/native_dump/extconf.rb b/ext/-test-/bignum/native_dump/extconf.rb
new file mode 100644
index 0000000..ca21da5
--- /dev/null
+++ b/ext/-test-/bignum/native_dump/extconf.rb
@@ -0,0 +1 @@
+create_makefile("-test-/bignum/native_dump")
diff --git a/ext/-test-/bignum/native_dump/native_dump.c b/ext/-test-/bignum/native_dump/native_dump.c
new file mode 100644
index 0000000..eb88c2a
--- /dev/null
+++ b/ext/-test-/bignum/native_dump/native_dump.c
@@ -0,0 +1,87 @@
+#include "ruby/ruby.h"
+
+static VALUE
+sizeof_long(VALUE obj)
+{
+ return INT2FIX(sizeof(long));
+}
+
+static VALUE
+sizeof_bdigit(VALUE obj)
+{
+ return INT2FIX(sizeof(BDIGIT));
+}
+
+static VALUE
+big_s_minimum(VALUE obj)
+{
+ return LONG2NUM(FIXNUM_MAX+1);
+}
+
+static VALUE
+big_s_native_dump_load(VALUE obj, VALUE x)
+{
+ unsigned long* ary;
+ size_t len;
+ int sign;
+ VALUE y;
+
+ Check_Type(x, T_BIGNUM);
+ len = rb_big_dump_to_cary(x, &ary, &sign);
+ y = rb_big_load_from_cary(ary, len, sign);
+ xfree(ary);
+
+ return y;
+}
+
+static VALUE
+big_s_native_dumped_size(VALUE obj, VALUE x)
+{
+ size_t len;
+
+ Check_Type(x, T_BIGNUM);
+ len = rb_big_dump_to_cary(x, NULL, NULL);
+
+ return SIZET2NUM(len);
+}
+
+static VALUE
+big_s_native_dumped_sign(VALUE obj, VALUE x)
+{
+ int sign;
+
+ Check_Type(x, T_BIGNUM);
+ rb_big_dump_to_cary(x, NULL, &sign);
+
+ return INT2NUM(sign);
+}
+
+static VALUE
+big_bdigit_len(VALUE x)
+{
+ return LONG2NUM(RBIGNUM_LEN(x));
+}
+
+static VALUE
+int_force_bignum(VALUE x)
+{
+ x = rb_check_to_int(x);
+ if (TYPE(x) == T_FIXNUM) {
+ x = rb_int2big(FIX2LONG(x));
+ }
+ return x;
+}
+
+void
+Init_native_dump(void)
+{
+ rb_define_singleton_method(rb_cBignum, "__sizeof_long__", sizeof_long, 0);
+ rb_define_singleton_method(rb_cBignum, "__sizeof_bdigit__", sizeof_bdigit, 0);
+ rb_define_singleton_method(rb_cBignum, "__minimum__", big_s_minimum, 0);
+ rb_define_singleton_method(rb_cBignum, "__native_dump_load__", big_s_native_dump_load, 1);
+ rb_define_singleton_method(rb_cBignum, "__native_dumped_size__", big_s_native_dumped_size, 1);
+ rb_define_singleton_method(rb_cBignum, "__native_dumped_sign__", big_s_native_dumped_sign, 1);
+ rb_define_method(rb_cBignum, "__bdigit_len__", big_bdigit_len, 0);
+ rb_define_method(rb_cInteger, "__force_bignum__", int_force_bignum, 0);
+}
+
diff --git a/include/ruby/intern.h b/include/ruby/intern.h
index e0c4710..d3828ff 100644
--- a/include/ruby/intern.h
+++ b/include/ruby/intern.h
@@ -136,6 +136,8 @@ VALUE rb_big_or(VALUE, VALUE);
VALUE rb_big_xor(VALUE, VALUE);
VALUE rb_big_lshift(VALUE, VALUE);
VALUE rb_big_rshift(VALUE, VALUE);
+size_t rb_big_dump_to_cary(VALUE, unsigned long**, int*);
+VALUE rb_big_load_from_cary(unsigned long const*, size_t, int);
/* rational.c */
VALUE rb_rational_raw(VALUE, VALUE);
#define rb_rational_raw1(x) rb_rational_raw((x), INT2FIX(1))
diff --git a/test/-ext-/bignum/test_native_dump.rb b/test/-ext-/bignum/test_native_dump.rb
new file mode 100644
index 0000000..5b72d7d
--- /dev/null
+++ b/test/-ext-/bignum/test_native_dump.rb
@@ -0,0 +1,75 @@
+require 'test/unit'
+require '-test-/bignum/native_dump'
+
+class TestBignum < Test::Unit::TestCase
+ class TestNativeDump < Test::Unit::TestCase
+ def setup
+ @long_size = Bignum.__sizeof_long__
+ @bdigit_size = Bignum.__sizeof_bdigit__
+ @bdigits_per_long = @long_size.div(@bdigit_size)
+ @bignum = Bignum.__minimum__ + 1
+ end
+
+ def test_native_dump_instance_6065
+ feature = '[ruby-core:42813][#6065]'
+ assert_equal_for_native_dump_loaded_instance(@bignum, @bignum, feature)
+ assert_equal_for_native_dump_loaded_instance(-@bignum, -@bignum, feature)
+ assert_equal_for_native_dump_loaded_instance(0, 0.__force_bignum__, feature)
+ assert_equal_for_native_dump_loaded_instance(1, 1.__force_bignum__, feature)
+ assert_equal_for_native_dump_loaded_instance(-1, -1.__force_bignum__, feature)
+ end
+
+ def test_native_dump_size_6065
+ feature = '[ruby-core:42813][#6065]'
+ assert_native_dumped_size(@bignum, feature)
+ assert_native_dumped_size(-@bignum, feature)
+ assert_native_dumped_size(0.__force_bignum__, feature)
+ assert_native_dumped_size(1.__force_bignum__, feature)
+ assert_native_dumped_size(-1.__force_bignum__, feature)
+ end
+
+ def test_native_dump_sign_6065
+ feature = '[ruby-core:42813][#6065]'
+ assert_native_dumped_sign(@bignum, feature)
+ assert_native_dumped_sign(-@bignum, feature)
+ assert_native_dumped_sign(0.__force_bignum__, feature)
+ assert_native_dumped_sign(1.__force_bignum__, feature)
+ assert_native_dumped_sign(-1.__force_bignum__, feature)
+ end
+
+ def test_native_dump_class_6065
+ feature = '[ruby-core:42813][#6065]'
+ assert_equal_for_native_dump_loaded_class(Bignum, @bignum, feature)
+ assert_equal_for_native_dump_loaded_class(Bignum, -@bignum, feature)
+ # NOTE: We must decide whether the following features are acceptible.
+ assert_equal_for_native_dump_loaded_class(Bignum, 0.__force_bignum__, feature)
+ assert_equal_for_native_dump_loaded_class(Bignum, 1.__force_bignum__, feature)
+ assert_equal_for_native_dump_loaded_class(Bignum, -1.__force_bignum__, feature)
+ end
+
+ private
+
+ def assert_equal_for_native_dump_loaded_instance(expected, actual, msg=nil)
+ assert_equal(expected, Bignum.__native_dump_load__(actual), msg)
+ end
+
+ def assert_native_dumped_size(x, msg=nil)
+ assert_equal(expected_size(x), Bignum.__native_dumped_size__(x), msg)
+ end
+
+ def assert_native_dumped_sign(x, msg=nil)
+ expected_sign = x >= 0 ? 1 : 0
+ assert_equal(expected_sign, Bignum.__native_dumped_sign__(x), msg)
+ end
+
+ def assert_equal_for_native_dump_loaded_class(cls, x, msg=nil)
+ assert_equal(cls, Bignum.__native_dump_load__(x).class, msg)
+ end
+
+ def expected_size(x)
+ n = x.__bdigit_len__
+ q, r = n.divmod(@bdigits_per_long)
+ q + (r > 0 ? 1 : 0)
+ end
+ end
+end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment