diff options
author | Nobuyoshi Nakada <[email protected]> | 2024-09-22 12:14:56 +0900 |
---|---|---|
committer | Nobuyoshi Nakada <[email protected]> | 2024-09-23 14:29:25 +0900 |
commit | 7be1fafe58e2baaa1da4dcd6f4854e674f9d3100 (patch) | |
tree | 79f254bca0e51b0a17f73f9059e7776da2093ca5 /time.c | |
parent | b811a9a097f2ce9ed9b200be84523698bc693e54 (diff) |
Refactor `Time#xmlschema`
And refine uncommon date cases.
# Iteration per second (i/s)
| |compare-ruby|built-ruby|
|:---------------------------|-----------:|---------:|
|time.xmlschema | 5.020M| 14.192M|
| | -| 2.83x|
|utc_time.xmlschema | 6.454M| 15.331M|
| | -| 2.38x|
|time.xmlschema(6) | 4.216M| 10.043M|
| | -| 2.38x|
|utc_time.xmlschema(6) | 5.486M| 10.592M|
| | -| 1.93x|
|time.xmlschema(9) | 4.294M| 10.340M|
| | -| 2.41x|
|utc_time.xmlschema(9) | 4.784M| 10.909M|
| | -| 2.28x|
|fraction_sec.xmlschema(10) | 366.982k| 3.406M|
| | -| 9.28x|
|future_time.xmlschema | 994.595k| 15.853M|
| | -| 15.94x|
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/11665
Diffstat (limited to 'time.c')
-rw-r--r-- | time.c | 129 |
1 files changed, 78 insertions, 51 deletions
@@ -42,6 +42,7 @@ #include "internal/time.h" #include "internal/variable.h" #include "ruby/encoding.h" +#include "ruby/util.h" #include "timev.h" #include "builtin.h" @@ -570,6 +571,9 @@ num_exact(VALUE v) /* time_t */ +/* TIME_SCALE should be 10000... */ +static const int TIME_SCALE_NUMDIGITS = rb_strlen_lit(STRINGIZE(TIME_SCALE)) - 1; + static wideval_t rb_time_magnify(wideval_t w) { @@ -2643,10 +2647,6 @@ time_init_parse(rb_execution_context_t *ec, VALUE time, VALUE str, VALUE zone, V } if (!NIL_P(subsec)) { /* subseconds is the last using ndigits */ - static const size_t TIME_SCALE_NUMDIGITS = - /* TIME_SCALE should be 10000... */ - rb_strlen_lit(STRINGIZE(TIME_SCALE)) - 1; - if (ndigits < TIME_SCALE_NUMDIGITS) { VALUE mul = rb_int_positive_pow(10, TIME_SCALE_NUMDIGITS - ndigits); subsec = rb_int_mul(subsec, mul); @@ -5232,51 +5232,79 @@ time_xmlschema(int argc, VALUE *argv, VALUE time) GetTimeval(time, tobj); MAKE_TM(time, tobj); - long year = -1; + const long size_after_year = sizeof("-MM-DDTHH:MM:SS+ZH:ZM") + fraction_digits + + (fraction_digits > 0); + VALUE str; + char *ptr; + +# define fill_digits_long(len, prec, n) \ + for (int fill_it = 1, written = snprintf(ptr, len, "%0*ld", prec, n); \ + fill_it; ptr += written, fill_it = 0) + if (FIXNUM_P(tobj->vtm.year)) { - year = FIX2LONG(tobj->vtm.year); - } - if (RB_UNLIKELY(year > 9999 || year < 0 || fraction_digits > 9)) { - // Slow path for uncommon dates. - VALUE format = rb_utf8_str_new_cstr("%FT%T"); - if (fraction_digits > 0) { - rb_str_catf(format, ".%%#%ldN", fraction_digits); + long year = FIX2LONG(tobj->vtm.year); + int year_width = (year < 0) + rb_strlen_lit("YYYY"); + int w = (year >= -9999 && year <= 9999 ? year_width : (year < 0) + DECIMAL_SIZE_OF(year)); + str = rb_usascii_str_new(0, w + size_after_year); + ptr = RSTRING_PTR(str); + fill_digits_long(w + 1, year_width, year) { + if (year >= -9999 && year <= 9999) { + RUBY_ASSERT(written == year_width); + } + else { + RUBY_ASSERT(written >= year_width); + RUBY_ASSERT(written <= w); + } } - rb_str_cat_cstr(format, TZMODE_UTC_P(tobj) ? "Z" : "%:z"); - return rb_funcallv(time, rb_intern("strftime"), 1, &format); - } - - long buf_size = sizeof("YYYY-MM-DDTHH:MM:SS+ZH:ZM") + fraction_digits + (fraction_digits > 0 ? 1 : 0); - - VALUE str = rb_str_buf_new(buf_size); - rb_enc_associate_index(str, rb_utf8_encindex()); - - char *ptr = RSTRING_PTR(str); - char *start = ptr; - int written = snprintf( - ptr, - sizeof("YYYY-MM-DDTHH:MM:SS"), - "%04ld-%02d-%02dT%02d:%02d:%02d", - year, - tobj->vtm.mon, - tobj->vtm.mday, - tobj->vtm.hour, - tobj->vtm.min, - tobj->vtm.sec - ); - RUBY_ASSERT(written == sizeof("YYYY-MM-DDTHH:MM:SS") - 1); - ptr += written; + } + else { + str = rb_int2str(tobj->vtm.year, 10); + rb_str_modify_expand(str, size_after_year); + ptr = RSTRING_END(str); + } - if (fraction_digits > 0) { - long nsec = NUM2LONG(mulquov(tobj->vtm.subsecx, INT2FIX(1000000000), INT2FIX(TIME_SCALE))); - long subsec = nsec / (long)pow(10, 9 - fraction_digits); +# define fill_2(c, n) (*ptr++ = c, *ptr++ = '0' + (n) / 10, *ptr++ = '0' + (n) % 10) + fill_2('-', tobj->vtm.mon); + fill_2('-', tobj->vtm.mday); + fill_2('T', tobj->vtm.hour); + fill_2(':', tobj->vtm.min); + fill_2(':', tobj->vtm.sec); - *ptr = '.'; - ptr++; - - written = snprintf(ptr, fraction_digits + 1, "%0*ld", (int)fraction_digits, subsec); // Always allow to write \0 - RUBY_ASSERT(written > 0); - ptr += written; + if (fraction_digits > 0) { + VALUE subsecx = tobj->vtm.subsecx; + long subsec; + int digits = -1; + *ptr++ = '.'; + if (fraction_digits <= TIME_SCALE_NUMDIGITS) { + digits = TIME_SCALE_NUMDIGITS - (int)fraction_digits; + } + else { + long w = fraction_digits - TIME_SCALE_NUMDIGITS; /* > 0 */ + subsecx = mulv(subsecx, rb_int_positive_pow(10, (unsigned long)w)); + if (!RB_INTEGER_TYPE_P(subsecx)) { /* maybe Rational */ + subsecx = rb_Integer(subsecx); + } + if (FIXNUM_P(subsecx)) digits = 0; + } + if (digits >= 0 && fraction_digits < INT_MAX) { + subsec = NUM2LONG(subsecx); + if (digits > 0) subsec /= (long)pow(10, digits); + fill_digits_long(fraction_digits + 1, (int)fraction_digits, subsec) { + RUBY_ASSERT(written == (int)fraction_digits); + } + } + else { + subsecx = rb_int2str(subsecx, 10); + long len = RSTRING_LEN(subsecx); + if (fraction_digits > len) { + memset(ptr, '0', fraction_digits - len); + } + else { + len = fraction_digits; + } + ptr += fraction_digits; + memcpy(ptr - len, RSTRING_PTR(subsecx), len); + } } if (TZMODE_UTC_P(tobj)) { @@ -5285,14 +5313,13 @@ time_xmlschema(int argc, VALUE *argv, VALUE time) } else { long offset = NUM2LONG(rb_time_utc_offset(time)); - *ptr++ = offset < 0 ? '-' : '+'; + char sign = offset < 0 ? '-' : '+'; if (offset < 0) offset = -offset; - int offset_hours = (int)(offset / 3600); - int offset_minutes = (int)(offset % 3600 / 60); - written = snprintf(ptr, sizeof("ZH:ZM"), "%02d:%02d", offset_hours, offset_minutes); - RUBY_ASSERT(written == sizeof("ZH:ZM") - 1, "%d[%.*s]", written, written, ptr); - ptr += written; + offset /= 60; + fill_2(sign, offset / 60); + fill_2(':', offset % 60); } + const char *const start = RSTRING_PTR(str); rb_str_set_len(str, ptr - start); // We could skip coderange scanning as we know it's full ASCII. return str; } |