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

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/

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