[前][次][番号順一覧][スレッド一覧]

ruby-changes:29212

From: akr <ko1@a...>
Date: Thu, 13 Jun 2013 00:19:28 +0900 (JST)
Subject: [ruby-changes:29212] akr:r41264 (trunk): * bignum.c (rb_integer_unpack_2comp): New function.

akr	2013-06-13 00:18:00 +0900 (Thu, 13 Jun 2013)

  New Revision: 41264

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=41264

  Log:
    * bignum.c (rb_integer_unpack_2comp): New function.
      (rb_integer_unpack_internal): Extracted from rb_integer_unpack and
      nlp_bits_ret argument added.
      (integer_unpack_num_bdigits_small): nlp_bits_ret argument added to
      return number of leading padding bits.
      (integer_unpack_num_bdigits_generic): Ditto.
    
    * internal.h (rb_integer_unpack_2comp): Declared.
    
    * pack.c (pack_unpack): Use rb_integer_unpack_2comp and
      rb_integer_unpack.

  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 41263)
+++ ChangeLog	(revision 41264)
@@ -1,3 +1,17 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1
+Thu Jun 13 00:17:18 2013  Tanaka Akira  <akr@f...>
+
+	* bignum.c (rb_integer_unpack_2comp): New function.
+	  (rb_integer_unpack_internal): Extracted from rb_integer_unpack and
+	  nlp_bits_ret argument added.
+	  (integer_unpack_num_bdigits_small): nlp_bits_ret argument added to
+	  return number of leading padding bits.
+	  (integer_unpack_num_bdigits_generic): Ditto.
+
+	* internal.h (rb_integer_unpack_2comp): Declared.
+
+	* pack.c (pack_unpack): Use rb_integer_unpack_2comp and
+	  rb_integer_unpack.
+
 Wed Jun 12 23:27:03 2013  Shugo Maeda  <shugo@r...>
 
 	* eval.c (mod_using): new method Module#using, which activates
Index: pack.c
===================================================================
--- pack.c	(revision 41263)
+++ pack.c	(revision 41264)
@@ -1725,38 +1725,16 @@ pack_unpack(VALUE str, VALUE fmt) https://github.com/ruby/ruby/blob/trunk/pack.c#L1725
 #endif
 
               default:
-                if (integer_size > MAX_INTEGER_PACK_SIZE)
-                    rb_bug("unexpected integer size for pack: %d", integer_size);
                 PACK_LENGTH_ADJUST_SIZE(integer_size);
                 while (len-- > 0) {
-                    union {
-                        unsigned long i[(MAX_INTEGER_PACK_SIZE+SIZEOF_LONG)/SIZEOF_LONG];
-                        char a[(MAX_INTEGER_PACK_SIZE+SIZEOF_LONG)/SIZEOF_LONG*SIZEOF_LONG];
-                    } v;
-                    int num_longs = (integer_size+SIZEOF_LONG)/SIZEOF_LONG;
-                    int i;
-
-                    if (signed_p && (signed char)s[bigendian_p ? 0 : (integer_size-1)] < 0)
-                        memset(v.a, 0xff, sizeof(long)*num_longs);
-                    else
-                        memset(v.a, 0, sizeof(long)*num_longs);
-                    if (bigendian_p)
-                        memcpy(v.a + sizeof(long)*num_longs - integer_size, s, integer_size);
+                    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
-                        memcpy(v.a, s, integer_size);
-                    if (bigendian_p) {
-                        for (i = 0; i < num_longs/2; i++) {
-                            unsigned long t = v.i[i];
-                            v.i[i] = v.i[num_longs-1-i];
-                            v.i[num_longs-1-i] = t;
-                        }
-                    }
-                    if (bigendian_p != BIGENDIAN_P()) {
-                        for (i = 0; i < num_longs; i++)
-                            v.i[i] = swapl(v.i[i]);
-                    }
+                        val = rb_integer_unpack(s, integer_size, 1, 0, flags);
+                    UNPACK_PUSH(val);
                     s += integer_size;
-                    UNPACK_PUSH(rb_big_unpack(v.i, num_longs));
                 }
                 PACK_ITEM_ADJUST();
 		break;
Index: ext/-test-/bignum/pack.c
===================================================================
--- ext/-test-/bignum/pack.c	(revision 41263)
+++ ext/-test-/bignum/pack.c	(revision 41264)
@@ -63,6 +63,16 @@ rb_integer_unpack_m(VALUE klass, VALUE b https://github.com/ruby/ruby/blob/trunk/ext/-test-/bignum/pack.c#L63
             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)
 {
@@ -70,6 +80,7 @@ Init_pack(VALUE klass) https://github.com/ruby/ruby/blob/trunk/ext/-test-/bignum/pack.c#L80
     rb_define_method(rb_cInteger, "test_pack", rb_integer_pack_m, 4);
     rb_define_method(rb_cInteger, "test_pack_2comp", rb_integer_pack_2comp_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 41263)
+++ internal.h	(revision 41264)
@@ -451,6 +451,7 @@ VALUE rb_thread_io_blocking_region(rb_bl https://github.com/ruby/ruby/blob/trunk/internal.h#L451
 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);
 int rb_integer_pack_2comp(VALUE val, 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 41263)
+++ bignum.c	(revision 41264)
@@ -1120,15 +1120,17 @@ rb_integer_pack_2comp(VALUE val, void *w https://github.com/ruby/ruby/blob/trunk/bignum.c#L1120
 }
 
 static size_t
-integer_unpack_num_bdigits_small(size_t numwords, size_t wordsize, size_t nails)
+integer_unpack_num_bdigits_small(size_t numwords, size_t wordsize, size_t nails, int *nlp_bits_ret)
 {
-    size_t num_bits;
-    num_bits = (wordsize * CHAR_BIT - nails) * numwords;
-    return (num_bits + SIZEOF_BDIGITS*CHAR_BIT - 1) / (SIZEOF_BDIGITS*CHAR_BIT);
+    /* nlp_bits stands for number of leading padding bits */
+    size_t num_bits = (wordsize * CHAR_BIT - nails) * numwords;
+    size_t num_bdigits = (num_bits + BITSPERDIG - 1) / BITSPERDIG;
+    *nlp_bits_ret = num_bdigits * BITSPERDIG - num_bits;
+    return num_bdigits;
 }
 
 static size_t
-integer_unpack_num_bdigits_generic(size_t numwords, size_t wordsize, size_t nails)
+integer_unpack_num_bdigits_generic(size_t numwords, size_t wordsize, size_t nails, int *nlp_bits_ret)
 {
     /* BITSPERDIG = SIZEOF_BDIGITS * CHAR_BIT */
     /* num_bits = (wordsize * CHAR_BIT - nails) * numwords */
@@ -1172,13 +1174,17 @@ integer_unpack_num_bdigits_generic(size_ https://github.com/ruby/ruby/blob/trunk/bignum.c#L1174
     if (CHAR_BIT * r3 >= r1 * r2) {
         size_t tmp1 = CHAR_BIT * BITSPERDIG - (CHAR_BIT * r3 - r1 * r2);
         size_t q4 = tmp1 / BITSPERDIG;
+        int r4 = tmp1 % BITSPERDIG;
         size_t num_digits2 = num_digits1 + CHAR_BIT - q4;
+        *nlp_bits_ret = r4;
         return num_digits2;
     }
     else {
         size_t tmp1 = - (CHAR_BIT * r3 - r1 * r2);
         size_t q4 = tmp1 / BITSPERDIG;
+        int r4 = tmp1 % BITSPERDIG;
         size_t num_digits2 = num_digits1 - q4;
+        *nlp_bits_ret = r4;
         return num_digits2;
     }
 }
@@ -1195,25 +1201,8 @@ integer_unpack_push_bits(int data, int n https://github.com/ruby/ruby/blob/trunk/bignum.c#L1201
     }
 }
 
-/*
- * 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 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;
     const unsigned char *buf = words;
@@ -1235,30 +1224,21 @@ rb_integer_unpack(const void *words, siz https://github.com/ruby/ruby/blob/trunk/bignum.c#L1224
     BDIGIT_DBL dd;
     int numbits_in_dd;
 
-    validate_integer_pack_format(numwords, wordsize, nails, flags,
-            INTEGER_PACK_MSWORD_FIRST|
-            INTEGER_PACK_LSWORD_FIRST|
-            INTEGER_PACK_MSBYTE_FIRST|
-            INTEGER_PACK_LSBYTE_FIRST|
-            INTEGER_PACK_NATIVE_BYTE_ORDER|
-            INTEGER_PACK_FORCE_BIGNUM|
-            INTEGER_PACK_NEGATIVE);
-
     if (numwords <= (SIZE_MAX - (SIZEOF_BDIGITS*CHAR_BIT-1)) / CHAR_BIT / wordsize) {
-        num_bdigits = integer_unpack_num_bdigits_small(numwords, wordsize, nails);
+        num_bdigits = integer_unpack_num_bdigits_small(numwords, wordsize, nails, nlp_bits_ret);
 #if 0
         {
-            size_t num_bdigits1 = integer_unpack_num_bdigits_generic(numwords, wordsize, nails);
+            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);
+        num_bdigits = integer_unpack_num_bdigits_generic(numwords, wordsize, nails, nlp_bits_ret);
     }
     if (num_bdigits == 0) {
-        if (flags & INTEGER_PACK_FORCE_BIGNUM)
-            return rb_int2big(0);
         return LONG2FIX(0);
     }
     if (LONG_MAX < num_bdigits)
@@ -1305,12 +1285,119 @@ rb_integer_unpack(const void *words, siz https://github.com/ruby/ruby/blob/trunk/bignum.c#L1285
     while (dp < de)
         *dp++ = 0;
 
-    if (flags & INTEGER_PACK_FORCE_BIGNUM)
-        return bigtrunc(result);
-    return bignorm(result);
+    return result;
 #undef PUSH_BITS
 }
 
+/*
+ * 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)
+{
+    int nlp_bits;
+    VALUE val;
+
+    validate_integer_pack_format(numwords, wordsize, nails, flags,
+            INTEGER_PACK_MSWORD_FIRST|
+            INTEGER_PACK_LSWORD_FIRST|
+            INTEGER_PACK_MSBYTE_FIRST|
+            INTEGER_PACK_LSBYTE_FIRST|
+            INTEGER_PACK_NATIVE_BYTE_ORDER|
+            INTEGER_PACK_FORCE_BIGNUM|
+            INTEGER_PACK_NEGATIVE);
+
+    val = rb_integer_unpack_internal(words, numwords, wordsize, nails, flags, &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);
+}
+
+/*
+ * 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] 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.)
+ *
+ * This function returns the imported integer as Fixnum or Bignum.
+ */
+VALUE
+rb_integer_unpack_2comp(const void *words, size_t numwords, size_t wordsize, size_t nails, int flags)
+{
+    VALUE val;
+    int nlp_bits;
+
+    validate_integer_pack_format(numwords, wordsize, nails, flags,
+            INTEGER_PACK_MSWORD_FIRST|
+            INTEGER_PACK_LSWORD_FIRST|
+            INTEGER_PACK_MSBYTE_FIRST|
+            INTEGER_PACK_LSBYTE_FIRST|
+            INTEGER_PACK_NATIVE_BYTE_ORDER|
+            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);
+    }
+
+    if (flags & INTEGER_PACK_FORCE_BIGNUM)
+        return bigtrunc(val);
+    return bignorm(val);
+}
+
 #define QUAD_SIZE 8
 
 #if SIZEOF_LONG_LONG == QUAD_SIZE && SIZEOF_BDIGITS*2 == SIZEOF_LONG_LONG
Index: test/-ext-/bignum/test_pack.rb
===================================================================
--- test/-ext-/bignum/test_pack.rb	(revision 41263)
+++ test/-ext-/bignum/test_pack.rb	(revision 41264)
@@ -162,5 +162,36 @@ class TestBignum < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/-ext-/bignum/test_pack.rb#L162
       assert_equal(-0x8070605040302010, Integer.test_unpack("\x80\x70\x60\x50\x40\x30\x20\x10", 8, 1, 0, BIG_ENDIAN|NEGATIVE))
     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))
+    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))
+      assert_equal(-1, Integer.test_unpack_2comp("\xFF"*9, 9, 1, 0, BIG_ENDIAN))
+    end
+
+    def test_unpack2comp_negative_single_byte
+      assert_equal(-256, Integer.test_unpack_2comp("\x00", 1, 1, 0, BIG_ENDIAN|NEGATIVE))
+      assert_equal(-255, Integer.test_unpack_2comp("\x01", 1, 1, 0, BIG_ENDIAN|NEGATIVE))
+      assert_equal(-254, Integer.test_unpack_2comp("\x02", 1, 1, 0, BIG_ENDIAN|NEGATIVE))
+      assert_equal(-129, Integer.test_unpack_2comp("\x7F", 1, 1, 0, BIG_ENDIAN|NEGATIVE))
+      assert_equal(-128, Integer.test_unpack_2comp("\x80", 1, 1, 0, BIG_ENDIAN|NEGATIVE))
+      assert_equal(  -2, Integer.test_unpack_2comp("\xFE", 1, 1, 0, BIG_ENDIAN|NEGATIVE))
+      assert_equal(  -1, Integer.test_unpack_2comp("\xFF", 1, 1, 0, BIG_ENDIAN|NEGATIVE))
+    end
+
   end
 end

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]