diff --git a/ChangeLog b/ChangeLog index f6aa2e078e..3073407d93 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Sun Aug 31 15:46:23 2014 Nobuyoshi Nakada + + * array.c (yield_indexed_values): extract from permute0(), + rpermute0(), and rcombinate0(). + Sun Aug 31 15:23:25 2014 Nobuyoshi Nakada * array.c (rb_ary_permutation): `p` is the array of size `r`, as diff --git a/array.c b/array.c index 5e95e87685..b302b8a268 100644 --- a/array.c +++ b/array.c @@ -4528,6 +4528,25 @@ rb_ary_cycle(int argc, VALUE *argv, VALUE ary) #define tmpary(n) rb_ary_tmp_new(n) #define tmpary_discard(a) (ary_discard(a), RBASIC(a)->klass = rb_cArray) +/* + * Build a ruby array of the corresponding values and yield it to the + * associated block. + * Return the class of +values+ for reentry check. + */ +static int +yield_indexed_values(const VALUE values, const long r, const long *const p) +{ + const VALUE result = rb_ary_new2(r); + VALUE *const result_array = RARRAY_PTR(result); + const VALUE *const values_array = RARRAY_PTR(values); + long i; + + for (i = 0; i < r; i++) result_array[i] = values_array[p[i]]; + ARY_SET_LEN(result, r); + rb_yield(result); + return !RBASIC(values)->klass; +} + /* * Recursively compute permutations of +r+ elements of the set * [0..n-1]. @@ -4545,7 +4564,7 @@ rb_ary_cycle(int argc, VALUE *argv, VALUE ary) static void permute0(long n, long r, long *p, long index, char *used, VALUE values) { - long i,j; + long i; for (i = 0; i < n; i++) { if (used[i] == 0) { p[index] = i; @@ -4556,17 +4575,7 @@ permute0(long n, long r, long *p, long index, char *used, VALUE values) used[i] = 0; /* index unused */ } else { - /* We have a complete permutation of array indexes */ - /* Build a ruby array of the corresponding values */ - /* And yield it to the associated block */ - VALUE result = rb_ary_new2(r); - VALUE *result_array = RARRAY_PTR(result); - const VALUE *values_array = RARRAY_PTR(values); - - for (j = 0; j < r; j++) result_array[j] = values_array[p[j]]; - ARY_SET_LEN(result, r); - rb_yield(result); - if (RBASIC(values)->klass) { + if (!yield_indexed_values(values, r, p)) { rb_raise(rb_eRuntimeError, "permute reentered"); } } @@ -4731,21 +4740,19 @@ rb_ary_combination(VALUE ary, VALUE num) } } else { - volatile VALUE t0 = tmpbuf(n+1, sizeof(long)); - long *stack = (long*)RSTRING_PTR(t0); - volatile VALUE cc = tmpary(n); - VALUE *chosen = RARRAY_PTR(cc); + VALUE ary0 = ary_make_shared_copy(ary); /* private defensive copy of ary */ + volatile VALUE t0; + long *stack = ALLOCV_N(long, t0, n+1); long lev = 0; - MEMZERO(stack, long, n); + RBASIC(ary0)->klass = 0; + MEMZERO(stack+1, long, n); stack[0] = -1; for (;;) { - chosen[lev] = RARRAY_PTR(ary)[stack[lev+1]]; for (lev++; lev < n; lev++) { - chosen[lev] = RARRAY_PTR(ary)[stack[lev+1] = stack[lev]+1]; + stack[lev+1] = stack[lev]+1; } - rb_yield(rb_ary_new4(n, chosen)); - if (RBASIC(t0)->klass) { + if (!yield_indexed_values(ary0, n, stack+1)) { rb_raise(rb_eRuntimeError, "combination reentered"); } do { @@ -4754,8 +4761,8 @@ rb_ary_combination(VALUE ary, VALUE num) } while (stack[lev+1]+n == len+lev+1); } done: - tmpbuf_discard(t0); - tmpary_discard(cc); + ALLOCV_END(t0); + RBASIC(ary0)->klass = rb_cArray; } return ary; } @@ -4776,24 +4783,14 @@ rb_ary_combination(VALUE ary, VALUE num) static void rpermute0(long n, long r, long *p, long index, VALUE values) { - long i, j; + long i; for (i = 0; i < n; i++) { p[index] = i; if (index < r-1) { /* if not done yet */ rpermute0(n, r, p, index+1, values); /* recurse */ } else { - /* We have a complete permutation of array indexes */ - /* Build a ruby array of the corresponding values */ - /* And yield it to the associated block */ - VALUE result = rb_ary_new2(r); - VALUE *result_array = RARRAY_PTR(result); - const VALUE *values_array = RARRAY_PTR(values); - - for (j = 0; j < r; j++) result_array[j] = values_array[p[j]]; - ARY_SET_LEN(result, r); - rb_yield(result); - if (RBASIC(values)->klass) { + if (!yield_indexed_values(values, r, p)) { rb_raise(rb_eRuntimeError, "repeated permute reentered"); } } @@ -4872,7 +4869,6 @@ rb_ary_repeated_permutation(VALUE ary, VALUE num) static void rcombinate0(long n, long r, long *p, long index, long rest, VALUE values) { - long j; if (rest > 0) { for (; index < n; ++index) { p[r-rest] = index; @@ -4880,14 +4876,7 @@ rcombinate0(long n, long r, long *p, long index, long rest, VALUE values) } } else { - VALUE result = rb_ary_new2(r); - VALUE *result_array = RARRAY_PTR(result); - const VALUE *values_array = RARRAY_PTR(values); - - for (j = 0; j < r; ++j) result_array[j] = values_array[p[j]]; - ARY_SET_LEN(result, r); - rb_yield(result); - if (RBASIC(values)->klass) { + if (!yield_indexed_values(values, r, p)) { rb_raise(rb_eRuntimeError, "repeated combination reentered"); } } diff --git a/test/ruby/test_array.rb b/test/ruby/test_array.rb index 2f03b93a4b..5812958f0d 100644 --- a/test/ruby/test_array.rb +++ b/test/ruby/test_array.rb @@ -2201,6 +2201,15 @@ class TestArray < Test::Unit::TestCase assert_equal(:called, (0..100).to_a.combination(50) { break :called }, "[ruby-core:29240] ... must be yielded even if 100C50 > signed integer") end + def test_combination_clear + bug9939 = '[ruby-core:63149] [Bug #9939]' + assert_separately([], <<-'end;') + 100_000.times {Array.new(1000)} + a = [*0..100] + a.combination(3) {|*,x| a.clear} + end; + end + def test_product2 a = (0..100).to_a assert_raise(RangeError) do diff --git a/version.h b/version.h index b47efb5314..2bde95c383 100644 --- a/version.h +++ b/version.h @@ -1,6 +1,6 @@ #define RUBY_VERSION "2.0.0" #define RUBY_RELEASE_DATE "2014-08-31" -#define RUBY_PATCHLEVEL 532 +#define RUBY_PATCHLEVEL 533 #define RUBY_RELEASE_YEAR 2014 #define RUBY_RELEASE_MONTH 8