ruby-changes:29059
From: akr <ko1@a...>
Date: Fri, 7 Jun 2013 06:20:18 +0900 (JST)
Subject: [ruby-changes:29059] akr:r41111 (trunk): * bignum.c (rb_int_import): New function.
akr 2013-06-07 06:20:04 +0900 (Fri, 07 Jun 2013) New Revision: 41111 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=41111 Log: * bignum.c (rb_int_import): New function. (int_import_push_bits): Ditto. * internal.h (rb_int_import): Declared. * pack.c (pack_unpack): Use rb_int_import for BER compressed integer. Added files: trunk/ext/-test-/bignum/import.c trunk/test/-ext-/bignum/test_import.rb Modified files: trunk/ChangeLog trunk/bignum.c trunk/ext/-test-/bignum/depend trunk/internal.h trunk/pack.c Index: ChangeLog =================================================================== --- ChangeLog (revision 41110) +++ ChangeLog (revision 41111) @@ -1,3 +1,12 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Fri Jun 7 06:15:31 2013 Tanaka Akira <akr@f...> + + * bignum.c (rb_int_import): New function. + (int_import_push_bits): Ditto. + + * internal.h (rb_int_import): Declared. + + * pack.c (pack_unpack): Use rb_int_import for BER compressed integer. + Thu Jun 6 22:24:00 2013 Kenta Murata <mrkn@m...> * numeric.c (num_quo): Use to_r method to convert the receiver to Index: pack.c =================================================================== --- pack.c (revision 41110) +++ pack.c (revision 41111) @@ -2137,32 +2137,18 @@ pack_unpack(VALUE str, VALUE fmt) https://github.com/ruby/ruby/blob/trunk/pack.c#L2137 case 'w': { - unsigned long ul = 0; - unsigned long ulmask = 0xfeUL << ((sizeof(unsigned long) - 1) * 8); - - while (len > 0 && s < send) { - ul <<= 7; - ul |= (*s & 0x7f); - if (!(*s++ & 0x80)) { - UNPACK_PUSH(ULONG2NUM(ul)); - len--; - ul = 0; - } - else if (ul & ulmask) { - VALUE big = rb_uint2big(ul); - VALUE big128 = rb_uint2big(128); - while (s < send) { - big = rb_big_mul(big, big128); - big = rb_big_plus(big, rb_uint2big(*s & 0x7f)); - if (!(*s++ & 0x80)) { - UNPACK_PUSH(big); - len--; - ul = 0; - break; - } - } - } - } + char *s0 = s; + while (len > 0 && s < send) { + if (*s & 0x80) { + s++; + } + else { + s++; + UNPACK_PUSH(rb_int_import(1, s0, s-s0, 1, 1, 1, 1)); + len--; + s0 = s; + } + } } break; Index: ext/-test-/bignum/depend =================================================================== --- ext/-test-/bignum/depend (revision 41110) +++ ext/-test-/bignum/depend (revision 41111) @@ -1,3 +1,4 @@ https://github.com/ruby/ruby/blob/trunk/ext/-test-/bignum/depend#L1 $(OBJS): $(HDRS) $(ruby_headers) export.o: export.c $(top_srcdir)/internal.h +import.o: import.c $(top_srcdir)/internal.h Index: ext/-test-/bignum/import.c =================================================================== --- ext/-test-/bignum/import.c (revision 0) +++ ext/-test-/bignum/import.c (revision 41111) @@ -0,0 +1,18 @@ https://github.com/ruby/ruby/blob/trunk/ext/-test-/bignum/import.c#L1 +#include "ruby.h" +#include "internal.h" + +static VALUE +rb_int_import_m(VALUE klass, VALUE sign, VALUE buf, VALUE wordcount, VALUE wordorder, VALUE wordsize, VALUE endian, VALUE nails) +{ + StringValue(buf); + + return rb_int_import(NUM2INT(sign), RSTRING_PTR(buf), + NUM2SIZE(wordcount), NUM2INT(wordorder), NUM2SIZE(wordsize), + NUM2INT(endian), NUM2SIZE(nails)); +} + +void +Init_import(VALUE klass) +{ + rb_define_singleton_method(rb_cInteger, "test_import", rb_int_import_m, 7); +} Index: internal.h =================================================================== --- internal.h (revision 41110) +++ internal.h (revision 41111) @@ -427,6 +427,7 @@ VALUE rb_thread_io_blocking_region(rb_bl https://github.com/ruby/ruby/blob/trunk/internal.h#L427 /* bignum.c */ void *rb_int_export(VALUE val, int *signp, void *bufarg, size_t *countp, int wordorder, size_t wordsize, int endian, size_t nails); +VALUE rb_int_import(int sign, const void *bufarg, size_t wordcount, int wordorder, size_t wordsize, int endian, size_t nails); /* io.c */ void rb_maygvl_fd_fix_cloexec(int fd); Index: bignum.c =================================================================== --- bignum.c (revision 41110) +++ bignum.c (revision 41111) @@ -607,7 +607,7 @@ rb_int_export(VALUE val, int *signp, voi https://github.com/ruby/ruby/blob/trunk/bignum.c#L607 #ifdef WORDS_BIGENDIAN endian = 1; #else - endian = 0; + endian = -1; #endif } @@ -770,6 +770,159 @@ rb_int_export(VALUE val, int *signp, voi https://github.com/ruby/ruby/blob/trunk/bignum.c#L770 #undef TAKE_LOWBITS } +static inline void +int_import_push_bits(int data, int numbits, BDIGIT_DBL *ddp, int *numbits_in_dd_p, BDIGIT **dpp) +{ + (*ddp) |= ((BDIGIT_DBL)data) << (*numbits_in_dd_p); + *numbits_in_dd_p += numbits; + while (SIZEOF_BDIGITS*CHAR_BIT <= *numbits_in_dd_p) { + *(*dpp)++ = (*ddp) & (((BDIGIT_DBL)1 << (SIZEOF_BDIGITS*CHAR_BIT))-1); + *ddp >>= SIZEOF_BDIGITS*CHAR_BIT; + *numbits_in_dd_p -= SIZEOF_BDIGITS*CHAR_BIT; + } +} + +/* + * Import an integer into a buffer. + * + * [sign] signedness of the value. + * -1 for non-positive. 0 or 1 for non-negative. + * [bufarg] buffer to import. + * [wordcount] the size of given buffer as number of words. + * [wordorder] order of words: 1 for most significant word first. -1 for least significant word first. + * [wordsize] the size of word as number of bytes. + * [endian] order of bytes in a word: 1 for most significant byte first. -1 for least significant byte first. 0 for native endian. + * [nails] number of padding bits in a word. Most significant nails bits of each word are filled by zero. + * + * This function returns the imported integer as Fixnum or Bignum. + */ +VALUE +rb_int_import(int sign, const void *bufarg, size_t wordcount, int wordorder, size_t wordsize, int endian, size_t nails) +{ + VALUE num_bits, num_bdigits; + VALUE result; + const unsigned char *buf = bufarg; + + BDIGIT *dp; + BDIGIT *de; + + int word_num_partialbits; + size_t word_num_fullbytes; + + ssize_t word_step; + size_t byte_start; + int byte_step; + + const unsigned char *bytep, *wordp, *last_wordp; + size_t index_in_word; + BDIGIT_DBL dd; + int numbits_in_dd; + + if (sign != 1 && sign != 0 && sign != -1) + rb_raise(rb_eArgError, "unexpected sign: %d", sign); + if (wordorder != 1 && wordorder != -1) + rb_raise(rb_eArgError, "unexpected wordorder: %d", wordorder); + if (endian != 1 && endian != -1 && endian != 0) + rb_raise(rb_eArgError, "unexpected endian: %d", endian); + if (wordsize == 0) + rb_raise(rb_eArgError, "invalid wordsize: %"PRI_SIZE_PREFIX"u", wordsize); + if (SSIZE_MAX < wordsize) + rb_raise(rb_eArgError, "too big wordsize: %"PRI_SIZE_PREFIX"u", wordsize); + if (SIZE_MAX / wordsize < wordcount) + rb_raise(rb_eArgError, "too big wordcount * wordsize: %"PRI_SIZE_PREFIX"u * %"PRI_SIZE_PREFIX"u", wordcount, wordsize); + if (wordsize <= nails / CHAR_BIT) + rb_raise(rb_eArgError, "too big nails: %"PRI_SIZE_PREFIX"u", nails); + + if (endian == 0) { +#ifdef WORDS_BIGENDIAN + endian = 1; +#else + endian = -1; +#endif + } + + /* + * num_bits = (wordsize * CHAR_BIT - nails) * count + * num_bdigits = (num_bits + SIZEOF_BDIGITS*CHAR_BIT - 1) / (SIZEOF_BDIGITS*CHAR_BIT) + */ + num_bits = SIZE2NUM(wordsize); + num_bits = rb_funcall(num_bits, '*', 1, LONG2FIX(CHAR_BIT)); + num_bits = rb_funcall(num_bits, '-', 1, SIZE2NUM(nails)); + num_bits = rb_funcall(num_bits, '*', 1, SIZE2NUM(wordcount)); + + if (num_bits == LONG2FIX(0)) + return LONG2FIX(0); + + num_bdigits = rb_funcall(num_bits, '+', 1, LONG2FIX(SIZEOF_BDIGITS*CHAR_BIT-1)); + num_bdigits = rb_funcall(num_bdigits, '/', 1, LONG2FIX(SIZEOF_BDIGITS*CHAR_BIT)); + + result = bignew(NUM2LONG(num_bdigits), 0 <= sign); + + dp = BDIGITS(result); + de = dp + RBIGNUM_LEN(result); + + word_num_partialbits = CHAR_BIT - (int)(nails % CHAR_BIT); + if (word_num_partialbits == CHAR_BIT) + word_num_partialbits = 0; + word_num_fullbytes = wordsize - (nails / CHAR_BIT); + if (word_num_partialbits != 0) { + word_num_fullbytes--; + } + + if (wordorder == 1) { + word_step = -(ssize_t)wordsize; + wordp = buf + wordsize*(wordcount-1); + last_wordp = buf; + } + else { + word_step = wordsize; + wordp = buf; + last_wordp = buf + wordsize*(wordcount-1); + } + + if (endian == 1) { + byte_step = -1; + byte_start = wordsize-1; + } + else { + byte_step = 1; + byte_start = 0; + } + + dd = 0; + numbits_in_dd = 0; + +#define PUSH_BITS(data, numbits) \ + int_import_push_bits(data, numbits, &dd, &numbits_in_dd, &dp) + + while (1) { + index_in_word = 0; + bytep = wordp + byte_start; + while (index_in_word < word_num_fullbytes) { + PUSH_BITS(*bytep, CHAR_BIT); + bytep += byte_step; + index_in_word++; + } + if (word_num_partialbits) { + PUSH_BITS(*bytep & ((1 << word_num_partialbits) - 1), word_num_partialbits); + bytep += byte_step; + index_in_word++; + } + + if (wordp == last_wordp) + break; + + wordp += word_step; + } + if (dd) + *dp++ = dd; + while (dp < de) + *dp++ = 0; + + return bignorm(result); +#undef PUSH_BITS +} + #define QUAD_SIZE 8 #if SIZEOF_LONG_LONG == QUAD_SIZE && SIZEOF_BDIGITS*2 == SIZEOF_LONG_LONG Index: test/-ext-/bignum/test_import.rb =================================================================== --- test/-ext-/bignum/test_import.rb (revision 0) +++ test/-ext-/bignum/test_import.rb (revision 41111) @@ -0,0 +1,54 @@ https://github.com/ruby/ruby/blob/trunk/test/-ext-/bignum/test_import.rb#L1 +# coding: ASCII-8BIT + +require 'test/unit' +require "-test-/bignum" + +class TestBignum < Test::Unit::TestCase + class TestExport < Test::Unit::TestCase + def test_import_zero + assert_equal(0, Integer.test_import(0, "", 1, 1, 1, 1, 0)) + end + + def test_argument_check + assert_raise(ArgumentError) { Integer.test_import(1, "x", 1, 0, 1, 1, 0) } + assert_raise(ArgumentError) { Integer.test_import(1, "x", 1, 1, 1, 2, 0) } + assert_raise(ArgumentError) { Integer.test_import(1, "x", 1, 1, 0, 1, 0) } + assert_raise(ArgumentError) { Integer.test_import(1, "x", 1, 1, 1, 1, 8) } + + # assume sizeof(ssize_t) == sizeof(intptr_t) + assert_raise(ArgumentError) { Integer.test_import(1, "x", 1, 1, 1 << ([""].pack("p").length * 8 - 1), 1, 0) } + end + + def test_import_wordsize + assert_equal(1, Integer.test_import(1, "\x01", 1, 1, 1, 1, 0)) + assert_equal(1, Integer.test_import(1, "\x00\x01", 1, 1, 2, 1, 0)) + assert_equal(1, Integer.test_import(1, "\x00\x00\x01", 1, 1, 3, 1, 0)) + assert_equal(1, Integer.test_import(1, "\x01", 1, 1, 1, -1, 0)) + assert_equal(1, Integer.test_import(1, "\x01\x00", 1, 1, 2, -1, 0)) + assert_equal(1, Integer.test_import(1, "\x01\x00\x00", 1, 1, 3, -1, 0)) + end + + def test_import_wordorder_and_endian + assert_equal(0x01020304, Integer.test_import(1, "\x01\x02\x03\x04", 2, 1, 2, 1, 0)) + assert_equal(0x02010403, Integer.test_import(1, "\x01\x02\x03\x04", 2, 1, 2, -1, 0)) + assert_equal(0x03040102, Integer.test_import(1, "\x01\x02\x03\x04", 2, -1, 2, 1, 0)) + assert_equal(0x04030201, Integer.test_import(1, "\x01\x02\x03\x04", 2, -1, 2, -1, 0)) + end + + def test_import_native_endian + assert_equal("\x12\x34".unpack("S!")[0], Integer.test_import(1, "\x12\x34", 1, 1, 2, 0, 0)) + end + + def test_import_nail + assert_equal(0b100011, Integer.test_import(1, "\x01\x00\x00\x00\x01\x01", 6, 1, 1, 1, 7)) + assert_equal(0x12345678, Integer.test_import(1, "\x01\x02\x03\x04\x05\x06\x07\x08", 8, 1, 1, 1, 4)) + assert_equal(0x12345678, Integer.test_import(1, "\x00\x12\x00\x34\x00\x56\x00\x78", 4, 1, 2, 1, 8)) + end + + def test_import_sign + assert_equal(-1, Integer.test_import(-1, "\x01", 1, 1, 1, 1, 0)) + assert_equal(-0x8070605040302010, Integer.test_import(-1, "\x80\x70\x60\x50\x40\x30\x20\x10", 8, 1, 1, 1, 0)) + end + + end +end -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/