From 19785f43d18891307dd18c1ed33b21099848ca72 Mon Sep 17 00:00:00 2001 From: ksss Date: Wed, 14 Jun 2017 17:07:32 +0900 Subject: Reimplement String#upto --- mrbgems/mruby-string-ext/src/string.c | 114 ++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) (limited to 'mrbgems/mruby-string-ext/src/string.c') diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c index 9dc1aeafc..4740b356f 100644 --- a/mrbgems/mruby-string-ext/src/string.c +++ b/mrbgems/mruby-string-ext/src/string.c @@ -521,6 +521,119 @@ mrb_str_ord(mrb_state* mrb, mrb_value str) } #endif +static mrb_bool +all_digits_p(const char *s, mrb_int len) +{ + while (len-- > 0) { + if (!ISDIGIT(*s)) return FALSE; + s++; + } + return TRUE; +} + +/* + * call-seq: + * str.upto(other_str, exclusive=false) {|s| block } -> str + * str.upto(other_str, exclusive=false) -> an_enumerator + * + * Iterates through successive values, starting at str and + * ending at other_str inclusive, passing each value in turn to + * the block. The String#succ method is used to generate + * each value. If optional second argument exclusive is omitted or is false, + * the last value will be included; otherwise it will be excluded. + * + * If no block is given, an enumerator is returned instead. + * + * "a8".upto("b6") {|s| print s, ' ' } + * for s in "a8".."b6" + * print s, ' ' + * end + * + * produces: + * + * a8 a9 b0 b1 b2 b3 b4 b5 b6 + * a8 a9 b0 b1 b2 b3 b4 b5 b6 + * + * If str and other_str contains only ascii numeric characters, + * both are recognized as decimal numbers. In addition, the width of + * string (e.g. leading zeros) is handled appropriately. + * + * "9".upto("11").to_a #=> ["9", "10", "11"] + * "25".upto("5").to_a #=> [] + * "07".upto("11").to_a #=> ["07", "08", "09", "10", "11"] + */ +static mrb_value +mrb_str_upto(mrb_state *mrb, mrb_value beg) +{ + mrb_value end; + mrb_value exclusive = mrb_false_value(); + mrb_value block = mrb_nil_value(); + mrb_value current, after_end; + mrb_int n; + mrb_bool excl; + + mrb_get_args(mrb, "o|o&", &end, &exclusive, &block); + + if (mrb_nil_p(block)) { + return mrb_funcall(mrb, beg, "to_enum", 3, mrb_symbol_value(mrb_intern_lit(mrb, "upto")), end, exclusive); + } + end = mrb_string_type(mrb, end); + excl = mrb_test(exclusive); + + /* single character */ + if (RSTRING_LEN(beg) == 1 && RSTRING_LEN(end) == 1 && + ISASCII(RSTRING_PTR(beg)[0]) && ISASCII(RSTRING_PTR(end)[0])) { + char c = RSTRING_PTR(beg)[0]; + char e = RSTRING_PTR(end)[0]; + + if (c > e || (excl && c == e)) return beg; + for (;;) { + mrb_yield(mrb, block, mrb_str_new(mrb, &c, 1)); + if (!excl && c == e) break; + c++; + if (excl && c == e) break; + } + return beg; + } + /* both edges are all digits */ + if (ISDIGIT(RSTRING_PTR(beg)[0]) && ISDIGIT(RSTRING_PTR(end)[0]) && + all_digits_p(RSTRING_PTR(beg), RSTRING_LEN(beg)) && + all_digits_p(RSTRING_PTR(end), RSTRING_LEN(end))) { + mrb_int min_width = RSTRING_LEN(beg); + mrb_int max_width = RSTRING_LEN(end); + mrb_int bi = mrb_int(mrb, mrb_str_to_inum(mrb, beg, 10, FALSE)); + mrb_int ei = mrb_int(mrb, mrb_str_to_inum(mrb, end, 10, FALSE)); + char buf[max_width+1]; + + while (bi <= ei) { + if (excl && bi == ei) break; + snprintf(buf, sizeof(buf), "%.*d", min_width, bi); + mrb_yield(mrb, block, mrb_str_new(mrb, buf, strlen(buf))); + bi++; + } + return beg; + } + /* normal case */ + n = mrb_int(mrb, mrb_funcall(mrb, beg, "<=>", 1, end)); + if (n > 0 || (excl && n == 0)) return beg; + + after_end = mrb_funcall(mrb, end, "succ", 0); + current = mrb_str_dup(mrb, beg); + while (!mrb_str_equal(mrb, current, after_end)) { + mrb_value next = mrb_nil_value(); + if (excl || !mrb_str_equal(mrb, current, end)) + next = mrb_funcall(mrb, current, "succ", 0); + mrb_yield(mrb, block, current); + if (mrb_nil_p(next)) break; + current = mrb_str_to_str(mrb, next); + if (excl && mrb_str_equal(mrb, current, end)) break; + if (RSTRING_LEN(current) > RSTRING_LEN(end) || RSTRING_LEN(current) == 0) + break; + } + + return beg; +} + void mrb_mruby_string_ext_gem_init(mrb_state* mrb) { @@ -545,6 +658,7 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb) mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "next"), mrb_intern_lit(mrb, "succ")); mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "next!"), mrb_intern_lit(mrb, "succ!")); mrb_define_method(mrb, s, "ord", mrb_str_ord, MRB_ARGS_NONE()); + mrb_define_method(mrb, s, "upto", mrb_str_upto, MRB_ARGS_ANY()); mrb_define_method(mrb, mrb->fixnum_class, "chr", mrb_fixnum_chr, MRB_ARGS_NONE()); } -- cgit v1.2.3 From 432a813905ec86fadeaa0842b136e4bd1f335b91 Mon Sep 17 00:00:00 2001 From: ksss Date: Wed, 14 Jun 2017 18:05:47 +0900 Subject: Fix arena overflow error --- mrbgems/mruby-string-ext/src/string.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'mrbgems/mruby-string-ext/src/string.c') diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c index 4740b356f..f3343447d 100644 --- a/mrbgems/mruby-string-ext/src/string.c +++ b/mrbgems/mruby-string-ext/src/string.c @@ -585,10 +585,12 @@ mrb_str_upto(mrb_state *mrb, mrb_value beg) ISASCII(RSTRING_PTR(beg)[0]) && ISASCII(RSTRING_PTR(end)[0])) { char c = RSTRING_PTR(beg)[0]; char e = RSTRING_PTR(end)[0]; + int ai = mrb_gc_arena_save(mrb); if (c > e || (excl && c == e)) return beg; for (;;) { mrb_yield(mrb, block, mrb_str_new(mrb, &c, 1)); + mrb_gc_arena_restore(mrb, ai); if (!excl && c == e) break; c++; if (excl && c == e) break; @@ -604,11 +606,13 @@ mrb_str_upto(mrb_state *mrb, mrb_value beg) mrb_int bi = mrb_int(mrb, mrb_str_to_inum(mrb, beg, 10, FALSE)); mrb_int ei = mrb_int(mrb, mrb_str_to_inum(mrb, end, 10, FALSE)); char buf[max_width+1]; + int ai = mrb_gc_arena_save(mrb); while (bi <= ei) { if (excl && bi == ei) break; snprintf(buf, sizeof(buf), "%.*d", min_width, bi); mrb_yield(mrb, block, mrb_str_new(mrb, buf, strlen(buf))); + mrb_gc_arena_restore(mrb, ai); bi++; } return beg; @@ -620,6 +624,7 @@ mrb_str_upto(mrb_state *mrb, mrb_value beg) after_end = mrb_funcall(mrb, end, "succ", 0); current = mrb_str_dup(mrb, beg); while (!mrb_str_equal(mrb, current, after_end)) { + int ai = mrb_gc_arena_save(mrb); mrb_value next = mrb_nil_value(); if (excl || !mrb_str_equal(mrb, current, end)) next = mrb_funcall(mrb, current, "succ", 0); @@ -629,6 +634,7 @@ mrb_str_upto(mrb_state *mrb, mrb_value beg) if (excl && mrb_str_equal(mrb, current, end)) break; if (RSTRING_LEN(current) > RSTRING_LEN(end) || RSTRING_LEN(current) == 0) break; + mrb_gc_arena_restore(mrb, ai); } return beg; -- cgit v1.2.3 From 4d9ca27ba6d44e7df56c02e240cc8795933072e5 Mon Sep 17 00:00:00 2001 From: ksss Date: Wed, 14 Jun 2017 18:28:39 +0900 Subject: Use malloc instead of dynamic allocation --- mrbgems/mruby-string-ext/src/string.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'mrbgems/mruby-string-ext/src/string.c') diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c index f3343447d..d52513026 100644 --- a/mrbgems/mruby-string-ext/src/string.c +++ b/mrbgems/mruby-string-ext/src/string.c @@ -605,7 +605,7 @@ mrb_str_upto(mrb_state *mrb, mrb_value beg) mrb_int max_width = RSTRING_LEN(end); mrb_int bi = mrb_int(mrb, mrb_str_to_inum(mrb, beg, 10, FALSE)); mrb_int ei = mrb_int(mrb, mrb_str_to_inum(mrb, end, 10, FALSE)); - char buf[max_width+1]; + char *buf = (char *)mrb_malloc(mrb, max_width+1); int ai = mrb_gc_arena_save(mrb); while (bi <= ei) { @@ -615,6 +615,8 @@ mrb_str_upto(mrb_state *mrb, mrb_value beg) mrb_gc_arena_restore(mrb, ai); bi++; } + + mrb_free(mrb, buf); return beg; } /* normal case */ -- cgit v1.2.3 From 473b2bb20df8e67cc3846c2ced8ea72513a477be Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Fri, 16 Jun 2017 11:42:51 +0900 Subject: Should not use `sizeof(buf)` when `buf` is `char*`; #3701 --- mrbgems/mruby-string-ext/src/string.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'mrbgems/mruby-string-ext/src/string.c') diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c index d52513026..138b9e3cf 100644 --- a/mrbgems/mruby-string-ext/src/string.c +++ b/mrbgems/mruby-string-ext/src/string.c @@ -610,7 +610,7 @@ mrb_str_upto(mrb_state *mrb, mrb_value beg) while (bi <= ei) { if (excl && bi == ei) break; - snprintf(buf, sizeof(buf), "%.*d", min_width, bi); + snprintf(buf, max_width+1, "%.*d", (int)min_width, bi); mrb_yield(mrb, block, mrb_str_new(mrb, buf, strlen(buf))); mrb_gc_arena_restore(mrb, ai); bi++; -- cgit v1.2.3 From e96f254ead50f70b49b7055eaa132dc4f756a8e3 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Fri, 16 Jun 2017 11:46:40 +0900 Subject: Use `mrb_str_new()` instead of `malloc()`; ref #3701 Otherwise the function may terminate and cause memory leaks. --- mrbgems/mruby-string-ext/src/string.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'mrbgems/mruby-string-ext/src/string.c') diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c index 138b9e3cf..c8f14040a 100644 --- a/mrbgems/mruby-string-ext/src/string.c +++ b/mrbgems/mruby-string-ext/src/string.c @@ -599,14 +599,15 @@ mrb_str_upto(mrb_state *mrb, mrb_value beg) } /* both edges are all digits */ if (ISDIGIT(RSTRING_PTR(beg)[0]) && ISDIGIT(RSTRING_PTR(end)[0]) && - all_digits_p(RSTRING_PTR(beg), RSTRING_LEN(beg)) && - all_digits_p(RSTRING_PTR(end), RSTRING_LEN(end))) { + all_digits_p(RSTRING_PTR(beg), RSTRING_LEN(beg)) && + all_digits_p(RSTRING_PTR(end), RSTRING_LEN(end))) { + int ai = mrb_gc_arena_save(mrb); mrb_int min_width = RSTRING_LEN(beg); mrb_int max_width = RSTRING_LEN(end); mrb_int bi = mrb_int(mrb, mrb_str_to_inum(mrb, beg, 10, FALSE)); mrb_int ei = mrb_int(mrb, mrb_str_to_inum(mrb, end, 10, FALSE)); - char *buf = (char *)mrb_malloc(mrb, max_width+1); - int ai = mrb_gc_arena_save(mrb); + mrb_value str = mrb_str_new(mrb, NULL, max_width); + char *buf = RSTRING_PTR(str); while (bi <= ei) { if (excl && bi == ei) break; @@ -616,7 +617,6 @@ mrb_str_upto(mrb_state *mrb, mrb_value beg) bi++; } - mrb_free(mrb, buf); return beg; } /* normal case */ -- cgit v1.2.3