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
1
Thu Sep  10 00:22:18 2016  Bouke van der Bijl  <boukevanderbijl@gmail.com>
2

  
3
	* string.c (rb_str_reverse_each_char, rb_str_reverse_chars): Add
4
	str.reverse_each_char and str.reverse_chars that allow for efficiently
5
	iterating over a string in reverse.
6

  
7
	* test/ruby/test_string.rb: add tests for above
8

  
1 9
Thu Sep  8 17:47:18 2016  Kazuki Tsujimoto  <kazuki@callcc.net>
2 10

  
3 11
	* array.c (flatten): use rb_obj_class instead of rb_class_of
string.c
7692 7692
    return rb_str_enumerate_chars(str, 1);
7693 7693
}
7694 7694

  
7695
static VALUE
7696
rb_str_reverse_enumerate_chars(VALUE str, int wantarray)
7697
{
7698
    VALUE orig = str;
7699
    VALUE substr;
7700
    long len, n;
7701
    const char *ptr, *end;
7702
    char *p;
7703
    rb_encoding *enc;
7704
    VALUE UNINITIALIZED_VAR(ary);
7705

  
7706
    str = rb_str_new_frozen(str);
7707
    ptr = RSTRING_PTR(str);
7708
    len = RSTRING_LEN(str);
7709
    end = ptr + len;
7710
    enc = rb_enc_get(str);
7711

  
7712
    if (rb_block_given_p()) {
7713
	if (wantarray) {
7714
#if STRING_ENUMERATORS_WANTARRAY
7715
	    rb_warn("given block not used");
7716
	    ary = rb_ary_new_capa(str_strlen(str, enc)); /* str's enc*/
7717
#else
7718
	    rb_warning("passing a block to String#chars is deprecated");
7719
	    wantarray = 0;
7720
#endif
7721
	}
7722
    }
7723
    else {
7724
	if (wantarray)
7725
	    ary = rb_ary_new_capa(str_strlen(str, enc)); /* str's enc*/
7726
	else
7727
	    return SIZED_ENUMERATOR(str, 0, 0, rb_str_each_char_size);
7728
    }
7729

  
7730
    if (ENC_CODERANGE_CLEAN_P(ENC_CODERANGE(str))) {
7731
	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)) {
7732
	    n = rb_enc_fast_mbclen(p, end, enc);
7733
	    substr = rb_str_subseq(str, p - ptr, n);
7734
	    if (wantarray)
7735
		rb_ary_push(ary, substr);
7736
	    else
7737
		rb_yield(substr);
7738
	}
7739
    }
7740
    else {
7741
	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)) {
7742
	    n = rb_enc_mbclen(p, end, enc);
7743
	    substr = rb_str_subseq(str, p - ptr, n);
7744
	    if (wantarray)
7745
		rb_ary_push(ary, substr);
7746
	    else
7747
		rb_yield(substr);
7748
	}
7749
    }
7750

  
7751
    RB_GC_GUARD(str);
7752
    if (wantarray)
7753
	return ary;
7754
    else
7755
	return orig;
7756
}
7757

  
7758
/*
7759
 *  call-seq:
7760
 *     str.reverse_each_char {|cstr| block }    -> str
7761
 *     str.reverse_each_char                    -> an_enumerator
7762
 *
7763
 *  Passes each character in <i>str</i> in reverse to the given block, or returns
7764
 *  an enumerator if no block is given.
7765
 *
7766
 *     "hello".reverse_each_char {|c| print c, ' ' }
7767
 *
7768
 *  <em>produces:</em>
7769
 *
7770
 *     o l l e h
7771
 */
7772

  
7773
static VALUE
7774
rb_str_reverse_each_char(VALUE str)
7775
{
7776
    return rb_str_reverse_enumerate_chars(str, 0);
7777
}
7778

  
7779
/*
7780
 *  call-seq:
7781
 *     str.reverse_chars    -> an_array
7782
 *
7783
 *  Returns an array of characters in <i>str</i> in reverse.  This is a shorthand
7784
 *  for <code>str.reverse_each_char.to_a</code>.
7785
 *
7786
 *  If a block is given, which is a deprecated form, works the same as
7787
 *  <code>reverse_each_char</code>.
7788
 */
7789

  
7790
static VALUE
7791
rb_str_reverse_chars(VALUE str)
7792
{
7793
    return rb_str_reverse_enumerate_chars(str, 1);
7794
}
7695 7795

  
7696 7796
static VALUE
7697 7797
rb_str_enumerate_codepoints(VALUE str, int wantarray)
......
9857 9957
    rb_define_method(rb_cString, "lines", rb_str_lines, -1);
9858 9958
    rb_define_method(rb_cString, "bytes", rb_str_bytes, 0);
9859 9959
    rb_define_method(rb_cString, "chars", rb_str_chars, 0);
9960
    rb_define_method(rb_cString, "reverse_chars", rb_str_reverse_chars, 0);
9860 9961
    rb_define_method(rb_cString, "codepoints", rb_str_codepoints, 0);
9861 9962
    rb_define_method(rb_cString, "reverse", rb_str_reverse, 0);
9862 9963
    rb_define_method(rb_cString, "reverse!", rb_str_reverse_bang, 0);
......
9908 10009
    rb_define_method(rb_cString, "each_line", rb_str_each_line, -1);
9909 10010
    rb_define_method(rb_cString, "each_byte", rb_str_each_byte, 0);
9910 10011
    rb_define_method(rb_cString, "each_char", rb_str_each_char, 0);
10012
    rb_define_method(rb_cString, "reverse_each_char", rb_str_reverse_each_char, 0);
9911 10013
    rb_define_method(rb_cString, "each_codepoint", rb_str_each_codepoint, 0);
9912 10014

  
9913 10015
    rb_define_method(rb_cString, "sum", rb_str_sum, -1);
test/ruby/test_string.rb
767 767
    end
768 768
  end
769 769

  
770
  def test_reverse_each_char
771
    s = S("ABC")
772

  
773
    res = []
774
    assert_equal s.object_id, s.reverse_each_char {|x| res << x }.object_id
775
    assert_equal("C", res[0])
776
    assert_equal("B", res[1])
777
    assert_equal("A", res[2])
778

  
779
    assert_equal "๐Ÿ˜€", S("ABC๐Ÿ˜€").reverse_each_char.next
780
  end
781

  
782
  def test_reverse_chars
783
    s = S("ABC")
784
    assert_equal ["C", "B", "A"], s.reverse_chars
785

  
786
    if ENUMERATOR_WANTARRAY
787
      assert_warn(/block not used/) {
788
        assert_equal ["C", "B", "A"], s.reverse_chars {}
789
      }
790
    else
791
      assert_warning(/deprecated/) {
792
        res = []
793
        assert_equal s.object_id, s.reverse_chars {|x| res << x }.object_id
794
        assert_equal("C", res[0])
795
        assert_equal("B", res[1])
796
        assert_equal("A", res[2])
797
      }
798
    end
799
  end
800

  
770 801
  def test_each_line
771 802
    save = $/
772 803
    $/ = "\n"