Project

General

Profile

Feature #12744 ยป add-reverse-string-iteration.patch

Patch - bouk (Bouke van der Bijl), 09/09/2016 03:36 PM

View differences:

ChangeLog
Thu Sep 10 00:22:18 2016 Bouke van der Bijl <boukevanderbijl@gmail.com>
* string.c (rb_str_reverse_each_char, rb_str_reverse_chars): Add
str.reverse_each_char and str.reverse_chars that allow for efficiently
iterating over a string in reverse.
* test/ruby/test_string.rb: add tests for above
Thu Sep 8 17:47:18 2016 Kazuki Tsujimoto <kazuki@callcc.net>
* array.c (flatten): use rb_obj_class instead of rb_class_of
string.c
return rb_str_enumerate_chars(str, 1);
}
static VALUE
rb_str_reverse_enumerate_chars(VALUE str, int wantarray)
{
VALUE orig = str;
VALUE substr;
long len, n;
const char *ptr, *end;
char *p;
rb_encoding *enc;
VALUE UNINITIALIZED_VAR(ary);
str = rb_str_new_frozen(str);
ptr = RSTRING_PTR(str);
len = RSTRING_LEN(str);
end = ptr + len;
enc = rb_enc_get(str);
if (rb_block_given_p()) {
if (wantarray) {
#if STRING_ENUMERATORS_WANTARRAY
rb_warn("given block not used");
ary = rb_ary_new_capa(str_strlen(str, enc)); /* str's enc*/
#else
rb_warning("passing a block to String#chars is deprecated");
wantarray = 0;
#endif
}
}
else {
if (wantarray)
ary = rb_ary_new_capa(str_strlen(str, enc)); /* str's enc*/
else
return SIZED_ENUMERATOR(str, 0, 0, rb_str_each_char_size);
}
if (ENC_CODERANGE_CLEAN_P(ENC_CODERANGE(str))) {
for(p = rb_enc_left_char_head(ptr, end - 1, end, enc); p >= ptr; p = rb_enc_left_char_head(ptr, p - 1, end, enc)) {
n = rb_enc_fast_mbclen(p, end, enc);
substr = rb_str_subseq(str, p - ptr, n);
if (wantarray)
rb_ary_push(ary, substr);
else
rb_yield(substr);
}
}
else {
for(p = rb_enc_left_char_head(ptr, end - 1, end, enc); p >= ptr; p = rb_enc_left_char_head(ptr, p - 1, end, enc)) {
n = rb_enc_mbclen(p, end, enc);
substr = rb_str_subseq(str, p - ptr, n);
if (wantarray)
rb_ary_push(ary, substr);
else
rb_yield(substr);
}
}
RB_GC_GUARD(str);
if (wantarray)
return ary;
else
return orig;
}
/*
* call-seq:
* str.reverse_each_char {|cstr| block } -> str
* str.reverse_each_char -> an_enumerator
*
* Passes each character in <i>str</i> in reverse to the given block, or returns
* an enumerator if no block is given.
*
* "hello".reverse_each_char {|c| print c, ' ' }
*
* <em>produces:</em>
*
* o l l e h
*/
static VALUE
rb_str_reverse_each_char(VALUE str)
{
return rb_str_reverse_enumerate_chars(str, 0);
}
/*
* call-seq:
* str.reverse_chars -> an_array
*
* Returns an array of characters in <i>str</i> in reverse. This is a shorthand
* for <code>str.reverse_each_char.to_a</code>.
*
* If a block is given, which is a deprecated form, works the same as
* <code>reverse_each_char</code>.
*/
static VALUE
rb_str_reverse_chars(VALUE str)
{
return rb_str_reverse_enumerate_chars(str, 1);
}
static VALUE
rb_str_enumerate_codepoints(VALUE str, int wantarray)
......
rb_define_method(rb_cString, "lines", rb_str_lines, -1);
rb_define_method(rb_cString, "bytes", rb_str_bytes, 0);
rb_define_method(rb_cString, "chars", rb_str_chars, 0);
rb_define_method(rb_cString, "reverse_chars", rb_str_reverse_chars, 0);
rb_define_method(rb_cString, "codepoints", rb_str_codepoints, 0);
rb_define_method(rb_cString, "reverse", rb_str_reverse, 0);
rb_define_method(rb_cString, "reverse!", rb_str_reverse_bang, 0);
......
rb_define_method(rb_cString, "each_line", rb_str_each_line, -1);
rb_define_method(rb_cString, "each_byte", rb_str_each_byte, 0);
rb_define_method(rb_cString, "each_char", rb_str_each_char, 0);
rb_define_method(rb_cString, "reverse_each_char", rb_str_reverse_each_char, 0);
rb_define_method(rb_cString, "each_codepoint", rb_str_each_codepoint, 0);
rb_define_method(rb_cString, "sum", rb_str_sum, -1);
test/ruby/test_string.rb
end
end
def test_reverse_each_char
s = S("ABC")
res = []
assert_equal s.object_id, s.reverse_each_char {|x| res << x }.object_id
assert_equal("C", res[0])
assert_equal("B", res[1])
assert_equal("A", res[2])
assert_equal "๐Ÿ˜€", S("ABC๐Ÿ˜€").reverse_each_char.next
end
def test_reverse_chars
s = S("ABC")
assert_equal ["C", "B", "A"], s.reverse_chars
if ENUMERATOR_WANTARRAY
assert_warn(/block not used/) {
assert_equal ["C", "B", "A"], s.reverse_chars {}
}
else
assert_warning(/deprecated/) {
res = []
assert_equal s.object_id, s.reverse_chars {|x| res << x }.object_id
assert_equal("C", res[0])
assert_equal("B", res[1])
assert_equal("A", res[2])
}
end
end
def test_each_line
save = $/
$/ = "\n"
    (1-1/1)