Created
August 18, 2010 19:59
-
-
Save marcandre/535974 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/array.c b/array.c | |
index 51d3ad2..29f2ca0 100644 | |
--- a/array.c | |
+++ b/array.c | |
@@ -22,7 +22,7 @@ | |
VALUE rb_cArray; | |
-static ID id_cmp; | |
+static ID id_cmp, id_div, id_power; | |
#define ARY_DEFAULT_SIZE 16 | |
#define ARY_MAX_SIZE (LONG_MAX / (int)sizeof(VALUE)) | |
@@ -1399,6 +1399,9 @@ rb_ary_insert(int argc, VALUE *argv, VALUE ary) | |
return ary; | |
} | |
+static VALUE | |
+rb_ary_length(VALUE ary); | |
+ | |
/* | |
* call-seq: | |
* ary.each {|item| block } -> ary | |
@@ -1422,7 +1425,7 @@ rb_ary_each(VALUE ary) | |
{ | |
long i; | |
- RETURN_ENUMERATOR(ary, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(ary, 0, 0, rb_ary_length); | |
for (i=0; i<RARRAY_LEN(ary); i++) { | |
rb_yield(RARRAY_PTR(ary)[i]); | |
} | |
@@ -1452,7 +1455,7 @@ static VALUE | |
rb_ary_each_index(VALUE ary) | |
{ | |
long i; | |
- RETURN_ENUMERATOR(ary, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(ary, 0, 0, rb_ary_length); | |
for (i=0; i<RARRAY_LEN(ary); i++) { | |
rb_yield(LONG2NUM(i)); | |
@@ -1481,7 +1484,7 @@ rb_ary_reverse_each(VALUE ary) | |
{ | |
long len; | |
- RETURN_ENUMERATOR(ary, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(ary, 0, 0, rb_ary_length); | |
len = RARRAY_LEN(ary); | |
while (len--) { | |
rb_yield(RARRAY_PTR(ary)[len]); | |
@@ -2128,7 +2131,7 @@ rb_ary_sort_by_bang(VALUE ary) | |
{ | |
VALUE sorted; | |
- RETURN_ENUMERATOR(ary, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(ary, 0, 0, rb_ary_length); | |
rb_ary_modify(ary); | |
sorted = rb_block_call(ary, rb_intern("sort_by"), 0, 0, sort_by_i, 0); | |
rb_ary_replace(ary, sorted); | |
@@ -2160,7 +2163,7 @@ rb_ary_collect(VALUE ary) | |
long i; | |
VALUE collect; | |
- RETURN_ENUMERATOR(ary, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(ary, 0, 0, rb_ary_length); | |
collect = rb_ary_new2(RARRAY_LEN(ary)); | |
for (i = 0; i < RARRAY_LEN(ary); i++) { | |
rb_ary_push(collect, rb_yield(RARRAY_PTR(ary)[i])); | |
@@ -2192,7 +2195,7 @@ rb_ary_collect_bang(VALUE ary) | |
{ | |
long i; | |
- RETURN_ENUMERATOR(ary, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(ary, 0, 0, rb_ary_length); | |
rb_ary_modify(ary); | |
for (i = 0; i < RARRAY_LEN(ary); i++) { | |
rb_ary_store(ary, i, rb_yield(RARRAY_PTR(ary)[i])); | |
@@ -2272,7 +2275,7 @@ rb_ary_select(VALUE ary) | |
VALUE result; | |
long i; | |
- RETURN_ENUMERATOR(ary, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(ary, 0, 0, rb_ary_length); | |
result = rb_ary_new2(RARRAY_LEN(ary)); | |
for (i = 0; i < RARRAY_LEN(ary); i++) { | |
if (RTEST(rb_yield(RARRAY_PTR(ary)[i]))) { | |
@@ -2302,7 +2305,7 @@ rb_ary_select_bang(VALUE ary) | |
{ | |
long i1, i2; | |
- RETURN_ENUMERATOR(ary, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(ary, 0, 0, rb_ary_length); | |
rb_ary_modify(ary); | |
for (i1 = i2 = 0; i1 < RARRAY_LEN(ary); i1++) { | |
VALUE v = RARRAY_PTR(ary)[i1]; | |
@@ -2337,7 +2340,7 @@ rb_ary_select_bang(VALUE ary) | |
static VALUE | |
rb_ary_keep_if(VALUE ary) | |
{ | |
- RETURN_ENUMERATOR(ary, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(ary, 0, 0, rb_ary_length); | |
rb_ary_select_bang(ary); | |
return ary; | |
} | |
@@ -2528,7 +2531,7 @@ rb_ary_reject_bang(VALUE ary) | |
{ | |
long i1, i2; | |
- RETURN_ENUMERATOR(ary, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(ary, 0, 0, rb_ary_length); | |
rb_ary_modify(ary); | |
for (i1 = i2 = 0; i1 < RARRAY_LEN(ary); i1++) { | |
VALUE v = RARRAY_PTR(ary)[i1]; | |
@@ -2561,7 +2564,7 @@ rb_ary_reject_bang(VALUE ary) | |
static VALUE | |
rb_ary_reject(VALUE ary) | |
{ | |
- RETURN_ENUMERATOR(ary, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(ary, 0, 0, rb_ary_length); | |
ary = rb_ary_dup(ary); | |
rb_ary_reject_bang(ary); | |
return ary; | |
@@ -2585,7 +2588,7 @@ rb_ary_reject(VALUE ary) | |
static VALUE | |
rb_ary_delete_if(VALUE ary) | |
{ | |
- RETURN_ENUMERATOR(ary, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(ary, 0, 0, rb_ary_length); | |
rb_ary_reject_bang(ary); | |
return ary; | |
} | |
@@ -3863,6 +3866,20 @@ rb_ary_sample(int argc, VALUE *argv, VALUE ary) | |
return result; | |
} | |
+static VALUE | |
+rb_ary_cycle_size(VALUE self, VALUE args) | |
+{ | |
+ long mul; | |
+ VALUE n = Qnil; | |
+ if (args && (RARRAY_LEN(args) > 0)) { | |
+ n = RARRAY_PTR(args)[0]; | |
+ } | |
+ if (RARRAY_LEN(self) == 0) return INT2FIX(0); | |
+ if (n == Qnil) return DBL2NUM(INFINITY); | |
+ mul = NUM2LONG(n); | |
+ if (mul <= 0) return INT2FIX(0); | |
+ return rb_funcall(rb_ary_length(self), '*', 1, LONG2FIX(mul)); | |
+} | |
/* | |
* call-seq: | |
@@ -3891,7 +3908,7 @@ rb_ary_cycle(int argc, VALUE *argv, VALUE ary) | |
rb_scan_args(argc, argv, "01", &nv); | |
- RETURN_ENUMERATOR(ary, argc, argv); | |
+ RETURN_SIZED_ENUMERATOR(ary, argc, argv, rb_ary_cycle_size); | |
if (NIL_P(nv)) { | |
n = -1; | |
} | |
@@ -3958,6 +3975,41 @@ permute0(long n, long r, long *p, long index, char *used, VALUE values) | |
} | |
/* | |
+ * Returns the product of from, from-1, ..., from - how_many + 1. | |
+ * http://en.wikipedia.org/wiki/Pochhammer_symbol | |
+ */ | |
+static VALUE | |
+descending_factorial(long from, long how_many) | |
+{ | |
+ VALUE cnt = LONG2FIX(1); | |
+ while(how_many-- > 0) { | |
+ cnt = rb_funcall(cnt, '*', 1, LONG2FIX(from--)); | |
+ } | |
+ return cnt; | |
+} | |
+ | |
+static VALUE | |
+binomial_coefficient(long comb, long size) | |
+{ | |
+ if (comb > size-comb) { | |
+ comb = size-comb; | |
+ } | |
+ if (comb < 0) { | |
+ return LONG2FIX(0); | |
+ } | |
+ return rb_funcall(descending_factorial(size, comb), id_div, 1, descending_factorial(comb, comb)); | |
+} | |
+ | |
+static VALUE | |
+rb_ary_permutation_size(VALUE ary, VALUE args) | |
+{ | |
+ long n = RARRAY_LEN(ary); | |
+ long k = (args && (RARRAY_LEN(args) > 0)) ? NUM2LONG(RARRAY_PTR(args)[0]) : n; | |
+ | |
+ return descending_factorial(n, k); | |
+} | |
+ | |
+/* | |
* call-seq: | |
* ary.permutation { |p| block } -> ary | |
* ary.permutation -> an_enumerator | |
@@ -3990,7 +4042,7 @@ rb_ary_permutation(int argc, VALUE *argv, VALUE ary) | |
long r, n, i; | |
n = RARRAY_LEN(ary); /* Array length */ | |
- RETURN_ENUMERATOR(ary, argc, argv); /* Return enumerator if no block */ | |
+ RETURN_SIZED_ENUMERATOR(ary, argc, argv, rb_ary_permutation_size); /* Return enumerator if no block */ | |
rb_scan_args(argc, argv, "01", &num); | |
r = NIL_P(num) ? n : NUM2LONG(num); /* Permutation size from argument */ | |
@@ -4023,6 +4075,15 @@ rb_ary_permutation(int argc, VALUE *argv, VALUE ary) | |
return ary; | |
} | |
+static VALUE | |
+rb_ary_combination_size(VALUE ary, VALUE args) | |
+{ | |
+ long n = RARRAY_LEN(ary); | |
+ long k = NUM2LONG(RARRAY_PTR(args)[0]); | |
+ | |
+ return binomial_coefficient(k, n); | |
+} | |
+ | |
/* | |
* call-seq: | |
* ary.combination(n) { |c| block } -> ary | |
@@ -4053,7 +4114,7 @@ rb_ary_combination(VALUE ary, VALUE num) | |
long n, i, len; | |
n = NUM2LONG(num); | |
- RETURN_ENUMERATOR(ary, 1, &num); | |
+ RETURN_SIZED_ENUMERATOR(ary, 1, &num, rb_ary_combination_size); | |
len = RARRAY_LEN(ary); | |
if (n < 0 || len < n) { | |
/* yield nothing */ | |
@@ -4135,6 +4196,19 @@ rpermute0(long n, long r, long *p, long index, VALUE values) | |
} | |
} | |
+static VALUE | |
+rb_ary_repeated_permutation_size(VALUE ary, VALUE args) | |
+{ | |
+ long n = RARRAY_LEN(ary); | |
+ long k = NUM2LONG(RARRAY_PTR(args)[0]); | |
+ | |
+ if (k < 0) { | |
+ return LONG2FIX(0); | |
+ } | |
+ | |
+ return rb_funcall(LONG2NUM(n), id_power, 1, LONG2NUM(k)); | |
+} | |
+ | |
/* | |
* call-seq: | |
* ary.repeated_permutation(n) { |p| block } -> ary | |
@@ -4163,7 +4237,7 @@ rb_ary_repeated_permutation(VALUE ary, VALUE num) | |
long r, n, i; | |
n = RARRAY_LEN(ary); /* Array length */ | |
- RETURN_ENUMERATOR(ary, 1, &num); /* Return enumerator if no block */ | |
+ RETURN_SIZED_ENUMERATOR(ary, 1, &num, rb_ary_repeated_permutation_size); /* Return enumerator if no block */ | |
r = NUM2LONG(num); /* Permutation size from argument */ | |
if (r < 0) { | |
@@ -4214,6 +4288,17 @@ rcombinate0(long n, long r, long *p, long index, long rest, VALUE values) | |
} | |
} | |
+static VALUE | |
+rb_ary_repeated_combination_size(VALUE ary, VALUE args) | |
+{ | |
+ long n = RARRAY_LEN(ary); | |
+ long k = NUM2LONG(RARRAY_PTR(args)[0]); | |
+ if (k == 0) { | |
+ return LONG2FIX(1); | |
+ } | |
+ return binomial_coefficient(k, n + k - 1); | |
+} | |
+ | |
/* | |
* call-seq: | |
* ary.repeated_combination(n) { |c| block } -> ary | |
@@ -4247,7 +4332,7 @@ rb_ary_repeated_combination(VALUE ary, VALUE num) | |
long n, i, len; | |
n = NUM2LONG(num); /* Combination size from argument */ | |
- RETURN_ENUMERATOR(ary, 1, &num); /* Return enumerator if no block */ | |
+ RETURN_SIZED_ENUMERATOR(ary, 1, &num, rb_ary_repeated_combination_size); /* Return enumerator if no block */ | |
len = RARRAY_LEN(ary); | |
if (n < 0) { | |
/* yield nothing */ | |
@@ -4609,4 +4694,6 @@ Init_Array(void) | |
rb_define_method(rb_cArray, "drop_while", rb_ary_drop_while, 0); | |
id_cmp = rb_intern("<=>"); | |
+ id_div = rb_intern("div"); | |
+ id_power = rb_intern("**"); | |
} | |
diff --git a/enum.c b/enum.c | |
index fc8a3c3..883ea22 100644 | |
--- a/enum.c | |
+++ b/enum.c | |
@@ -14,7 +14,7 @@ | |
#include "node.h" | |
VALUE rb_mEnumerable; | |
-static ID id_each, id_eqq, id_cmp, id_next, id_size; | |
+static ID id_each, id_eqq, id_cmp, id_next, id_size, id_div; | |
static VALUE | |
enum_values_pack(int argc, VALUE *argv) | |
@@ -296,6 +296,14 @@ find_all_i(VALUE i, VALUE ary, int argc, VALUE *argv) | |
return Qnil; | |
} | |
+static VALUE | |
+enum_size(VALUE self, VALUE args) | |
+{ | |
+ VALUE r; | |
+ r = rb_check_funcall(self, id_size, 0, 0); | |
+ return (r == Qundef) ? Qnil : r; | |
+} | |
+ | |
/* | |
* call-seq: | |
* enum.find_all {| obj | block } -> array | |
@@ -319,7 +327,7 @@ enum_find_all(VALUE obj) | |
{ | |
VALUE ary; | |
- RETURN_ENUMERATOR(obj, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size); | |
ary = rb_ary_new(); | |
rb_block_call(obj, id_each, 0, 0, find_all_i, ary); | |
@@ -357,7 +365,7 @@ enum_reject(VALUE obj) | |
{ | |
VALUE ary; | |
- RETURN_ENUMERATOR(obj, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size); | |
ary = rb_ary_new(); | |
rb_block_call(obj, id_each, 0, 0, reject_i, ary); | |
@@ -404,7 +412,7 @@ enum_collect(VALUE obj) | |
{ | |
VALUE ary; | |
- RETURN_ENUMERATOR(obj, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size); | |
ary = rb_ary_new(); | |
rb_block_call(obj, id_each, 0, 0, collect_i, ary); | |
@@ -450,7 +458,7 @@ enum_flat_map(VALUE obj) | |
{ | |
VALUE ary; | |
- RETURN_ENUMERATOR(obj, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size); | |
ary = rb_ary_new(); | |
rb_block_call(obj, id_each, 0, 0, flat_map_i, ary); | |
@@ -621,7 +629,7 @@ enum_partition(VALUE obj) | |
{ | |
VALUE ary[2]; | |
- RETURN_ENUMERATOR(obj, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size); | |
ary[0] = rb_ary_new(); | |
ary[1] = rb_ary_new(); | |
@@ -670,7 +678,7 @@ enum_group_by(VALUE obj) | |
{ | |
VALUE hash; | |
- RETURN_ENUMERATOR(obj, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size); | |
hash = rb_hash_new(); | |
rb_block_call(obj, id_each, 0, 0, group_by_i, hash); | |
@@ -867,7 +875,7 @@ enum_sort_by(VALUE obj) | |
VALUE ary; | |
long i; | |
- RETURN_ENUMERATOR(obj, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size); | |
if (TYPE(obj) == T_ARRAY) { | |
ary = rb_ary_new2(RARRAY_LEN(obj)); | |
@@ -1382,7 +1390,7 @@ enum_min_by(VALUE obj) | |
{ | |
VALUE memo[2]; | |
- RETURN_ENUMERATOR(obj, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size); | |
memo[0] = Qundef; | |
memo[1] = Qnil; | |
@@ -1428,7 +1436,7 @@ enum_max_by(VALUE obj) | |
{ | |
VALUE memo[2]; | |
- RETURN_ENUMERATOR(obj, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size); | |
memo[0] = Qundef; | |
memo[1] = Qnil; | |
@@ -1526,7 +1534,7 @@ enum_minmax_by(VALUE obj) | |
{ | |
struct minmax_by_t memo; | |
- RETURN_ENUMERATOR(obj, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size); | |
memo.min_bv = Qundef; | |
memo.max_bv = Qundef; | |
@@ -1606,7 +1614,7 @@ enum_each_with_index(int argc, VALUE *argv, VALUE obj) | |
{ | |
long memo; | |
- RETURN_ENUMERATOR(obj, argc, argv); | |
+ RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_size); | |
memo = 0; | |
rb_block_call(obj, id_each, argc, argv, each_with_index_i, (VALUE)&memo); | |
@@ -1631,7 +1639,7 @@ enum_reverse_each(int argc, VALUE *argv, VALUE obj) | |
VALUE ary; | |
long i; | |
- RETURN_ENUMERATOR(obj, argc, argv); | |
+ RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_size); | |
ary = enum_to_a(argc, argv, obj); | |
@@ -1679,7 +1687,7 @@ each_val_i(VALUE i, VALUE p, int argc, VALUE *argv) | |
static VALUE | |
enum_each_entry(int argc, VALUE *argv, VALUE obj) | |
{ | |
- RETURN_ENUMERATOR(obj, argc, argv); | |
+ RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_size); | |
rb_block_call(obj, id_each, argc, argv, each_val_i, 0); | |
return obj; | |
} | |
@@ -1702,6 +1710,20 @@ each_slice_i(VALUE i, VALUE *memo, int argc, VALUE *argv) | |
return v; | |
} | |
+static VALUE | |
+enum_each_slice_size(VALUE obj, VALUE args) | |
+{ | |
+ VALUE n, size; | |
+ long slice_size = NUM2LONG(RARRAY_PTR(args)[0]); | |
+ if (slice_size <= 0) rb_raise(rb_eArgError, "invalid slice size"); | |
+ | |
+ size = enum_size(obj, 0); | |
+ if (size == Qnil) return Qnil; | |
+ | |
+ n = rb_funcall(size, '+', 1, LONG2NUM(slice_size-1)); | |
+ return rb_funcall(n, id_div, 1, LONG2FIX(slice_size)); | |
+} | |
+ | |
/* | |
* call-seq: | |
* enum.each_slice(n) {...} -> nil | |
@@ -1726,7 +1748,7 @@ enum_each_slice(VALUE obj, VALUE n) | |
VALUE args[2], ary; | |
if (size <= 0) rb_raise(rb_eArgError, "invalid slice size"); | |
- RETURN_ENUMERATOR(obj, 1, &n); | |
+ RETURN_SIZED_ENUMERATOR(obj, 1, &n, enum_each_slice_size); | |
args[0] = rb_ary_new2(size); | |
args[1] = (VALUE)size; | |
@@ -1756,6 +1778,20 @@ each_cons_i(VALUE i, VALUE *memo, int argc, VALUE *argv) | |
return v; | |
} | |
+static VALUE | |
+enum_each_cons_size(VALUE obj, VALUE args) | |
+{ | |
+ VALUE n, size; | |
+ long cons_size = NUM2LONG(RARRAY_PTR(args)[0]); | |
+ if (cons_size <= 0) rb_raise(rb_eArgError, "invalid size"); | |
+ | |
+ size = enum_size(obj, 0); | |
+ if (size == Qnil) return Qnil; | |
+ | |
+ n = rb_funcall(size, '+', 1, LONG2NUM(1 - cons_size)); | |
+ return (rb_cmpint(rb_funcall(n, id_cmp, 1, LONG2FIX(0)), n, LONG2FIX(0)) == -1) ? LONG2FIX(0) : n; | |
+} | |
+ | |
/* | |
* call-seq: | |
* enum.each_cons(n) {...} -> nil | |
@@ -1784,7 +1820,7 @@ enum_each_cons(VALUE obj, VALUE n) | |
VALUE args[2]; | |
if (size <= 0) rb_raise(rb_eArgError, "invalid size"); | |
- RETURN_ENUMERATOR(obj, 1, &n); | |
+ RETURN_SIZED_ENUMERATOR(obj, 1, &n, enum_each_cons_size); | |
args[0] = rb_ary_new2(size); | |
args[1] = (VALUE)size; | |
@@ -1818,7 +1854,7 @@ each_with_object_i(VALUE i, VALUE memo, int argc, VALUE *argv) | |
static VALUE | |
enum_each_with_object(VALUE obj, VALUE memo) | |
{ | |
- RETURN_ENUMERATOR(obj, 1, &memo); | |
+ RETURN_SIZED_ENUMERATOR(obj, 1, &memo, enum_size); | |
rb_block_call(obj, id_each, 0, 0, each_with_object_i, memo); | |
@@ -2124,6 +2160,24 @@ cycle_i(VALUE i, VALUE ary, int argc, VALUE *argv) | |
return Qnil; | |
} | |
+static VALUE | |
+enum_cycle_size(VALUE self, VALUE args) | |
+{ | |
+ long mul; | |
+ VALUE n = Qnil; | |
+ VALUE size = enum_size(self, args); | |
+ | |
+ if (size == Qnil) return Qnil; | |
+ | |
+ if (args && (RARRAY_LEN(args) > 0)) { | |
+ n = RARRAY_PTR(args)[0]; | |
+ } | |
+ if (n == Qnil) return DBL2NUM(INFINITY); | |
+ mul = NUM2LONG(n); | |
+ if (mul <= 0) return INT2FIX(0); | |
+ return rb_funcall(size, '*', 1, LONG2FIX(mul)); | |
+} | |
+ | |
/* | |
* call-seq: | |
* enum.cycle(n=nil) {|obj| block } -> nil | |
@@ -2154,7 +2208,7 @@ enum_cycle(int argc, VALUE *argv, VALUE obj) | |
rb_scan_args(argc, argv, "01", &nv); | |
- RETURN_ENUMERATOR(obj, argc, argv); | |
+ RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_cycle_size); | |
if (NIL_P(nv)) { | |
n = -1; | |
} | |
@@ -2664,5 +2718,6 @@ Init_Enumerable(void) | |
id_cmp = rb_intern("<=>"); | |
id_next = rb_intern("next"); | |
id_size = rb_intern("size"); | |
+ id_div = rb_intern("div"); | |
} | |
diff --git a/enumerator.c b/enumerator.c | |
index 47a5c7e..fd5cc88 100644 | |
--- a/enumerator.c | |
+++ b/enumerator.c | |
@@ -90,6 +90,8 @@ struct enumerator { | |
VALUE lookahead; | |
VALUE feedvalue; | |
VALUE stop_exc; | |
+ VALUE size; | |
+ VALUE (*size_fn)(VALUE, VALUE); | |
}; | |
static VALUE rb_cGenerator, rb_cYielder; | |
@@ -119,6 +121,7 @@ enumerator_mark(void *p) | |
rb_gc_mark(ptr->lookahead); | |
rb_gc_mark(ptr->feedvalue); | |
rb_gc_mark(ptr->stop_exc); | |
+ rb_gc_mark(ptr->size); | |
} | |
#define enumerator_free RUBY_TYPED_DEFAULT_FREE | |
@@ -150,12 +153,19 @@ enumerator_ptr(VALUE obj) | |
return ptr; | |
} | |
+static VALUE | |
+enumerator_set_size(VALUE obj, VALUE size); | |
+ | |
/* | |
* call-seq: | |
* obj.to_enum(method = :each, *args) | |
* obj.enum_for(method = :each, *args) | |
+ * obj.to_enum(method = :each, *args) {|obj, *args| block} | |
+ * obj.enum_for(method = :each, *args){|obj, *args| block} | |
* | |
* Returns Enumerator.new(self, method, *args). | |
+ * If a block is given, it will be used to calculate the size of | |
+ * the enumerator (see Enumerator#size=). | |
* | |
* e.g.: | |
* | |
@@ -172,13 +182,17 @@ enumerator_ptr(VALUE obj) | |
static VALUE | |
obj_to_enum(int argc, VALUE *argv, VALUE obj) | |
{ | |
- VALUE meth = sym_each; | |
+ VALUE enumerator, meth = sym_each; | |
if (argc > 0) { | |
--argc; | |
meth = *argv++; | |
} | |
- return rb_enumeratorize(obj, meth, argc, argv); | |
+ enumerator = rb_enumeratorize(obj, meth, argc, argv, 0); | |
+ if (rb_block_given_p()) { | |
+ enumerator_set_size(enumerator, rb_block_proc()); | |
+ } | |
+ return enumerator; | |
} | |
static VALUE | |
@@ -194,7 +208,7 @@ enumerator_allocate(VALUE klass) | |
} | |
static VALUE | |
-enumerator_init(VALUE enum_obj, VALUE obj, VALUE meth, int argc, VALUE *argv) | |
+enumerator_init(VALUE enum_obj, VALUE obj, VALUE meth, int argc, VALUE *argv, VALUE (*size_fn)(ANYARGS)) | |
{ | |
struct enumerator *ptr; | |
@@ -212,6 +226,8 @@ enumerator_init(VALUE enum_obj, VALUE obj, VALUE meth, int argc, VALUE *argv) | |
ptr->lookahead = Qundef; | |
ptr->feedvalue = Qundef; | |
ptr->stop_exc = Qfalse; | |
+ ptr->size = Qnil; | |
+ ptr->size_fn = size_fn; | |
return enum_obj; | |
} | |
@@ -267,7 +283,7 @@ enumerator_initialize(int argc, VALUE *argv, VALUE obj) | |
} | |
} | |
- return enumerator_init(obj, recv, meth, argc, argv); | |
+ return enumerator_init(obj, recv, meth, argc, argv, 0); | |
} | |
/* :nodoc: */ | |
@@ -294,14 +310,16 @@ enumerator_init_copy(VALUE obj, VALUE orig) | |
ptr1->fib = 0; | |
ptr1->lookahead = Qundef; | |
ptr1->feedvalue = Qundef; | |
+ ptr1->size = ptr0->size; | |
+ ptr1->size_fn = ptr0->size_fn; | |
return obj; | |
} | |
VALUE | |
-rb_enumeratorize(VALUE obj, VALUE meth, int argc, VALUE *argv) | |
+rb_enumeratorize(VALUE obj, VALUE meth, int argc, VALUE *argv, VALUE (*size_fn)(ANYARGS)) | |
{ | |
- return enumerator_init(enumerator_allocate(rb_cEnumerator), obj, meth, argc, argv); | |
+ return enumerator_init(enumerator_allocate(rb_cEnumerator), obj, meth, argc, argv, size_fn); | |
} | |
static VALUE | |
@@ -349,6 +367,9 @@ enumerator_with_index_i(VALUE val, VALUE m, int argc, VALUE *argv) | |
return rb_yield_values(2, rb_ary_new4(argc, argv), idx); | |
} | |
+static VALUE | |
+enumerator_size(VALUE obj); | |
+ | |
/* | |
* call-seq: | |
* e.with_index(offset = 0) {|(*args), idx| ... } | |
@@ -364,7 +385,7 @@ enumerator_with_index(int argc, VALUE *argv, VALUE obj) | |
VALUE memo; | |
rb_scan_args(argc, argv, "01", &memo); | |
- RETURN_ENUMERATOR(obj, argc, argv); | |
+ RETURN_SIZED_ENUMERATOR(obj, argc, argv, enumerator_size); | |
memo = NIL_P(memo) ? 0 : (VALUE)NUM2LONG(memo); | |
return enumerator_block_call(obj, enumerator_with_index_i, (VALUE)&memo); | |
} | |
@@ -407,7 +428,7 @@ enumerator_with_object_i(VALUE val, VALUE memo, int argc, VALUE *argv) | |
static VALUE | |
enumerator_with_object(VALUE obj, VALUE memo) | |
{ | |
- RETURN_ENUMERATOR(obj, 1, &memo); | |
+ RETURN_SIZED_ENUMERATOR(obj, 1, &memo, enumerator_size); | |
enumerator_block_call(obj, enumerator_with_object_i, memo); | |
return memo; | |
@@ -794,6 +815,86 @@ enumerator_inspect(VALUE obj) | |
return rb_exec_recursive(inspect_enumerator, obj, 0); | |
} | |
+static VALUE | |
+enumerator_size_i(VALUE val, VALUE m, int argc, VALUE *argv) | |
+{ | |
+ ++*(long *)m; | |
+ return rb_yield_values2(argc, argv); | |
+} | |
+ | |
+/* | |
+ * call-seq: | |
+ * e.size -> int, Float::INFINITY or nil | |
+ * e.size {block} -> int | |
+ * | |
+ * Returns the size of the enumerator. | |
+ * The form with no block given will do a lazy evaluation of the size without going through the enumeration. If the size can not be determined then +nil+ is returned. | |
+ * The form with a block will always iterate through the enumerator and return the number of times it yielded. | |
+ * | |
+ * (1..100).to_a.permutation(4).size # => 94109400 | |
+ * loop.size # => Float::INFINITY | |
+ * | |
+ * a = [1, 2, 3] | |
+ * a.keep_if.size # => 3 | |
+ * a # => [1, 2, 3] | |
+ * a.keep_if.size{false} # => 3 | |
+ * a # => [] | |
+ * | |
+ * [1, 2, 3].drop_while.size # => nil | |
+ * [1, 2, 3].drop_while.size{|i| i < 3} # => 2 | |
+ */ | |
+ | |
+static VALUE | |
+enumerator_size(VALUE obj) | |
+{ | |
+ struct enumerator *e = enumerator_ptr(obj); | |
+ | |
+ if (rb_block_given_p()) { | |
+ long size = 0; | |
+ enumerator_block_call(obj, enumerator_size_i, (VALUE)&size); | |
+ return LONG2NUM(size); | |
+ } | |
+ if (e->size_fn) { | |
+ return (*e->size_fn)(e->obj, e->args); | |
+ } | |
+ if (rb_obj_is_proc(e->size)) { | |
+ VALUE args = rb_ary_new3(1, e->obj); | |
+ if(e->args) { | |
+ rb_ary_concat(args, e->args); | |
+ } | |
+ return rb_proc_call(e->size, args); | |
+ } | |
+ return e->size; | |
+} | |
+ | |
+/* | |
+ * call-seq: | |
+ * e.size = sz | |
+ * | |
+ * Sets the size of the enumerator. If +sz+ is a Proc or a Method, it will be called each time +size+ is requested, otherwise +sz+ is returned. | |
+ * | |
+ * first = [1, 2, 3] | |
+ * second = [4, 5] | |
+ * enum = Enumerator.new do |y| | |
+ * first.each{|o| y << o} | |
+ * second.each{|o| y << o} | |
+ * end | |
+ * enum.size # => nil | |
+ * enum.size = ->(e){first.size + second.size} | |
+ * enum.size # => 5 | |
+ * first << 42 | |
+ */ | |
+ | |
+static VALUE | |
+enumerator_set_size(VALUE obj, VALUE size) | |
+{ | |
+ struct enumerator *e = enumerator_ptr(obj); | |
+ VALUE proc = rb_check_convert_type(size, T_DATA, "Proc", "to_proc"); | |
+ e->size = NIL_P(proc) ? size : proc; | |
+ e->size_fn = 0; | |
+ return size; | |
+} | |
+ | |
/* | |
* Yielder | |
*/ | |
@@ -1099,6 +1200,8 @@ Init_Enumerator(void) | |
rb_define_method(rb_cEnumerator, "feed", enumerator_feed, 1); | |
rb_define_method(rb_cEnumerator, "rewind", enumerator_rewind, 0); | |
rb_define_method(rb_cEnumerator, "inspect", enumerator_inspect, 0); | |
+ rb_define_method(rb_cEnumerator, "size", enumerator_size, 0); | |
+ rb_define_method(rb_cEnumerator, "size=", enumerator_set_size, 1); | |
rb_eStopIteration = rb_define_class("StopIteration", rb_eIndexError); | |
rb_define_method(rb_eStopIteration, "result", stop_result, 0); | |
diff --git a/hash.c b/hash.c | |
index 8bba586..dd3f379 100644 | |
--- a/hash.c | |
+++ b/hash.c | |
@@ -885,6 +885,8 @@ delete_if_i(VALUE key, VALUE value, VALUE hash) | |
return ST_CONTINUE; | |
} | |
+static VALUE rb_hash_size(VALUE hash); | |
+ | |
/* | |
* call-seq: | |
* hsh.delete_if {| key, value | block } -> hsh | |
@@ -903,7 +905,7 @@ delete_if_i(VALUE key, VALUE value, VALUE hash) | |
VALUE | |
rb_hash_delete_if(VALUE hash) | |
{ | |
- RETURN_ENUMERATOR(hash, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(hash, 0, 0, rb_hash_size); | |
rb_hash_modify(hash); | |
rb_hash_foreach(hash, delete_if_i, hash); | |
return hash; | |
@@ -923,7 +925,7 @@ rb_hash_reject_bang(VALUE hash) | |
{ | |
st_index_t n; | |
- RETURN_ENUMERATOR(hash, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(hash, 0, 0, rb_hash_size); | |
rb_hash_modify(hash); | |
if (!RHASH(hash)->ntbl) | |
return Qnil; | |
@@ -1000,7 +1002,7 @@ rb_hash_select(VALUE hash) | |
{ | |
VALUE result; | |
- RETURN_ENUMERATOR(hash, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(hash, 0, 0, rb_hash_size); | |
result = rb_hash_new(); | |
rb_hash_foreach(hash, select_i, result); | |
return result; | |
@@ -1030,7 +1032,7 @@ rb_hash_select_bang(VALUE hash) | |
{ | |
st_index_t n; | |
- RETURN_ENUMERATOR(hash, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(hash, 0, 0, rb_hash_size); | |
rb_hash_modify(hash); | |
if (!RHASH(hash)->ntbl) | |
return Qnil; | |
@@ -1055,7 +1057,7 @@ rb_hash_select_bang(VALUE hash) | |
VALUE | |
rb_hash_keep_if(VALUE hash) | |
{ | |
- RETURN_ENUMERATOR(hash, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(hash, 0, 0, rb_hash_size); | |
rb_hash_modify(hash); | |
rb_hash_foreach(hash, keep_if_i, hash); | |
return hash; | |
@@ -1239,7 +1241,7 @@ each_value_i(VALUE key, VALUE value) | |
static VALUE | |
rb_hash_each_value(VALUE hash) | |
{ | |
- RETURN_ENUMERATOR(hash, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(hash, 0, 0, rb_hash_size); | |
rb_hash_foreach(hash, each_value_i, 0); | |
return hash; | |
} | |
@@ -1273,7 +1275,7 @@ each_key_i(VALUE key, VALUE value) | |
static VALUE | |
rb_hash_each_key(VALUE hash) | |
{ | |
- RETURN_ENUMERATOR(hash, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(hash, 0, 0, rb_hash_size); | |
rb_hash_foreach(hash, each_key_i, 0); | |
return hash; | |
} | |
@@ -1311,7 +1313,7 @@ each_pair_i(VALUE key, VALUE value) | |
static VALUE | |
rb_hash_each_pair(VALUE hash) | |
{ | |
- RETURN_ENUMERATOR(hash, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(hash, 0, 0, rb_hash_size); | |
rb_hash_foreach(hash, each_pair_i, 0); | |
return hash; | |
} | |
@@ -2305,12 +2307,30 @@ env_keys(void) | |
} | |
static VALUE | |
+rb_env_size(VALUE ehash) | |
+{ | |
+ char **env; | |
+ long cnt = 0; | |
+ | |
+ rb_secure(4); | |
+ | |
+ env = GET_ENVIRON(environ); | |
+ for (; *env ; ++env) { | |
+ if (strchr(*env, '=')) { | |
+ cnt++; | |
+ } | |
+ } | |
+ FREE_ENVIRON(environ); | |
+ return LONG2FIX(cnt); | |
+} | |
+ | |
+static VALUE | |
env_each_key(VALUE ehash) | |
{ | |
VALUE keys; | |
long i; | |
- RETURN_ENUMERATOR(ehash, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(ehash, 0, 0, rb_env_size); | |
keys = env_keys(); /* rb_secure(4); */ | |
for (i=0; i<RARRAY_LEN(keys); i++) { | |
rb_yield(RARRAY_PTR(keys)[i]); | |
@@ -2344,7 +2364,7 @@ env_each_value(VALUE ehash) | |
VALUE values; | |
long i; | |
- RETURN_ENUMERATOR(ehash, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(ehash, 0, 0, rb_env_size); | |
values = env_values(); /* rb_secure(4); */ | |
for (i=0; i<RARRAY_LEN(values); i++) { | |
rb_yield(RARRAY_PTR(values)[i]); | |
@@ -2359,7 +2379,7 @@ env_each_pair(VALUE ehash) | |
VALUE ary; | |
long i; | |
- RETURN_ENUMERATOR(ehash, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(ehash, 0, 0, rb_env_size); | |
rb_secure(4); | |
ary = rb_ary_new(); | |
@@ -2387,7 +2407,7 @@ env_reject_bang(VALUE ehash) | |
long i; | |
int del = 0; | |
- RETURN_ENUMERATOR(ehash, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(ehash, 0, 0, rb_env_size); | |
keys = env_keys(); /* rb_secure(4); */ | |
for (i=0; i<RARRAY_LEN(keys); i++) { | |
VALUE val = rb_f_getenv(Qnil, RARRAY_PTR(keys)[i]); | |
@@ -2406,7 +2426,7 @@ env_reject_bang(VALUE ehash) | |
static VALUE | |
env_delete_if(VALUE ehash) | |
{ | |
- RETURN_ENUMERATOR(ehash, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(ehash, 0, 0, rb_env_size); | |
env_reject_bang(ehash); | |
return envtbl; | |
} | |
@@ -2431,7 +2451,7 @@ env_select(VALUE ehash) | |
VALUE result; | |
char **env; | |
- RETURN_ENUMERATOR(ehash, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(ehash, 0, 0, rb_env_size); | |
rb_secure(4); | |
result = rb_hash_new(); | |
env = GET_ENVIRON(environ); | |
@@ -2458,7 +2478,7 @@ env_select_bang(VALUE ehash) | |
long i; | |
int del = 0; | |
- RETURN_ENUMERATOR(ehash, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(ehash, 0, 0, rb_env_size); | |
keys = env_keys(); /* rb_secure(4); */ | |
for (i=0; i<RARRAY_LEN(keys); i++) { | |
VALUE val = rb_f_getenv(Qnil, RARRAY_PTR(keys)[i]); | |
@@ -2477,7 +2497,7 @@ env_select_bang(VALUE ehash) | |
static VALUE | |
env_keep_if(VALUE ehash) | |
{ | |
- RETURN_ENUMERATOR(ehash, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(ehash, 0, 0, rb_env_size); | |
env_select_bang(ehash); | |
return envtbl; | |
} | |
diff --git a/include/ruby/intern.h b/include/ruby/intern.h | |
index 0ae4d47..fd02da1 100644 | |
--- a/include/ruby/intern.h | |
+++ b/include/ruby/intern.h | |
@@ -195,12 +195,13 @@ VALUE rb_fiber_current(void); | |
VALUE rb_fiber_alive_p(VALUE); | |
/* enum.c */ | |
/* enumerator.c */ | |
-VALUE rb_enumeratorize(VALUE, VALUE, int, VALUE *); | |
-#define RETURN_ENUMERATOR(obj, argc, argv) do { \ | |
+VALUE rb_enumeratorize(VALUE, VALUE, int, VALUE *, VALUE (*)(ANYARGS)); | |
+#define RETURN_SIZED_ENUMERATOR(obj, argc, argv, size_fn) do { \ | |
if (!rb_block_given_p()) \ | |
return rb_enumeratorize(obj, ID2SYM(rb_frame_this_func()), \ | |
- argc, argv); \ | |
+ argc, argv, size_fn); \ | |
} while (0) | |
+#define RETURN_ENUMERATOR(obj, argc, argv) RETURN_SIZED_ENUMERATOR(obj, argc, argv, 0) | |
/* error.c */ | |
VALUE rb_exc_new(VALUE, const char*, long); | |
VALUE rb_exc_new2(VALUE, const char*); | |
diff --git a/numeric.c b/numeric.c | |
index 740ef54..8e2ef90 100644 | |
--- a/numeric.c | |
+++ b/numeric.c | |
@@ -97,7 +97,7 @@ round(double x) | |
} | |
#endif | |
-static ID id_coerce, id_to_i, id_eq; | |
+static ID id_coerce, id_to_i, id_eq, id_div; | |
VALUE rb_cNumeric; | |
VALUE rb_cFloat; | |
@@ -1596,6 +1596,44 @@ ruby_float_step(VALUE from, VALUE to, VALUE step, int excl) | |
return FALSE; | |
} | |
+static VALUE | |
+num_step_size(VALUE from, VALUE args) { | |
+ VALUE to = RARRAY_PTR(args)[0]; | |
+ VALUE step = (RARRAY_LEN(args) > 1) ? RARRAY_PTR(args)[1] : INT2FIX(1); | |
+ if (FIXNUM_P(from) && FIXNUM_P(to) && FIXNUM_P(step)) { | |
+ long delta, diff, result; | |
+ | |
+ delta = FIX2LONG(to) - FIX2LONG(from); | |
+ diff = FIX2LONG(step); | |
+ result = delta / diff + 1; | |
+ return LONG2FIX(result >= 0 ? result : 0); | |
+ } | |
+ else if (TYPE(from) == T_FLOAT || TYPE(to) == T_FLOAT || TYPE(step) == T_FLOAT) { | |
+ const double epsilon = DBL_EPSILON; | |
+ double beg = NUM2DBL(from); | |
+ double end = NUM2DBL(to); | |
+ double unit = NUM2DBL(step); | |
+ double n = (end - beg)/unit; | |
+ double err = (fabs(beg) + fabs(end) + fabs(end-beg)) / fabs(unit) * epsilon; | |
+ | |
+ if (isinf(unit)) { | |
+ return (LONG2FIX(((unit > 0) == (end > beg)) || (end == beg))); | |
+ } | |
+ else { | |
+ if (n < 0) return LONG2FIX(0); | |
+ if (err>0.5) err=0.5; | |
+ return LONG2FIX(floor(n + err) + 1); | |
+ } | |
+ } | |
+ else { | |
+ VALUE delta; | |
+ ID cmp = RTEST(rb_funcall(step, '>', 1, INT2FIX(0))) ? '>' : '<'; | |
+ if (RTEST(rb_funcall(from, cmp, 1, to))) return INT2FIX(0); | |
+ delta = rb_funcall(to, '-', 1, from); | |
+ return rb_funcall(rb_funcall(delta, id_div, 1, step), '+', 1, INT2FIX(1)); | |
+ } | |
+} | |
+ | |
/* | |
* call-seq: | |
* num.step(limit[, step]) {|i| block } -> self | |
@@ -1631,7 +1669,7 @@ num_step(int argc, VALUE *argv, VALUE from) | |
{ | |
VALUE to, step; | |
- RETURN_ENUMERATOR(from, argc, argv); | |
+ RETURN_SIZED_ENUMERATOR(from, argc, argv, num_step_size); | |
if (argc == 1) { | |
to = argv[0]; | |
step = INT2FIX(1); | |
@@ -3049,6 +3087,20 @@ fix_size(VALUE fix) | |
return INT2FIX(sizeof(long)); | |
} | |
+static VALUE | |
+int_upto_size(VALUE from, VALUE args) { | |
+ VALUE to = RARRAY_PTR(args)[0]; | |
+ | |
+ if (FIXNUM_P(from) && FIXNUM_P(to)) { | |
+ long size = FIX2LONG(to) + 1 - FIX2LONG(from); | |
+ return LONG2NUM(size >= 0 ? size : 0); | |
+ } | |
+ else { | |
+ if (RTEST(rb_funcall(from, '>', 1, to))) return INT2FIX(0); | |
+ return rb_funcall(rb_funcall(to, '+', 1, INT2FIX(1)), '-', 1, from); | |
+ } | |
+} | |
+ | |
/* | |
* call-seq: | |
* int.upto(limit) {|i| block } -> self | |
@@ -3069,7 +3121,7 @@ fix_size(VALUE fix) | |
static VALUE | |
int_upto(VALUE from, VALUE to) | |
{ | |
- RETURN_ENUMERATOR(from, 1, &to); | |
+ RETURN_SIZED_ENUMERATOR(from, 1, &to, int_upto_size); | |
if (FIXNUM_P(from) && FIXNUM_P(to)) { | |
long i, end; | |
@@ -3090,6 +3142,20 @@ int_upto(VALUE from, VALUE to) | |
return from; | |
} | |
+static VALUE | |
+int_downto_size(VALUE from, VALUE args) { | |
+ VALUE to = RARRAY_PTR(args)[0]; | |
+ | |
+ if (FIXNUM_P(from) && FIXNUM_P(to)) { | |
+ long size = FIX2LONG(from) + 1 - FIX2LONG(to); | |
+ return LONG2NUM(size >= 0 ? size : 0); | |
+ } | |
+ else { | |
+ if (RTEST(rb_funcall(from, '<', 1, to))) return INT2FIX(0); | |
+ return rb_funcall(rb_funcall(from, '+', 1, INT2FIX(1)), '-', 1, to); | |
+ } | |
+} | |
+ | |
/* | |
* call-seq: | |
* int.downto(limit) {|i| block } -> self | |
@@ -3111,7 +3177,7 @@ int_upto(VALUE from, VALUE to) | |
static VALUE | |
int_downto(VALUE from, VALUE to) | |
{ | |
- RETURN_ENUMERATOR(from, 1, &to); | |
+ RETURN_SIZED_ENUMERATOR(from, 1, &to, int_downto_size); | |
if (FIXNUM_P(from) && FIXNUM_P(to)) { | |
long i, end; | |
@@ -3132,6 +3198,18 @@ int_downto(VALUE from, VALUE to) | |
return from; | |
} | |
+static VALUE | |
+int_dotimes_size(VALUE num) | |
+{ | |
+ if (FIXNUM_P(num)) { | |
+ if (NUM2LONG(num) <= 0) return INT2FIX(0); | |
+ } | |
+ else { | |
+ if (RTEST(rb_funcall(num, '<', 1, INT2FIX(0)))) return INT2FIX(0); | |
+ } | |
+ return num; | |
+} | |
+ | |
/* | |
* call-seq: | |
* int.times {|i| block } -> self | |
@@ -3154,7 +3232,7 @@ int_downto(VALUE from, VALUE to) | |
static VALUE | |
int_dotimes(VALUE num) | |
{ | |
- RETURN_ENUMERATOR(num, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(num, 0, 0, int_dotimes_size); | |
if (FIXNUM_P(num)) { | |
long i, end; | |
@@ -3326,6 +3404,7 @@ Init_Numeric(void) | |
id_coerce = rb_intern("coerce"); | |
id_to_i = rb_intern("to_i"); | |
id_eq = rb_intern("=="); | |
+ id_div = rb_intern("div"); | |
rb_eZeroDivError = rb_define_class("ZeroDivisionError", rb_eStandardError); | |
rb_eFloatDomainError = rb_define_class("FloatDomainError", rb_eRangeError); | |
diff --git a/string.c b/string.c | |
index d18f47e..012027e 100644 | |
--- a/string.c | |
+++ b/string.c | |
@@ -5867,6 +5867,11 @@ rb_str_each_line(int argc, VALUE *argv, VALUE str) | |
return orig; | |
} | |
+static VALUE | |
+rb_str_each_byte_size(VALUE str, VALUE args) | |
+{ | |
+ return LONG2FIX(RSTRING_LEN(str)); | |
+} | |
/* | |
* call-seq: | |
@@ -5891,13 +5896,27 @@ rb_str_each_byte(VALUE str) | |
{ | |
long i; | |
- RETURN_ENUMERATOR(str, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(str, 0, 0, rb_str_each_byte_size); | |
for (i=0; i<RSTRING_LEN(str); i++) { | |
rb_yield(INT2FIX(RSTRING_PTR(str)[i] & 0xff)); | |
} | |
return str; | |
} | |
+static VALUE | |
+rb_str_each_char_size(VALUE str) | |
+{ | |
+ long len = RSTRING_LEN(str); | |
+ if (!single_byte_optimizable(str)) { | |
+ const char *ptr = RSTRING_PTR(str); | |
+ rb_encoding *enc = rb_enc_get(str); | |
+ const char *end_ptr = ptr + len; | |
+ for (len = 0; ptr < end_ptr; ++len) { | |
+ ptr += rb_enc_mbclen(ptr, end_ptr, enc); | |
+ } | |
+ } | |
+ return LONG2FIX(len); | |
+} | |
/* | |
* call-seq: | |
@@ -5925,7 +5944,7 @@ rb_str_each_char(VALUE str) | |
const char *ptr; | |
rb_encoding *enc; | |
- RETURN_ENUMERATOR(str, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(str, 0, 0, rb_str_each_char_size); | |
str = rb_str_new4(str); | |
ptr = RSTRING_PTR(str); | |
len = RSTRING_LEN(str); | |
@@ -5979,7 +5998,7 @@ rb_str_each_codepoint(VALUE str) | |
rb_encoding *enc; | |
if (single_byte_optimizable(str)) return rb_str_each_byte(str); | |
- RETURN_ENUMERATOR(str, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(str, 0, 0, rb_str_each_char_size); | |
str = rb_str_new4(str); | |
ptr = RSTRING_PTR(str); | |
len = RSTRING_LEN(str); | |
diff --git a/struct.c b/struct.c | |
index f0a377c..c38ef53 100644 | |
--- a/struct.c | |
+++ b/struct.c | |
@@ -439,6 +439,9 @@ rb_struct_new(VALUE klass, ...) | |
return rb_class_new_instance(size, mem, klass); | |
} | |
+static VALUE | |
+rb_struct_size(VALUE s); | |
+ | |
/* | |
* call-seq: | |
* struct.each {|obj| block } -> struct | |
@@ -465,7 +468,7 @@ rb_struct_each(VALUE s) | |
{ | |
long i; | |
- RETURN_ENUMERATOR(s, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(s, 0, 0, rb_struct_size); | |
for (i=0; i<RSTRUCT_LEN(s); i++) { | |
rb_yield(RSTRUCT_PTR(s)[i]); | |
} | |
@@ -499,7 +502,7 @@ rb_struct_each_pair(VALUE s) | |
VALUE members; | |
long i; | |
- RETURN_ENUMERATOR(s, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(s, 0, 0, rb_struct_size); | |
members = rb_struct_members(s); | |
for (i=0; i<RSTRUCT_LEN(s); i++) { | |
rb_yield_values(2, rb_ary_entry(members, i), RSTRUCT_PTR(s)[i]); | |
@@ -781,7 +784,7 @@ rb_struct_select(int argc, VALUE *argv, VALUE s) | |
if (argc > 0) { | |
rb_raise(rb_eArgError, "wrong number of arguments (%d for 0)", argc); | |
} | |
- RETURN_ENUMERATOR(s, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(s, 0, 0, rb_struct_size); | |
result = rb_ary_new(); | |
for (i = 0; i < RSTRUCT_LEN(s); i++) { | |
if (RTEST(rb_yield(RSTRUCT_PTR(s)[i]))) { | |
diff --git a/vm_eval.c b/vm_eval.c | |
index ca3fbba..6f2a892 100644 | |
--- a/vm_eval.c | |
+++ b/vm_eval.c | |
@@ -791,6 +791,11 @@ loop_i(void) | |
return Qnil; | |
} | |
+static VALUE | |
+rb_f_loop_size(VALUE self, VALUE args) { | |
+ return DBL2NUM(INFINITY); | |
+} | |
+ | |
/* | |
* call-seq: | |
* loop { block } | |
@@ -813,7 +818,7 @@ loop_i(void) | |
static VALUE | |
rb_f_loop(VALUE self) | |
{ | |
- RETURN_ENUMERATOR(self, 0, 0); | |
+ RETURN_SIZED_ENUMERATOR(self, 0, 0, rb_f_loop_size); | |
rb_rescue2(loop_i, (VALUE)0, 0, 0, rb_eStopIteration, (VALUE)0); | |
return Qnil; /* dummy */ | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment