ruby-changes:29285
From: akr <ko1@a...>
Date: Sun, 16 Jun 2013 21:59:37 +0900 (JST)
Subject: [ruby-changes:29285] akr:r41337 (trunk): * bignum.c (bary_2comp): Extracted from get2comp.
akr 2013-06-16 21:59:26 +0900 (Sun, 16 Jun 2013) New Revision: 41337 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=41337 Log: * bignum.c (bary_2comp): Extracted from get2comp. (integer_unpack_num_bdigits): Extracted from rb_integer_unpack_internal. (bary_unpack_internal): Renamed from bary_unpack and support INTEGER_PACK_2COMP. (bary_unpack): New function to validate arguments and invoke bary_unpack_internal. (rb_integer_unpack_internal): Removed. (rb_integer_unpack): Invoke bary_unpack_internal. (rb_integer_unpack_2comp): Removed. * internal.h (rb_integer_unpack_2comp): Removed. * pack.c: Follow the above change. Modified files: trunk/ChangeLog trunk/bignum.c trunk/ext/-test-/bignum/pack.c trunk/internal.h trunk/pack.c trunk/test/-ext-/bignum/test_pack.rb Index: ChangeLog =================================================================== --- ChangeLog (revision 41336) +++ ChangeLog (revision 41337) @@ -1,3 +1,20 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Sun Jun 16 21:41:39 2013 Tanaka Akira <akr@f...> + + * bignum.c (bary_2comp): Extracted from get2comp. + (integer_unpack_num_bdigits): Extracted from + rb_integer_unpack_internal. + (bary_unpack_internal): Renamed from bary_unpack and support + INTEGER_PACK_2COMP. + (bary_unpack): New function to validate arguments and invoke + bary_unpack_internal. + (rb_integer_unpack_internal): Removed. + (rb_integer_unpack): Invoke bary_unpack_internal. + (rb_integer_unpack_2comp): Removed. + + * internal.h (rb_integer_unpack_2comp): Removed. + + * pack.c: Follow the above change. + Sun Jun 16 18:41:42 2013 Tanaka Akira <akr@f...> * internal.h (INTEGER_PACK_2COMP): Defined. Index: pack.c =================================================================== --- pack.c (revision 41336) +++ pack.c (revision 41337) @@ -1731,9 +1731,8 @@ pack_unpack(VALUE str, VALUE fmt) https://github.com/ruby/ruby/blob/trunk/pack.c#L1731 int flags = bigendian_p ? INTEGER_PACK_BIG_ENDIAN : INTEGER_PACK_LITTLE_ENDIAN; VALUE val; if (signed_p) - val = rb_integer_unpack_2comp(s, integer_size, 1, 0, flags); - else - val = rb_integer_unpack(s, integer_size, 1, 0, flags); + flags |= INTEGER_PACK_2COMP; + val = rb_integer_unpack(s, integer_size, 1, 0, flags); UNPACK_PUSH(val); s += integer_size; } Index: ext/-test-/bignum/pack.c =================================================================== --- ext/-test-/bignum/pack.c (revision 41336) +++ ext/-test-/bignum/pack.c (revision 41337) @@ -45,23 +45,12 @@ rb_integer_unpack_m(VALUE klass, VALUE b https://github.com/ruby/ruby/blob/trunk/ext/-test-/bignum/pack.c#L45 NUM2SIZET(nails), NUM2INT(flags)); } -static VALUE -rb_integer_unpack_2comp_m(VALUE klass, VALUE buf, VALUE numwords, VALUE wordsize, VALUE nails, VALUE flags) -{ - StringValue(buf); - - return rb_integer_unpack_2comp(RSTRING_PTR(buf), - NUM2SIZET(numwords), NUM2SIZET(wordsize), - NUM2SIZET(nails), NUM2INT(flags)); -} - void Init_pack(VALUE klass) { rb_define_method(rb_cInteger, "test_pack_raw", rb_integer_pack_raw_m, 5); rb_define_method(rb_cInteger, "test_pack", rb_integer_pack_m, 4); rb_define_singleton_method(rb_cInteger, "test_unpack", rb_integer_unpack_m, 5); - rb_define_singleton_method(rb_cInteger, "test_unpack_2comp", rb_integer_unpack_2comp_m, 5); rb_define_const(rb_cInteger, "INTEGER_PACK_MSWORD_FIRST", INT2NUM(INTEGER_PACK_MSWORD_FIRST)); rb_define_const(rb_cInteger, "INTEGER_PACK_LSWORD_FIRST", INT2NUM(INTEGER_PACK_LSWORD_FIRST)); rb_define_const(rb_cInteger, "INTEGER_PACK_MSBYTE_FIRST", INT2NUM(INTEGER_PACK_MSBYTE_FIRST)); Index: internal.h =================================================================== --- internal.h (revision 41336) +++ internal.h (revision 41337) @@ -451,7 +451,6 @@ VALUE rb_thread_io_blocking_region(rb_bl https://github.com/ruby/ruby/blob/trunk/internal.h#L451 /* bignum.c */ int rb_integer_pack(VALUE val, void *words, size_t numwords, size_t wordsize, size_t nails, int flags); VALUE rb_integer_unpack(const void *words, size_t numwords, size_t wordsize, size_t nails, int flags); -VALUE rb_integer_unpack_2comp(const void *words, size_t numwords, size_t wordsize, size_t nails, int flags); /* io.c */ void rb_maygvl_fd_fix_cloexec(int fd); Index: bignum.c =================================================================== --- bignum.c (revision 41336) +++ bignum.c (revision 41337) @@ -64,6 +64,7 @@ static void bary_sub(BDIGIT *zds, size_t https://github.com/ruby/ruby/blob/trunk/bignum.c#L64 static void bary_divmod(BDIGIT *qds, size_t nq, BDIGIT *rds, size_t nr, BDIGIT *xds, size_t nx, BDIGIT *yds, size_t ny); static void bary_add(BDIGIT *zds, size_t zn, BDIGIT *xds, size_t xn, BDIGIT *yds, size_t yn); static int bary_pack(int sign, BDIGIT *ds, size_t num_bdigits, void *words, size_t numwords, size_t wordsize, size_t nails, int flags); +static BDIGIT bary_2comp(BDIGIT *ds, size_t n); #define BIGNUM_DEBUG 0 #if BIGNUM_DEBUG @@ -218,6 +219,22 @@ rb_big_clone(VALUE x) https://github.com/ruby/ruby/blob/trunk/bignum.c#L219 return z; } +static BDIGIT +bary_2comp(BDIGIT *ds, size_t n) +{ + size_t i = n; + BDIGIT_DBL num; + if (!n) return 1; + while (i--) ds[i] = ~ds[i]; + i = 0; num = 1; + do { + num += ds[i]; + ds[i++] = BIGLO(num); + num = BIGDN(num); + } while (i < n); + return (BDIGIT)num; +} + /* modify a bignum by 2's complement */ static void get2comp(VALUE x) @@ -1228,6 +1245,28 @@ integer_unpack_num_bdigits_generic(size_ https://github.com/ruby/ruby/blob/trunk/bignum.c#L1245 } } +static size_t +integer_unpack_num_bdigits(size_t numwords, size_t wordsize, size_t nails, int *nlp_bits_ret) +{ + size_t num_bdigits; + + if (numwords <= (SIZE_MAX - (BITSPERDIG-1)) / CHAR_BIT / wordsize) { + num_bdigits = integer_unpack_num_bdigits_small(numwords, wordsize, nails, nlp_bits_ret); +#ifdef DEBUG_INTEGER_PACK + { + int nlp_bits1; + size_t num_bdigits1 = integer_unpack_num_bdigits_generic(numwords, wordsize, nails, &nlp_bits1); + assert(num_bdigits == num_bdigits1); + assert(*nlp_bits_ret == nlp_bits1); + } +#endif + } + else { + num_bdigits = integer_unpack_num_bdigits_generic(numwords, wordsize, nails, nlp_bits_ret); + } + return num_bdigits; +} + static inline void integer_unpack_push_bits(int data, int numbits, BDIGIT_DBL *ddp, int *numbits_in_dd_p, BDIGIT **dpp) { @@ -1240,9 +1279,11 @@ integer_unpack_push_bits(int data, int n https://github.com/ruby/ruby/blob/trunk/bignum.c#L1279 } } -static void -bary_unpack(BDIGIT *bdigits, size_t num_bdigits, const void *words, size_t numwords, size_t wordsize, size_t nails, int flags) +static int +bary_unpack_internal(BDIGIT *bdigits, size_t num_bdigits, const void *words, size_t numwords, size_t wordsize, size_t nails, int flags, int nlp_bits) { + int sign = (flags & INTEGER_PACK_NEGATIVE) ? -1 : 1; + const unsigned char *buf = words; BDIGIT *dp; @@ -1260,104 +1301,75 @@ bary_unpack(BDIGIT *bdigits, size_t num_ https://github.com/ruby/ruby/blob/trunk/bignum.c#L1301 BDIGIT_DBL dd; int numbits_in_dd; - dp = bdigits; - de = dp + num_bdigits; + if (num_bdigits) { + dp = bdigits; + de = dp + num_bdigits; - integer_pack_loop_setup(numwords, wordsize, nails, flags, - &word_num_fullbytes, &word_num_partialbits, - &word_start, &word_step, &word_last, &byte_start, &byte_step); + integer_pack_loop_setup(numwords, wordsize, nails, flags, + &word_num_fullbytes, &word_num_partialbits, + &word_start, &word_step, &word_last, &byte_start, &byte_step); - wordp = buf + word_start; - last_wordp = buf + word_last; + wordp = buf + word_start; + last_wordp = buf + word_last; - dd = 0; - numbits_in_dd = 0; + dd = 0; + numbits_in_dd = 0; #define PUSH_BITS(data, numbits) \ - integer_unpack_push_bits(data, numbits, &dd, &numbits_in_dd, &dp) + integer_unpack_push_bits(data, numbits, &dd, &numbits_in_dd, &dp) - while (1) { - size_t index_in_word = 0; - const unsigned char *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++; - } + while (1) { + size_t index_in_word = 0; + const unsigned char *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; + if (wordp == last_wordp) + break; - wordp += word_step; - } - if (dd) - *dp++ = (BDIGIT)dd; - assert(dp <= de); - while (dp < de) - *dp++ = 0; + wordp += word_step; + } + if (dd) + *dp++ = (BDIGIT)dd; + assert(dp <= de); + while (dp < de) + *dp++ = 0; #undef PUSH_BITS -} - -static VALUE -rb_integer_unpack_internal(const void *words, size_t numwords, size_t wordsize, size_t nails, int flags, int *nlp_bits_ret) -{ - VALUE result; - size_t num_bdigits; - int sign = (flags & INTEGER_PACK_NEGATIVE) ? -1 : 1; + } - if (numwords <= (SIZE_MAX - (BITSPERDIG-1)) / CHAR_BIT / wordsize) { - num_bdigits = integer_unpack_num_bdigits_small(numwords, wordsize, nails, nlp_bits_ret); -#ifdef DEBUG_INTEGER_PACK - { - int nlp_bits1; - size_t num_bdigits1 = integer_unpack_num_bdigits_generic(numwords, wordsize, nails, &nlp_bits1); - assert(num_bdigits == num_bdigits1); - assert(*nlp_bits_ret == nlp_bits1); + if (flags & INTEGER_PACK_2COMP) { + if (num_bdigits == 0) { + if (flags & INTEGER_PACK_NEGATIVE) + sign = -1; + else + sign = 0; + } + else if ((flags & INTEGER_PACK_NEGATIVE) || + (num_bdigits != 0 && + (bdigits[num_bdigits-1] >> (BITSPERDIG - nlp_bits - 1)))) { + if (nlp_bits) + bdigits[num_bdigits-1] |= (~(BDIGIT)0) << (BITSPERDIG - nlp_bits); + bary_2comp(bdigits, num_bdigits); + sign = -1; } -#endif - } - else { - num_bdigits = integer_unpack_num_bdigits_generic(numwords, wordsize, nails, nlp_bits_ret); - } - if (num_bdigits == 0) { - return LONG2FIX(0); } - if (LONG_MAX < num_bdigits) - rb_raise(rb_eArgError, "too big to unpack as an integer"); - result = bignew((long)num_bdigits, 0 <= sign); - - bary_unpack(BDIGITS(result), num_bdigits, words, numwords, wordsize, nails, flags); - return result; + return sign; } -/* - * Import an integer into a buffer. - * - * [words] buffer to import. - * [numwords] the size of given buffer as number of words. - * [wordsize] the size of word as number of bytes. - * [nails] number of padding bits in a word. - * Most significant nails bits of each word are ignored. - * [flags] bitwise or of constants which name starts "INTEGER_PACK_". - * It specifies word order and byte order. - * [INTEGER_PACK_FORCE_BIGNUM] the result will be a Bignum - * even if it is representable as a Fixnum. - * [INTEGER_PACK_NEGATIVE] Returns non-positive value. - * (Returns non-negative value if not specified.) - * - * This function returns the imported integer as Fixnum or Bignum. - */ -VALUE -rb_integer_unpack(const void *words, size_t numwords, size_t wordsize, size_t nails, int flags) +static void +bary_unpack(BDIGIT *bdigits, size_t num_bdigits, const void *words, size_t numwords, size_t wordsize, size_t nails, int flags) { + size_t num_bdigits0; int nlp_bits; - VALUE val; validate_integer_pack_format(numwords, wordsize, nails, flags, INTEGER_PACK_MSWORD_FIRST| @@ -1365,19 +1377,15 @@ rb_integer_unpack(const void *words, siz https://github.com/ruby/ruby/blob/trunk/bignum.c#L1377 INTEGER_PACK_MSBYTE_FIRST| INTEGER_PACK_LSBYTE_FIRST| INTEGER_PACK_NATIVE_BYTE_ORDER| + INTEGER_PACK_2COMP| INTEGER_PACK_FORCE_BIGNUM| INTEGER_PACK_NEGATIVE); - val = rb_integer_unpack_internal(words, numwords, wordsize, nails, flags, &nlp_bits); + num_bdigits0 = integer_unpack_num_bdigits(numwords, wordsize, nails, &nlp_bits); - if (val == LONG2FIX(0)) { - if (flags & INTEGER_PACK_FORCE_BIGNUM) - return rb_int2big(0); - return LONG2FIX(0); - } - if (flags & INTEGER_PACK_FORCE_BIGNUM) - return bigtrunc(val); - return bignorm(val); + assert(num_bdigits0 <= num_bdigits); + + bary_unpack_internal(bdigits, num_bdigits, words, numwords, wordsize, nails, flags, nlp_bits); } /* @@ -1389,21 +1397,48 @@ rb_integer_unpack(const void *words, siz https://github.com/ruby/ruby/blob/trunk/bignum.c#L1397 * [nails] number of padding bits in a word. * Most significant nails bits of each word are ignored. * [flags] bitwise or of constants which name starts "INTEGER_PACK_". - * It specifies word order and byte order. - * [INTEGER_PACK_FORCE_BIGNUM] the result will be a Bignum - * even if it is representable as a Fixnum. - * [INTEGER_PACK_NEGATIVE] Assume the higher bits are 1. - * (If INTEGER_PACK_NEGATIVE is not specified, the higher bits are - * assumed same as the most significant bit. - * i.e. sign extension is applied.) + * + * flags: + * [INTEGER_PACK_MSWORD_FIRST] Interpret the first word as the most significant word. + * [INTEGER_PACK_LSWORD_FIRST] Interpret the first word as the least significant word. + * [INTEGER_PACK_MSBYTE_FIRST] Interpret the first byte in a word as the most significant byte in the word. + * [INTEGER_PACK_LSBYTE_FIRST] Interpret the first byte in a word as the least significant byte in the word. + * [INTEGER_PACK_NATIVE_BYTE_ORDER] INTEGER_PACK_MSBYTE_FIRST or INTEGER_PACK_LSBYTE_FIRST corresponding to the host's endian. + * [INTEGER_PACK_2COMP] Use 2's complement representation. + * [INTEGER_PACK_LITTLE_ENDIAN] Same as INTEGER_PACK_LSWORD_FIRST|INTEGER_PACK_LSBYTE_FIRST + * [INTEGER_PACK_BIG_ENDIAN] Same as INTEGER_PACK_MSWORD_FIRST|INTEGER_PACK_MSBYTE_FIRST + * [INTEGER_PACK_FORCE_BIGNUM] the result will be a Bignum + * even if it is representable as a Fixnum. + * [INTEGER_PACK_NEGATIVE] Returns non-positive value. + * (Returns non-negative value if not specified.) * * This function returns the imported integer as Fixnum or Bignum. + * + * The range of the result value depends on INTEGER_PACK_2COMP and INTEGER_PACK_NEGATIVE. + * + * INTEGER_PACK_2COMP is not set: + * 0 <= val < 2**(numwords*(wordsize*CHAR_BIT-nails)) if !INTEGER_PACK_NEGATIVE + * -2**(numwords*(wordsize*CHAR_BIT-nails)) < val <= 0 if INTEGER_PACK_NEGATIVE + * + * INTEGER_PACK_2COMP is set: + * -2**(numwords*(wordsize*CHAR_BIT-nails)-1) <= val <= 2**(numwords*(wordsize*CHAR_BIT-nails)-1)-1 if !INTEGER_PACK_NEGATIVE + * -2**(numwords*(wordsize*CHAR_BIT-nails)) <= val <= -1 if INTEGER_PACK_NEGATIVE + * + * INTEGER_PACK_2COMP without INTEGER_PACK_NEGATIVE means sign extension. + * INTEGER_PACK_2COMP with INTEGER_PACK_NEGATIVE mean assuming the higher bits are 1. + * + * Note that this function returns 0 when numwords is zero and + * INTEGER_PACK_2COMP is set but INTEGER_PACK_NEGATIVE is not set. */ + VALUE -rb_integer_unpack_2comp(const void *words, size_t numwords, size_t wordsize, size_t nails, int flags) +rb_integer_unpack(const void *words, size_t numwords, size_t wordsize, size_t nails, int flags) { VALUE val; + size_t num_bdigits; + int sign; int nlp_bits; + BDIGIT *ds; validate_integer_pack_format(numwords, wordsize, nails, flags, INTEGER_PACK_MSWORD_FIRST| @@ -1411,34 +1446,23 @@ rb_integer_unpack_2comp(const void *word https://github.com/ruby/ruby/blob/trunk/bignum.c#L1446 INTEGER_PACK_MSBYTE_FIRST| INTEGER_PACK_LSBYTE_FIRST| INTEGER_PACK_NATIVE_BYTE_ORDER| + INTEGER_PACK_2COMP| INTEGER_PACK_FORCE_BIGNUM| INTEGER_PACK_NEGATIVE); - val = rb_integer_unpack_internal(words, numwords, wordsize, nails, - (flags & (INTEGER_PACK_WORDORDER_MASK|INTEGER_PACK_BYTEORDER_MASK) | - INTEGER_PACK_FORCE_BIGNUM), - &nlp_bits); - - if (val == LONG2FIX(0)) { - /* num_bdigits == 0 i.e. num_bits == 0 */ - int v; - if (flags & INTEGER_PACK_NEGATIVE) - v = -1; - else - v = 0; - if (flags & INTEGER_PACK_FORCE_BIGNUM) - return rb_int2big(v); - else - return LONG2FIX(v); - } - else if ((flags & INTEGER_PACK_NEGATIVE) || - (RBIGNUM_LEN(val) != 0 && - (RBIGNUM_DIGITS(val)[RBIGNUM_LEN(val)-1] >> (BITSPERDIG - nlp_bits - 1)))) { - if (nlp_bits) - RBIGNUM_DIGITS(val)[RBIGNUM_LEN(val)-1] |= (~(BDIGIT)0) << (BITSPERDIG - nlp_bits); - rb_big_2comp(val); - RBIGNUM_SET_SIGN(val, 0); + num_bdigits = integer_unpack_num_bdigits(numwords, wordsize, nails, &nlp_bits); + + if (LONG_MAX < num_bdigits) + rb_raise(rb_eArgError, "too big to unpack as an integer"); + val = bignew((long)num_bdigits, 0); + ds = BDIGITS(val); + sign = bary_unpack_internal(ds, num_bdigits, words, numwords, wordsize, nails, flags, nlp_bits); + + if ((flags & INTEGER_PACK_2COMP) && num_bdigits == 0 && sign < 0) { + rb_big_resize(val, 1); + ds[0] = 1; } + RBIGNUM_SET_SIGN(val, 0 <= sign); if (flags & INTEGER_PACK_FORCE_BIGNUM) return bigtrunc(val); Index: test/-ext-/bignum/test_pack.rb =================================================================== --- test/-ext-/bignum/test_pack.rb (revision 41336) +++ test/-ext-/bignum/test_pack.rb (revision 41337) @@ -164,34 +164,34 @@ class TestBignum < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/-ext-/bignum/test_pack.rb#L164 end def test_unpack2comp_single_byte - assert_equal(-128, Integer.test_unpack_2comp("\x80", 1, 1, 0, BIG_ENDIAN)) - assert_equal( -2, Integer.test_unpack_2comp("\xFE", 1, 1, 0, BIG_ENDIAN)) - assert_equal( -1, Integer.test_unpack_2comp("\xFF", 1, 1, 0, BIG_ENDIAN)) - assert_equal( 0, Integer.test_unpack_2comp("\x00", 1, 1, 0, BIG_ENDIAN)) - assert_equal( 1, Integer.test_unpack_2comp("\x01", 1, 1, 0, BIG_ENDIAN)) - assert_equal( 2, Integer.test_unpack_2comp("\x02", 1, 1, 0, BIG_ENDIAN)) - assert_equal( 127, Integer.test_unpack_2comp("\x7F", 1, 1, 0, BIG_ENDIAN)) + assert_equal(-128, Integer.test_unpack("\x80", 1, 1, 0, TWOCOMP|BIG_ENDIAN)) + assert_equal( -2, Integer.test_unpack("\xFE", 1, 1, 0, TWOCOMP|BIG_ENDIAN)) + assert_equal( -1, Integer.test_unpack("\xFF", 1, 1, 0, TWOCOMP|BIG_ENDIAN)) + assert_equal( 0, Integer.test_unpack("\x00", 1, 1, 0, TWOCOMP|BIG_ENDIAN)) + assert_equal( 1, Integer.test_unpack("\x01", 1, 1, 0, TWOCOMP|BIG_ENDIAN)) + assert_equal( 2, Integer.test_unpack("\x02", 1, 1, 0, TWOCOMP|BIG_ENDIAN)) + assert_equal( 127, Integer.test_unpack("\x7F", 1, 1, 0, TWOCOMP|BIG_ENDIAN)) end def test_unpack2comp_sequence_of_ff - assert_equal(-1, Integer.test_unpack_2comp("\xFF"*2, 2, 1, 0, BIG_ENDIAN)) - assert_equal(-1, Integer.test_unpack_2comp("\xFF"*3, 3, 1, 0, BIG_ENDIAN)) - assert_equal(-1, Integer.test_unpack_2comp("\xFF"*4, 4, 1, 0, BIG_ENDIAN)) - assert_equal(-1, Integer.test_unpack_2comp("\xFF"*5, 5, 1, 0, BIG_ENDIAN)) - assert_equal(-1, Integer.test_unpack_2comp("\xFF"*6, 6, 1, 0, BIG_ENDIAN)) - assert_equal(-1, Integer.test_unpack_2comp("\xFF"*7, 7, 1, 0, BIG_ENDIAN)) - assert_equal(-1, Integer.test_unpack_2comp("\xFF"*8, 8, 1, 0, BIG_ENDIAN)) - (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/