* configure.in: Invoke RUBY_REPLACE_TYPE for size_t.

Don't invoke RUBY_CHECK_PRINTF_PREFIX for size_t to avoid conflict
  with RUBY_REPLACE_TYPE.

* internal.h (rb_absint_size): Declared.
  (rb_absint_size_in_word): Ditto.
  (rb_int_export): Ditto.

* bignum.c (rb_absint_size): New function.
  (rb_absint_size_in_word): Ditto.
  (int_export_fill_dd): Ditto.
  (int_export_take_lowbits): Ditto.
  (rb_int_export): Ditto.

* pack.c (pack_pack): Use rb_int_export for BER compressed integer.



git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@41106 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
akr 2013-06-06 11:57:35 +00:00
Родитель c247e62808
Коммит 9fea8758e9
8 изменённых файлов: 465 добавлений и 39 удалений

Просмотреть файл

@ -1,3 +1,21 @@
Thu Jun 6 20:40:17 2013 Tanaka Akira <akr@fsij.org>
* configure.in: Invoke RUBY_REPLACE_TYPE for size_t.
Don't invoke RUBY_CHECK_PRINTF_PREFIX for size_t to avoid conflict
with RUBY_REPLACE_TYPE.
* internal.h (rb_absint_size): Declared.
(rb_absint_size_in_word): Ditto.
(rb_int_export): Ditto.
* bignum.c (rb_absint_size): New function.
(rb_absint_size_in_word): Ditto.
(int_export_fill_dd): Ditto.
(int_export_take_lowbits): Ditto.
(rb_int_export): Ditto.
* pack.c (pack_pack): Use rb_int_export for BER compressed integer.
Thu Jun 6 19:31:33 2013 Tadayoshi Funaba <tadf@dotrb.org>
* ext/date/date_core.c: fixed coding error [ruby-core:55337].

321
bignum.c
Просмотреть файл

@ -50,6 +50,8 @@ static VALUE big_three = Qnil;
(BDIGITS(x)[0] == 0 && \
(RBIGNUM_LEN(x) == 1 || bigzero_p(x))))
static int nlz(BDIGIT x);
#define BIGNUM_DEBUG 0
#if BIGNUM_DEBUG
#define ON_DEBUG(x) do { x; } while (0)
@ -449,6 +451,325 @@ rb_big_unpack(unsigned long *buf, long num_longs)
}
}
/* number of bytes of abs(val). additionaly number of leading zeros can be returned. */
size_t
rb_absint_size(VALUE val, int *number_of_leading_zero_bits)
{
BDIGIT *dp;
BDIGIT *de;
BDIGIT fixbuf[(sizeof(long) + SIZEOF_BDIGITS - 1) / SIZEOF_BDIGITS];
int i;
int num_leading_zeros;
val = rb_to_int(val);
if (FIXNUM_P(val)) {
long v = FIX2LONG(val);
if (v < 0) {
v = -v;
}
#if SIZEOF_BDIGITS == SIZEOF_LONG
fixbuf[0] = v;
#else
for (i = 0; i < (int)(sizeof(fixbuf)/sizeof(*fixbuf)); i++) {
fixbuf[i] = v & ((1L << (SIZEOF_BDIGITS * CHAR_BIT)) - 1);
v >>= SIZEOF_BDIGITS * CHAR_BIT;
}
#endif
dp = fixbuf;
de = fixbuf + sizeof(fixbuf)/sizeof(*fixbuf);
}
else {
dp = BDIGITS(val);
de = dp + RBIGNUM_LEN(val);
}
while (dp < de && de[-1] == 0)
de--;
if (dp == de) {
if (number_of_leading_zero_bits)
*number_of_leading_zero_bits = 0;
return 0;
}
num_leading_zeros = nlz(de[-1]);
if (number_of_leading_zero_bits)
*number_of_leading_zero_bits = num_leading_zeros % CHAR_BIT;
return (de - dp) * SIZEOF_BDIGITS - num_leading_zeros / CHAR_BIT;
}
size_t
rb_absint_size_in_word(VALUE val, size_t word_numbits_arg, size_t *number_of_leading_zero_bits)
{
size_t numbytes;
size_t numwords;
int zerobits_in_byte;
VALUE val_numbits, word_numbits;
VALUE div_mod, div, mod;
numbytes = rb_absint_size(val, &zerobits_in_byte);
/*
* val_numbits = numbytes * CHAR_BIT - zerobits_in_byte
* div, mod = val_numbits.divmod(word_numbits)
* numwords = mod == 0 ? div : div + 1
* number_of_leading_zero_bits_in_word = mod == 0 ? 0 : word_numbits - mod
*/
val_numbits = SIZET2NUM(numbytes);
val_numbits = rb_funcall(val_numbits, '*', 1, LONG2FIX(CHAR_BIT));
if (zerobits_in_byte)
val_numbits = rb_funcall(val_numbits, '-', 1, LONG2FIX(zerobits_in_byte));
word_numbits = SIZET2NUM(word_numbits_arg);
div_mod = rb_funcall(val_numbits, rb_intern("divmod"), 1, word_numbits);
div = RARRAY_AREF(div_mod, 0);
mod = RARRAY_AREF(div_mod, 1);
if (mod == LONG2FIX(0)) {
numwords = NUM2SIZE(div);
if (number_of_leading_zero_bits)
*number_of_leading_zero_bits = 0;
}
else {
numwords = NUM2SIZE(rb_funcall(div, '+', 1, LONG2FIX(1)));
if (number_of_leading_zero_bits)
*number_of_leading_zero_bits = word_numbits_arg - NUM2SIZE(mod);
}
return numwords;
}
static inline void
int_export_fill_dd(BDIGIT **dpp, BDIGIT **dep, BDIGIT_DBL *ddp, int *numbits_in_dd_p)
{
if (*dpp < *dep && SIZEOF_BDIGITS * CHAR_BIT <= (int)sizeof(*ddp) * CHAR_BIT - *numbits_in_dd_p) {
*ddp |= (BDIGIT_DBL)(*(*dpp)++) << *numbits_in_dd_p;
*numbits_in_dd_p += SIZEOF_BDIGITS * CHAR_BIT;
}
else if (*dpp == *dep) {
/* higher bits are infinity zeros */
*numbits_in_dd_p = (int)sizeof(*ddp) * CHAR_BIT;
}
}
static inline BDIGIT_DBL
int_export_take_lowbits(int n, BDIGIT_DBL *ddp, int *numbits_in_dd_p)
{
BDIGIT_DBL ret;
ret = (*ddp) & (((BDIGIT_DBL)1 << n) - 1);
*ddp >>= n;
*numbits_in_dd_p -= n;
return ret;
}
/*
* Export an integer into a buffer.
*
* [val] Fixnum, Bignum or another object which has to_int.
* [signp] signedness is returned in *signp if it is not NULL.
* 0 for zero.
* -1 for negative without overflow. 1 for positive without overflow.
* -2 for negative overflow. 2 for positive overflow.
* [buf] buffer to export abs(val). allocated by xmalloc if it is NULL.
* [countp] the size of given buffer as number of words (only meaningful when buf is not NULL).
* *countp is overwritten as the number of allocated words when buf is NULL and allocated.
* [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 buf or the allocated buffer if buf is NULL.
*
*/
void *
rb_int_export(VALUE val, int *signp, void *bufarg, size_t *countp, int wordorder, size_t wordsize, int endian, size_t nails)
{
int sign;
BDIGIT *dp;
BDIGIT *de;
BDIGIT fixbuf[(sizeof(long) + SIZEOF_BDIGITS - 1) / SIZEOF_BDIGITS];
int i;
unsigned char *buf = bufarg;
unsigned char *bufend;
size_t wordcount;
val = rb_to_int(val);
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 (buf && SIZE_MAX / wordsize < *countp)
rb_raise(rb_eArgError, "too big count * wordsize: %"PRI_SIZE_PREFIX"u * %"PRI_SIZE_PREFIX"u", *countp, 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 = 0;
#endif
}
if (FIXNUM_P(val)) {
long v = FIX2LONG(val);
if (v < 0) {
sign = -1;
v = -v;
}
else {
sign = 1;
}
#if SIZEOF_BDIGITS == SIZEOF_LONG
fixbuf[0] = v;
#else
for (i = 0; i < (int)(sizeof(fixbuf)/sizeof(*fixbuf)); i++) {
fixbuf[i] = v & ((1L << (SIZEOF_BDIGITS * CHAR_BIT)) - 1);
v >>= SIZEOF_BDIGITS * CHAR_BIT;
}
#endif
dp = fixbuf;
de = fixbuf + sizeof(fixbuf)/sizeof(*fixbuf);
}
else {
sign = RBIGNUM_POSITIVE_P(val) ? 1 : -1;
dp = BDIGITS(val);
de = dp + RBIGNUM_LEN(val);
}
while (dp < de && de[-1] == 0)
de--;
if (dp == de) {
sign = 0;
}
if (buf) {
wordcount = *countp;
bufend = buf + wordcount * wordsize;
}
else {
/*
* val_numbits = (de - dp) * SIZEOF_BDIGITS * CHAR_BIT - nlz(de[-1])
* word_numbits = wordsize * CHAR_BIT - nails
* wordcount = (val_numbits + word_numbits - 1) / word_numbits
*/
VALUE val_numbits, word_numbits, wordcountv;
val_numbits = SIZET2NUM((de - dp) * SIZEOF_BDIGITS);
val_numbits = rb_funcall(val_numbits, '*', 1, LONG2FIX(CHAR_BIT));
if (dp != de)
val_numbits = rb_funcall(val_numbits, '-', 1, LONG2FIX(nlz(de[-1])));
word_numbits = SIZET2NUM(wordsize);
word_numbits = rb_funcall(word_numbits, '*', 1, LONG2FIX(CHAR_BIT));
if (nails != 0)
word_numbits = rb_funcall(word_numbits, '-', 1, SIZET2NUM(nails));
wordcountv = rb_funcall(val_numbits, '+', 1, word_numbits);
wordcountv = rb_funcall(wordcountv, '-', 1, LONG2FIX(1));
wordcountv = rb_funcall(wordcountv, rb_intern("div"), 1, word_numbits);
wordcount = NUM2SIZE(wordcountv);
buf = xmalloc(wordcount * wordsize);
bufend = buf + wordcount * wordsize;
}
if (buf == bufend) {
sign *= 2; /* overflow if non-zero*/
}
else if (dp == de) {
memset(buf, '\0', bufend - buf);
}
else if (dp < de && buf < bufend) {
int word_num_partialbits;
size_t word_num_fullbytes;
size_t word_num_nailbytes;
ssize_t word_step;
size_t byte_start;
int byte_step;
unsigned char *bytep, *wordp, *last_wordp;
size_t index_in_word;
BDIGIT_DBL dd;
int numbits_in_dd;
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--;
word_num_nailbytes = wordsize - word_num_fullbytes - 1;
}
else {
word_num_nailbytes = wordsize - 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 FILL_DD \
int_export_fill_dd(&dp, &de, &dd, &numbits_in_dd)
#define TAKE_LOWBITS(n) \
int_export_take_lowbits(n, &dd, &numbits_in_dd)
while (1) {
index_in_word = 0;
bytep = wordp + byte_start;
while (index_in_word < word_num_fullbytes) {
FILL_DD;
*bytep = TAKE_LOWBITS(CHAR_BIT);
bytep += byte_step;
index_in_word++;
}
if (word_num_partialbits) {
FILL_DD;
*bytep = TAKE_LOWBITS(word_num_partialbits);
bytep += byte_step;
index_in_word++;
}
while (wordsize - word_num_nailbytes <= index_in_word && index_in_word < wordsize) {
*bytep = 0;
bytep += byte_step;
index_in_word++;
}
if (wordp == last_wordp)
break;
wordp += word_step;
}
if (dp != de || dd)
sign *= 2; /* overflow */
}
if (signp)
*signp = sign;
if (!bufarg)
*countp = wordcount;
return buf;
#undef FILL_DD
#undef TAKE_LOWBITS
}
#define QUAD_SIZE 8
#if SIZEOF_LONG_LONG == QUAD_SIZE && SIZEOF_BDIGITS*2 == SIZEOF_LONG_LONG

Просмотреть файл

@ -1234,6 +1234,7 @@ RUBY_REPLACE_TYPE(gid_t, int, GIDT)
RUBY_REPLACE_TYPE(time_t, [], TIMET, [@%:@include <time.h>])
RUBY_REPLACE_TYPE(dev_t, [int long "long long"], DEVT)
RUBY_REPLACE_TYPE(mode_t, ["unsigned int" long], MODET, [@%:@include <sys/stat.h>])
RUBY_REPLACE_TYPE(size_t, ["unsigned int" "unsigned long" "unsigned long long"], SIZE)
RUBY_REPLACE_TYPE(rlim_t, [int long "long long"], RLIM, [
@%:@ifdef HAVE_SYS_TYPES_H
@%:@include <sys/types.h>
@ -1482,7 +1483,6 @@ fi
AC_TYPE_SIZE_T
RUBY_CHECK_SIZEOF(size_t, [int long void*], [], [@%:@include <sys/types.h>])
RUBY_CHECK_SIZEOF(ptrdiff_t, size_t, [], [@%:@include <stddef.h>])
RUBY_CHECK_PRINTF_PREFIX(size_t, z)
RUBY_CHECK_PRINTF_PREFIX(ptrdiff_t, t)
AC_STRUCT_ST_BLKSIZE
AC_STRUCT_ST_BLOCKS

Просмотреть файл

@ -0,0 +1,29 @@
#include "ruby.h"
#include "internal.h"
static VALUE
rb_int_export_m(VALUE val, VALUE buf, VALUE wordorder, VALUE wordsize_arg, VALUE endian, VALUE nails)
{
int sign;
size_t count;
void *ret;
size_t wordsize = NUM2SIZE(wordsize_arg);
if (!NIL_P(buf)) {
StringValue(buf);
rb_str_modify(buf);
count = RSTRING_LEN(buf) / wordsize;
}
ret = rb_int_export(val,
&sign, NIL_P(buf) ? NULL : RSTRING_PTR(buf), &count,
NUM2INT(wordorder), wordsize, NUM2INT(endian), NUM2INT(nails));
return rb_ary_new_from_args(3, INT2NUM(sign), ret ? rb_str_new(ret, wordsize * count) : Qnil, SIZE2NUM(count));
}
void
Init_export(VALUE klass)
{
rb_define_method(rb_cInteger, "test_export", rb_int_export_m, 5);
}

Просмотреть файл

@ -1,3 +1,4 @@
$INCFLAGS << " -I$(topdir) -I$(top_srcdir)"
$srcs = Dir[File.join($srcdir, "*.{#{SRC_EXT.join(%q{,})}}")]
inits = $srcs.map {|s| File.basename(s, ".*")}
inits.delete("init")

Просмотреть файл

@ -99,6 +99,8 @@ VALUE rb_big_fdiv(VALUE x, VALUE y);
VALUE rb_big_uminus(VALUE x);
VALUE rb_integer_float_cmp(VALUE x, VALUE y);
VALUE rb_integer_float_eq(VALUE x, VALUE y);
size_t rb_absint_size(VALUE val, int *number_of_leading_zero_bits);
size_t rb_absint_size_in_word(VALUE val, size_t word_numbits, size_t *number_of_leading_zero_bits);
/* class.c */
VALUE rb_obj_methods(int argc, VALUE *argv, VALUE obj);
@ -423,6 +425,9 @@ const char *rb_objspace_data_type_name(VALUE obj);
/* Temporary. This API will be removed (renamed). */
VALUE rb_thread_io_blocking_region(rb_blocking_function_t *func, void *data1, int fd);
/* 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);
/* io.c */
void rb_maygvl_fd_fix_cloexec(int fd);

61
pack.c
Просмотреть файл

@ -1011,50 +1011,35 @@ pack_pack(VALUE ary, VALUE fmt)
case 'w': /* BER compressed integer */
while (len-- > 0) {
unsigned long ul;
VALUE buf = rb_str_new(0, 0);
char c, *bufs, *bufe;
size_t numbytes;
int sign;
size_t count;
char *cp;
from = NEXTFROM;
if (RB_TYPE_P(from, T_BIGNUM)) {
VALUE big128 = rb_uint2big(128);
while (RB_TYPE_P(from, T_BIGNUM)) {
from = rb_big_divmod(from, big128);
c = castchar(NUM2INT(RARRAY_AREF(from, 1)) | 0x80); /* mod */
rb_str_buf_cat(buf, &c, sizeof(char));
from = RARRAY_AREF(from, 0); /* div */
}
}
from = rb_to_int(from);
numbytes = rb_absint_size_in_word(from, 7, NULL);
if (numbytes == 0)
numbytes = 1;
buf = rb_str_new(NULL, numbytes);
{
long l = NUM2LONG(from);
if (l < 0) {
rb_raise(rb_eArgError, "can't compress negative numbers");
}
ul = l;
}
count = RSTRING_LEN(buf);
rb_int_export(from, &sign, RSTRING_PTR(buf), &count, 1, 1, 1, 1);
while (ul) {
c = castchar((ul & 0x7f) | 0x80);
rb_str_buf_cat(buf, &c, sizeof(char));
ul >>= 7;
}
if (sign < 0)
rb_raise(rb_eArgError, "can't compress negative numbers");
if (sign == 2)
rb_bug("buffer size problem?");
if (RSTRING_LEN(buf)) {
bufs = RSTRING_PTR(buf);
bufe = bufs + RSTRING_LEN(buf) - 1;
*bufs &= 0x7f; /* clear continue bit */
while (bufs < bufe) { /* reverse */
c = *bufs;
*bufs++ = *bufe;
*bufe-- = c;
}
rb_str_buf_cat(res, RSTRING_PTR(buf), RSTRING_LEN(buf));
}
else {
c = 0;
rb_str_buf_cat(res, &c, sizeof(char));
}
cp = RSTRING_PTR(buf);
while (1 < numbytes) {
*cp |= 0x80;
cp++;
numbytes--;
}
rb_str_buf_cat(res, RSTRING_PTR(buf), RSTRING_LEN(buf));
}
break;

Просмотреть файл

@ -0,0 +1,67 @@
# coding: ASCII-8BIT
require 'test/unit'
require "-test-/bignum"
class TestBignum < Test::Unit::TestCase
class TestExport < Test::Unit::TestCase
def test_export_zero
assert_equal([0, "", 0], 0.test_export(nil, 1, 1, 1, 0))
end
def test_argument_check
assert_raise(ArgumentError) { 0.test_export(nil, 0, 1, 1, 0) }
assert_raise(ArgumentError) { 0.test_export(nil, 1, 1, 2, 0) }
assert_raise(ArgumentError) { 0.test_export(nil, 1, 0, 1, 0) }
assert_raise(ArgumentError) { 0.test_export(nil, 1, 1, 1, 8) }
# assume sizeof(ssize_t) == sizeof(intptr_t)
assert_raise(ArgumentError) { 0.test_export(nil, 1, 1 << ([""].pack("p").length * 8 - 1), 1, 0) }
end
def test_export_wordsize
assert_equal([1, "\x01", 1], 1.test_export(nil, 1, 1, 1, 0))
assert_equal([1, "\x00\x01", 1], 1.test_export(nil, 1, 2, 1, 0))
assert_equal([1, "\x00\x00\x01", 1], 1.test_export(nil, 1, 3, 1, 0))
assert_equal([1, "\x01", 1], 1.test_export(nil, 1, 1, -1, 0))
assert_equal([1, "\x01\x00", 1], 1.test_export(nil, 1, 2, -1, 0))
assert_equal([1, "\x01\x00\x00", 1], 1.test_export(nil, 1, 3, -1, 0))
end
def test_export_fixed_buffer
assert_equal([0, "\x00\x00", 2], 0.test_export("xx", 1, 1, 1, 0))
assert_equal([1, "\x00\x01", 2], 0x01.test_export("xx", 1, 1, 1, 0))
assert_equal([1, "\x02\x01", 2], 0x0201.test_export("xx", 1, 1, 1, 0))
assert_equal([2, "\x02\x01", 2], 0x030201.test_export("xx", 1, 1, 1, 0))
assert_equal([2, "\x02\x01", 2], 0x04030201.test_export("xx", 1, 1, 1, 0))
assert_equal([0, "\x00\x00", 2], 0.test_export("xx", -1, 1, 1, 0))
assert_equal([1, "\x01\x00", 2], 0x01.test_export("xx", -1, 1, 1, 0))
assert_equal([1, "\x01\x02", 2], 0x0201.test_export("xx", -1, 1, 1, 0))
assert_equal([2, "\x01\x02", 2], 0x030201.test_export("xx", -1, 1, 1, 0))
assert_equal([2, "\x01\x02", 2], 0x04030201.test_export("xx", -1, 1, 1, 0))
end
def test_export_wordorder_and_endian
assert_equal([1, "\x12\x34\x56\x78", 2], 0x12345678.test_export(nil, 1, 2, 1, 0))
assert_equal([1, "\x34\x12\x78\x56", 2], 0x12345678.test_export(nil, 1, 2, -1, 0))
assert_equal([1, "\x56\x78\x12\x34", 2], 0x12345678.test_export(nil, -1, 2, 1, 0))
assert_equal([1, "\x78\x56\x34\x12", 2], 0x12345678.test_export(nil, -1, 2, -1, 0))
end
def test_export_native_endian
assert_equal([1, [0x1234].pack("S!"), 1], 0x1234.test_export(nil, 1, 2, 0, 0))
end
def test_export_nail
assert_equal([1, "\x01\x00\x00\x00\x01\x01", 6], 0b100011.test_export(nil, 1, 1, 1, 7))
assert_equal([1, "\x01\x02\x03\x04\x05\x06\x07\x08", 8], 0x12345678.test_export(nil, 1, 1, 1, 4))
assert_equal([1, "\x00\x12\x00\x34\x00\x56\x00\x78", 4], 0x12345678.test_export(nil, 1, 2, 1, 8))
end
def test_export_sign
assert_equal([-1, "\x01", 1], (-1).test_export(nil, 1, 1, 1, 0))
assert_equal([-1, "\x80\x70\x60\x50\x40\x30\x20\x10", 8], (-0x8070605040302010).test_export(nil, 1, 1, 1, 0))
end
end
end