diff options
Diffstat (limited to 'array.c')
| -rw-r--r-- | array.c | 223 |
1 files changed, 168 insertions, 55 deletions
@@ -29,6 +29,7 @@ #include "ruby/st.h" #include "ruby/thread.h" #include "ruby/util.h" +#include "ruby/ractor.h" #include "vm_core.h" #include "builtin.h" @@ -107,10 +108,12 @@ should_be_T_ARRAY(VALUE ary) } while (0) #define FL_UNSET_SHARED(ary) FL_UNSET((ary), RARRAY_SHARED_FLAG) +#define ARY_SET_PTR_FORCE(ary, p) \ + (RARRAY(ary)->as.heap.ptr = (p)) #define ARY_SET_PTR(ary, p) do { \ RUBY_ASSERT(!ARY_EMBED_P(ary)); \ RUBY_ASSERT(!OBJ_FROZEN(ary)); \ - RARRAY(ary)->as.heap.ptr = (p); \ + ARY_SET_PTR_FORCE(ary, p); \ } while (0) #define ARY_SET_EMBED_LEN(ary, n) do { \ long tmp_n = (n); \ @@ -148,11 +151,13 @@ should_be_T_ARRAY(VALUE ary) #define ARY_CAPA(ary) (ARY_EMBED_P(ary) ? ary_embed_capa(ary) : \ ARY_SHARED_ROOT_P(ary) ? RARRAY_LEN(ary) : ARY_HEAP_CAPA(ary)) +#define ARY_SET_CAPA_FORCE(ary, n) \ + RARRAY(ary)->as.heap.aux.capa = (n); #define ARY_SET_CAPA(ary, n) do { \ RUBY_ASSERT(!ARY_EMBED_P(ary)); \ RUBY_ASSERT(!ARY_SHARED_P(ary)); \ RUBY_ASSERT(!OBJ_FROZEN(ary)); \ - RARRAY(ary)->as.heap.aux.capa = (n); \ + ARY_SET_CAPA_FORCE(ary, n); \ } while (0) #define ARY_SHARED_ROOT_OCCUPIED(ary) (!OBJ_FROZEN(ary) && ARY_SHARED_ROOT_REFCNT(ary) == 1) @@ -189,7 +194,9 @@ ary_embed_capa(VALUE ary) static size_t ary_embed_size(long capa) { - return offsetof(struct RArray, as.ary) + (sizeof(VALUE) * capa); + size_t size = offsetof(struct RArray, as.ary) + (sizeof(VALUE) * capa); + if (size < sizeof(struct RArray)) size = sizeof(struct RArray); + return size; } static bool @@ -560,8 +567,8 @@ rb_ary_cancel_sharing(VALUE ary) VALUE *ptr = ary_heap_alloc_buffer(len); MEMCPY(ptr, ARY_HEAP_PTR(ary), VALUE, len); rb_ary_unshare(ary); - ARY_SET_CAPA(ary, len); - ARY_SET_PTR(ary, ptr); + ARY_SET_CAPA_FORCE(ary, len); + ARY_SET_PTR_FORCE(ary, ptr); } rb_gc_writebarrier_remember(ary); @@ -695,6 +702,11 @@ ary_alloc_heap(VALUE klass) NEWOBJ_OF(ary, struct RArray, klass, T_ARRAY | (RGENGC_WB_PROTECTED_ARRAY ? FL_WB_PROTECTED : 0), sizeof(struct RArray), 0); + + ary->as.heap.len = 0; + ary->as.heap.aux.capa = 0; + ary->as.heap.ptr = NULL; + return (VALUE)ary; } @@ -808,6 +820,11 @@ ec_ary_alloc_heap(rb_execution_context_t *ec, VALUE klass) NEWOBJ_OF(ary, struct RArray, klass, T_ARRAY | (RGENGC_WB_PROTECTED_ARRAY ? FL_WB_PROTECTED : 0), sizeof(struct RArray), ec); + + ary->as.heap.len = 0; + ary->as.heap.aux.capa = 0; + ary->as.heap.ptr = NULL; + return (VALUE)ary; } @@ -1439,10 +1456,12 @@ rb_ary_pop(VALUE ary) { ary_resize_capa(ary, n * 2); } - --n; - ARY_SET_LEN(ary, n); + + VALUE obj = RARRAY_AREF(ary, n - 1); + + ARY_SET_LEN(ary, n - 1); ary_verify(ary); - return RARRAY_AREF(ary, n); + return obj; } /* @@ -1770,14 +1789,10 @@ static VALUE rb_ary_aref2(VALUE ary, VALUE b, VALUE e); /* * call-seq: - * self[index] -> object or nil - * self[start, length] -> object or nil + * self[offset] -> object or nil + * self[offset, size] -> object or nil * self[range] -> object or nil * self[aseq] -> object or nil - * slice(index) -> object or nil - * slice(start, length) -> object or nil - * slice(range) -> object or nil - * slice(aseq) -> object or nil * * Returns elements from +self+; does not modify +self+. * @@ -1785,27 +1800,27 @@ static VALUE rb_ary_aref2(VALUE ary, VALUE b, VALUE e); * * a = [:foo, 'bar', 2] * - * # Single argument index: returns one element. + * # Single argument offset: returns one element. * a[0] # => :foo # Zero-based index. * a[-1] # => 2 # Negative index counts backwards from end. * - * # Arguments start and length: returns an array. + * # Arguments offset and size: returns an array. * a[1, 2] # => ["bar", 2] - * a[-2, 2] # => ["bar", 2] # Negative start counts backwards from end. + * a[-2, 2] # => ["bar", 2] # Negative offset counts backwards from end. * * # Single argument range: returns an array. * a[0..1] # => [:foo, "bar"] * a[0..-2] # => [:foo, "bar"] # Negative range-begin counts backwards from end. * a[-2..2] # => ["bar", 2] # Negative range-end counts backwards from end. * - * When a single integer argument +index+ is given, returns the element at offset +index+: + * When a single integer argument +offset+ is given, returns the element at offset +offset+: * * a = [:foo, 'bar', 2] * a[0] # => :foo * a[2] # => 2 * a # => [:foo, "bar", 2] * - * If +index+ is negative, counts backwards from the end of +self+: + * If +offset+ is negative, counts backwards from the end of +self+: * * a = [:foo, 'bar', 2] * a[-1] # => 2 @@ -1813,29 +1828,29 @@ static VALUE rb_ary_aref2(VALUE ary, VALUE b, VALUE e); * * If +index+ is out of range, returns +nil+. * - * When two Integer arguments +start+ and +length+ are given, - * returns a new array of size +length+ containing successive elements beginning at offset +start+: + * When two Integer arguments +offset+ and +size+ are given, + * returns a new array of size +size+ containing successive elements beginning at offset +offset+: * * a = [:foo, 'bar', 2] * a[0, 2] # => [:foo, "bar"] * a[1, 2] # => ["bar", 2] * - * If <tt>start + length</tt> is greater than <tt>self.length</tt>, - * returns all elements from offset +start+ to the end: + * If <tt>offset + size</tt> is greater than <tt>self.size</tt>, + * returns all elements from offset +offset+ to the end: * * a = [:foo, 'bar', 2] * a[0, 4] # => [:foo, "bar", 2] * a[1, 3] # => ["bar", 2] * a[2, 2] # => [2] * - * If <tt>start == self.size</tt> and <tt>length >= 0</tt>, + * If <tt>offset == self.size</tt> and <tt>size >= 0</tt>, * returns a new empty array. * - * If +length+ is negative, returns +nil+. + * If +size+ is negative, returns +nil+. * * When a single Range argument +range+ is given, - * treats <tt>range.min</tt> as +start+ above - * and <tt>range.size</tt> as +length+ above: + * treats <tt>range.min</tt> as +offset+ above + * and <tt>range.size</tt> as +size+ above: * * a = [:foo, 'bar', 2] * a[0..1] # => [:foo, "bar"] @@ -2070,6 +2085,95 @@ rb_ary_fetch(int argc, VALUE *argv, VALUE ary) } /* + * call-seq: + * find(if_none_proc = nil) {|element| ... } -> object or nil + * find(if_none_proc = nil) -> enumerator + * + * Returns the first element for which the block returns a truthy value. + * + * With a block given, calls the block with successive elements of the array; + * returns the first element for which the block returns a truthy value: + * + * [1, 3, 5].find {|element| element > 2} # => 3 + * + * If no such element is found, calls +if_none_proc+ and returns its return value. + * + * [1, 3, 5].find(proc {-1}) {|element| element > 12} # => -1 + * + * With no block given, returns an Enumerator. + * + */ + +static VALUE +rb_ary_find(int argc, VALUE *argv, VALUE ary) +{ + VALUE if_none; + long idx; + + RETURN_ENUMERATOR(ary, argc, argv); + if_none = rb_check_arity(argc, 0, 1) ? argv[0] : Qnil; + + for (idx = 0; idx < RARRAY_LEN(ary); idx++) { + VALUE elem = RARRAY_AREF(ary, idx); + if (RTEST(rb_yield(elem))) { + return elem; + } + } + + if (!NIL_P(if_none)) { + return rb_funcallv(if_none, idCall, 0, 0); + } + return Qnil; +} + +/* + * call-seq: + * rfind(if_none_proc = nil) {|element| ... } -> object or nil + * rfind(if_none_proc = nil) -> enumerator + * + * Returns the last element for which the block returns a truthy value. + * + * With a block given, calls the block with successive elements of the array in + * reverse order; returns the first element for which the block returns a truthy + * value: + * + * [1, 2, 3, 4, 5, 6].rfind {|element| element < 5} # => 4 + * + * If no such element is found, calls +if_none_proc+ and returns its return value. + * + * [1, 2, 3, 4].rfind(proc {0}) {|element| element < -2} # => 0 + * + * With no block given, returns an Enumerator. + * + */ + +static VALUE +rb_ary_rfind(int argc, VALUE *argv, VALUE ary) +{ + VALUE if_none; + long len, idx; + + RETURN_ENUMERATOR(ary, argc, argv); + if_none = rb_check_arity(argc, 0, 1) ? argv[0] : Qnil; + + idx = RARRAY_LEN(ary); + while (idx--) { + VALUE elem = RARRAY_AREF(ary, idx); + if (RTEST(rb_yield(elem))) { + return elem; + } + + len = RARRAY_LEN(ary); + idx = (idx >= len) ? len : idx; + } + + if (!NIL_P(if_none)) { + return rb_funcallv(if_none, idCall, 0, 0); + } + return Qnil; +} + +/* * call-seq: * find_index(object) -> integer or nil * find_index {|element| ... } -> integer or nil @@ -2898,23 +3002,28 @@ rb_ary_join(VALUE ary, VALUE sep) StringValue(sep); len += RSTRING_LEN(sep) * (RARRAY_LEN(ary) - 1); } - for (i=0; i<RARRAY_LEN(ary); i++) { + long len_memo = RARRAY_LEN(ary); + for (i=0; i < len_memo; i++) { val = RARRAY_AREF(ary, i); - tmp = rb_check_string_type(val); - - if (NIL_P(tmp) || tmp != val) { - int first; - long n = RARRAY_LEN(ary); - if (i > n) i = n; - result = rb_str_buf_new(len + (n-i)*10); - rb_enc_associate(result, rb_usascii_encoding()); - i = ary_join_0(ary, sep, i, result); - first = i == 0; - ary_join_1(ary, ary, sep, i, result, &first); - return result; + if (RB_UNLIKELY(!RB_TYPE_P(val, T_STRING))) { + tmp = rb_check_string_type(val); + if (NIL_P(tmp) || tmp != val) { + int first; + long n = RARRAY_LEN(ary); + if (i > n) i = n; + result = rb_str_buf_new(len + (n-i)*10); + rb_enc_associate(result, rb_usascii_encoding()); + i = ary_join_0(ary, sep, i, result); + first = i == 0; + ary_join_1(ary, ary, sep, i, result, &first); + return result; + } + len += RSTRING_LEN(tmp); + len_memo = RARRAY_LEN(ary); + } + else { + len += RSTRING_LEN(val); } - - len += RSTRING_LEN(tmp); } result = rb_str_new(0, len); @@ -3439,10 +3548,9 @@ rb_ary_sort_bang(VALUE ary) ARY_SET_CAPA(ary, ARY_HEAP_LEN(tmp)); } /* tmp was lost ownership for the ptr */ - FL_UNSET(tmp, FL_FREEZE); FL_SET_EMBED(tmp); ARY_SET_EMBED_LEN(tmp, 0); - FL_SET(tmp, FL_FREEZE); + OBJ_FREEZE(tmp); } /* tmp will be GC'ed. */ RBASIC_SET_CLASS_RAW(tmp, rb_cArray); /* rb_cArray must be marked */ @@ -3503,7 +3611,7 @@ static VALUE rb_ary_bsearch_index(VALUE ary); * Returns the element from +self+ found by a binary search, * or +nil+ if the search found no suitable element. * - * See {Binary Searching}[rdoc-ref:bsearch.rdoc]. + * See {Binary Searching}[rdoc-ref:language/bsearch.rdoc]. * * Related: see {Methods for Fetching}[rdoc-ref:Array@Methods+for+Fetching]. */ @@ -3527,7 +3635,7 @@ rb_ary_bsearch(VALUE ary) * Returns the integer index of the element from +self+ found by a binary search, * or +nil+ if the search found no suitable element. * - * See {Binary Searching}[rdoc-ref:bsearch.rdoc]. + * See {Binary Searching}[rdoc-ref:language/bsearch.rdoc]. * * Related: see {Methods for Fetching}[rdoc-ref:Array@Methods+for+Fetching]. */ @@ -3660,9 +3768,9 @@ rb_ary_collect(VALUE ary) /* * call-seq: - * collect! {|element| ... } -> new_array + * collect! {|element| ... } -> self * collect! -> new_enumerator - * map! {|element| ... } -> new_array + * map! {|element| ... } -> self * map! -> new_enumerator * * With a block given, calls the block with each element of +self+ @@ -4558,7 +4666,7 @@ take_items(VALUE obj, long n) * [:c3, :b3, :a3]] * * For an *object* in *other_arrays* that is not actually an array, - * forms the the "other array" as <tt>object.to_ary</tt>, if defined, + * forms the "other array" as <tt>object.to_ary</tt>, if defined, * or as <tt>object.each.to_a</tt> otherwise. * * Related: see {Methods for Converting}[rdoc-ref:Array@Methods+for+Converting]. @@ -4718,6 +4826,8 @@ rb_ary_replace(VALUE copy, VALUE orig) ARY_SET_PTR(copy, ARY_HEAP_PTR(orig)); ARY_SET_LEN(copy, ARY_HEAP_LEN(orig)); rb_ary_set_shared(copy, shared_root); + + RUBY_ASSERT(RB_OBJ_SHAREABLE_P(copy) ? RB_OBJ_SHAREABLE_P(shared_root) : 1); } ary_verify(copy); return copy; @@ -4756,10 +4866,10 @@ rb_ary_clear(VALUE ary) /* * call-seq: - * fill(object, start = nil, count = nil) -> new_array - * fill(object, range) -> new_array - * fill(start = nil, count = nil) {|element| ... } -> new_array - * fill(range) {|element| ... } -> new_array + * fill(object, start = nil, count = nil) -> self + * fill(object, range) -> self + * fill(start = nil, count = nil) {|element| ... } -> self + * fill(range) {|element| ... } -> self * * Replaces selected elements in +self+; * may add elements to +self+; @@ -8723,7 +8833,7 @@ rb_ary_deconstruct(VALUE ary) * - #collect! (aliased as #map!): Replaces each element with a block return-value. * - #flatten: Returns an array that is a recursive flattening of +self+. * - #inspect (aliased as #to_s): Returns a new String containing the elements. - * - #join: Returns a newsString containing the elements joined by the field separator. + * - #join: Returns a new String containing the elements joined by the field separator. * - #to_a: Returns +self+ or a new array containing all elements. * - #to_ary: Returns +self+. * - #to_h: Returns a new hash formed from the elements. @@ -8791,6 +8901,9 @@ Init_Array(void) rb_define_method(rb_cArray, "length", rb_ary_length, 0); rb_define_method(rb_cArray, "size", rb_ary_length, 0); rb_define_method(rb_cArray, "empty?", rb_ary_empty_p, 0); + rb_define_method(rb_cArray, "find", rb_ary_find, -1); + rb_define_method(rb_cArray, "detect", rb_ary_find, -1); + rb_define_method(rb_cArray, "rfind", rb_ary_rfind, -1); rb_define_method(rb_cArray, "find_index", rb_ary_index, -1); rb_define_method(rb_cArray, "index", rb_ary_index, -1); rb_define_method(rb_cArray, "rindex", rb_ary_rindex, -1); @@ -8872,7 +8985,7 @@ Init_Array(void) rb_define_method(rb_cArray, "deconstruct", rb_ary_deconstruct, 0); - rb_cArray_empty_frozen = rb_ary_freeze(rb_ary_new()); + rb_cArray_empty_frozen = RB_OBJ_SET_SHAREABLE(rb_ary_freeze(rb_ary_new())); rb_vm_register_global_object(rb_cArray_empty_frozen); } |
