diff options
-rw-r--r-- | ChangeLog | 10 | ||||
-rw-r--r-- | NEWS | 6 | ||||
-rw-r--r-- | array.c | 215 |
3 files changed, 166 insertions, 65 deletions
@@ -1,3 +1,13 @@ +Thu Apr 10 19:29:48 2008 Akinori MUSHA <knu@iDaemons.org> + + * array.c (rb_ary_first, rb_ary_last): Return a shared array when + possible. + + * array.c (rb_ary_pop, rb_ary_pop_m, rb_ary_shift, rb_ary_shift_m): + Array#pop and Array#shift can take an optional argument + specifying the number of elements to remove and return; + backported from 1.9. + Thu Apr 10 14:00:44 2008 Tanaka Akira <akr@fsij.org> * lib/resolv.rb (Resolv::DNS#each_address): backport from 1.9 for @@ -35,6 +35,12 @@ with all sufficient information, see the ChangeLog file. Return an enumerator if no block is given. + * Array#pop + * Array#shift + + Take an optional argument specifying the number of elements to + remove. + * Integer#ord implemented. * Integer#odd? implemented. * Integer#even? implemented. @@ -195,6 +195,27 @@ rb_ary_new4(n, elts) return ary; } +static VALUE +ary_make_shared(ary) + VALUE ary; +{ + if (!FL_TEST(ary, ELTS_SHARED)) { + NEWOBJ(shared, struct RArray); + OBJSETUP(shared, rb_cArray, T_ARRAY); + + shared->len = RARRAY(ary)->len; + shared->ptr = RARRAY(ary)->ptr; + shared->aux.capa = RARRAY(ary)->aux.capa; + RARRAY(ary)->aux.shared = (VALUE)shared; + FL_SET(ary, ELTS_SHARED); + OBJ_FREEZE(shared); + return (VALUE)shared; + } + else { + return RARRAY(ary)->aux.shared; + } +} + VALUE rb_assoc_new(car, cdr) VALUE car, cdr; @@ -384,6 +405,50 @@ rb_ary_store(ary, idx, val) RARRAY(ary)->ptr[idx] = val; } +static VALUE +ary_shared_array(klass, ary) + VALUE klass; + VALUE ary; +{ + VALUE val = ary_alloc(klass); + + ary_make_shared(ary); + RARRAY(val)->ptr = RARRAY(ary)->ptr; + RARRAY(val)->len = RARRAY(ary)->len; + RARRAY(val)->aux.shared = RARRAY(ary)->aux.shared; + FL_SET(val, ELTS_SHARED); + return val; +} + +static VALUE +ary_shared_first(argc, argv, ary, last) + int argc; + VALUE *argv; + VALUE ary; + int last; +{ + VALUE nv, result; + long n; + long offset = 0; + + rb_scan_args(argc, argv, "1", &nv); + n = NUM2LONG(nv); + if (n > RARRAY(ary)->len) { + n = RARRAY(ary)->len; + } + else if (n < 0) { + rb_raise(rb_eArgError, "negative array size"); + } + if (last) { + offset = RARRAY(ary)->len - n; + } + result = ary_shared_array(rb_cArray, ary); + RARRAY(result)->ptr += offset; + RARRAY(result)->len = n; + + return result; +} + /* * call-seq: * array << obj -> array @@ -431,18 +496,6 @@ rb_ary_push_m(argc, argv, ary) return ary; } -/* - * call-seq: - * array.pop -> obj or nil - * - * Removes the last element from <i>self</i> and returns it, or - * <code>nil</code> if the array is empty. - * - * a = [ "a", "m", "z" ] - * a.pop #=> "z" - * a #=> ["a", "m"] - */ - VALUE rb_ary_pop(ary) VALUE ary; @@ -458,62 +511,112 @@ rb_ary_pop(ary) return RARRAY(ary)->ptr[--RARRAY(ary)->len]; } +/* + * call-seq: + * array.pop -> obj or nil + * array.pop(n) -> array + * + * Removes the last element from <i>self</i> and returns it, or + * <code>nil</code> if the array is empty. + * + * If a number _n_ is given, returns an array of the last n elements + * (or less) just like <code>array.slice!(-n, n)</code> does. + * + * a = [ "a", "b", "c", "d" ] + * a.pop #=> "d" + * a.pop(2) #=> ["b", "c"] + * a #=> ["a"] + */ + static VALUE -ary_make_shared(ary) +rb_ary_pop_m(argc, argv, ary) + int argc; + VALUE *argv; VALUE ary; { - if (!FL_TEST(ary, ELTS_SHARED)) { - NEWOBJ(shared, struct RArray); - OBJSETUP(shared, rb_cArray, T_ARRAY); + VALUE result; - shared->len = RARRAY(ary)->len; - shared->ptr = RARRAY(ary)->ptr; - shared->aux.capa = RARRAY(ary)->aux.capa; - RARRAY(ary)->aux.shared = (VALUE)shared; - FL_SET(ary, ELTS_SHARED); - OBJ_FREEZE(shared); - return (VALUE)shared; + if (argc == 0) { + return rb_ary_pop(ary); } - else { - return RARRAY(ary)->aux.shared; + + rb_ary_modify_check(ary); + result = ary_shared_first(argc, argv, ary, Qtrue); + RARRAY(ary)->len -= RARRAY(result)->len; + return result; +} + +VALUE +rb_ary_shift(ary) + VALUE ary; +{ + VALUE top; + + rb_ary_modify_check(ary); + if (RARRAY(ary)->len == 0) return Qnil; + top = RARRAY(ary)->ptr[0]; + if (!FL_TEST(ary, ELTS_SHARED)) { + if (RARRAY(ary)->len < ARY_DEFAULT_SIZE) { + MEMMOVE(RARRAY(ary)->ptr, RARRAY(ary)->ptr+1, VALUE, RARRAY(ary)->len-1); + RARRAY(ary)->len--; + return top; + } + RARRAY(ary)->ptr[0] = Qnil; + ary_make_shared(ary); } + RARRAY(ary)->ptr++; /* shift ptr */ + RARRAY(ary)->len--; + + return top; } /* * call-seq: - * array.shift -> obj or nil + * array.shift -> obj or nil + * array.shift(n) -> array * * Returns the first element of <i>self</i> and removes it (shifting all * other elements down by one). Returns <code>nil</code> if the array * is empty. + * + * If a number _n_ is given, returns an array of the first n elements + * (or less) just like <code>array.slice!(0, n)</code> does. * * args = [ "-m", "-q", "filename" ] - * args.shift #=> "-m" - * args #=> ["-q", "filename"] + * args.shift #=> "-m" + * args #=> ["-q", "filename"] + * + * args = [ "-m", "-q", "filename" ] + * args.shift(2) #=> ["-m", "-q"] + * args #=> ["filename"] */ -VALUE -rb_ary_shift(ary) +static VALUE +rb_ary_shift_m(argc, argv, ary) + int argc; + VALUE *argv; VALUE ary; { - VALUE top; + VALUE result; + long n; - rb_ary_modify_check(ary); - if (RARRAY(ary)->len == 0) return Qnil; - top = RARRAY(ary)->ptr[0]; - if (RARRAY_LEN(ary) < ARY_DEFAULT_SIZE && !FL_TEST(ary, ELTS_SHARED)) { - MEMMOVE(RARRAY_PTR(ary), RARRAY_PTR(ary)+1, VALUE, RARRAY_LEN(ary)-1); + if (argc == 0) { + return rb_ary_shift(ary); } - else { - if (!FL_TEST(ary, ELTS_SHARED)) { - RARRAY(ary)->ptr[0] = Qnil; + + rb_ary_modify_check(ary); + result = ary_shared_first(argc, argv, ary, Qfalse); + n = RARRAY(result)->len; + if (FL_TEST(ary, ELTS_SHARED)) { + RARRAY(ary)->ptr += n; + RARRAY(ary)->len -= n; } - ary_make_shared(ary); - RARRAY(ary)->ptr++; /* shift ptr */ + else { + MEMMOVE(RARRAY(ary)->ptr, RARRAY(ary)->ptr+n, VALUE, RARRAY(ary)->len-n); + RARRAY(ary)->len -= n; } - RARRAY(ary)->len--; - return top; + return result; } VALUE @@ -748,15 +851,7 @@ rb_ary_first(argc, argv, ary) return RARRAY(ary)->ptr[0]; } else { - VALUE nv; - long n; - - rb_scan_args(argc, argv, "01", &nv); - n = NUM2LONG(nv); - if (n < 0) { - rb_raise(rb_eArgError, "negative array size"); - } - return rb_ary_subseq(ary, 0, n); + return ary_shared_first(argc, argv, ary, Qfalse); } } @@ -782,17 +877,7 @@ rb_ary_last(argc, argv, ary) return RARRAY(ary)->ptr[RARRAY(ary)->len-1]; } else { - VALUE nv, result; - long n, i; - - rb_scan_args(argc, argv, "01", &nv); - n = NUM2LONG(nv); - if (n > RARRAY(ary)->len) n = RARRAY(ary)->len; - result = rb_ary_new2(n); - for (i=RARRAY(ary)->len-n; n--; i++) { - rb_ary_push(result, RARRAY(ary)->ptr[i]); - } - return result; + return ary_shared_first(argc, argv, ary, Qtrue); } } @@ -3062,8 +3147,8 @@ Init_Array() rb_define_method(rb_cArray, "concat", rb_ary_concat, 1); rb_define_method(rb_cArray, "<<", rb_ary_push, 1); rb_define_method(rb_cArray, "push", rb_ary_push_m, -1); - rb_define_method(rb_cArray, "pop", rb_ary_pop, 0); - rb_define_method(rb_cArray, "shift", rb_ary_shift, 0); + rb_define_method(rb_cArray, "pop", rb_ary_pop_m, -1); + rb_define_method(rb_cArray, "shift", rb_ary_shift_m, -1); rb_define_method(rb_cArray, "unshift", rb_ary_unshift_m, -1); rb_define_method(rb_cArray, "insert", rb_ary_insert, -1); rb_define_method(rb_cArray, "each", rb_ary_each, 0); |