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

ruby-changes:29178

From: akr <ko1@a...>
Date: Tue, 11 Jun 2013 21:06:54 +0900 (JST)
Subject: [ruby-changes:29178] akr:r41230 (trunk): * bignum.c (rb_integer_pack_internal): Renamed from rb_integer_pack

akr	2013-06-11 21:06:40 +0900 (Tue, 11 Jun 2013)

  New Revision: 41230

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

  Log:
    * bignum.c (rb_integer_pack_internal): Renamed from rb_integer_pack
      and overflow_2comp argument added.
      (rb_integer_pack): Just call rb_integer_pack_internal.
      (rb_integer_pack_2comp): New function.
    
    * internal.h (rb_integer_pack_2comp): Declared.
    
    * sprintf.c (rb_str_format): Use rb_integer_pack and
      rb_integer_pack_2comp to format binary/octal/hexadecimal integers.
      (ruby_digitmap): Declared.
      (remove_sign_bits): Removed.
      (BITSPERDIG): Ditto.
      (EXTENDSIGN): Ditto.

  Modified files:
    trunk/ChangeLog
    trunk/bignum.c
    trunk/ext/-test-/bignum/pack.c
    trunk/internal.h
    trunk/sprintf.c
    trunk/test/-ext-/bignum/test_pack.rb

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 41229)
+++ ChangeLog	(revision 41230)
@@ -1,3 +1,19 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1
+Tue Jun 11 20:52:43 2013  Tanaka Akira  <akr@f...>
+
+	* bignum.c (rb_integer_pack_internal): Renamed from rb_integer_pack
+	  and overflow_2comp argument added.
+	  (rb_integer_pack): Just call rb_integer_pack_internal.
+	  (rb_integer_pack_2comp): New function.
+
+	* internal.h (rb_integer_pack_2comp): Declared.
+
+	* sprintf.c (rb_str_format): Use rb_integer_pack and
+	  rb_integer_pack_2comp to format binary/octal/hexadecimal integers.
+	  (ruby_digitmap): Declared.
+	  (remove_sign_bits): Removed.
+	  (BITSPERDIG): Ditto.
+	  (EXTENDSIGN): Ditto.
+
 Tue Jun 11 16:15:03 2013  Nobuyoshi Nakada  <nobu@r...>
 
 	* array.c (ary_shrink_capa): shrink the capacity so it fits just with
Index: sprintf.c
===================================================================
--- sprintf.c	(revision 41229)
+++ sprintf.c	(revision 41230)
@@ -23,35 +23,10 @@ https://github.com/ruby/ruby/blob/trunk/sprintf.c#L23
 #endif
 
 #define BIT_DIGITS(N)   (((N)*146)/485 + 1)  /* log2(10) =~ 146/485 */
-#define BITSPERDIG (SIZEOF_BDIGITS*CHAR_BIT)
-#define EXTENDSIGN(n, l) (((~0 << (n)) >> (((n)*(l)) % BITSPERDIG)) & ~(~0 << (n)))
 
-static void fmt_setup(char*,size_t,int,int,int,int);
-
-static char*
-remove_sign_bits(char *str, int base)
-{
-    char *t = str;
+extern const char ruby_digitmap[];
 
-    if (base == 16) {
-	while (*t == 'f') {
-	    t++;
-	}
-    }
-    else if (base == 8) {
-	*t |= EXTENDSIGN(3, strlen(t));
-	while (*t == '7') {
-	    t++;
-	}
-    }
-    else if (base == 2) {
-	while (*t == '1') {
-	    t++;
-	}
-    }
-
-    return t;
-}
+static void fmt_setup(char*,size_t,int,int,int,int);
 
 static char
 sign_bits(int base, const char *p)
@@ -758,6 +733,7 @@ rb_str_format(int argc, const VALUE *arg https://github.com/ruby/ruby/blob/trunk/sprintf.c#L733
 	  case 'u':
 	    {
 		volatile VALUE val = GETARG();
+                int valsign;
 		char fbuf[32], nbuf[64], *s;
 		const char *prefix = 0;
 		int sign = 0, dots = 0;
@@ -835,96 +811,101 @@ rb_str_format(int argc, const VALUE *arg https://github.com/ruby/ruby/blob/trunk/sprintf.c#L811
 		    base = 10; break;
 		}
 
-		if (!bignum) {
-		    if (base == 2) {
-			val = rb_int2big(v);
-			goto bin_retry;
-		    }
-		    if (sign) {
-			char c = *p;
-			if (c == 'i') c = 'd'; /* %d and %i are identical */
-			if (v < 0) {
-			    v = -v;
-			    sc = '-';
-			    width--;
-			}
-			else if (flags & FPLUS) {
-			    sc = '+';
-			    width--;
-			}
-			else if (flags & FSPACE) {
-			    sc = ' ';
-			    width--;
-			}
-			snprintf(fbuf, sizeof(fbuf), "%%l%c", c);
-			snprintf(nbuf, sizeof(nbuf), fbuf, v);
-			s = nbuf;
-		    }
-		    else {
-			s = nbuf;
-			if (v < 0) {
-			    dots = 1;
-			}
-			snprintf(fbuf, sizeof(fbuf), "%%l%c", *p == 'X' ? 'x' : *p);
-			snprintf(++s, sizeof(nbuf) - 1, fbuf, v);
-			if (v < 0) {
-			    char d = 0;
-
-			    s = remove_sign_bits(s, base);
-			    switch (base) {
-			      case 16:
-				d = 'f'; break;
-			      case 8:
-				d = '7'; break;
-			    }
-			    if (d && *s != d) {
-				*--s = d;
-			    }
-			}
-		    }
+                if (base != 10) {
+                    int numbits = ffs(base)-1;
+                    size_t abs_nlz_bits;
+                    size_t numdigits = rb_absint_numwords(val, numbits, &abs_nlz_bits);
+                    long i;
+                    if (INT_MAX-1 < numdigits) /* INT_MAX is used because rb_long2int is used later. */
+                        rb_raise(rb_eArgError, "size too big");
+                    if (sign) {
+                        if (numdigits == 0)
+                            numdigits = 1;
+                        tmp = rb_str_new(NULL, numdigits);
+                        valsign = rb_integer_pack(val, RSTRING_PTR(tmp), RSTRING_LEN(tmp),
+                                1, CHAR_BIT-numbits, INTEGER_PACK_BIG_ENDIAN);
+                        for (i = 0; i < RSTRING_LEN(tmp); i++)
+                            RSTRING_PTR(tmp)[i] = ruby_digitmap[((unsigned char *)RSTRING_PTR(tmp))[i]];
+                        s = RSTRING_PTR(tmp);
+                        if (valsign < 0) {
+                            sc = '-';
+                            width--;
+                        }
+                        else if (flags & FPLUS) {
+                            sc = '+';
+                            width--;
+                        }
+                        else if (flags & FSPACE) {
+                            sc = ' ';
+                            width--;
+                        }
+                    }
+                    else {
+                        /* Following conditional "numdigits++" guarantees the
+                         * most significant digit as
+                         * - '1'(bin), '7'(oct) or 'f'(hex) for negative numbers
+                         * - '0' for zero
+                         * - not '0' for positive numbers.
+                         *
+                         * It also guarantees the most significant two
+                         * digits will not be '11'(bin), '77'(oct), 'ff'(hex)
+                         * or '00'.  */
+                        if (numdigits == 0 ||
+                                ((abs_nlz_bits != (size_t)(numbits-1) ||
+                                  !rb_absint_singlebit_p(val)) &&
+                                 (!bignum ? v < 0 : RBIGNUM_NEGATIVE_P(val))))
+                            numdigits++;
+                        tmp = rb_str_new(NULL, numdigits);
+                        valsign = rb_integer_pack_2comp(val, RSTRING_PTR(tmp), RSTRING_LEN(tmp),
+                                1, CHAR_BIT-numbits, INTEGER_PACK_BIG_ENDIAN);
+                        for (i = 0; i < RSTRING_LEN(tmp); i++)
+                            RSTRING_PTR(tmp)[i] = ruby_digitmap[((unsigned char *)RSTRING_PTR(tmp))[i]];
+                        s = RSTRING_PTR(tmp);
+                        dots = valsign < 0;
+                    }
+                    len = rb_long2int(RSTRING_END(tmp) - s);
+                }
+                else if (!bignum) {
+                    char c = *p;
+                    if (c == 'i') c = 'd'; /* %d and %i are identical */
+                    valsign = 1;
+                    if (v < 0) {
+                        v = -v;
+                        sc = '-';
+                        width--;
+                        valsign = -1;
+                    }
+                    else if (flags & FPLUS) {
+                        sc = '+';
+                        width--;
+                    }
+                    else if (flags & FSPACE) {
+                        sc = ' ';
+                        width--;
+                    }
+                    snprintf(fbuf, sizeof(fbuf), "%%l%c", c);
+                    snprintf(nbuf, sizeof(nbuf), fbuf, v);
+                    s = nbuf;
 		    len = (int)strlen(s);
 		}
 		else {
-		    if (sign) {
-			tmp = rb_big2str(val, base);
-			s = RSTRING_PTR(tmp);
-			if (s[0] == '-') {
-			    s++;
-			    sc = '-';
-			    width--;
-			}
-			else if (flags & FPLUS) {
-			    sc = '+';
-			    width--;
-			}
-			else if (flags & FSPACE) {
-			    sc = ' ';
-			    width--;
-			}
-		    }
-		    else {
-			if (!RBIGNUM_SIGN(val)) {
-			    val = rb_big_clone(val);
-			    rb_big_2comp(val);
-			}
-			tmp = rb_big2str0(val, base, RBIGNUM_SIGN(val));
-			s = RSTRING_PTR(tmp);
-			if (*s == '-') {
-			    dots = 1;
-			    if (base == 10) {
-				rb_warning("negative number for %%u specifier");
-			    }
-			    s = remove_sign_bits(++s, base);
-			    switch (base) {
-			      case 16:
-				if (s[0] != 'f') *--s = 'f'; break;
-			      case 8:
-				if (s[0] != '7') *--s = '7'; break;
-			      case 2:
-				if (s[0] != '1') *--s = '1'; break;
-			    }
-			}
-		    }
+                    tmp = rb_big2str(val, base);
+                    s = RSTRING_PTR(tmp);
+                    valsign = 1;
+                    if (s[0] == '-') {
+                        s++;
+                        sc = '-';
+                        width--;
+                        valsign = -1;
+                    }
+                    else if (flags & FPLUS) {
+                        sc = '+';
+                        width--;
+                    }
+                    else if (flags & FSPACE) {
+                        sc = ' ';
+                        width--;
+                    }
 		    len = rb_long2int(RSTRING_END(tmp) - s);
 		}
 
@@ -983,21 +964,15 @@ rb_str_format(int argc, const VALUE *arg https://github.com/ruby/ruby/blob/trunk/sprintf.c#L964
 		}
 		CHECK(prec - len);
 		if (dots) PUSH("..", 2);
-		if (!bignum && v < 0) {
+		if (!sign && valsign < 0) {
 		    char c = sign_bits(base, p);
 		    while (len < prec--) {
 			buf[blen++] = c;
 		    }
 		}
 		else if ((flags & (FMINUS|FPREC)) != FMINUS) {
-		    char c;
-
-		    if (!sign && bignum && !RBIGNUM_SIGN(val))
-			c = sign_bits(base, p);
-		    else
-			c = '0';
 		    while (len < prec--) {
-			buf[blen++] = c;
+			buf[blen++] = '0';
 		    }
 		}
 		PUSH(s, len);
Index: ext/-test-/bignum/pack.c
===================================================================
--- ext/-test-/bignum/pack.c	(revision 41229)
+++ ext/-test-/bignum/pack.c	(revision 41230)
@@ -19,6 +19,24 @@ rb_integer_pack_m(VALUE val, VALUE buf, https://github.com/ruby/ruby/blob/trunk/ext/-test-/bignum/pack.c#L19
 }
 
 static VALUE
+rb_integer_pack_2comp_m(VALUE val, VALUE numwords_arg, VALUE wordsize_arg, VALUE nails, VALUE flags)
+{
+  int sign;
+  size_t numwords = NUM2SIZET(numwords_arg);
+  size_t wordsize = NUM2SIZET(wordsize_arg);
+  VALUE buf;
+
+  if (numwords != 0 && wordsize != 0 && LONG_MAX / wordsize < numwords)
+      rb_raise(rb_eArgError, "too big numwords * wordsize");
+  buf = rb_str_new(NULL, numwords * wordsize);
+  sign = rb_integer_pack_2comp(val,
+      RSTRING_PTR(buf), numwords,
+      wordsize, NUM2SIZET(nails), NUM2INT(flags));
+
+  return rb_assoc_new(INT2NUM(sign), buf);
+}
+
+static VALUE
 rb_integer_unpack_m(VALUE klass, VALUE sign, VALUE buf, VALUE wordcount, VALUE wordsize, VALUE nails, VALUE flags)
 {
     StringValue(buf);
@@ -32,6 +50,7 @@ void https://github.com/ruby/ruby/blob/trunk/ext/-test-/bignum/pack.c#L50
 Init_pack(VALUE klass)
 {
     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, 6);
     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));
Index: internal.h
===================================================================
--- internal.h	(revision 41229)
+++ internal.h	(revision 41230)
@@ -449,6 +449,7 @@ VALUE rb_thread_io_blocking_region(rb_bl https://github.com/ruby/ruby/blob/trunk/internal.h#L449
 /* 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(int sign, 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);
 
 /* io.c */
 void rb_maygvl_fd_fix_cloexec(int fd);
Index: bignum.c
===================================================================
--- bignum.c	(revision 41229)
+++ bignum.c	(revision 41230)
@@ -842,37 +842,11 @@ integer_pack_take_lowbits(int n, BDIGIT_ https://github.com/ruby/ruby/blob/trunk/bignum.c#L842
     return ret;
 }
 
-/*
- * Export an integer into a buffer.
- *
- * This function fills the buffer specified by _words_ and _numwords_ as
- * abs(val) in the format specified by _wordsize_, _nails_ and _flags_.
- *
- * [val] Fixnum, Bignum or another integer like object which has to_int method.
- * [words] buffer to export abs(val).
- * [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 filled by zero.
- * [flags] bitwise or of constants which name starts "INTEGER_PACK_".
- *   It specifies word order and byte order.
- *
- * This function returns the signedness and overflow condition as follows:
- *   -2 : negative overflow.  val <= -2**(numwords*(wordsize*CHAR_BIT-nails))
- *   -1 : negative without overflow.  -2**(numwords*(wordsize*CHAR_BIT-nails)) < val < 0
- *   0 : zero.  val == 0
- *   1 : positive without overflow.  0 < val < 2**(numwords*(wordsize*CHAR_BIT-nails))
- *   2 : positive overflow.  2**(numwords*(wordsize*CHAR_BIT-nails)) <= val
- *
- * The least significant words of abs(val) are filled in the buffer when overflow occur.
- */
-
-int
-rb_integer_pack(VALUE val, void *words, size_t numwords, size_t wordsize, size_t nails, int flags)
+static int
+rb_integer_pack_internal(VALUE val, void *words, size_t numwords, size_t wordsize, size_t nails, int flags, int overflow_2comp)
 {
     int sign;
-    BDIGIT *dp;
-    BDIGIT *de;
+    BDIGIT *ds, *dp, *de;
     BDIGIT fixbuf[(sizeof(long) + SIZEOF_BDIGITS - 1) / SIZEOF_BDIGITS];
     unsigned char *buf, *bufend;
 
@@ -902,12 +876,12 @@ rb_integer_pack(VALUE val, void *words, https://github.com/ruby/ruby/blob/trunk/bignum.c#L876
             }
         }
 #endif
-        dp = fixbuf;
+        ds = dp = fixbuf;
         de = fixbuf + numberof(fixbuf);
     }
     else {
         sign = RBIGNUM_POSITIVE_P(val) ? 1 : -1;
-        dp = BDIGITS(val);
+        ds = dp = BDIGITS(val);
         de = dp + RBIGNUM_LEN(val);
     }
     while (dp < de && de[-1] == 0)
@@ -920,7 +894,15 @@ rb_integer_pack(VALUE val, void *words, https://github.com/ruby/ruby/blob/trunk/bignum.c#L894
     bufend = buf + numwords * wordsize;
 
     if (buf == bufend) {
-        sign *= 2; /* overflow if non-zero*/
+        /* overflow if non-zero*/
+        if (!overflow_2comp || 0 <= sign)
+            sign *= 2;
+        else {
+            if (de - dp == 1 && dp[0] == 1)
+                sign = -1; /* val == -1 == -2**(numwords*(wordsize*CHAR_BIT-nails)) */
+            else
+                sign = -2; /* val < -1 == -2**(numwords*(wordsize*CHAR_BIT-nails)) */
+        }
     }
     else if (dp == de) {
         memset(buf, '\0', bufend - buf);
@@ -979,8 +961,28 @@ rb_integer_pack(VALUE val, void *words, https://github.com/ruby/ruby/blob/trunk/bignum.c#L961
 
             wordp += word_step;
         }
-        if (dp != de || dd)
-            sign *= 2; /* overflow */
+        FILL_DD;
+        /* overflow tests */
+        if (dp != de || 1 < dd) {
+            /* 2**(numwords*(wordsize*CHAR_BIT-nails)+1) <= abs(val) */
+            sign *= 2;
+        }
+        else if (dd == 1) {
+            /* 2**(numwords*(wordsize*CHAR_BIT-nails)) <= abs(val) < 2**(numwords*(wordsize*CHAR_BIT-nails)+1) */
+            if (!overflow_2comp || 0 <= sign)
+                sign *= 2;
+            else { /* overflow_2comp && sign == -1 */
+                /* test lower bits are all zero. */
+                dp = ds;
+                while (dp < de && *dp == 0)
+                    dp++;
+                if (de - dp == 1 && /* only one non-zero word. */
+                    (*dp & (*dp-1)) == 0) /* *dp contains only one bit set. */
+                    sign = -1; /* val == -2**(numwords*(wordsize*CHAR_BIT-nails)) */
+                else
+                    sign = -2; /* val < -2**(numwords*(wordsize*CHAR_BIT-nails)) */
+            }
+        }
     }
 
     return sign;
@@ -988,6 +990,119 @@ rb_integer_pack(VALUE val, void *words, https://github.com/ruby/ruby/blob/trunk/bignum.c#L990
 #undef TAKE_LOWBITS
 }
 
+/*
+ * Export an integer into a buffer.
+ *
+ * This function fills the buffer specified by _words_ and _numwords_ as
+ * abs(val) in the format specified by _wordsize_, _nails_ and _flags_.
+ *
+ * [val] Fixnum, Bignum or another integer like object which has to_int method.
+ * [words] buffer to export abs(val).
+ * [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 filled by zero.
+ * [flags] bitwise or of constants which name starts "INTEGER_PACK_".
+ *   It specifies word order and byte order.
+ *
+ * This function returns the signedness and overflow condition as follows:
+ *   -2 : negative overflow.  val <= -2**(numwords*(wordsize*CHAR_BIT-nails))
+ *   -1 : negative without overflow.  -2**(numwords*(wordsize*CHAR_BIT-nails)) < val < 0
+ *   0 : zero.  val == 0
+ *   1 : positive without overflow.  0 < val < 2**(numwords*(wordsize*CHAR_BIT-nails))
+ *   2 : positive overflow.  2**(numwords*(wordsize*CHAR_BIT-nails)) <= val
+ *
+ * The least significant words of abs(val) are filled in the buffer when overflow occur.
+ */
+
+int
+rb_integer_pack(VALUE val, void *words, size_t numwords, size_t wordsize, size_t nails, int flags)
+{
+    return rb_integer_pack_internal(val, words, numwords, wordsize, nails, flags, 0);
+}
+
+/*
+ * Export an integer into a buffer in 2's comlement representation.
+ *
+ * This function is similar to rb_integer_pack_2comp but
+ * the number is filled as 2's comlement representation and
+ * return value is bit different (because overflow condition
+ * is differnt between absolute value and 2's comlement).
+ *
+ * This function returns the signedness and overflow condition as follows:
+ *   -2 : negative overflow.  val < -2**(numwords*(wordsize*CHAR_BIT-nails))
+ *   -1 : negative without overflow.  -2**(numwords*(wordsize*CHAR_BIT-nails)) <= val < 0
+ *   0 : zero.  val == 0
+ *   1 : positive without overflow.  0 < val < 2**(numwords*(wordsize*CHAR_BIT-nails))
+ *   2 : positive overflow.  2**(numwords*(wordsize*CHAR_BIT-nails)) <= val
+ *
+ * rb_integer_pack_2comp returns -1 for val == -2**(numwords*(wordsize*CHAR_BIT-nails)) but
+ * rb_integer_pack returns -2.
+ *
+ */
+
+int
+rb_integer_pack_2comp(VALUE val, void *words, size_t numwords, size_t wordsize, size_t nails, int flags)
+{
+    int sign;
+
+    sign = rb_integer_pack_internal(val, words, numwords, wordsize, nails, flags, 1);
+
+    if (sign < 0 && numwords != 0) {
+        unsigned char *buf;
+
+        int word_num_partialbits;
+        size_t word_num_fullbytes;
+
+        ssize_t word_step;
+        size_t byte_start;
+        int byte_step;
+
+        size_t word_start, word_last;
+        unsigned char *wordp, *last_wordp;
+
+        unsigned int partialbits_mask;
+        int carry;
+
+        integer_pack_loop_setup(numwords, wordsize, nails, flags,
+            &word_num_fullbytes, &word_num_partialbits,
+            &word_start, &word_step, &word_last, &byte_start, &byte_step);
+
+        partialbits_mask = (1 << word_num_partialbits) - 1;
+
+        buf = words;
+        wordp = buf + word_start;
+        last_wordp = buf + word_last;
+
+        carry = 1;
+        while (1) {
+            size_t index_in_word = 0;
+            unsigned char *bytep = wordp + byte_start;
+            while (index_in_word < word_num_fullbytes) {
+                carry += (unsigned char)~*bytep;
+                *bytep = (unsigned char)carry;
+                carry >>= CHAR_BIT;
+                bytep += byte_step;
+                index_in_word++;
+            }
+            if (word_num_partialbits) {
+                carry += (*bytep & partialbits_mask) ^ partialbits_mask;
+                *bytep = carry & partialbits_mask;
+                carry >>= word_num_partialbits;
+                bytep += byte_step;
+                index_in_word++;
+            }
+
+            if (wordp == last_wordp)
+                (... truncated)

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

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