From 242b21947102d98aba2fa3db2725b129ca547f20 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Wed, 16 Nov 2016 02:05:19 +0900 Subject: Fixed memory disclosure in String#lines Reported from from Alex Snaps via Mathieu Leduc-Hamel, both from shopify.com. Thank you! --- mrbgems/mruby-string-ext/src/string.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'mrbgems/mruby-string-ext/src') diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c index 2a52d53b3..122ee5454 100644 --- a/mrbgems/mruby-string-ext/src/string.c +++ b/mrbgems/mruby-string-ext/src/string.c @@ -307,8 +307,9 @@ mrb_str_lines(mrb_state *mrb, mrb_value self) int ai; mrb_int len; mrb_value arg; - char *p = RSTRING_PTR(self), *t; - char *e = p + RSTRING_LEN(self); + char *b = RSTRING_PTR(self); + char *p = b, *t; + char *e = b + RSTRING_LEN(self); mrb_get_args(mrb, "&", &blk); @@ -322,6 +323,12 @@ mrb_str_lines(mrb_state *mrb, mrb_value self) len = (mrb_int) (p - t); arg = mrb_str_new(mrb, t, len); mrb_yield_argv(mrb, blk, 1, &arg); + if (b != RSTRING_PTR(self)) { + ptrdiff_t diff = p - b; + b = RSTRING_PTR(self); + p = b + diff; + } + e = b + RSTRING_LEN(self); } return self; } -- cgit v1.2.3 From 83005d83d8ba95524436409d5d73fd82b63bc115 Mon Sep 17 00:00:00 2001 From: Craig Lehmann Date: Tue, 15 Nov 2016 14:50:52 -0500 Subject: Read length after args in String#setbyte Prevents RCE Reported by https://hackerone.com/raydot --- mrbgems/mruby-string-ext/src/string.c | 3 ++- mrbgems/mruby-string-ext/test/string.rb | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'mrbgems/mruby-string-ext/src') diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c index 122ee5454..dfac907ec 100644 --- a/mrbgems/mruby-string-ext/src/string.c +++ b/mrbgems/mruby-string-ext/src/string.c @@ -23,10 +23,11 @@ static mrb_value mrb_str_setbyte(mrb_state *mrb, mrb_value str) { mrb_int pos, byte; - long len = RSTRING_LEN(str); + long len; mrb_get_args(mrb, "ii", &pos, &byte); + len = RSTRING_LEN(str); if (pos < -len || len <= pos) mrb_raisef(mrb, E_INDEX_ERROR, "index %S is out of array", mrb_fixnum_value(pos)); if (pos < 0) diff --git a/mrbgems/mruby-string-ext/test/string.rb b/mrbgems/mruby-string-ext/test/string.rb index a5d55a7ee..228a236af 100644 --- a/mrbgems/mruby-string-ext/test/string.rb +++ b/mrbgems/mruby-string-ext/test/string.rb @@ -30,6 +30,18 @@ assert('String#setbyte') do assert_equal("Hello", str1) end +assert("String#setbyte raises IndexError if arg conversion resizes String") do + $s = "01234\n" + class Tmp + def to_i + $s.chomp! '' + 95 + end + end + tmp = Tmp.new + assert_raise(IndexError) { $s.setbyte(5, tmp) } +end + assert('String#byteslice') do str1 = "hello" assert_equal("e", str1.byteslice(1)) -- cgit v1.2.3 From 803bf010ca9a963abfab2ec348ae1c5c98f41bc2 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sun, 25 Dec 2016 00:44:33 +0900 Subject: save/restore arena index around yield; ref #3359 --- mrbgems/mruby-string-ext/src/string.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'mrbgems/mruby-string-ext/src') diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c index dfac907ec..d48028b4a 100644 --- a/mrbgems/mruby-string-ext/src/string.c +++ b/mrbgems/mruby-string-ext/src/string.c @@ -315,7 +315,7 @@ mrb_str_lines(mrb_state *mrb, mrb_value self) mrb_get_args(mrb, "&", &blk); result = mrb_ary_new(mrb); - + ai = mrb_gc_arena_save(mrb); if (!mrb_nil_p(blk)) { while (p < e) { t = p; @@ -324,6 +324,7 @@ mrb_str_lines(mrb_state *mrb, mrb_value self) len = (mrb_int) (p - t); arg = mrb_str_new(mrb, t, len); mrb_yield_argv(mrb, blk, 1, &arg); + mrb_gc_arena_restore(mrb, ai); if (b != RSTRING_PTR(self)) { ptrdiff_t diff = p - b; b = RSTRING_PTR(self); @@ -334,7 +335,6 @@ mrb_str_lines(mrb_state *mrb, mrb_value self) return self; } while (p < e) { - ai = mrb_gc_arena_save(mrb); t = p; while (p < e && *p != '\n') p++; if (*p == '\n') p++; -- cgit v1.2.3 From eeca6ec8f511933e9a403e6b6166b874eead25b1 Mon Sep 17 00:00:00 2001 From: ksss Date: Wed, 4 Jan 2017 14:00:28 +0900 Subject: Rewrite String#prepend with Ruby Fix #3357 --- mrbgems/mruby-string-ext/mrblib/string.rb | 14 +++++++++++ mrbgems/mruby-string-ext/src/string.c | 39 ------------------------------- 2 files changed, 14 insertions(+), 39 deletions(-) (limited to 'mrbgems/mruby-string-ext/src') diff --git a/mrbgems/mruby-string-ext/mrblib/string.rb b/mrbgems/mruby-string-ext/mrblib/string.rb index e6fbe7ddc..6e5f3c73d 100644 --- a/mrbgems/mruby-string-ext/mrblib/string.rb +++ b/mrbgems/mruby-string-ext/mrblib/string.rb @@ -385,4 +385,18 @@ class String end end alias each_codepoint codepoints + + ## + # call-seq: + # str.prepend(other_str) -> str + # + # Prepend---Prepend the given string to str. + # + # a = "world" + # a.prepend("hello ") #=> "hello world" + # a #=> "hello world" + def prepend(arg) + self[0, 0] = arg + self + end end diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c index d48028b4a..c6a9e1d0b 100644 --- a/mrbgems/mruby-string-ext/src/string.c +++ b/mrbgems/mruby-string-ext/src/string.c @@ -437,44 +437,6 @@ mrb_str_succ(mrb_state *mrb, mrb_value self) return str; } -/* - * call-seq: - * str.prepend(other_str) -> str - * - * Prepend---Prepend the given string to str. - * - * a = "world" - * a.prepend("hello ") #=> "hello world" - * a #=> "hello world" - */ -static mrb_value -mrb_str_prepend(mrb_state *mrb, mrb_value self) -{ - struct RString *s1 = mrb_str_ptr(self), *s2, *temp_s; - mrb_int len; - mrb_value other, temp_str; - - mrb_get_args(mrb, "S", &other); - - mrb_str_modify(mrb, s1); - if (!mrb_string_p(other)) { - other = mrb_str_to_str(mrb, other); - } - s2 = mrb_str_ptr(other); - len = RSTR_LEN(s1) + RSTR_LEN(s2); - temp_str = mrb_str_new(mrb, NULL, RSTR_LEN(s1)); - temp_s = mrb_str_ptr(temp_str); - memcpy(RSTR_PTR(temp_s), RSTR_PTR(s1), RSTR_LEN(s1)); - if (RSTRING_CAPA(self) < len) { - mrb_str_resize(mrb, self, len); - } - memcpy(RSTR_PTR(s1), RSTR_PTR(s2), RSTR_LEN(s2)); - memcpy(RSTR_PTR(s1) + RSTR_LEN(s2), RSTR_PTR(temp_s), RSTR_LEN(temp_s)); - RSTR_SET_LEN(s1, len); - RSTR_PTR(s1)[len] = '\0'; - return self; -} - #ifdef MRB_UTF8_STRING static const char utf8len_codepage_zero[256] = { @@ -562,7 +524,6 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb) mrb_define_method(mrb, s, "lines", mrb_str_lines, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "succ", mrb_str_succ, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "succ!", mrb_str_succ_bang, MRB_ARGS_NONE()); - mrb_define_method(mrb, s, "prepend", mrb_str_prepend, MRB_ARGS_REQ(1)); 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()); -- cgit v1.2.3 From 5e1d923381072ebcbe002d70bc198a6e95c31f50 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Mon, 23 Jan 2017 14:35:26 +0900 Subject: Changed the behavior of mrb_range_beg_len(); close #3411 The new API is: int mrb_range_beg_len(mrb, range, &beg, &len, len, trunc) The new argument `trunc` is a boolean value that specifies whether the function truncates the range. The new return value is an integer instead of a boolean, that is: 0: not a range 1: range with proper edges 2: out of range To get the old behavior, you have to rewrite: mrb_range_beg_len(mrb, range, &beg, &len, len) to: mrn_range_beg_len(mrb, range, &beg, &len, len, TRUE) == 1 [Breaking Change] --- include/mruby/range.h | 2 +- mrbgems/mruby-kernel-ext/src/kernel.c | 2 +- mrbgems/mruby-string-ext/src/string.c | 8 +++++++- src/array.c | 19 ++++++++----------- src/range.c | 20 +++++++------------- src/string.c | 27 +++++++++++++++------------ 6 files changed, 39 insertions(+), 39 deletions(-) (limited to 'mrbgems/mruby-string-ext/src') diff --git a/include/mruby/range.h b/include/mruby/range.h index fb602b3f3..41e4f1254 100644 --- a/include/mruby/range.h +++ b/include/mruby/range.h @@ -41,7 +41,7 @@ MRB_API struct RRange* mrb_range_ptr(mrb_state *mrb, mrb_value v); */ MRB_API mrb_value mrb_range_new(mrb_state *mrb, mrb_value start, mrb_value end, mrb_bool exclude); -MRB_API mrb_bool mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len); +MRB_API int mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc); mrb_value mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, const mrb_value *argv, mrb_value (*func)(mrb_state*, mrb_value, mrb_int)); MRB_END_DECL diff --git a/mrbgems/mruby-kernel-ext/src/kernel.c b/mrbgems/mruby-kernel-ext/src/kernel.c index 1aa40260e..7bb4dea68 100644 --- a/mrbgems/mruby-kernel-ext/src/kernel.c +++ b/mrbgems/mruby-kernel-ext/src/kernel.c @@ -22,7 +22,7 @@ mrb_f_caller(mrb_state *mrb, mrb_value self) case 1: if (mrb_type(v) == MRB_TT_RANGE) { mrb_int beg, len; - if (mrb_range_beg_len(mrb, v, &beg, &len, bt_len)) { + if (mrb_range_beg_len(mrb, v, &beg, &len, bt_len, TRUE) == 1) { lev = beg; n = len; } diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c index c6a9e1d0b..7e87b3db4 100644 --- a/mrbgems/mruby-string-ext/src/string.c +++ b/mrbgems/mruby-string-ext/src/string.c @@ -56,8 +56,14 @@ mrb_str_byteslice(mrb_state *mrb, mrb_value str) mrb_int beg; len = RSTRING_LEN(str); - if (mrb_range_beg_len(mrb, a1, &beg, &len, len)) { + switch (mrb_range_beg_len(mrb, a1, &beg, &len, len, TRUE)) { + case 0: /* not range */ + break; + case 1: /* range */ return mrb_str_substr(mrb, str, beg, len); + case 2: /* out of range */ + mrb_raisef(mrb, E_RANGE_ERROR, "%S out of range", a1); + break; } return mrb_nil_value(); } diff --git a/src/array.c b/src/array.c index f9155d173..e41183d68 100644 --- a/src/array.c +++ b/src/array.c @@ -742,7 +742,7 @@ mrb_ary_aget(mrb_state *mrb, mrb_value self) switch (mrb_type(index)) { /* a[n..m] */ case MRB_TT_RANGE: - if (mrb_range_beg_len(mrb, index, &i, &len, a->len)) { + if (mrb_range_beg_len(mrb, index, &i, &len, a->len, TRUE) == 1) { return ary_subseq(mrb, a, i, len); } else { @@ -808,19 +808,16 @@ mrb_ary_aset(mrb_state *mrb, mrb_value self) mrb_ary_modify(mrb, mrb_ary_ptr(self)); if (mrb_get_args(mrb, "oo|o", &v1, &v2, &v3) == 2) { - switch (mrb_type(v1)) { /* a[n..m] = v */ - case MRB_TT_RANGE: - if (mrb_range_beg_len(mrb, v1, &i, &len, RARRAY_LEN(self))) { - mrb_ary_splice(mrb, self, i, len, v2); - } + switch (mrb_range_beg_len(mrb, v1, &i, &len, RARRAY_LEN(self), FALSE)) { + case 0: /* not range */ + mrb_ary_set(mrb, self, aget_index(mrb, v1), v2); break; - /* a[n] = v */ - case MRB_TT_FIXNUM: - mrb_ary_set(mrb, self, mrb_fixnum(v1), v2); + case 1: /* range */ + mrb_ary_splice(mrb, self, i, len, v2); break; - default: - mrb_ary_set(mrb, self, aget_index(mrb, v1), v2); + case 2: /* out of range */ + mrb_raisef(mrb, E_RANGE_ERROR, "%S out of range", v1); break; } return v2; diff --git a/src/range.c b/src/range.c index 73fe7589b..800e64611 100644 --- a/src/range.c +++ b/src/range.c @@ -248,13 +248,13 @@ mrb_range_include(mrb_state *mrb, mrb_value range) return mrb_bool_value(include_p); } -static mrb_bool -range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc) +int +mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc) { mrb_int beg, end; struct RRange *r; - if (mrb_type(range) != MRB_TT_RANGE) return FALSE; + if (mrb_type(range) != MRB_TT_RANGE) return 0; r = mrb_range_ptr(mrb, range); beg = mrb_int(mrb, r->edges->beg); @@ -262,11 +262,11 @@ range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb if (beg < 0) { beg += len; - if (beg < 0) return FALSE; + if (beg < 0) return 2; } if (trunc) { - if (beg > len) return FALSE; + if (beg > len) return 2; if (end > len) end = len; } @@ -278,13 +278,7 @@ range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb *begp = beg; *lenp = len; - return TRUE; -} - -MRB_API mrb_bool -mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len) -{ - return range_beg_len(mrb, range, begp, lenp, len, TRUE); + return 1; } /* 15.2.14.4.12(x) */ @@ -405,7 +399,7 @@ mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, con if (mrb_fixnum_p(argv[i])) { mrb_ary_push(mrb, result, func(mrb, obj, mrb_fixnum(argv[i]))); } - else if (range_beg_len(mrb, argv[i], &beg, &len, olen, FALSE)) { + else if (mrb_range_beg_len(mrb, argv[i], &beg, &len, olen, FALSE) == 1) { mrb_int const end = olen < beg + len ? olen : beg + len; for (j = beg; j < end; ++j) { mrb_ary_push(mrb, result, func(mrb, obj, j)); diff --git a/src/string.c b/src/string.c index 02c7ce426..7a75bb63e 100644 --- a/src/string.c +++ b/src/string.c @@ -1091,22 +1091,25 @@ num_index: return mrb_nil_value(); case MRB_TT_RANGE: - /* check if indx is Range */ - { - mrb_int beg, len; + goto range_arg; - len = RSTRING_CHAR_LEN(str); - if (mrb_range_beg_len(mrb, indx, &beg, &len, len)) { - return str_subseq(mrb, str, beg, len); - } - else { - return mrb_nil_value(); - } - } - case MRB_TT_FLOAT: default: indx = mrb_Integer(mrb, indx); if (mrb_nil_p(indx)) { + range_arg: + { + mrb_int beg, len; + + len = RSTRING_CHAR_LEN(str); + switch (mrb_range_beg_len(mrb, indx, &beg, &len, len, TRUE)) { + case 1: + return str_subseq(mrb, str, beg, len); + case 2: + return mrb_nil_value(); + default: + break; + } + } mrb_raise(mrb, E_TYPE_ERROR, "can't convert to Fixnum"); } idx = mrb_fixnum(indx); -- cgit v1.2.3 From 981105b3e6758455646e9834b1c2695bf774a401 Mon Sep 17 00:00:00 2001 From: Tomasz Dabrowski Date: Fri, 10 Feb 2017 12:58:41 +0100 Subject: String#ljust and String#rjust reimplementation (fix #3445) - String#ljust and String#rjust are now C functions to improve performance - infinite loop because of an empty padding argument is now prevented (ArgumentError is raised) - extra tests for ljust/rjust added --- mrbgems/mruby-string-ext/mrblib/string.rb | 46 ---------------- mrbgems/mruby-string-ext/src/string.c | 92 +++++++++++++++++++++++++++++++ mrbgems/mruby-string-ext/test/string.rb | 20 +++++++ 3 files changed, 112 insertions(+), 46 deletions(-) (limited to 'mrbgems/mruby-string-ext/src') diff --git a/mrbgems/mruby-string-ext/mrblib/string.rb b/mrbgems/mruby-string-ext/mrblib/string.rb index 6e5f3c73d..610a462a7 100644 --- a/mrbgems/mruby-string-ext/mrblib/string.rb +++ b/mrbgems/mruby-string-ext/mrblib/string.rb @@ -263,52 +263,6 @@ class String self end - ## - # call-seq: - # str.ljust(integer, padstr=' ') -> new_str - # - # If integer is greater than the length of str, returns a new - # String of length integer with str left justified - # and padded with padstr; otherwise, returns str. - # - # "hello".ljust(4) #=> "hello" - # "hello".ljust(20) #=> "hello " - # "hello".ljust(20, '1234') #=> "hello123412341234123" - def ljust(idx, padstr = ' ') - if idx <= self.size - return self - end - newstr = self.dup - newstr << padstr - while newstr.size <= idx - newstr << padstr - end - return newstr.slice(0,idx) - end - - ## - # call-seq: - # str.rjust(integer, padstr=' ') -> new_str - # - # If integer is greater than the length of str, returns a new - # String of length integer with str right justified - # and padded with padstr; otherwise, returns str. - # - # "hello".rjust(4) #=> "hello" - # "hello".rjust(20) #=> " hello" - # "hello".rjust(20, '1234') #=> "123412341234123hello" - def rjust(idx, padstr = ' ') - if idx <= self.size - return self - end - padsize = idx - self.size - newstr = padstr.dup - while newstr.size <= padsize - newstr << padstr - end - return newstr.slice(0,padsize) + self - end - # str.upto(other_str, exclusive=false) {|s| block } -> str # str.upto(other_str, exclusive=false) -> an_enumerator # diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c index 7e87b3db4..45d406f6f 100644 --- a/mrbgems/mruby-string-ext/src/string.c +++ b/mrbgems/mruby-string-ext/src/string.c @@ -5,6 +5,9 @@ #include #include +#define MAX(a,b) ((a)>(b) ? (a) : (b)) +#define MIN(a,b) ((a)<(b) ? (a) : (b)) + static mrb_value mrb_str_getbyte(mrb_state *mrb, mrb_value str) { @@ -509,6 +512,93 @@ mrb_str_ord(mrb_state* mrb, mrb_value str) } #endif +static mrb_value +mrb_str_just(mrb_state* mrb, mrb_value str, mrb_bool right_just) +{ + mrb_value new_str; + mrb_int idx = 0, i = 0, bytes_to_copy = 0, start_pos = 0, final_pos = 0, + pad_str_length = 0; + mrb_int str_length = RSTRING_LEN(str); + const char *pad_str = NULL; + char *new_str_ptr = NULL; + + mrb_get_args(mrb, "i|s!", &idx, &pad_str, &pad_str_length); + + if (pad_str == NULL) + { + pad_str = " "; + pad_str_length = 1; + } + + if (pad_str_length == 0) + { + mrb_raise(mrb, E_ARGUMENT_ERROR, "zero width padding"); + } + + if (idx <= str_length) + { + return str; + } + + new_str = mrb_str_dup(mrb, str); + mrb_str_resize(mrb, new_str, idx); + + new_str_ptr = RSTRING_PTR(new_str); + + if (right_just) + { + memcpy(new_str_ptr + idx - str_length, RSTRING_PTR(str), str_length); + } + + start_pos = right_just ? 0 : str_length; + final_pos = idx - (right_just ? str_length : 0); + + for (i = start_pos; i < final_pos; i += pad_str_length) + { + bytes_to_copy = idx - i - (right_just ? str_length : 0); + bytes_to_copy = MIN(pad_str_length, bytes_to_copy); + memcpy(new_str_ptr + i, pad_str, bytes_to_copy); + } + + return new_str; +} + +/* + * call-seq: + * str.ljust(integer, padstr=' ') -> new_str + * + * If integer is greater than the length of str, returns a new + * String of length integer with str left justified + * and padded with padstr; otherwise, returns str. + * + * "hello".ljust(4) #=> "hello" + * "hello".ljust(20) #=> "hello " + * "hello".ljust(20, '1234') #=> "hello123412341234123" + */ +static mrb_value +mrb_str_ljust(mrb_state* mrb, mrb_value str) +{ + return mrb_str_just(mrb, str, FALSE); +} + +/* + * call-seq: + * str.rjust(integer, padstr=' ') -> new_str + * + * If integer is greater than the length of str, returns a new + * String of length integer with str right justified + * and padded with padstr; otherwise, returns str. + * + * "hello".rjust(4) #=> "hello" + * "hello".rjust(20) #=> " hello" + * "hello".rjust(20, '1234') #=> "123412341234123hello" + */ +static mrb_value +mrb_str_rjust(mrb_state* mrb, mrb_value str) +{ + return mrb_str_just(mrb, str, TRUE); +} + void mrb_mruby_string_ext_gem_init(mrb_state* mrb) { @@ -530,6 +620,8 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb) mrb_define_method(mrb, s, "lines", mrb_str_lines, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "succ", mrb_str_succ, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "succ!", mrb_str_succ_bang, MRB_ARGS_NONE()); + mrb_define_method(mrb, s, "ljust", mrb_str_ljust, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); + mrb_define_method(mrb, s, "rjust", mrb_str_rjust, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); 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()); diff --git a/mrbgems/mruby-string-ext/test/string.rb b/mrbgems/mruby-string-ext/test/string.rb index 228a236af..2b2c02b8b 100644 --- a/mrbgems/mruby-string-ext/test/string.rb +++ b/mrbgems/mruby-string-ext/test/string.rb @@ -444,6 +444,26 @@ assert('String#rjust') do assert_equal "hello", "hello".rjust(-3) end +assert('String#ljust should not change string') do + a = "hello" + a.ljust(20) + assert_equal "hello", a +end + +assert('String#rjust should not change string') do + a = "hello" + a.rjust(20) + assert_equal "hello", a +end + +assert('String#ljust should raise on zero width padding') do + assert_raise(ArgumentError) { "foo".ljust(10, '') } +end + +assert('String#rjust should raise on zero width padding') do + assert_raise(ArgumentError) { "foo".rjust(10, '') } +end + assert('String#upto') do a = "aa" start = "aa" -- cgit v1.2.3 From ac9d04f4af6e229ad8e0f306c745b07b404978f5 Mon Sep 17 00:00:00 2001 From: Tomasz Dabrowski Date: Fri, 10 Feb 2017 15:17:20 +0100 Subject: String#ljust and String#rjust reimplemented with optimized Ruby --- mrbgems/mruby-string-ext/mrblib/string.rb | 38 +++++++++++++ mrbgems/mruby-string-ext/src/string.c | 92 ------------------------------- 2 files changed, 38 insertions(+), 92 deletions(-) (limited to 'mrbgems/mruby-string-ext/src') diff --git a/mrbgems/mruby-string-ext/mrblib/string.rb b/mrbgems/mruby-string-ext/mrblib/string.rb index 610a462a7..8895b7ad3 100644 --- a/mrbgems/mruby-string-ext/mrblib/string.rb +++ b/mrbgems/mruby-string-ext/mrblib/string.rb @@ -263,6 +263,44 @@ class String self end + ## + # call-seq: + # str.ljust(integer, padstr=' ') -> new_str + # + # If integer is greater than the length of str, returns a new + # String of length integer with str left justified + # and padded with padstr; otherwise, returns str. + # + # "hello".ljust(4) #=> "hello" + # "hello".ljust(20) #=> "hello " + # "hello".ljust(20, '1234') #=> "hello123412341234123" + def ljust(idx, padstr = ' ') + raise ArgumentError, 'zero width padding' if padstr == '' + return self if idx <= self.size + pad_repetitions = (idx / padstr.length).ceil + padding = (padstr * pad_repetitions)[0...(idx - self.length)] + self + padding + end + + ## + # call-seq: + # str.rjust(integer, padstr=' ') -> new_str + # + # If integer is greater than the length of str, returns a new + # String of length integer with str right justified + # and padded with padstr; otherwise, returns str. + # + # "hello".rjust(4) #=> "hello" + # "hello".rjust(20) #=> " hello" + # "hello".rjust(20, '1234') #=> "123412341234123hello" + def rjust(idx, padstr = ' ') + raise ArgumentError, 'zero width padding' if padstr == '' + return self if idx <= self.size + pad_repetitions = (idx / padstr.length).ceil + padding = (padstr * pad_repetitions)[0...(idx - self.length)] + padding + self + end + # str.upto(other_str, exclusive=false) {|s| block } -> str # str.upto(other_str, exclusive=false) -> an_enumerator # diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c index 45d406f6f..7e87b3db4 100644 --- a/mrbgems/mruby-string-ext/src/string.c +++ b/mrbgems/mruby-string-ext/src/string.c @@ -5,9 +5,6 @@ #include #include -#define MAX(a,b) ((a)>(b) ? (a) : (b)) -#define MIN(a,b) ((a)<(b) ? (a) : (b)) - static mrb_value mrb_str_getbyte(mrb_state *mrb, mrb_value str) { @@ -512,93 +509,6 @@ mrb_str_ord(mrb_state* mrb, mrb_value str) } #endif -static mrb_value -mrb_str_just(mrb_state* mrb, mrb_value str, mrb_bool right_just) -{ - mrb_value new_str; - mrb_int idx = 0, i = 0, bytes_to_copy = 0, start_pos = 0, final_pos = 0, - pad_str_length = 0; - mrb_int str_length = RSTRING_LEN(str); - const char *pad_str = NULL; - char *new_str_ptr = NULL; - - mrb_get_args(mrb, "i|s!", &idx, &pad_str, &pad_str_length); - - if (pad_str == NULL) - { - pad_str = " "; - pad_str_length = 1; - } - - if (pad_str_length == 0) - { - mrb_raise(mrb, E_ARGUMENT_ERROR, "zero width padding"); - } - - if (idx <= str_length) - { - return str; - } - - new_str = mrb_str_dup(mrb, str); - mrb_str_resize(mrb, new_str, idx); - - new_str_ptr = RSTRING_PTR(new_str); - - if (right_just) - { - memcpy(new_str_ptr + idx - str_length, RSTRING_PTR(str), str_length); - } - - start_pos = right_just ? 0 : str_length; - final_pos = idx - (right_just ? str_length : 0); - - for (i = start_pos; i < final_pos; i += pad_str_length) - { - bytes_to_copy = idx - i - (right_just ? str_length : 0); - bytes_to_copy = MIN(pad_str_length, bytes_to_copy); - memcpy(new_str_ptr + i, pad_str, bytes_to_copy); - } - - return new_str; -} - -/* - * call-seq: - * str.ljust(integer, padstr=' ') -> new_str - * - * If integer is greater than the length of str, returns a new - * String of length integer with str left justified - * and padded with padstr; otherwise, returns str. - * - * "hello".ljust(4) #=> "hello" - * "hello".ljust(20) #=> "hello " - * "hello".ljust(20, '1234') #=> "hello123412341234123" - */ -static mrb_value -mrb_str_ljust(mrb_state* mrb, mrb_value str) -{ - return mrb_str_just(mrb, str, FALSE); -} - -/* - * call-seq: - * str.rjust(integer, padstr=' ') -> new_str - * - * If integer is greater than the length of str, returns a new - * String of length integer with str right justified - * and padded with padstr; otherwise, returns str. - * - * "hello".rjust(4) #=> "hello" - * "hello".rjust(20) #=> " hello" - * "hello".rjust(20, '1234') #=> "123412341234123hello" - */ -static mrb_value -mrb_str_rjust(mrb_state* mrb, mrb_value str) -{ - return mrb_str_just(mrb, str, TRUE); -} - void mrb_mruby_string_ext_gem_init(mrb_state* mrb) { @@ -620,8 +530,6 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb) mrb_define_method(mrb, s, "lines", mrb_str_lines, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "succ", mrb_str_succ, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "succ!", mrb_str_succ_bang, MRB_ARGS_NONE()); - mrb_define_method(mrb, s, "ljust", mrb_str_ljust, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); - mrb_define_method(mrb, s, "rjust", mrb_str_rjust, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); 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()); -- cgit v1.2.3