diff --git a/mrbgems/mruby-string-ext/mrblib/string.rb b/mrbgems/mruby-string-ext/mrblib/string.rb index 9f940ca32..37b5fcb75 100644 --- a/mrbgems/mruby-string-ext/mrblib/string.rb +++ b/mrbgems/mruby-string-ext/mrblib/string.rb @@ -13,103 +13,6 @@ class String self.replace("") end - ## - # call-seq: - # str.lstrip -> new_str - # - # Returns a copy of str with leading whitespace removed. See also - # String#rstrip and String#strip. - # - # " hello ".lstrip #=> "hello " - # "hello".lstrip #=> "hello" - # - def lstrip - a = 0 - z = self.size - 1 - a += 1 while a <= z and " \f\n\r\t\v".include?(self[a]) - (z >= 0) ? self[a..z] : "" - end - - ## - # call-seq: - # str.rstrip -> new_str - # - # Returns a copy of str with trailing whitespace removed. See also - # String#lstrip and String#strip. - # - # " hello ".rstrip #=> " hello" - # "hello".rstrip #=> "hello" - # - def rstrip - a = 0 - z = self.size - 1 - z -= 1 while a <= z and " \f\n\r\t\v\0".include?(self[z]) - (z >= 0) ? self[a..z] : "" - end - - ## - # call-seq: - # str.strip -> new_str - # - # Returns a copy of str with leading and trailing whitespace removed. - # - # " hello ".strip #=> "hello" - # "\tgoodbye\r\n".strip #=> "goodbye" - # - def strip - a = 0 - z = self.size - 1 - a += 1 while a <= z and " \f\n\r\t\v".include?(self[a]) - z -= 1 while a <= z and " \f\n\r\t\v\0".include?(self[z]) - (z >= 0) ? self[a..z] : "" - end - - ## - # call-seq: - # str.lstrip! -> self or nil - # - # Removes leading whitespace from str, returning nil if no - # change was made. See also String#rstrip! and - # String#strip!. - # - # " hello ".lstrip #=> "hello " - # "hello".lstrip! #=> nil - # - def lstrip! - raise FrozenError, "can't modify frozen String" if frozen? - s = self.lstrip - (s == self) ? nil : self.replace(s) - end - - ## - # call-seq: - # str.rstrip! -> self or nil - # - # Removes trailing whitespace from str, returning nil if - # no change was made. See also String#lstrip! and - # String#strip!. - # - # " hello ".rstrip #=> " hello" - # "hello".rstrip! #=> nil - # - def rstrip! - raise FrozenError, "can't modify frozen String" if frozen? - s = self.rstrip - (s == self) ? nil : self.replace(s) - end - - ## - # call-seq: - # str.strip! -> str or nil - # - # Removes leading and trailing whitespace from str. Returns - # nil if str was not altered. - # - def strip! - raise FrozenError, "can't modify frozen String" if frozen? - s = self.strip - (s == self) ? nil : self.replace(s) - end def partition(sep) raise TypeError, "type mismatch: #{sep.class} given" unless sep.is_a? String diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c index 2410612b4..d3ee353bc 100644 --- a/mrbgems/mruby-string-ext/src/string.c +++ b/mrbgems/mruby-string-ext/src/string.c @@ -1344,6 +1344,255 @@ str_b(mrb_state *mrb, mrb_value self) return str; } +/* + * Check if character is whitespace (space, tab, newline, carriage return, form feed, vertical tab) + */ +static inline mrb_bool +is_whitespace(char c) +{ + return (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v'); +} + +/* + * Check if character is whitespace or null (for rstrip) + */ +static inline mrb_bool +is_whitespace_or_null(char c) +{ + return (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v' || c == '\0'); +} + +/* + * call-seq: + * str.lstrip -> new_str + * + * Returns a copy of str with leading whitespace removed. + * + * " hello ".lstrip #=> "hello " + * "hello".lstrip #=> "hello" + */ +static mrb_value +str_lstrip(mrb_state *mrb, mrb_value self) +{ + struct RString *s = mrb_str_ptr(self); + const char *ptr = RSTR_PTR(s); + mrb_int len = RSTR_LEN(s); + mrb_int start = 0; + + /* Find first non-whitespace character */ + while (start < len && is_whitespace(ptr[start])) { + start++; + } + + /* Return empty string if all whitespace */ + if (start >= len) { + return mrb_str_new_lit(mrb, ""); + } + + /* Return substring from first non-whitespace to end */ + return mrb_str_substr(mrb, self, start, len - start); +} + +/* + * call-seq: + * str.rstrip -> new_str + * + * Returns a copy of str with trailing whitespace removed. + * + * " hello ".rstrip #=> " hello" + * "hello".rstrip #=> "hello" + */ +static mrb_value +str_rstrip(mrb_state *mrb, mrb_value self) +{ + struct RString *s = mrb_str_ptr(self); + const char *ptr = RSTR_PTR(s); + mrb_int len = RSTR_LEN(s); + mrb_int end = len; + + /* Find last non-whitespace character */ + while (end > 0 && is_whitespace_or_null(ptr[end - 1])) { + end--; + } + + /* Return empty string if all whitespace */ + if (end <= 0) { + return mrb_str_new_lit(mrb, ""); + } + + /* Return substring from start to last non-whitespace */ + return mrb_str_substr(mrb, self, 0, end); +} + +/* + * call-seq: + * str.strip -> new_str + * + * Returns a copy of str with leading and trailing whitespace removed. + * + * " hello ".strip #=> "hello" + * "\tgoodbye\r\n".strip #=> "goodbye" + */ +static mrb_value +str_strip(mrb_state *mrb, mrb_value self) +{ + struct RString *s = mrb_str_ptr(self); + const char *ptr = RSTR_PTR(s); + mrb_int len = RSTR_LEN(s); + mrb_int start = 0; + mrb_int end = len; + + /* Find first non-whitespace character */ + while (start < len && is_whitespace(ptr[start])) { + start++; + } + + /* Find last non-whitespace character */ + while (end > start && is_whitespace_or_null(ptr[end - 1])) { + end--; + } + + /* Return empty string if all whitespace */ + if (start >= end) { + return mrb_str_new_lit(mrb, ""); + } + + /* Return substring from first to last non-whitespace */ + return mrb_str_substr(mrb, self, start, end - start); +} + +/* + * call-seq: + * str.lstrip! -> self or nil + * + * Removes leading whitespace from str, returning nil if no change was made. + * + * " hello ".lstrip! #=> "hello " + * "hello".lstrip! #=> nil + */ +static mrb_value +str_lstrip_bang(mrb_state *mrb, mrb_value self) +{ + struct RString *s = mrb_str_ptr(self); + char *ptr = RSTR_PTR(s); + mrb_int len = RSTR_LEN(s); + mrb_int start = 0; + + mrb_check_frozen(mrb, mrb_obj_ptr(self)); + mrb_str_modify(mrb, s); + + /* Find first non-whitespace character */ + while (start < len && is_whitespace(ptr[start])) { + start++; + } + + /* No change needed */ + if (start == 0) { + return mrb_nil_value(); + } + + /* Move remaining characters to beginning */ + if (start < len) { + memmove(ptr, ptr + start, len - start); + RSTR_SET_LEN(s, len - start); + } else { + /* All whitespace - make empty */ + RSTR_SET_LEN(s, 0); + } + + return self; +} + +/* + * call-seq: + * str.rstrip! -> self or nil + * + * Removes trailing whitespace from str, returning nil if no change was made. + * + * " hello ".rstrip! #=> " hello" + * "hello".rstrip! #=> nil + */ +static mrb_value +str_rstrip_bang(mrb_state *mrb, mrb_value self) +{ + struct RString *s = mrb_str_ptr(self); + char *ptr = RSTR_PTR(s); + mrb_int len = RSTR_LEN(s); + mrb_int end = len; + + mrb_check_frozen(mrb, mrb_obj_ptr(self)); + mrb_str_modify(mrb, s); + + /* Find last non-whitespace character */ + while (end > 0 && is_whitespace_or_null(ptr[end - 1])) { + end--; + } + + /* No change needed */ + if (end == len) { + return mrb_nil_value(); + } + + /* Truncate string */ + RSTR_SET_LEN(s, end); + + return self; +} + +/* + * call-seq: + * str.strip! -> self or nil + * + * Removes leading and trailing whitespace from str, returning nil if no change was made. + * + * " hello ".strip! #=> "hello" + * "hello".strip! #=> nil + */ +static mrb_value +str_strip_bang(mrb_state *mrb, mrb_value self) +{ + struct RString *s = mrb_str_ptr(self); + char *ptr = RSTR_PTR(s); + mrb_int len = RSTR_LEN(s); + mrb_int start = 0; + mrb_int end = len; + mrb_bool changed = FALSE; + + mrb_check_frozen(mrb, mrb_obj_ptr(self)); + mrb_str_modify(mrb, s); + + /* Find first non-whitespace character */ + while (start < len && is_whitespace(ptr[start])) { + start++; + } + + /* Find last non-whitespace character */ + while (end > start && is_whitespace_or_null(ptr[end - 1])) { + end--; + } + + /* Check if any changes needed */ + if (start > 0) { + changed = TRUE; + if (start < end) { + memmove(ptr, ptr + start, end - start); + } + } + + if (end != len) { + changed = TRUE; + } + + if (!changed) { + return mrb_nil_value(); + } + + /* Set new length */ + RSTR_SET_LEN(s, end - start); + + return self; +} + void mrb_mruby_string_ext_gem_init(mrb_state* mrb) { @@ -1388,6 +1637,14 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb) mrb_define_method_id(mrb, s, MRB_SYM(__lines), str_lines, MRB_ARGS_NONE()); mrb_define_method_id(mrb, s, MRB_SYM(__codepoints), str_codepoints, MRB_ARGS_NONE()); + /* Optimized strip methods implemented in C */ + mrb_define_method_id(mrb, s, MRB_SYM(lstrip), str_lstrip, MRB_ARGS_NONE()); + mrb_define_method_id(mrb, s, MRB_SYM(rstrip), str_rstrip, MRB_ARGS_NONE()); + mrb_define_method_id(mrb, s, MRB_SYM(strip), str_strip, MRB_ARGS_NONE()); + mrb_define_method_id(mrb, s, MRB_SYM_B(lstrip), str_lstrip_bang, MRB_ARGS_NONE()); + mrb_define_method_id(mrb, s, MRB_SYM_B(rstrip), str_rstrip_bang, MRB_ARGS_NONE()); + mrb_define_method_id(mrb, s, MRB_SYM_B(strip), str_strip_bang, MRB_ARGS_NONE()); + mrb_define_method_id(mrb, mrb->integer_class, MRB_SYM(chr), int_chr, MRB_ARGS_OPT(1)); }