diff options
author | Nobuyoshi Nakada <nobu@ruby-lang.org> | 2016-09-30 20:38:23 +0900 |
---|---|---|
committer | Nobuyoshi Nakada <nobu@ruby-lang.org> | 2019-08-12 20:41:11 +0900 |
commit | d96feee37c2d109d0103f08985e85641a23108bf (patch) | |
tree | 36f18fafde299f291a178a23185602f520f60ece /ext/date/date_parse.c | |
parent | e6a0a954c9ef5170b8b25088455106b3748c2187 (diff) | |
download | ruby-d96feee37c2d109d0103f08985e85641a23108bf.tar.gz |
date_parse.c: avoid copying
* ext/date/date_parse.c (date_zone_to_diff): get rid of copying
the whole argument string.
Diffstat (limited to 'ext/date/date_parse.c')
-rw-r--r-- | ext/date/date_parse.c | 112 |
1 files changed, 68 insertions, 44 deletions
diff --git a/ext/date/date_parse.c b/ext/date/date_parse.c index ab46bda4ad..8717850eba 100644 --- a/ext/date/date_parse.c +++ b/ext/date/date_parse.c @@ -361,64 +361,88 @@ do { \ #include "zonetab.h" static int -str_end_with(const char *s, long l, const char *w) +str_end_with_word(const char *s, long l, const char *w) { int n = (int)strlen(w); - return (l >= n && strncmp(s - n, w, n) == 0); + if (l <= n || !isspace(s[l - n - 1])) return 0; + if (strncasecmp(&s[l - n], w, n)) return 0; + do ++n; while (l > n && isspace(s[l - n - 1])); + return n; } -VALUE -date_zone_to_diff(VALUE str) +static long +shrunk_size(const char *s, long l) { - VALUE offset = Qnil; - VALUE vbuf = 0; - - long l, i; - char *s, *dest, *d; - int sp = 1; - - l = RSTRING_LEN(str); - s = RSTRING_PTR(str); - - dest = d = ALLOCV_N(char, vbuf, l + 1); - - for (i = 0; i < l; i++) { - if (isspace((unsigned char)s[i]) || s[i] == '\0') { - if (!sp) - *d++ = ' '; - sp = 1; + long i, ni; + int sp = 0; + for (i = ni = 0; i < l; ++i) { + if (!isspace(s[i])) { + if (sp) ni++; + sp = 0; + ni++; } else { - if (isalpha((unsigned char)s[i])) - *d++ = tolower((unsigned char)s[i]); - else - *d++ = s[i]; - sp = 0; + sp = 1; } } - if (d > dest) { - if (*(d - 1) == ' ') - --d; - *d = '\0'; + return ni < l ? ni : 0; +} + +static long +shrink_space(char *d, const char *s, long l) +{ + long i, ni; + int sp = 0; + for (i = ni = 0; i < l; ++i) { + if (!isspace(s[i])) { + if (sp) d[ni++] = ' '; + sp = 0; + d[ni++] = s[i]; + } + else { + sp = 1; + } } - l = d - dest; - s = dest; + return ni; +} + +VALUE +date_zone_to_diff(VALUE str) +{ + VALUE offset = Qnil; + VALUE vbuf = 0; + long l = RSTRING_LEN(str); + const char *s = RSTRING_PTR(str); + { - static const char STD[] = " standard time"; - static const char DST1[] = " daylight time"; - static const char DST2[] = " dst"; int dst = 0; + int w; - if (str_end_with(d, l, STD)) { - l -= sizeof(STD) - 1; + if ((w = str_end_with_word(s, l, "time")) > 0) { + int wtime = w; + l -= w; + if ((w = str_end_with_word(s, l, "standard")) > 0) { + l -= w; + } + else if ((w = str_end_with_word(s, l, "daylight")) > 0) { + l -= w; + dst = 1; + } + else { + l += wtime; + } } - else if (str_end_with(d, l, DST1)) { - l -= sizeof(DST1) - 1; + else if ((w = str_end_with_word(s, l, "dst")) > 0) { + l -= w; dst = 1; } - else if (str_end_with(d, l, DST2)) { - l -= sizeof(DST2) - 1; - dst = 1; + { + long sl = shrunk_size(s, l); + if (sl) { + char *d = ALLOCV_N(char, vbuf, sl); + l = shrink_space(d, s, l); + s = d; + } } { const struct zone *z = zonetab(s, (unsigned int)l); @@ -436,8 +460,8 @@ date_zone_to_diff(VALUE str) long hour = 0, min = 0, sec = 0; if (l > 3 && - (strncmp(s, "gmt", 3) == 0 || - strncmp(s, "utc", 3) == 0)) { + (strncasecmp(s, "gmt", 3) == 0 || + strncasecmp(s, "utc", 3) == 0)) { s += 3; l -= 3; } |