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

ruby-changes:56140

From: Nobuyoshi <ko1@a...>
Date: Wed, 19 Jun 2019 16:04:27 +0900 (JST)
Subject: [ruby-changes:56140] Nobuyoshi Nakada: e690df1f1e (trunk): Marshal distant past/future

https://git.ruby-lang.org/ruby.git/commit/?id=e690df1f1e

From e690df1f1ef4f791295448f9192d6e027400ee72 Mon Sep 17 00:00:00 2001
From: Nobuyoshi Nakada <nobu@r...>
Date: Thu, 27 Sep 2018 18:17:28 +0900
Subject: Marshal distant past/future

[Feature #15160]

diff --git a/marshal.c b/marshal.c
index 73d36e2..8523051 100644
--- a/marshal.c
+++ b/marshal.c
@@ -256,6 +256,7 @@ class2path(VALUE klass) https://github.com/ruby/ruby/blob/trunk/marshal.c#L256
     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 https://github.com/ruby/ruby/blob/trunk/marshal.c#L299
 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) https://github.com/ruby/ruby/blob/trunk/marshal.c#L342
 	    break;
 	}
     }
-    w_nbyte(buf, i+1, arg);
+    return i+1;
 }
 
 #ifdef DBL_MANT_DIG
@@ -1228,6 +1239,19 @@ r_long(struct load_arg *arg) https://github.com/ruby/ruby/blob/trunk/marshal.c#L1239
     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)
 {
diff --git a/test/ruby/test_time.rb b/test/ruby/test_time.rb
index 4c0a104..7269bdc 100644
--- a/test/ruby/test_time.rb
+++ b/test/ruby/test_time.rb
@@ -375,6 +375,16 @@ class TestTime < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_time.rb#L375
     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))
diff --git a/time.c b/time.c
index b569bf6..3ed694f 100644
--- a/time.c
+++ b/time.c
@@ -5005,6 +5005,8 @@ time_strftime(VALUE time, VALUE format) https://github.com/ruby/ruby/blob/trunk/time.c#L5005
     }
 }
 
+int ruby_marshal_write_long(long x, char *buf);
+
 /* :nodoc: */
 static VALUE
 time_mdump(VALUE time)
@@ -5020,19 +5022,33 @@ time_mdump(VALUE time) https://github.com/ruby/ruby/blob/trunk/time.c#L5022
     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) https://github.com/ruby/ruby/blob/trunk/time.c#L5081
     }
 
     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) https://github.com/ruby/ruby/blob/trunk/time.c#L5178
     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) https://github.com/ruby/ruby/blob/trunk/time.c#L5192
     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) https://github.com/ruby/ruby/blob/trunk/time.c#L5208
     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) https://github.com/ruby/ruby/blob/trunk/time.c#L5216
 
     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) https://github.com/ruby/ruby/blob/trunk/time.c#L5241
 	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;
-- 
cgit v0.10.2


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

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