diff options
Diffstat (limited to '')
4 files changed, 1443 insertions, 0 deletions
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/mrbgem.rake new file mode 100644 index 000000000..882caf1ab --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/mrbgem.rake @@ -0,0 +1,5 @@ +MRuby::Gem::Specification.new('mruby-array-ext') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'Array class extension' +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/mrblib/array.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/mrblib/array.rb new file mode 100644 index 000000000..e28e5238d --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/mrblib/array.rb @@ -0,0 +1,811 @@ +class Array + ## + # call-seq: + # Array.try_convert(obj) -> array or nil + # + # Tries to convert +obj+ into an array, using +to_ary+ method. + # converted array or +nil+ if +obj+ cannot be converted for any reason. + # This method can be used to check if an argument is an array. + # + # Array.try_convert([1]) #=> [1] + # Array.try_convert("1") #=> nil + # + # if tmp = Array.try_convert(arg) + # # the argument is an array + # elsif tmp = String.try_convert(arg) + # # the argument is a string + # end + # + def self.try_convert(obj) + if obj.respond_to?(:to_ary) + obj.to_ary + else + nil + end + end + + ## + # call-seq: + # ary.uniq! -> ary or nil + # ary.uniq! { |item| ... } -> ary or nil + # + # Removes duplicate elements from +self+. + # Returns <code>nil</code> if no changes are made (that is, no + # duplicates are found). + # + # a = [ "a", "a", "b", "b", "c" ] + # a.uniq! #=> ["a", "b", "c"] + # b = [ "a", "b", "c" ] + # b.uniq! #=> nil + # c = [["student","sam"], ["student","george"], ["teacher","matz"]] + # c.uniq! { |s| s.first } # => [["student", "sam"], ["teacher", "matz"]] + # + def uniq!(&block) + ary = self.dup + result = [] + if block + hash = {} + while ary.size > 0 + val = ary.shift + key = block.call(val) + hash[key] = val unless hash.has_key?(key) + end + hash.each_value do |value| + result << value + end + else + while ary.size > 0 + result << ary.shift + ary.delete(result.last) + end + end + if result.size == self.size + nil + else + self.replace(result) + end + end + + ## + # call-seq: + # ary.uniq -> new_ary + # ary.uniq { |item| ... } -> new_ary + # + # Returns a new array by removing duplicate values in +self+. + # + # a = [ "a", "a", "b", "b", "c" ] + # a.uniq #=> ["a", "b", "c"] + # + # b = [["student","sam"], ["student","george"], ["teacher","matz"]] + # b.uniq { |s| s.first } # => [["student", "sam"], ["teacher", "matz"]] + # + def uniq(&block) + ary = self.dup + ary.uniq!(&block) + ary + end + + ## + # call-seq: + # ary - other_ary -> new_ary + # + # Array Difference---Returns a new array that is a copy of + # the original array, removing any items that also appear in + # <i>other_ary</i>. (If you need set-like behavior, see the + # library class Set.) + # + # [ 1, 1, 2, 2, 3, 3, 4, 5 ] - [ 1, 2, 4 ] #=> [ 3, 3, 5 ] + # + def -(elem) + raise TypeError, "can't convert #{elem.class} into Array" unless elem.class == Array + + hash = {} + array = [] + idx = 0 + len = elem.size + while idx < len + hash[elem[idx]] = true + idx += 1 + end + idx = 0 + len = size + while idx < len + v = self[idx] + array << v unless hash[v] + idx += 1 + end + array + end + + ## + # call-seq: + # ary | other_ary -> new_ary + # + # Set Union---Returns a new array by joining this array with + # <i>other_ary</i>, removing duplicates. + # + # [ "a", "b", "c" ] | [ "c", "d", "a" ] + # #=> [ "a", "b", "c", "d" ] + # + def |(elem) + raise TypeError, "can't convert #{elem.class} into Array" unless elem.class == Array + + ary = self + elem + ary.uniq! or ary + end + + ## + # call-seq: + # ary & other_ary -> new_ary + # + # Set Intersection---Returns a new array + # containing elements common to the two arrays, with no duplicates. + # + # [ 1, 1, 3, 5 ] & [ 1, 2, 3 ] #=> [ 1, 3 ] + # + def &(elem) + raise TypeError, "can't convert #{elem.class} into Array" unless elem.class == Array + + hash = {} + array = [] + idx = 0 + len = elem.size + while idx < len + hash[elem[idx]] = true + idx += 1 + end + idx = 0 + len = size + while idx < len + v = self[idx] + if hash[v] + array << v + hash.delete v + end + idx += 1 + end + array + end + + ## + # call-seq: + # ary.flatten -> new_ary + # ary.flatten(level) -> new_ary + # + # Returns a new array that is a one-dimensional flattening of this + # array (recursively). That is, for every element that is an array, + # extract its elements into the new array. If the optional + # <i>level</i> argument determines the level of recursion to flatten. + # + # s = [ 1, 2, 3 ] #=> [1, 2, 3] + # t = [ 4, 5, 6, [7, 8] ] #=> [4, 5, 6, [7, 8]] + # a = [ s, t, 9, 10 ] #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10] + # a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + # a = [ 1, 2, [3, [4, 5] ] ] + # a.flatten(1) #=> [1, 2, 3, [4, 5]] + # + def flatten(depth=nil) + res = dup + res.flatten! depth + res + end + + ## + # call-seq: + # ary.flatten! -> ary or nil + # ary.flatten!(level) -> array or nil + # + # Flattens +self+ in place. + # Returns <code>nil</code> if no modifications were made (i.e., + # <i>ary</i> contains no subarrays.) If the optional <i>level</i> + # argument determines the level of recursion to flatten. + # + # a = [ 1, 2, [3, [4, 5] ] ] + # a.flatten! #=> [1, 2, 3, 4, 5] + # a.flatten! #=> nil + # a #=> [1, 2, 3, 4, 5] + # a = [ 1, 2, [3, [4, 5] ] ] + # a.flatten!(1) #=> [1, 2, 3, [4, 5]] + # + def flatten!(depth=nil) + modified = false + ar = [] + idx = 0 + len = size + while idx < len + e = self[idx] + if e.is_a?(Array) && (depth.nil? || depth > 0) + ar += e.flatten(depth.nil? ? nil : depth - 1) + modified = true + else + ar << e + end + idx += 1 + end + if modified + self.replace(ar) + else + nil + end + end + + ## + # call-seq: + # ary.compact -> new_ary + # + # Returns a copy of +self+ with all +nil+ elements removed. + # + # [ "a", nil, "b", nil, "c", nil ].compact + # #=> [ "a", "b", "c" ] + # + def compact + result = self.dup + result.compact! + result + end + + ## + # call-seq: + # ary.compact! -> ary or nil + # + # Removes +nil+ elements from the array. + # Returns +nil+ if no changes were made, otherwise returns + # <i>ary</i>. + # + # [ "a", nil, "b", nil, "c" ].compact! #=> [ "a", "b", "c" ] + # [ "a", "b", "c" ].compact! #=> nil + # + def compact! + result = self.select { |e| !e.nil? } + if result.size == self.size + nil + else + self.replace(result) + end + end + + # for efficiency + def reverse_each(&block) + return to_enum :reverse_each unless block + + i = self.size - 1 + while i>=0 + block.call(self[i]) + i -= 1 + end + self + end + + NONE=Object.new + ## + # call-seq: + # ary.fetch(index) -> obj + # ary.fetch(index, default) -> obj + # ary.fetch(index) { |index| block } -> obj + # + # Tries to return the element at position +index+, but throws an IndexError + # exception if the referenced +index+ lies outside of the array bounds. This + # error can be prevented by supplying a second argument, which will act as a + # +default+ value. + # + # Alternatively, if a block is given it will only be executed when an + # invalid +index+ is referenced. + # + # Negative values of +index+ count from the end of the array. + # + # a = [ 11, 22, 33, 44 ] + # a.fetch(1) #=> 22 + # a.fetch(-1) #=> 44 + # a.fetch(4, 'cat') #=> "cat" + # a.fetch(100) { |i| puts "#{i} is out of bounds" } + # #=> "100 is out of bounds" + # + + def fetch(n=nil, ifnone=NONE, &block) + warn "block supersedes default value argument" if !n.nil? && ifnone != NONE && block + + idx = n + if idx < 0 + idx += size + end + if idx < 0 || size <= idx + return block.call(n) if block + if ifnone == NONE + raise IndexError, "index #{n} outside of array bounds: #{-size}...#{size}" + end + return ifnone + end + self[idx] + end + + ## + # call-seq: + # ary.fill(obj) -> ary + # ary.fill(obj, start [, length]) -> ary + # ary.fill(obj, range ) -> ary + # ary.fill { |index| block } -> ary + # ary.fill(start [, length] ) { |index| block } -> ary + # ary.fill(range) { |index| block } -> ary + # + # The first three forms set the selected elements of +self+ (which + # may be the entire array) to +obj+. + # + # A +start+ of +nil+ is equivalent to zero. + # + # A +length+ of +nil+ is equivalent to the length of the array. + # + # The last three forms fill the array with the value of the given block, + # which is passed the absolute index of each element to be filled. + # + # Negative values of +start+ count from the end of the array, where +-1+ is + # the last element. + # + # a = [ "a", "b", "c", "d" ] + # a.fill("x") #=> ["x", "x", "x", "x"] + # a.fill("w", -1) #=> ["x", "x", "x", "w"] + # a.fill("z", 2, 2) #=> ["x", "x", "z", "z"] + # a.fill("y", 0..1) #=> ["y", "y", "z", "z"] + # a.fill { |i| i*i } #=> [0, 1, 4, 9] + # a.fill(-2) { |i| i*i*i } #=> [0, 1, 8, 27] + # a.fill(1, 2) { |i| i+1 } #=> [0, 2, 3, 27] + # a.fill(0..1) { |i| i+1 } #=> [1, 2, 3, 27] + # + + def fill(arg0=nil, arg1=nil, arg2=nil, &block) + if arg0.nil? && arg1.nil? && arg2.nil? && !block + raise ArgumentError, "wrong number of arguments (0 for 1..3)" + end + + beg = len = 0 + ary = [] + if block + if arg0.nil? && arg1.nil? && arg2.nil? + # ary.fill { |index| block } -> ary + beg = 0 + len = self.size + elsif !arg0.nil? && arg0.kind_of?(Range) + # ary.fill(range) { |index| block } -> ary + beg = arg0.begin + beg += self.size if beg < 0 + len = arg0.end + len += self.size if len < 0 + len += 1 unless arg0.exclude_end? + elsif !arg0.nil? + # ary.fill(start [, length] ) { |index| block } -> ary + beg = arg0 + beg += self.size if beg < 0 + if arg1.nil? + len = self.size + else + len = arg0 + arg1 + end + end + else + if !arg0.nil? && arg1.nil? && arg2.nil? + # ary.fill(obj) -> ary + beg = 0 + len = self.size + elsif !arg0.nil? && !arg1.nil? && arg1.kind_of?(Range) + # ary.fill(obj, range ) -> ary + beg = arg1.begin + beg += self.size if beg < 0 + len = arg1.end + len += self.size if len < 0 + len += 1 unless arg1.exclude_end? + elsif !arg0.nil? && !arg1.nil? + # ary.fill(obj, start [, length]) -> ary + beg = arg1 + beg += self.size if beg < 0 + if arg2.nil? + len = self.size + else + len = beg + arg2 + end + end + end + + i = beg + if block + while i < len + self[i] = block.call(i) + i += 1 + end + else + while i < len + self[i] = arg0 + i += 1 + end + end + self + end + + ## + # call-seq: + # ary.rotate(count=1) -> new_ary + # + # Returns a new array by rotating +self+ so that the element at +count+ is + # the first element of the new array. + # + # If +count+ is negative then it rotates in the opposite direction, starting + # from the end of +self+ where +-1+ is the last element. + # + # a = [ "a", "b", "c", "d" ] + # a.rotate #=> ["b", "c", "d", "a"] + # a #=> ["a", "b", "c", "d"] + # a.rotate(2) #=> ["c", "d", "a", "b"] + # a.rotate(-3) #=> ["b", "c", "d", "a"] + + def rotate(count=1) + ary = [] + len = self.length + + if len > 0 + idx = (count < 0) ? (len - (~count % len) - 1) : (count % len) # rotate count + len.times do + ary << self[idx] + idx += 1 + idx = 0 if idx > len-1 + end + end + ary + end + + ## + # call-seq: + # ary.rotate!(count=1) -> ary + # + # Rotates +self+ in place so that the element at +count+ comes first, and + # returns +self+. + # + # If +count+ is negative then it rotates in the opposite direction, starting + # from the end of the array where +-1+ is the last element. + # + # a = [ "a", "b", "c", "d" ] + # a.rotate! #=> ["b", "c", "d", "a"] + # a #=> ["b", "c", "d", "a"] + # a.rotate!(2) #=> ["d", "a", "b", "c"] + # a.rotate!(-3) #=> ["a", "b", "c", "d"] + + def rotate!(count=1) + self.replace(self.rotate(count)) + end + + ## + # call-seq: + # ary.delete_if { |item| block } -> ary + # ary.delete_if -> Enumerator + # + # Deletes every element of +self+ for which block evaluates to +true+. + # + # The array is changed instantly every time the block is called, not after + # the iteration is over. + # + # See also Array#reject! + # + # If no block is given, an Enumerator is returned instead. + # + # scores = [ 97, 42, 75 ] + # scores.delete_if {|score| score < 80 } #=> [97] + + def delete_if(&block) + return to_enum :delete_if unless block + + idx = 0 + while idx < self.size do + if block.call(self[idx]) + self.delete_at(idx) + else + idx += 1 + end + end + self + end + + ## + # call-seq: + # ary.reject! { |item| block } -> ary or nil + # ary.reject! -> Enumerator + # + # Equivalent to Array#delete_if, deleting elements from +self+ for which the + # block evaluates to +true+, but returns +nil+ if no changes were made. + # + # The array is changed instantly every time the block is called, not after + # the iteration is over. + # + # See also Enumerable#reject and Array#delete_if. + # + # If no block is given, an Enumerator is returned instead. + + def reject!(&block) + return to_enum :reject! unless block + + len = self.size + idx = 0 + while idx < self.size do + if block.call(self[idx]) + self.delete_at(idx) + else + idx += 1 + end + end + if self.size == len + nil + else + self + end + end + + ## + # call-seq: + # ary.insert(index, obj...) -> ary + # + # Inserts the given values before the element with the given +index+. + # + # Negative indices count backwards from the end of the array, where +-1+ is + # the last element. + # + # a = %w{ a b c d } + # a.insert(2, 99) #=> ["a", "b", 99, "c", "d"] + # a.insert(-2, 1, 2, 3) #=> ["a", "b", 99, "c", 1, 2, 3, "d"] + + def insert(idx, *args) + idx += self.size + 1 if idx < 0 + self[idx, 0] = args + self + end + + ## + # call-seq: + # ary.bsearch {|x| block } -> elem + # + # By using binary search, finds a value from this array which meets + # the given condition in O(log n) where n is the size of the array. + # + # You can use this method in two use cases: a find-minimum mode and + # a find-any mode. In either case, the elements of the array must be + # monotone (or sorted) with respect to the block. + # + # In find-minimum mode (this is a good choice for typical use case), + # the block must return true or false, and there must be an index i + # (0 <= i <= ary.size) so that: + # + # - the block returns false for any element whose index is less than + # i, and + # - the block returns true for any element whose index is greater + # than or equal to i. + # + # This method returns the i-th element. If i is equal to ary.size, + # it returns nil. + # + # ary = [0, 4, 7, 10, 12] + # ary.bsearch {|x| x >= 4 } #=> 4 + # ary.bsearch {|x| x >= 6 } #=> 7 + # ary.bsearch {|x| x >= -1 } #=> 0 + # ary.bsearch {|x| x >= 100 } #=> nil + # + # In find-any mode (this behaves like libc's bsearch(3)), the block + # must return a number, and there must be two indices i and j + # (0 <= i <= j <= ary.size) so that: + # + # - the block returns a positive number for ary[k] if 0 <= k < i, + # - the block returns zero for ary[k] if i <= k < j, and + # - the block returns a negative number for ary[k] if + # j <= k < ary.size. + # + # Under this condition, this method returns any element whose index + # is within i...j. If i is equal to j (i.e., there is no element + # that satisfies the block), this method returns nil. + # + # ary = [0, 4, 7, 10, 12] + # # try to find v such that 4 <= v < 8 + # ary.bsearch {|x| 1 - (x / 4).truncate } #=> 4 or 7 + # # try to find v such that 8 <= v < 10 + # ary.bsearch {|x| 4 - (x / 2).truncate } #=> nil + # + # You must not mix the two modes at a time; the block must always + # return either true/false, or always return a number. It is + # undefined which value is actually picked up at each iteration. + + def bsearch(&block) + return to_enum :bsearch unless block + + if idx = bsearch_index(&block) + self[idx] + else + nil + end + end + + ## + # call-seq: + # ary.bsearch_index {|x| block } -> int or nil + # + # By using binary search, finds an index of a value from this array which + # meets the given condition in O(log n) where n is the size of the array. + # + # It supports two modes, depending on the nature of the block and they are + # exactly the same as in the case of #bsearch method with the only difference + # being that this method returns the index of the element instead of the + # element itself. For more details consult the documentation for #bsearch. + + def bsearch_index(&block) + return to_enum :bsearch_index unless block + + low = 0 + high = size + satisfied = false + + while low < high + mid = ((low+high)/2).truncate + res = block.call self[mid] + + case res + when 0 # find-any mode: Found! + return mid + when Numeric # find-any mode: Continue... + in_lower_half = res < 0 + when true # find-min mode + in_lower_half = true + satisfied = true + when false, nil # find-min mode + in_lower_half = false + else + raise TypeError, 'invalid block result (must be numeric, true, false or nil)' + end + + if in_lower_half + high = mid + else + low = mid + 1 + end + end + + satisfied ? low : nil + end + + ## + # call-seq: + # ary.delete_if { |item| block } -> ary + # ary.delete_if -> Enumerator + # + # Deletes every element of +self+ for which block evaluates to +true+. + # + # The array is changed instantly every time the block is called, not after + # the iteration is over. + # + # See also Array#reject! + # + # If no block is given, an Enumerator is returned instead. + # + # scores = [ 97, 42, 75 ] + # scores.delete_if {|score| score < 80 } #=> [97] + + def delete_if(&block) + return to_enum :delete_if unless block + + idx = 0 + while idx < self.size do + if block.call(self[idx]) + self.delete_at(idx) + else + idx += 1 + end + end + self + end + + ## + # call-seq: + # ary.keep_if { |item| block } -> ary + # ary.keep_if -> Enumerator + # + # Deletes every element of +self+ for which the given block evaluates to + # +false+. + # + # See also Array#select! + # + # If no block is given, an Enumerator is returned instead. + # + # a = [1, 2, 3, 4, 5] + # a.keep_if { |val| val > 3 } #=> [4, 5] + + def keep_if(&block) + return to_enum :keep_if unless block + + idx = 0 + len = self.size + while idx < self.size do + if block.call(self[idx]) + idx += 1 + else + self.delete_at(idx) + end + end + self + end + + ## + # call-seq: + # ary.select! {|item| block } -> ary or nil + # ary.select! -> Enumerator + # + # Invokes the given block passing in successive elements from +self+, + # deleting elements for which the block returns a +false+ value. + # + # If changes were made, it will return +self+, otherwise it returns +nil+. + # + # See also Array#keep_if + # + # If no block is given, an Enumerator is returned instead. + + def select!(&block) + return to_enum :select! unless block + + result = [] + idx = 0 + len = size + while idx < len + elem = self[idx] + result << elem if block.call(elem) + idx += 1 + end + return nil if len == result.size + self.replace(result) + end + + ## + # call-seq: + # ary.index(val) -> int or nil + # ary.index {|item| block } -> int or nil + # + # Returns the _index_ of the first object in +ary+ such that the object is + # <code>==</code> to +obj+. + # + # If a block is given instead of an argument, returns the _index_ of the + # first object for which the block returns +true+. Returns +nil+ if no + # match is found. + # + # ISO 15.2.12.5.14 + def index(val=NONE, &block) + return to_enum(:find_index, val) if !block && val == NONE + + if block + idx = 0 + len = size + while idx < len + return idx if block.call self[idx] + idx += 1 + end + else + return self.__ary_index(val) + end + nil + end + + ## + # call-seq: + # ary.to_ary -> ary + # + # Returns +self+. + # + def to_ary + self + end + + ## + # call-seq: + # ary.dig(idx, ...) -> object + # + # Extracts the nested value specified by the sequence of <i>idx</i> + # objects by calling +dig+ at each step, returning +nil+ if any + # intermediate step is +nil+. + # + def dig(idx,*args) + n = self[idx] + if args.size > 0 + n&.dig(*args) + else + n + end + end +end diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/src/array.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/src/array.c new file mode 100644 index 000000000..e99599b09 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/src/array.c @@ -0,0 +1,244 @@ +#include <mruby.h> +#include <mruby/value.h> +#include <mruby/array.h> +#include <mruby/range.h> +#include <mruby/hash.h> + +/* + * call-seq: + * ary.assoc(obj) -> new_ary or nil + * + * Searches through an array whose elements are also arrays + * comparing _obj_ with the first element of each contained array + * using obj.==. + * Returns the first contained array that matches (that + * is, the first associated array), + * or +nil+ if no match is found. + * See also <code>Array#rassoc</code>. + * + * s1 = [ "colors", "red", "blue", "green" ] + * s2 = [ "letters", "a", "b", "c" ] + * s3 = "foo" + * a = [ s1, s2, s3 ] + * a.assoc("letters") #=> [ "letters", "a", "b", "c" ] + * a.assoc("foo") #=> nil + */ + +static mrb_value +mrb_ary_assoc(mrb_state *mrb, mrb_value ary) +{ + mrb_int i; + mrb_value v, k; + + mrb_get_args(mrb, "o", &k); + + for (i = 0; i < RARRAY_LEN(ary); ++i) { + v = mrb_check_array_type(mrb, RARRAY_PTR(ary)[i]); + if (!mrb_nil_p(v) && RARRAY_LEN(v) > 0 && + mrb_equal(mrb, RARRAY_PTR(v)[0], k)) + return v; + } + return mrb_nil_value(); +} + +/* + * call-seq: + * ary.rassoc(obj) -> new_ary or nil + * + * Searches through the array whose elements are also arrays. Compares + * _obj_ with the second element of each contained array using + * <code>==</code>. Returns the first contained array that matches. See + * also <code>Array#assoc</code>. + * + * a = [ [ 1, "one"], [2, "two"], [3, "three"], ["ii", "two"] ] + * a.rassoc("two") #=> [2, "two"] + * a.rassoc("four") #=> nil + */ + +static mrb_value +mrb_ary_rassoc(mrb_state *mrb, mrb_value ary) +{ + mrb_int i; + mrb_value v, value; + + mrb_get_args(mrb, "o", &value); + + for (i = 0; i < RARRAY_LEN(ary); ++i) { + v = RARRAY_PTR(ary)[i]; + if (mrb_type(v) == MRB_TT_ARRAY && + RARRAY_LEN(v) > 1 && + mrb_equal(mrb, RARRAY_PTR(v)[1], value)) + return v; + } + return mrb_nil_value(); +} + +/* + * call-seq: + * ary.at(index) -> obj or nil + * + * Returns the element at _index_. A + * negative index counts from the end of +self+. Returns +nil+ + * if the index is out of range. See also <code>Array#[]</code>. + * + * a = [ "a", "b", "c", "d", "e" ] + * a.at(0) #=> "a" + * a.at(-1) #=> "e" + */ + +static mrb_value +mrb_ary_at(mrb_state *mrb, mrb_value ary) +{ + mrb_int pos; + mrb_get_args(mrb, "i", &pos); + + return mrb_ary_entry(ary, pos); +} + +static mrb_value +mrb_ary_values_at(mrb_state *mrb, mrb_value self) +{ + mrb_int argc; + mrb_value *argv; + + mrb_get_args(mrb, "*", &argv, &argc); + + return mrb_get_values_at(mrb, self, RARRAY_LEN(self), argc, argv, mrb_ary_ref); +} + +/* + * call-seq: + * ary.to_h -> Hash + * + * Returns the result of interpreting <i>aray</i> as an array of + * <tt>[key, value]</tt> paris. + * + * [[:foo, :bar], [1, 2]].to_h + * # => {:foo => :bar, 1 => 2} + * + */ + +static mrb_value +mrb_ary_to_h(mrb_state *mrb, mrb_value ary) +{ + mrb_int i; + mrb_value v, hash; + + hash = mrb_hash_new_capa(mrb, 0); + + for (i = 0; i < RARRAY_LEN(ary); ++i) { + mrb_value elt = RARRAY_PTR(ary)[i]; + v = mrb_check_array_type(mrb, elt); + + if (mrb_nil_p(v)) { + mrb_raisef(mrb, E_TYPE_ERROR, "wrong element type %S at %S (expected array)", + mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, elt)), + mrb_fixnum_value(i) + ); + } + + if (RARRAY_LEN(v) != 2) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong array length at %S (expected 2, was %S)", + mrb_fixnum_value(i), + mrb_fixnum_value(RARRAY_LEN(v)) + ); + } + + mrb_hash_set(mrb, hash, RARRAY_PTR(v)[0], RARRAY_PTR(v)[1]); + } + + return hash; +} + +/* + * call-seq: + * ary.slice!(index) -> obj or nil + * ary.slice!(start, length) -> new_ary or nil + * ary.slice!(range) -> new_ary or nil + * + * Deletes the element(s) given by an +index+ (optionally up to +length+ + * elements) or by a +range+. + * + * Returns the deleted object (or objects), or +nil+ if the +index+ is out of + * range. + * + * a = [ "a", "b", "c" ] + * a.slice!(1) #=> "b" + * a #=> ["a", "c"] + * a.slice!(-1) #=> "c" + * a #=> ["a"] + * a.slice!(100) #=> nil + * a #=> ["a"] + */ + +static mrb_value +mrb_ary_slice_bang(mrb_state *mrb, mrb_value self) +{ + struct RArray *a = mrb_ary_ptr(self); + mrb_int i, j, k, len, alen = ARY_LEN(a); + mrb_value index; + mrb_value val; + mrb_value *ptr; + mrb_value ary; + + mrb_ary_modify(mrb, a); + + if (mrb_get_args(mrb, "o|i", &index, &len) == 1) { + switch (mrb_type(index)) { + case MRB_TT_RANGE: + if (mrb_range_beg_len(mrb, index, &i, &len, alen, TRUE) == 1) { + goto delete_pos_len; + } + else { + return mrb_nil_value(); + } + case MRB_TT_FIXNUM: + val = mrb_funcall(mrb, self, "delete_at", 1, index); + return val; + default: + val = mrb_funcall(mrb, self, "delete_at", 1, index); + return val; + } + } + + i = mrb_fixnum(index); + delete_pos_len: + if (i < 0) i += alen; + if (i < 0 || alen < i) return mrb_nil_value(); + if (len < 0) return mrb_nil_value(); + if (alen == i) return mrb_ary_new(mrb); + if (len > alen - i) len = alen - i; + + ary = mrb_ary_new_capa(mrb, len); + ptr = ARY_PTR(a); + for (j = i, k = 0; k < len; ++j, ++k) { + mrb_ary_push(mrb, ary, ptr[j]); + } + + ptr += i; + for (j = i; j < alen - len; ++j) { + *ptr = *(ptr+len); + ++ptr; + } + + mrb_ary_resize(mrb, self, alen - len); + return ary; +} + +void +mrb_mruby_array_ext_gem_init(mrb_state* mrb) +{ + struct RClass * a = mrb->array_class; + + mrb_define_method(mrb, a, "assoc", mrb_ary_assoc, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, a, "at", mrb_ary_at, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, a, "rassoc", mrb_ary_rassoc, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, a, "values_at", mrb_ary_values_at, MRB_ARGS_ANY()); + mrb_define_method(mrb, a, "to_h", mrb_ary_to_h, MRB_ARGS_REQ(0)); + mrb_define_method(mrb, a, "slice!", mrb_ary_slice_bang, MRB_ARGS_ANY()); +} + +void +mrb_mruby_array_ext_gem_final(mrb_state* mrb) +{ +} diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/test/array.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/test/array.rb new file mode 100644 index 000000000..c0db1b1cc --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/test/array.rb @@ -0,0 +1,383 @@ +## +# Array(Ext) Test + +assert("Array.try_convert") do + assert_nil Array.try_convert(0) + assert_nil Array.try_convert(nil) + assert_equal [], Array.try_convert([]) + assert_equal [1,2,3], Array.try_convert([1,2,3]) +end + +assert("Array#assoc") do + s1 = [ "colors", "red", "blue", "green" ] + s2 = [ "letters", "a", "b", "c" ] + s3 = "foo" + a = [ s1, s2, s3 ] + + assert_equal [ "letters", "a", "b", "c" ], a.assoc("letters") + assert_nil a.assoc("foo") +end + +assert("Array#at") do + a = [ "a", "b", "c", "d", "e" ] + assert_equal "a", a.at(0) + assert_equal "e", a.at(-1) +end + +assert("Array#rassoc") do + a = [ [ 1, "one"], [2, "two"], [3, "three"], ["ii", "two"] ] + + assert_equal [2, "two"], a.rassoc("two") + assert_nil a.rassoc("four") +end + +assert("Array#uniq!") do + a = [1, 2, 3, 1] + a.uniq! + assert_equal [1, 2, 3], a + + b = [ "a", "b", "c" ] + assert_nil b.uniq! + + c = [["student","sam"], ["student","george"], ["teacher","matz"]] + assert_equal [["student", "sam"], ["teacher", "matz"]], c.uniq! { |s| s.first } + + d = [["student","sam"], ["teacher","matz"]] + assert_nil d.uniq! { |s| s.first } +end + +assert("Array#uniq") do + a = [1, 2, 3, 1] + assert_equal [1, 2, 3], a.uniq + assert_equal [1, 2, 3, 1], a + + b = [["student","sam"], ["student","george"], ["teacher","matz"]] + assert_equal [["student", "sam"], ["teacher", "matz"]], b.uniq { |s| s.first } +end + +assert("Array#-") do + a = [1, 2, 3, 1] + b = [1] + c = 1 + + assert_raise(TypeError) { a - c } + assert_equal [2, 3], (a - b) + assert_equal [1, 2, 3, 1], a +end + +assert("Array#|") do + a = [1, 2, 3, 1] + b = [1, 4] + c = 1 + + assert_raise(TypeError) { a | c } + assert_equal [1, 2, 3, 4], (a | b) + assert_equal [1, 2, 3, 1], a +end + +assert("Array#&") do + a = [1, 2, 3, 1] + b = [1, 4] + c = 1 + + assert_raise(TypeError) { a & c } + assert_equal [1], (a & b) + assert_equal [1, 2, 3, 1], a +end + +assert("Array#flatten") do + assert_equal [1, 2, "3", {4=>5}, :'6'], [1, 2, "3", {4=>5}, :'6'].flatten + assert_equal [1, 2, 3, 4, 5, 6], [1, 2, [3, 4, 5], 6].flatten + assert_equal [1, 2, 3, 4, 5, 6], [1, 2, [3, [4, 5], 6]].flatten + assert_equal [1, [2, [3, [4, [5, [6]]]]]], [1, [2, [3, [4, [5, [6]]]]]].flatten(0) + assert_equal [1, 2, [3, [4, [5, [6]]]]], [1, [2, [3, [4, [5, [6]]]]]].flatten(1) + assert_equal [1, 2, 3, [4, [5, [6]]]], [1, [2, [3, [4, [5, [6]]]]]].flatten(2) + assert_equal [1, 2, 3, 4, [5, [6]]], [1, [2, [3, [4, [5, [6]]]]]].flatten(3) + assert_equal [1, 2, 3, 4, 5, [6]], [1, [2, [3, [4, [5, [6]]]]]].flatten(4) + assert_equal [1, 2, 3, 4, 5, 6], [1, [2, [3, [4, [5, [6]]]]]].flatten(5) +end + +assert("Array#flatten!") do + assert_equal [1, 2, 3, 4, 5, 6], [1, 2, [3, [4, 5], 6]].flatten! +end + +assert("Array#compact") do + a = [1, nil, "2", nil, :t, false, nil] + assert_equal [1, "2", :t, false], a.compact + assert_equal [1, nil, "2", nil, :t, false, nil], a +end + +assert("Array#compact!") do + a = [1, nil, "2", nil, :t, false, nil] + a.compact! + assert_equal [1, "2", :t, false], a +end + +assert("Array#fetch") do + a = [ 11, 22, 33, 44 ] + assert_equal 22, a.fetch(1) + assert_equal 44, a.fetch(-1) + assert_equal 'cat', a.fetch(4, 'cat') + ret = 0 + a.fetch(100) { |i| ret = i } + assert_equal 100, ret + assert_raise(IndexError) { a.fetch(100) } +end + +assert("Array#fill") do + a = [ "a", "b", "c", "d" ] + assert_equal ["x", "x", "x", "x"], a.fill("x") + assert_equal ["x", "x", "x", "w"], a.fill("w", -1) + assert_equal ["x", "x", "z", "z"], a.fill("z", 2, 2) + assert_equal ["y", "y", "z", "z"], a.fill("y", 0..1) + assert_equal [0, 1, 4, 9], a.fill { |i| i*i } + assert_equal [0, 1, 8, 27], a.fill(-2) { |i| i*i*i } + assert_equal [0, 2, 3, 27], a.fill(1, 2) { |i| i+1 } + assert_equal [1, 2, 3, 27], a.fill(0..1) { |i| i+1 } + assert_raise(ArgumentError) { a.fill } + + assert_equal([0, 1, 2, 3, -1, 5], [0, 1, 2, 3, 4, 5].fill(-1, -2, 1)) + assert_equal([0, 1, 2, 3, -1, -1, -1], [0, 1, 2, 3, 4, 5].fill(-1, -2, 3)) + assert_equal([0, 1, 2, -1, -1, 5], [0, 1, 2, 3, 4, 5].fill(-1, 3..4)) + assert_equal([0, 1, 2, -1, 4, 5], [0, 1, 2, 3, 4, 5].fill(-1, 3...4)) + assert_equal([0, 1, -1, -1, -1, 5], [0, 1, 2, 3, 4, 5].fill(-1, 2..-2)) + assert_equal([0, 1, -1, -1, 4, 5], [0, 1, 2, 3, 4, 5].fill(-1, 2...-2)) + assert_equal([0, 1, 2, 13, 14, 5], [0, 1, 2, 3, 4, 5].fill(3..4){|i| i+10}) + assert_equal([0, 1, 2, 13, 4, 5], [0, 1, 2, 3, 4, 5].fill(3...4){|i| i+10}) + assert_equal([0, 1, 12, 13, 14, 5], [0, 1, 2, 3, 4, 5].fill(2..-2){|i| i+10}) + assert_equal([0, 1, 12, 13, 4, 5], [0, 1, 2, 3, 4, 5].fill(2...-2){|i| i+10}) + + assert_equal [1, 2, 3, 4, 'x', 'x'], [1, 2, 3, 4, 5, 6].fill('x', -2..-1) + assert_equal [1, 2, 3, 4, 'x', 6], [1, 2, 3, 4, 5, 6].fill('x', -2...-1) + assert_equal [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6].fill('x', -2...-2) + assert_equal [1, 2, 3, 4, 'x', 6], [1, 2, 3, 4, 5, 6].fill('x', -2..-2) + assert_equal [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6].fill('x', -2..0) +end + +assert("Array#reverse_each") do + a = [ "a", "b", "c", "d" ] + b = [] + a.reverse_each do |i| + b << i + end + assert_equal [ "d", "c", "b", "a" ], b + + if Object.const_defined?(:Enumerator) + assert_equal [ "d", "c", "b", "a" ], a.reverse_each.to_a + else + true + end +end + +assert("Array#rotate") do + a = ["a", "b", "c", "d"] + assert_equal ["b", "c", "d", "a"], a.rotate + assert_equal ["a", "b", "c", "d"], a + assert_equal ["c", "d", "a", "b"], a.rotate(2) + assert_equal ["b", "c", "d", "a"], a.rotate(-3) + assert_equal ["c", "d", "a", "b"], a.rotate(10) + assert_equal [], [].rotate +end + +assert("Array#rotate!") do + a = ["a", "b", "c", "d"] + assert_equal ["b", "c", "d", "a"], a.rotate! + assert_equal ["b", "c", "d", "a"], a + assert_equal ["d", "a", "b", "c"], a.rotate!(2) + assert_equal ["a", "b", "c", "d"], a.rotate!(-3) + assert_equal ["c", "d", "a", "b"], a.rotate(10) + assert_equal [], [].rotate! +end + +assert("Array#delete_if") do + a = [1, 2, 3, 4, 5] + assert_equal [1, 2, 3, 4, 5], a.delete_if { false } + assert_equal [1, 2, 3, 4, 5], a + + a = [1, 2, 3, 4, 5] + assert_equal [], a.delete_if { true } + assert_equal [], a + + a = [1, 2, 3, 4, 5] + assert_equal [1, 2, 3], a.delete_if { |i| i > 3 } + assert_equal [1, 2, 3], a +end + +assert("Array#reject!") do + a = [1, 2, 3, 4, 5] + assert_nil a.reject! { false } + assert_equal [1, 2, 3, 4, 5], a + + a = [1, 2, 3, 4, 5] + assert_equal [], a.reject! { true } + assert_equal [], a + + a = [1, 2, 3, 4, 5] + assert_equal [1, 2, 3], a.reject! { |val| val > 3 } + assert_equal [1, 2, 3], a +end + +assert("Array#insert") do + a = ["a", "b", "c", "d"] + assert_equal ["a", "b", 99, "c", "d"], a.insert(2, 99) + assert_equal ["a", "b", 99, "c", 1, 2, 3, "d"], a.insert(-2, 1, 2, 3) + + b = ["a", "b", "c", "d"] + assert_equal ["a", "b", "c", "d", nil, nil, 99], b.insert(6, 99) +end + +assert("Array#bsearch") do + # Find minimum mode + a = [0, 2, 4] + assert_equal 0, a.bsearch{ |x| x >= -1 } + assert_equal 0, a.bsearch{ |x| x >= 0 } + assert_equal 2, a.bsearch{ |x| x >= 1 } + assert_equal 2, a.bsearch{ |x| x >= 2 } + assert_equal 4, a.bsearch{ |x| x >= 3 } + assert_equal 4, a.bsearch{ |x| x >= 4 } + assert_nil a.bsearch{ |x| x >= 5 } + + # Find any mode + a = [0, 4, 8] + def between(lo, x, hi) + if x < lo + 1 + elsif x > hi + -1 + else + 0 + end + end + assert_nil a.bsearch{ |x| between(-3, x, -1) } + assert_equal 0, a.bsearch{ |x| between(-1, x, 1) } + assert_nil a.bsearch{ |x| between( 1, x, 3) } + assert_equal 4, a.bsearch{ |x| between( 3, x, 5) } + assert_nil a.bsearch{ |x| between( 5, x, 7) } + assert_equal 8, a.bsearch{ |x| between( 7, x, 9) } + assert_nil a.bsearch{ |x| between( 9, x, 11) } + + assert_equal 0, a.bsearch{ |x| between( 0, x, 3) } + assert_equal 4, a.bsearch{ |x| between( 0, x, 4) } + assert_equal 4, a.bsearch{ |x| between( 4, x, 8) } + assert_equal 8, a.bsearch{ |x| between( 5, x, 8) } + + # Invalid block result + assert_raise TypeError, 'invalid block result (must be numeric, true, false or nil)' do + a.bsearch{ 'I like to watch the world burn' } + end +end + +assert("Array#bsearch_index") do + # tested through Array#bsearch +end + +assert("Array#delete_if") do + a = [1, 2, 3, 4, 5] + assert_equal [1, 2, 3, 4, 5], a.delete_if { false } + assert_equal [1, 2, 3, 4, 5], a + + a = [1, 2, 3, 4, 5] + assert_equal [], a.delete_if { true } + assert_equal [], a + + a = [ 1, 2, 3, 4, 5 ] + assert_equal [1, 2, 3], a.delete_if { |val| val > 3 } +end + +assert("Array#keep_if") do + a = [1, 2, 3, 4, 5] + assert_equal [1, 2, 3, 4, 5], a.keep_if { true } + assert_equal [1, 2, 3, 4, 5], a + + a = [1, 2, 3, 4, 5] + assert_equal [], a.keep_if { false } + assert_equal [], a + + a = [1, 2, 3, 4, 5] + assert_equal [4, 5], a.keep_if { |val| val > 3 } + assert_equal [4, 5], a +end + +assert("Array#select!") do + a = [1, 2, 3, 4, 5] + assert_nil a.select! { true } + assert_equal [1, 2, 3, 4, 5], a + + a = [1, 2, 3, 4, 5] + assert_equal [], a.select! { false } + assert_equal [], a + + a = [1, 2, 3, 4, 5] + assert_equal [4, 5], a.select! { |val| val > 3 } + assert_equal [4, 5], a +end + +assert('Array#values_at') do + a = %w{red green purple white none} + + assert_equal %w{red purple none}, a.values_at(0, 2, 4) + assert_equal ['green', 'white', nil, nil], a.values_at(1, 3, 5, 7) + assert_equal ['none', 'white', 'white', nil], a.values_at(-1, -2, -2, -7) + assert_equal ['none', nil, nil, 'red', 'green', 'purple'], a.values_at(4..6, 0...3) + assert_raise(TypeError) { a.values_at 'tt' } +end + +assert('Array#to_h') do + assert_equal({}, [].to_h) + assert_equal({a: 1, b:2}, [[:a, 1], [:b, 2]].to_h) + + assert_raise(TypeError) { [1].to_h } + assert_raise(ArgumentError) { [[1]].to_h } +end + +assert('Array#to_h (Modified)') do + class A + def to_ary + $a.clear + nil + end + end + $a = [A.new] + assert_raise(TypeError) { $a.to_h } +end + +assert("Array#index (block)") do + assert_nil (1..10).to_a.index { |i| i % 5 == 0 and i % 7 == 0 } + assert_equal 34, (1..100).to_a.index { |i| i % 5 == 0 and i % 7 == 0 } +end + +assert("Array#to_ary") do + assert_equal [], [].to_ary + assert_equal [1,2,3], [1,2,3].to_ary +end + +assert("Array#dig") do + h = [[[1]], 0] + assert_equal(1, h.dig(0, 0, 0)) + assert_nil(h.dig(2, 0)) + assert_raise(TypeError) {h.dig(:a)} +end + +assert("Array#slice!") do + a = [1, 2, 3] + b = a.slice!(0) + c = [1, 2, 3, 4, 5] + d = c.slice!(0, 2) + e = [1, 2, 3, 4, 5] + f = e.slice!(1..3) + g = [1, 2, 3] + h = g.slice!(-1) + i = [1, 2, 3] + j = i.slice!(0, -1) + + assert_equal(a, [2, 3]) + assert_equal(b, 1) + assert_equal(c, [3, 4, 5]) + assert_equal(d, [1, 2]) + assert_equal(e, [1, 5]) + assert_equal(f, [2, 3, 4]) + assert_equal(g, [1, 2]) + assert_equal(h, 3) + assert_equal(i, [1, 2, 3]) + assert_equal(j, nil) +end |