mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
parent
8797f48373
commit
e690df1f1e
3 changed files with 109 additions and 16 deletions
40
marshal.c
40
marshal.c
|
@ -256,6 +256,7 @@ class2path(VALUE klass)
|
|||
return path;
|
||||
}
|
||||
|
||||
int ruby_marshal_write_long(long x, char *buf);
|
||||
static void w_long(long, struct dump_arg*);
|
||||
static void w_encoding(VALUE encname, struct dump_call_arg *arg);
|
||||
static VALUE encoding_name(VALUE obj, struct dump_arg *arg);
|
||||
|
@ -298,26 +299,36 @@ static void
|
|||
w_long(long x, struct dump_arg *arg)
|
||||
{
|
||||
char buf[sizeof(long)+1];
|
||||
int i = ruby_marshal_write_long(x, buf);
|
||||
if (i < 0) {
|
||||
rb_raise(rb_eTypeError, "long too big to dump");
|
||||
}
|
||||
w_nbyte(buf, i, arg);
|
||||
}
|
||||
|
||||
int
|
||||
ruby_marshal_write_long(long x, char *buf)
|
||||
{
|
||||
int i;
|
||||
|
||||
#if SIZEOF_LONG > 4
|
||||
if (!(RSHIFT(x, 31) == 0 || RSHIFT(x, 31) == -1)) {
|
||||
/* big long does not fit in 4 bytes */
|
||||
rb_raise(rb_eTypeError, "long too big to dump");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (x == 0) {
|
||||
w_byte(0, arg);
|
||||
return;
|
||||
buf[0] = 0;
|
||||
return 1;
|
||||
}
|
||||
if (0 < x && x < 123) {
|
||||
w_byte((char)(x + 5), arg);
|
||||
return;
|
||||
buf[0] = (char)(x + 5);
|
||||
return 1;
|
||||
}
|
||||
if (-124 < x && x < 0) {
|
||||
w_byte((char)((x - 5)&0xff), arg);
|
||||
return;
|
||||
buf[0] = (char)((x - 5)&0xff);
|
||||
return 1;
|
||||
}
|
||||
for (i=1;i<(int)sizeof(long)+1;i++) {
|
||||
buf[i] = (char)(x & 0xff);
|
||||
|
@ -331,7 +342,7 @@ w_long(long x, struct dump_arg *arg)
|
|||
break;
|
||||
}
|
||||
}
|
||||
w_nbyte(buf, i+1, arg);
|
||||
return i+1;
|
||||
}
|
||||
|
||||
#ifdef DBL_MANT_DIG
|
||||
|
@ -1228,6 +1239,19 @@ r_long(struct load_arg *arg)
|
|||
return x;
|
||||
}
|
||||
|
||||
long
|
||||
ruby_marshal_read_long(const char **buf, long len)
|
||||
{
|
||||
long x;
|
||||
struct RString src;
|
||||
struct load_arg arg;
|
||||
memset(&arg, 0, sizeof(arg));
|
||||
arg.src = rb_setup_fake_str(&src, *buf, len, 0);
|
||||
x = r_long(&arg);
|
||||
*buf += arg.offset;
|
||||
return x;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
r_bytes1(long len, struct load_arg *arg)
|
||||
{
|
||||
|
|
|
@ -375,6 +375,16 @@ class TestTime < Test::Unit::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_marshal_distant_past
|
||||
assert_marshal_roundtrip(Time.utc(1890, 1, 1))
|
||||
assert_marshal_roundtrip(Time.utc(-4.5e9, 1, 1))
|
||||
end
|
||||
|
||||
def test_marshal_distant_future
|
||||
assert_marshal_roundtrip(Time.utc(30000, 1, 1))
|
||||
assert_marshal_roundtrip(Time.utc(5.67e9, 4, 8))
|
||||
end
|
||||
|
||||
def test_at3
|
||||
t2000 = get_t2000
|
||||
assert_equal(t2000, Time.at(t2000))
|
||||
|
|
75
time.c
75
time.c
|
@ -5005,6 +5005,8 @@ time_strftime(VALUE time, VALUE format)
|
|||
}
|
||||
}
|
||||
|
||||
int ruby_marshal_write_long(long x, char *buf);
|
||||
|
||||
/* :nodoc: */
|
||||
static VALUE
|
||||
time_mdump(VALUE time)
|
||||
|
@ -5020,19 +5022,33 @@ time_mdump(VALUE time)
|
|||
long usec, nsec;
|
||||
VALUE subsecx, nano, subnano, v, zone;
|
||||
|
||||
VALUE year_extend = Qnil;
|
||||
const int max_year = 1900+0xffff;
|
||||
|
||||
GetTimeval(time, tobj);
|
||||
|
||||
gmtimew(tobj->timew, &vtm);
|
||||
|
||||
if (FIXNUM_P(vtm.year)) {
|
||||
year = FIX2LONG(vtm.year);
|
||||
if (year < 1900 || 1900+0xffff < year)
|
||||
rb_raise(rb_eArgError, "year too %s to marshal: %ld UTC",
|
||||
(year < 1900 ? "small" : "big"), year);
|
||||
if (year > max_year) {
|
||||
year_extend = INT2FIX(year - max_year);
|
||||
year = max_year;
|
||||
}
|
||||
else if (year < 1900) {
|
||||
year_extend = LONG2NUM(1900 - year);
|
||||
year = 1900;
|
||||
}
|
||||
}
|
||||
else {
|
||||
rb_raise(rb_eArgError, "year too %s to marshal: %"PRIsVALUE" UTC",
|
||||
(le(vtm.year, INT2FIX(1900)) ? "small" : "big"), vtm.year);
|
||||
if (rb_int_positive_p(vtm.year)) {
|
||||
year_extend = rb_int_minus(vtm.year, INT2FIX(max_year));
|
||||
year = max_year;
|
||||
}
|
||||
else {
|
||||
year_extend = rb_int_minus(INT2FIX(1900), vtm.year);
|
||||
year = 1900;
|
||||
}
|
||||
}
|
||||
|
||||
subsecx = vtm.subsecx;
|
||||
|
@ -5065,6 +5081,26 @@ time_mdump(VALUE time)
|
|||
}
|
||||
|
||||
str = rb_str_new(buf, 8);
|
||||
if (!NIL_P(year_extend)) {
|
||||
/*
|
||||
* Append extended year distance from 1900..(1900+0xffff). In
|
||||
* each cases, there is no sign as the value is positive. The
|
||||
* format is length (marshaled long) + little endian packed
|
||||
* binary (like as Fixnum and Bignum).
|
||||
*/
|
||||
size_t ysize = rb_absint_size(year_extend, NULL);
|
||||
char *p;
|
||||
if (ysize > LONG_MAX ||
|
||||
(i = ruby_marshal_write_long((long)ysize, buf)) < 0) {
|
||||
rb_raise(rb_eArgError, "year too %s to marshal: %"PRIsVALUE" UTC",
|
||||
(year == 1900 ? "small" : "big"), vtm.year);
|
||||
}
|
||||
rb_str_resize(str, sizeof(buf) + i + ysize);
|
||||
p = RSTRING_PTR(str) + sizeof(buf);
|
||||
memcpy(p, buf, i);
|
||||
p += i;
|
||||
rb_integer_pack(year_extend, p, ysize, 1, 0, INTEGER_PACK_LITTLE_ENDIAN);
|
||||
}
|
||||
rb_copy_generic_ivar(str, time);
|
||||
if (!rb_equal(nano, INT2FIX(0))) {
|
||||
if (RB_TYPE_P(nano, T_RATIONAL)) {
|
||||
|
@ -5142,6 +5178,8 @@ mload_zone(VALUE time, VALUE zone)
|
|||
return z;
|
||||
}
|
||||
|
||||
long ruby_marshal_read_long(const char **buf, long len);
|
||||
|
||||
/* :nodoc: */
|
||||
static VALUE
|
||||
time_mload(VALUE time, VALUE str)
|
||||
|
@ -5154,7 +5192,7 @@ time_mload(VALUE time, VALUE str)
|
|||
struct vtm vtm;
|
||||
int i, gmt;
|
||||
long nsec;
|
||||
VALUE submicro, nano_num, nano_den, offset, zone;
|
||||
VALUE submicro, nano_num, nano_den, offset, zone, year;
|
||||
wideval_t timew;
|
||||
|
||||
time_modify(time);
|
||||
|
@ -5170,6 +5208,7 @@ time_mload(VALUE time, VALUE str)
|
|||
get_attr(submicro, {});
|
||||
get_attr(offset, (offset = rb_rescue(validate_utc_offset, offset, NULL, Qnil)));
|
||||
get_attr(zone, (zone = rb_rescue(validate_zone_name, zone, NULL, Qnil)));
|
||||
get_attr(year, {});
|
||||
|
||||
#undef get_attr
|
||||
|
||||
|
@ -5177,7 +5216,8 @@ time_mload(VALUE time, VALUE str)
|
|||
|
||||
StringValue(str);
|
||||
buf = (unsigned char *)RSTRING_PTR(str);
|
||||
if (RSTRING_LEN(str) != 8) {
|
||||
if (RSTRING_LEN(str) < 8) {
|
||||
invalid_format:
|
||||
rb_raise(rb_eTypeError, "marshaled time format differ");
|
||||
}
|
||||
|
||||
|
@ -5201,7 +5241,26 @@ time_mload(VALUE time, VALUE str)
|
|||
p &= ~(1UL<<31);
|
||||
gmt = (int)((p >> 30) & 0x1);
|
||||
|
||||
vtm.year = INT2FIX(((int)(p >> 14) & 0xffff) + 1900);
|
||||
if (NIL_P(year)) {
|
||||
year = INT2FIX(((int)(p >> 14) & 0xffff) + 1900);
|
||||
}
|
||||
if (RSTRING_LEN(str) > 8) {
|
||||
long len = RSTRING_LEN(str) - 8;
|
||||
long ysize = 0;
|
||||
VALUE year_extend;
|
||||
const char *ybuf = (const char *)(buf += 8);
|
||||
ysize = ruby_marshal_read_long(&ybuf, len);
|
||||
len -= ybuf - (const char *)buf;
|
||||
if (ysize < 0 || ysize > len) goto invalid_format;
|
||||
year_extend = rb_integer_unpack(ybuf, ysize, 1, 0, INTEGER_PACK_LITTLE_ENDIAN);
|
||||
if (year == INT2FIX(1900)) {
|
||||
year = rb_int_minus(year, year_extend);
|
||||
}
|
||||
else {
|
||||
year = rb_int_plus(year, year_extend);
|
||||
}
|
||||
}
|
||||
vtm.year = year;
|
||||
vtm.mon = ((int)(p >> 10) & 0xf) + 1;
|
||||
vtm.mday = (int)(p >> 5) & 0x1f;
|
||||
vtm.hour = (int) p & 0x1f;
|
||||
|
|
Loading…
Add table
Reference in a new issue