diff --git a/ChangeLog b/ChangeLog index 841b0bdb0a..93fcceb24b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -79,6 +79,15 @@ Wed Jul 1 06:47:09 2009 Nobuyoshi Nakada * enum.c (enum_grep): gets rid of type-punning calls. +Wed Jul 1 06:36:28 2009 Yukihiro Matsumoto + + * enum.c (enum_join): add Enumerable#join. + + * array.c (ary_join_1): recursive join for Enumerators (and + objects with #to_a). + + * array.c (rb_ary_join): performance tune. + Tue Jun 30 18:19:07 2009 Yukihiro Matsumoto * hash.c (rb_hash_hash): documentation fix. a patch from diff --git a/array.c b/array.c index cb37a2ead7..059574026c 100644 --- a/array.c +++ b/array.c @@ -1519,14 +1519,86 @@ rb_ary_resurrect(VALUE ary) extern VALUE rb_output_fs; +static void ary_join_1(VALUE ary, VALUE sep, long i, VALUE result); + static VALUE -recursive_join(VALUE ary, VALUE argp, int recur) +recursive_join(VALUE obj, VALUE argp, int recur) { VALUE *arg = (VALUE *)argp; + VALUE ary = arg[0]; + VALUE sep = arg[1]; + VALUE result = arg[2]; + if (recur) { - return rb_usascii_str_new2("[...]"); + rb_str_buf_cat_ascii(result, "[...]"); + } + else { + ary_join_1(ary, sep, 0, result); + } + return Qnil; +} + +static void +ary_join_0(VALUE ary, VALUE sep, long max, VALUE result) +{ + long i; + VALUE val; + + for (i=0; i 0 && !NIL_P(sep)) + rb_str_buf_append(result, sep); + rb_str_buf_append(result, val); + if (OBJ_TAINTED(val)) OBJ_TAINT(result); + if (OBJ_UNTRUSTED(val)) OBJ_TAINT(result); + } +} + +static void +ary_join_1(VALUE ary, VALUE sep, long i, VALUE result) +{ + VALUE val, tmp; + + for (; i 0 && !NIL_P(sep)) + rb_str_buf_append(result, sep); + + val = RARRAY_PTR(ary)[i]; + switch (TYPE(val)) { + case T_STRING: + str_join: + rb_str_buf_append(result, val); + break; + case T_ARRAY: + ary_join: + if (val == ary) { + val = rb_usascii_str_new2("[...]"); + goto str_join; + } + else { + VALUE args[3]; + + args[0] = val; + args[1] = sep; + args[2] = result; + rb_exec_recursive(recursive_join, ary, (VALUE)args); + } + break; + default: + tmp = rb_check_string_type(val); + if (!NIL_P(tmp)) { + val = tmp; + goto str_join; + } + tmp = rb_check_convert_type(val, T_ARRAY, "Array", "to_a"); + if (!NIL_P(tmp)) { + val = tmp; + goto ary_join; + } + val = rb_obj_as_string(val); + goto str_join; + } } - return rb_ary_join(arg[0], arg[1]); } VALUE @@ -1535,50 +1607,37 @@ rb_ary_join(VALUE ary, VALUE sep) long len = 1, i; int taint = Qfalse; int untrust = Qfalse; - VALUE result, tmp; + VALUE val, tmp, result; if (RARRAY_LEN(ary) == 0) return rb_str_new(0, 0); if (OBJ_TAINTED(ary) || OBJ_TAINTED(sep)) taint = Qtrue; if (OBJ_UNTRUSTED(ary) || OBJ_UNTRUSTED(sep)) untrust = Qtrue; - for (i=0; i 0 && !NIL_P(sep)) - rb_str_buf_append(result, sep); - rb_str_buf_append(result, tmp); - if (OBJ_TAINTED(tmp)) taint = Qtrue; - if (OBJ_UNTRUSTED(tmp)) untrust = Qtrue; + + len += RSTRING_LEN(tmp); } + result = rb_str_buf_new(len); if (taint) OBJ_TAINT(result); if (untrust) OBJ_UNTRUST(result); + ary_join_0(ary, sep, RARRAY_LEN(ary), result); + return result; } diff --git a/enum.c b/enum.c index 8205492e70..dd2ed9244c 100644 --- a/enum.c +++ b/enum.c @@ -1803,6 +1803,17 @@ enum_cycle(int argc, VALUE *argv, VALUE obj) return Qnil; /* not reached */ } +static VALUE +enum_join(int argc, VALUE *argv, VALUE obj) +{ + VALUE sep; + + rb_scan_args(argc, argv, "01", &sep); + if (NIL_P(sep)) sep = rb_output_fs; + + return rb_ary_join(enum_to_a(0, 0, obj), sep); +} + /* * The Enumerable mixin provides collection classes with * several traversal and searching methods, and with the ability to @@ -1862,6 +1873,7 @@ Init_Enumerable(void) rb_define_method(rb_mEnumerable, "drop", enum_drop, 1); rb_define_method(rb_mEnumerable, "drop_while", enum_drop_while, 0); rb_define_method(rb_mEnumerable, "cycle", enum_cycle, -1); + rb_define_method(rb_mEnumerable, "join", enum_join, -1); id_eqq = rb_intern("==="); id_each = rb_intern("each");