1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Marshal distant past/future

[Feature #15160]
This commit is contained in:
Nobuyoshi Nakada 2018-09-27 18:17:28 +09:00
parent 8797f48373
commit e690df1f1e
No known key found for this signature in database
GPG key ID: 4BC7D6DF58D8DF60
3 changed files with 109 additions and 16 deletions

View file

@ -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)
{

View file

@ -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
View file

@ -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;