1 # depends on: class.rb enumerable.rb tuple.rb kernel.rb
4 # Arrays are ordered, integer-indexed collections of any object. Array
5 # indexing starts at 0, as in C or Java. A negative index is assumed to be
6 # relative to the end of the array---that is, an index of -1 indicates the
7 # last element of the array, -2 is the next to last element in the array, and
10 # Arrays can be created with the <tt>[]</tt> syntax, or via <tt>Array.new</tt>.
13 def total=(n) ; @total = n ; end
14 def tuple=(t) ; @tuple = t ; end
15 def start=(s) ; @start = s ; end
19 # The flow control for many of these methods is
20 # pretty evil due to how MRI works. There is
21 # also a lot of duplication of code due to very
22 # subtle processing differences and, in some
23 # cases, to avoid mutual dependency. Apologies.
26 # Returns a new Array populated with the given objects
35 ary.tuple = Tuple.new 8
39 # Creates a new Array. Without arguments, an empty
40 # Array is returned. If the only argument is an object
41 # that responds to +to_ary+, a copy of that Array is
42 # created. Otherwise the first argument is the size
43 # of the new Array (default 0.) The second argument
44 # may be an object used to fill the Array up to the
45 # size given (the same object, not a copy.) Alternatively,
46 # a block that takes the numeric index can be given and
47 # will be run size times to fill the Array with its
48 # result. The block supercedes any object given. If
49 # neither is provided, the Array is filled with nil.
51 raise ArgumentError, "Wrong number of arguments, #{args.size} for 2" if args.size > 2
54 if args.size == 1 and (args.first.__kind_of__ Array or args.first.respond_to? :to_ary)
55 ary = Type.coerce_to args.first, Array, :to_ary
57 tuple = Tuple.new(ary.size + 10)
59 tuple.copy_from ary.tuple, ary.start, 0
62 count = Type.coerce_to args.first, Fixnum, :to_int
63 raise ArgumentError, "Size must be positive" if count < 0
66 @tuple = Tuple.new(count + 10)
70 count.times { |i| @tuple.put i, yield(i) }
72 count.times { |i| @tuple.put i, obj }
82 # Element reference, returns the element at the given index or
83 # a subarray starting from the index continuing for length
84 # elements or returns a subarray from range elements. Negative
85 # indices count from the end. Returns nil if the index or subarray
86 # request cannot be completed. Array#slice is synonymous with #[].
87 # Subclasses return instances of themselves.
88 def [](one, two = nil)
89 Ruby.primitive :array_aref
91 # Normalise the argument variants
92 start, finish, count, simple, is_range = nil, nil, nil, false, false
96 start, finish = one.begin, one.end
98 start, count = one, Type.coerce_to(two, Fixnum, :to_int)
99 return nil if count < 0 # No need to go further
101 start, finish, simple = one, one, true
104 # Convert negative indices
105 start = Type.coerce_to start, Fixnum, :to_int
106 start += @total if start < 0
109 return nil if start < 0 or start >= @total
110 return @tuple.at(@start + start)
112 # ONE past end only, MRI compat
113 elsif start == @total
114 return self.class.new
116 elsif start < 0 or start >= @total
121 finish = Type.coerce_to finish, Fixnum, :to_int if finish
122 finish = (start + count - 1) if count # For non-ranges
124 finish += @total if finish < 0
126 finish -= 1 if is_range and one.exclude_end?
128 # Going past the end is ignored (sort of)
129 finish = (@total - 1) if finish >= @total
132 return self.class.new if is_range
138 return out if finish < start or count == 0
140 start.upto(finish) { |i| out << at(i) }
144 alias_method :slice, :[]
146 def []=(idx, ent, *args)
147 Ruby.primitive :array_aset
152 ent = args[0] # 2nd arg (cnt) is the optional one!
158 raise ArgumentError, "Second argument invalid with a range"
161 unless idx.first.respond_to?(:to_int)
162 raise TypeError, "can't convert #{idx.first.class} into Integer"
165 unless idx.last.respond_to?(:to_int)
166 raise TypeError, "can't convert #{idx.last.class} into Integer"
169 lst = idx.last.to_int
173 lst += 1 unless idx.exclude_end?
175 idx = idx.first.to_int
178 raise RangeError if idx < 0
181 # m..n, m > n allowed
182 lst = idx if idx > lst
191 raise IndexError.new("Index #{idx -= @total} out of bounds") if idx < 0
195 # count < 0 not allowed
196 raise IndexError.new("Negative length #{cnt}") if cnt < 0
198 cnt = @total - idx if cnt > @total - idx # MRI seems to be forgiving here!
202 elsif ent.is_a?(Array)
203 replacement = ent.dup
204 elsif ent.respond_to?(:to_ary)
205 replacement = ent.to_ary
210 if replacement.size > cnt
211 newtotal = @total + replacement.size - cnt
212 if newtotal > @tuple.fields - @start
213 nt = Tuple.new(newtotal + 10)
214 nt.copy_from @tuple, @start, 0 # FIXME: double copy of right part
217 end # this should be an else
223 @tuple.put(@start+t, @tuple.at(@start+f))
227 replacement.each_with_index { |el, i|
228 @tuple.put(@start+idx+i, el)
231 if replacement.size < cnt
232 f = @start + idx + cnt
233 t = @start + idx + replacement.size
235 # shift fields to the left
237 @tuple.put(t, @tuple.at(f))
242 # unset any extraneous fields
243 while t < @tuple.fields
248 @total -= (cnt - replacement.size)
254 nt = @start + idx + 1
255 reallocate(nt) if @tuple.size < nt
257 @tuple.put @start + idx, ent
264 # Appends the object to the end of the Array.
265 # Returns self so several appends can be chained.
267 nt = @start + @total + 1
268 reallocate nt if @tuple.size < nt
269 @tuple.put @start + @total, obj
274 # Creates a new Array containing only elements common to
275 # both Arrays, without duplicates. Also known as a 'set
278 other = Type.coerce_to other, Array, :to_ary
280 out, set_include = [], {}
282 other.each { |x| set_include[x] = [true, x] }
284 if set_include[x] and set_include[x].last.eql?(x)
286 set_include[x] = false
293 # Creates a new Array by combining the two Arrays' items,
294 # without duplicates. Also known as a 'set union.'
296 other = Type.coerce_to other, Array, :to_ary
298 out, exclude = [], {}
300 (self + other).each { |x|
310 # Repetition operator when supplied a #to_int argument:
311 # returns a new Array as a concatenation of the given number
312 # of the original Arrays. With an argument that responds to
313 # #to_str, functions exactly like #join instead.
315 if val.respond_to? :to_str
319 # Aaargh stupid MRI's stupid specific stupid error stupid types stupid
320 val = Type.coerce_to val, Fixnum, :to_int
322 raise ArgumentError, "Count cannot be negative" if val < 0
325 val.times { out.push(*self) }
330 # Create a concatenation of the two Arrays.
332 other = Type.coerce_to other, Array, :to_ary
335 each { |e| out << e }
336 other.each { |e| out << e }
341 # Creates a new Array that contains the items of the original
342 # Array that do not appear in the other Array, effectively
343 # 'deducting' those items. The matching method is Hash-based.
345 other = Type.coerce_to other, Array, :to_ary
346 out, exclude = [], {}
348 other.each { |x| exclude[x] = true }
349 each { |x| out << x unless exclude[x] }
354 # Compares the two Arrays and returns -1, 0 or 1 depending
355 # on whether the first one is 'smaller', 'equal' or 'greater'
356 # in relation to the second. Two Arrays are equal only if all
357 # their elements are 0 using first_e <=> second_e and their
358 # lengths are the same. The element comparison is the primary
359 # and length is only checked if the former results in 0's.
361 other = Type.coerce_to other, Array, :to_ary
364 return 1 unless other.size > i
366 diff = at(i) <=> other.at(i)
367 return diff if diff != 0
370 return 1 if size > other.size
371 return -1 if size < other.size
375 # The two Arrays are considered equal only if their
376 # lengths are the same and each of their elements
377 # are equal according to first_e == second_e . Both
378 # Array subclasses and to_ary objects are accepted.
380 unless other.kind_of? Array
381 return false unless other.respond_to? :to_ary
385 return false unless size == other.size
387 size.times { |i| return false unless @tuple.at(@start + i) == other.at(i) }
392 # Assumes the Array contains other Arrays and searches through
393 # it comparing the given object with the first element of each
394 # contained Array using elem == obj. Returns the first contained
395 # Array that matches (the first 'associated' Array) or nil.
397 # FIX: use break when it works again
398 found, res = nil, nil
401 if found.nil? and elem.kind_of? Array and elem.first == obj
402 found, res = true, elem
409 # Returns the element at the given index. If the
410 # index is negative, counts from the end of the
411 # Array. If the index is out of range, nil is
412 # returned. Slightly faster than +Array#[]+
414 Ruby.primitive :array_aref
415 idx = Type.coerce_to idx, Fixnum, :to_int
416 idx += @total if idx < 0
418 return nil if idx < 0 or idx >= @total
420 @tuple.at @start + idx
423 # Removes all elements in the Array and leaves it empty
425 @tuple = Tuple.new(1)
431 # Returns a copy of self with all nil elements removed
436 # Removes all nil elements from self, returns nil if no changes
437 # TODO: Needs improvement
440 tot = @start + @total
442 # Low-level because pretty much anything else breaks everything
449 if @tuple.at(i) != nil
450 @tuple.put j, @tuple.at(i)
457 # OK to leave tuple size larger?
468 # Appends the elements in the other Array to self
470 ary = Type.coerce_to(other, Array, :to_ary)
471 size = @total + ary.size
472 tuple = Tuple.new size
473 tuple.copy_from @tuple, @start, 0 if @total > 0
474 tuple.copy_from ary.tuple, ary.start, @total
481 # Stupid subtle differences prevent proper reuse in these three
483 # Removes all elements from self that are #== to the given object.
484 # If the object does not appear at all, nil is returned unless a
485 # block is provided in which case the value of running it is
489 tot = @start + @total
491 # Leaves the tuple to the original size still
493 if @tuple.at(i) == obj
498 if @tuple.at(i) != obj
499 @tuple.put(j, @tuple.at(i))
513 yield if block_given?
516 # Deletes the element at the given index and returns
517 # the deleted element or nil if the index is out of
518 # range. Negative indices count backwards from end.
520 idx = Type.coerce_to idx, Fixnum, :to_int
522 # Flip to positive and weed out out of bounds
523 idx += @total if idx < 0
524 return nil if idx < 0 or idx >= @total
526 # Grab the object and adjust the indices for the rest
527 obj = @tuple.at(@start + idx)
529 idx.upto(@total - 2) { |i| @tuple.put(@start + i, @tuple.at(@start + i + 1)) }
530 @tuple.put(@start + @total - 1, nil)
536 # Deletes every element from self for which block evaluates to true
539 tot = @total + @start
541 # Leaves the tuple to the original size still
543 if yield @tuple.at(i)
548 unless yield @tuple.at(i)
549 @tuple.put(j, @tuple.at(i))
566 # Passes each element in the Array to the given block
567 # and returns self. We re-evaluate @total each time
568 # through the loop in case the array has changed.
578 # Passes each index of the Array to the given block
579 # and returns self. We re-evaluate @total each time
580 # through the loop in case the array has changed.
590 # Returns true if both are the same object or if both
591 # have the same elements (#eql? used for testing.)
593 return true if equal? other
594 return false unless other.kind_of?(Array)
595 return false if @total != other.size
597 each_with_index { |o, i| return false unless o.eql?(other[i]) }
602 # True if Array has no elements.
607 # Attempts to return the element at the given index. By default
608 # an IndexError is raised if the element is out of bounds. The
609 # user may supply either a default value or a block that takes
610 # the index object instead.
611 def fetch(idx, *rest)
612 raise ArgumentError, "Expected 1-2, got #{1 + rest.length}" if rest.length > 1
613 warn 'Block supercedes default object' if !rest.empty? && block_given?
615 idx, orig = Type.coerce_to(idx, Fixnum, :to_int), idx
616 idx += @total if idx < 0
618 if idx < 0 || idx >= @total
619 return yield(orig) if block_given?
620 return rest.at(0) unless rest.empty?
622 raise IndexError, "Index #{idx} out of array" if rest.empty?
628 # Fill some portion of the Array with a given element. The
629 # element to be used can be either given as the first argument
630 # or as a block that takes the index as its argument. The
631 # section that is to be filled can be defined by the following
632 # arguments. The first following argument is either a starting
633 # index or a Range. If the first argument is a starting index,
634 # the second argument can be the length. No length given
635 # defaults to rest of Array, no starting defaults to 0. Negative
636 # indices are treated as counting backwards from the end. Negative
637 # counts leave the Array unchanged. Returns self.
639 # array.fill(obj) -> array
640 # array.fill(obj, start [, length]) -> array
641 # array.fill(obj, range) -> array
642 # array.fill {|index| block } -> array
643 # array.fill(start [, length]) {|index| block } -> array
644 # array.fill(range) {|index| block } -> array
647 raise ArgumentError, "Wrong number of arguments" if block_given? and args.size > 2
648 raise ArgumentError, "Wrong number of arguments" if args.size > 3
650 # Normalise arguments
651 start, finish, obj = 0, (@total - 1), nil
653 obj = args.shift unless block_given?
654 one, two = args.at(0), args.at(1)
656 if one.kind_of? Range
657 raise TypeError, "Length invalid with range" if args.size > 1 # WTF, MRI, TypeError?
659 start, finish = Type.coerce_to(one.begin, Fixnum, :to_int), Type.coerce_to(one.end, Fixnum, :to_int)
661 start += @total if start < 0
662 finish += @total if finish < 0
665 return self if start == finish
669 raise RangeError, "#{one.inspect} out of range" if start < 0
670 return self if finish < 0 # Nothing to modify
674 start = Type.coerce_to one, Fixnum, :to_int
676 start += @total if start < 0
677 start = 0 if start < 0 # MRI comp adjusts to 0
680 finish = Type.coerce_to two, Fixnum, :to_int
681 raise ArgumentError, "argument too big" if finish < 0 && start < finish.abs
682 return self if finish < 1 # Nothing to modify
684 finish = start + finish - 1
687 end # Argument normalisation
689 # Adjust the size progressively
690 unless finish < @total
692 reallocate(nt) if @tuple.size < nt
697 start.upto(finish) { |i| @tuple.put @start + i, yield(i) }
699 start.upto(finish) { |i| @tuple.put @start + i, obj }
706 # Returns the first or first n elements of the Array.
707 # If no argument is given, returns nil if the item
708 # is not found. If there is an argument, an empty
709 # Array is returned instead.
711 return at(0) unless n
713 n = Type.coerce_to n, Fixnum, :to_int
714 raise ArgumentError, "Size must be positive" if n < 0
716 Array.new(self[0...n])
719 # Recursively flatten any contained Arrays into an one-dimensional result.
724 # Flattens self in place as #flatten. If no changes are
725 # made, returns nil, otherwise self.
729 ret = recursively_flatten(self, out)
734 # Computes a Fixnum hash code for this Array. Any two
735 # Arrays with the same content will have the same hash
736 # code (similar to #eql?)
738 # IMPROVE: This is a really really poor implementation of hash for an array, but
739 # it does work. It should be replaced with something much better, but I'm not sure
740 # what level it belongs at.
742 each { |item| str << item.hash.to_s }
746 # Returns true if the given obj is present in the Array.
747 # Presence is determined by calling elem == obj until found.
749 @total.times { |i| return true if @tuple.at(@start + i) == obj }
753 # Returns the index of the first element in the Array
754 # for which elem == obj is true or nil.
756 @total.times { |i| return i if @tuple.at(@start + i) == obj }
760 # Returns an Array populated with the objects at the given indices of the original.
761 # Range arguments are given as nested Arrays as from #[].
763 warn 'Array#indexes is deprecated, use Array#values_at instead'
771 out << at(Type.coerce_to(a, Fixnum, :to_int))
778 alias_method :indices, :indexes
780 # For a positive index, inserts the given values before
781 # the element at the given index. Negative indices count
782 # backwards from the end and the values are inserted
784 def insert(idx, *items)
785 return self if items.length == 0
787 # Adjust the index for correct insertion
788 idx = Type.coerce_to idx, Fixnum, :to_int
789 idx += (@total + 1) if idx < 0 # Negatives add AFTER the element
790 raise IndexError, "#{idx} out of bounds" if idx < 0
792 self[idx, 0] = items # Cheat
796 # Produces a printable string of the Array. The string
797 # is constructed by calling #inspect on all elements.
798 # Descends through contained Arrays, recursive ones
799 # are indicated as [...].
801 return "[...]" if RecursionGuard.inspecting?(self)
804 RecursionGuard.inspect(self) do
813 # Generates a string from converting all elements of
814 # the Array to strings, inserting a separator between
815 # each. The separator defaults to $,. Detects recursive
817 def join(sep = nil, method = :to_s)
818 return "" if @total == 0
823 raise TypeError, "Cannot convert #{sep.inspect} to str"
831 out << sep unless i == 0
832 if elem.kind_of?(Array)
833 if RecursionGuard.inspecting?(elem)
836 RecursionGuard.inspect(self) do
837 out << elem.join(sep, method)
841 out << elem.__send__(method)
847 # Returns the last element or n elements of self. If
848 # the Array is empty, without a count nil is returned,
849 # otherwise an empty Array. Always returns an Array.
851 return at(-1) unless n
853 n = Type.coerce_to n, Fixnum, :to_int
855 raise ArgumentError, "Number must be positive" if n < 0
858 Array.new self[-n..-1]
861 # Creates a new Array from the return values of passing
862 # each element in self to the supplied block.
865 each { |elem| out << yield(elem) }
869 alias_method :collect, :map
871 # Replaces each element in self with the return value
872 # of passing that element to the supplied block.
877 alias_method :collect!, :map!
879 # Returns number of non-nil elements in self, may be zero
882 each { |elem| sum += 1 unless elem.nil? }
887 def self.after_loaded
888 (00..25).each {|x| BASE_64_B2A[x] = (?A + x - 00).chr}
889 (26..51).each {|x| BASE_64_B2A[x] = (?a + x - 26).chr}
890 (52..61).each {|x| BASE_64_B2A[x] = (?0 + x - 52).chr}
891 BASE_64_B2A[62] = '+'
892 BASE_64_B2A[63] = '/'
897 # arr.pack ( aTemplateString ) -> aBinaryString
899 # Packs the contents of <i>arr</i> into a binary sequence according to
900 # the directives in <i>aTemplateString</i> (see the table below)
901 # Directives ``A,'' ``a,'' and ``Z'' may be followed by a count,
902 # which gives the width of the resulting field. The remaining
903 # directives also may take a count, indicating the number of array
904 # elements to convert. If the count is an asterisk
905 # (``<code>*</code>''), all remaining array elements will be
906 # converted. Any of the directives ``<code>sSiIlL</code>'' may be
907 # followed by an underscore (``<code>_</code>'') to use the underlying
908 # platform's native size for the specified type; otherwise, they use a
909 # platform-independent size. Spaces are ignored in the template
910 # string. See also <code>String#unpack</code>.
912 # a = [ "a", "b", "c" ]
914 # a.pack("A3A3A3") #=> "a b c "
915 # a.pack("a3a3a3") #=> "a\000\000b\000\000c\000\000"
916 # n.pack("ccc") #=> "ABC"
918 # Directives for +pack+.
921 # ---------------------------------------------------------------
922 # @ | Moves to absolute position
923 # A | ASCII string (space padded, count is width)
924 # a | ASCII string (null padded, count is width)
925 # B | Bit string (descending bit order)
926 # b | Bit string (ascending bit order)
929 # D, d | Double-precision float, native format
930 # E | Double-precision float, little-endian byte order
931 # e | Single-precision float, little-endian byte order
932 # F, f | Single-precision float, native format
933 # G | Double-precision float, network (big-endian) byte order
934 # g | Single-precision float, network (big-endian) byte order
935 # H | Hex string (high nibble first)
936 # h | Hex string (low nibble first)
937 # I | Unsigned integer
941 # M | Quoted printable, MIME encoding (see RFC2045)
942 # m | Base64 encoded string
943 # N | Long, network (big-endian) byte order
944 # n | Short, network (big-endian) byte-order
945 # P | Pointer to a structure (fixed-length string)
946 # p | Pointer to a null-terminated string
947 # Q, q | 64-bit number
951 # u | UU-encoded string
952 # V | Long, little-endian byte order
953 # v | Short, little-endian byte order
954 # w | BER-compressed integer\fnm
957 # Z | Same as ``a'', except that null is added with *
960 # The schema is an array of arrays like [["A", "6"], ["u", "*"],
961 # ["X", ""]]. It represents the parsed form of "A6u*X". Remove
962 # strings in the schema between # and \n
963 schema = schema.gsub(/#.*/, '')
964 schema = schema.scan(/([^\s\d\*][\d\*]*)/).flatten.map {|x|
965 x.match(/([^\s\d\*])([\d\*]*)/)[1..-1]
971 schema.each do |kind, t|
972 # p :iter => [kind, t]
976 # MRI nil compatibilty for string functions
977 item = "" if item.nil? && kind =~ /[aAZbBhH]/
979 # if there's no item, that means there's more schema items than
980 # array items, so throw an error. All actions that DON'T
981 # increment arr_idx must occur before this test.
982 raise ArgumentError, "too few array elements" if
983 arr_idx >= self.length and kind !~ /x/i
985 case kind # TODO: switch kind to ints
988 raise ArgumentError, "you're backing up too far" if size > ret.size
989 ret[-size..-1] = '' if size > 0
995 4.times do # TODO: const?
996 parts << (item % 256).chr
999 ret << parts.reverse.join
1005 4.times do # TODO: const?
1006 parts << (item % 256).chr
1014 parts << (item % 256).chr
1019 when 'a', 'A', 'Z' then
1020 item = Type.coerce_to(item, String, :to_str)
1025 item.size + (kind == "Z" ? 1 : 0)
1030 padsize = size - item.size
1031 filler = kind == "A" ? " " : "\0"
1033 ret << item.split(//).first(size).join
1034 ret << filler * padsize if padsize > 0
1038 item = Type.coerce_to(item, String, :to_str)
1050 bits = item.split(//).map { |c| c[0] & 01 }
1051 min = [size, item.size].min
1053 bits.first(min).each_with_index do |bit, i| # TODO: this can be cleaner
1056 byte |= bit << (lsb ? i : 07 - i)
1064 # always output an incomplete byte
1065 if ((size & 07) != 0 || min != size) && item.size > 0 then
1069 # Emulate the weird MRI spec for every 2 chars over output a \000 # FIX
1070 (item.length).step(size-1, 2) { |i| ret << 0 } if size > item.length
1078 self.size # TODO: - arr_idx?
1083 # FIX: uhh... size is the same as length. just tests that arr_idx == 0
1084 raise ArgumentError, "too few array elements" if
1085 arr_idx + size > self.length
1087 sub = self[arr_idx...arr_idx+size]
1088 sub.map! { |o| (Type.coerce_to(o, Integer, :to_int) & 0xff).chr }
1093 # for some reason MRI responds to to_s here
1094 item = Type.coerce_to(item, String, :to_s)
1095 ret << item.scan(/.{1,73}/m).map { |line| # 75 chars per line incl =\n
1096 line.gsub(/[^ -<>-~\t\n]/) { |m| "=%02X" % m[0] } + "=\n"
1099 when 'm' then # REFACTOR: merge with u
1100 item = Type.coerce_to(item, String, :to_str)
1102 ret << item.scan(/.{1,45}/m).map { |line|
1103 encoded = line.scan(/(.)(.?)(.?)/m).map { |a,b,c|
1108 [BASE_64_B2A[( a >> 2 ) & 077],
1109 BASE_64_B2A[((a << 4) | ((b >> 4) & 017)) & 077],
1110 BASE_64_B2A[((b << 2) | ((c >> 6) & 003)) & 077],
1111 BASE_64_B2A[( c ) & 077]]
1114 "#{encoded.flatten.join}\n"
1115 }.join.sub(/(A{1,2})\n\Z/) { "#{'=' * $1.size}\n" }
1119 item = Type.coerce_to(item, Integer, :to_i)
1120 raise ArgumentError, "can't compress negative numbers" if item < 0
1122 ret << (item & 0x7f)
1123 while (item >>= 7) > 0 do
1124 ret << ((item & 0x7f) | 0x80)
1127 ret.reverse! # FIX - breaks anything following BER?
1129 when 'u' then # REFACTOR: merge with m
1130 item = Type.coerce_to(item, String, :to_str)
1132 # http://www.opengroup.org/onlinepubs/009695399/utilities/uuencode.html
1133 ret << item.scan(/.{1,45}/m).map { |line|
1134 encoded = line.scan(/(.)(.?)(.?)/m).map { |a,b,c|
1139 [(?\s + (( a >> 2 ) & 077)).chr,
1140 (?\s + (((a << 4) | ((b >> 4) & 017)) & 077)).chr,
1141 (?\s + (((b << 2) | ((c >> 6) & 003)) & 077)).chr,
1142 (?\s + (( c ) & 077)).chr]
1145 "#{(line.size + ?\s).chr}#{encoded.join}\n"
1146 }.join.gsub(/ /, '`')
1148 when 'i', 's', 'l', 'n', 'I', 'S', 'L' then
1158 native = t && t == '_'
1159 unsigned = (kind =~ /I|S|L/)
1160 little_endian = kind !~ /n/i && endian?(:little)
1162 raise "unsupported - fix me" if native
1174 item = Type.coerce_to(self[arr_idx], Integer, :to_i)
1176 # MRI seems only only raise RangeError at 2**32 and above, even shorts
1177 raise RangeError, "bignum too big to convert into 'unsigned long'" if
1178 item.abs >= 2**32 # FIX: const
1180 ret << if little_endian then
1181 item += 2 ** (8 * bytes) if item < 0
1182 (0...bytes).map { |b| ((item >> (b * 8)) & 0xFF).chr }
1184 (0...bytes).map {n=(item % 256).chr;item /= 256; n}.reverse
1196 str = item.scan(/..?/).first(size)
1198 ret << if kind == "h" then
1199 str.map { |b| b.reverse.hex.chr }.join
1201 str.map { |b| b. hex.chr }.join
1206 count = if t.nil? then
1214 raise ArgumentError, "too few array elements" if
1215 arr_idx + count > self.length
1218 item = Type.coerce_to(self[arr_idx], Integer, :to_i)
1220 raise RangeError, "pack(U): value out of range" if item < 0
1223 f = [2 ** 7, 2 ** 11, 2 ** 16, 2 ** 21, 2 ** 26, 2 ** 31].find { |n|
1228 raise RangeError, "pack(U): value out of range" if f.nil?
1240 buf.unshift((item | 0x80) & 0xBF)
1243 # catch the highest bits - the mask depends on the byte count
1244 buf.unshift(item | ((0x3F00 >> bytes)) & 0xFC)
1247 ret << buf.map { |n| n.chr }.join
1252 raise ArgumentError, "Unknown kind #{kind}"
1259 # Removes and returns the last element from the Array.
1261 return nil if empty?
1263 # TODO Reduce tuple if there are a lot of free slots
1270 # Rubinius-only, better inspect representation of the Array
1271 def indented_inspect(indent = 0)
1272 # Here there be dragons. In fact, there is one jusAAAAAAAARGH
1280 if Array === element
1281 estr = element.indented_inspect(indent + 2)
1282 if str.size > 30 or estr.size > 30
1284 estr = "#{' ' * (indent + 2)}#{estr}"
1293 str << element.inspect
1296 str << ", " unless i == lst
1301 str << "\n#{' ' * indent}]"
1307 return "#{' ' * indent}#{str}"
1313 # Appends the given object(s) to the Array and returns
1314 # the modified self.
1316 args.each { |ent| self << ent }
1320 # Searches through contained Arrays within the Array,
1321 # comparing obj with the second element of each using
1322 # elem == obj. Returns the first matching Array, nil
1323 # on failure. See also Array#assoc.
1325 # FIX: Use break when it works
1326 found, res = nil, nil
1329 if found.nil? and elem.kind_of? Array and elem.at(1) == obj
1330 res, found = elem, true
1337 # Returns a new Array by removing items from self for
1338 # which block is true. An Array is also returned when
1339 # invoked on subclasses. See #reject!
1341 Array.new(self).reject!(&block) || self
1344 # Equivalent to #delete_if except that returns nil if
1345 # no changes were made.
1348 self.delete_if(&block)
1350 self if was != length # Too clever?
1353 # Replaces contents of self with contents of other,
1354 # adjusting size as needed.
1356 other = Type.coerce_to other, Array, :to_ary
1358 @tuple = other.tuple.dup
1359 @total = other.total
1360 @start = other.start
1364 # Returns a new Array or subclass populated from self
1365 # but in reverse order.
1370 # Reverses the order of elements in self. Returns self
1371 # even if no changes are made
1373 return self unless @total > 1
1375 tuple = Tuple.new @total
1377 (@start + @total - 1).downto(@start) do |j|
1378 tuple.put i, @tuple.at(j)
1388 # Goes through the Array back to front and yields
1389 # each element to the supplied block. Returns self.
1391 (@total - 1).downto(0) { |i| yield(at(i)) }
1395 # Returns the index of the last element in the Array
1396 # for which elem == obj is true.
1398 (@total - 1).downto(0) { |i| return i if at(i) == obj }
1402 # Removes and returns the first element in the
1403 # Array or nil if empty. All other elements are
1404 # moved down one index.
1406 return nil if @total == 0
1408 obj = @tuple.at(@start)
1415 # Deletes the element(s) given by an index (optionally with a length)
1416 # or by a range. Returns the deleted object, subarray, or nil if the
1417 # index is out of range. Equivalent to:
1420 if !(Range === args[0])
1421 # make sure that negative values are not passed through to the
1423 args[0] = Type.coerce_to args[0], Integer, :to_int
1424 args[0] = args[0] + self.length if args[0] < 0
1425 # This is to match the MRI behaviour of not extending the array
1426 # with nil when specifying an index greater than the length
1429 return out unless args[0] >= 0 && args[0] < self.length
1437 # Returns a new Array created by sorting elements in self. Comparisons
1438 # to sort are either done using <=> or a block may be provided. The
1439 # block must take two parameters and return -1, 0, 1 depending on
1440 # whether the first parameter is smaller, equal or larger than the
1446 # Sorts this Array in-place. See #sort.
1448 return self unless @total > 1
1450 if (@total - @start) < 6
1452 isort_block! @start, (@total - 1), block
1454 isort! @start, (@total - 1)
1467 # Returns self except on subclasses which are converted
1468 # or 'upcast' to Arrays.
1470 if self.class == Array
1473 Array.new(self[0..-1])
1482 # Produces a string by joining all elements without a
1483 # separator. See #join
1488 # Treats all elements as being Arrays of equal lengths and
1489 # transposes their rows and columns so that the first contained
1490 # Array in the result includes the first elements of all the
1498 ary = Type.coerce_to ary, Array, :to_ary
1501 # Catches too-large as well as too-small (for which #fetch would suffice)
1502 raise IndexError, "All arrays must be same length" if ary.size != max
1504 ary.size.times { |i| (out[i] ||= []) << ary.at(i) }
1510 # Returns a new Array by removing duplicate entries
1511 # from self. Equality is determined by using a Hash
1513 seen, out = {}, self.class.new
1516 out << elem unless seen[elem]
1523 # Removes duplicates from the Array in place as #uniq
1526 replace(ary) if size != ary.size
1529 # # Inserts the element to the front of the Array and
1530 # # moves the other elements up by one index.
1532 # raise TypeError, "Array is frozen" if frozen?
1533 # return self if val.empty?
1539 # Returns a new Array populated from the elements in
1540 # self corresponding to the given selector(s.) The
1541 # arguments may be one or more integer indices or
1543 def values_at(*args)
1547 # Cannot use #[] because of subtly different errors
1548 if elem.kind_of? Range
1552 start, finish = Type.coerce_to(start, Fixnum, :to_int), Type.coerce_to(finish, Fixnum, :to_int)
1554 start += @total if start < 0
1557 finish += @total if finish < 0
1558 finish -= 1 if elem.exclude_end?
1559 finish = @total unless finish < @total
1561 next if finish < start
1563 start.upto(finish) { |i| out << at(i) }
1566 i = Type.coerce_to elem, Fixnum, :to_int
1574 # Interleaves all given :to_ary's so that the n-th element of each
1575 # Array is inserted into the n-th subarray of the returned Array.
1576 # If a block is provided, then each subarray is passed to it
1577 # instead. The maximum number of subarrays and therefore elements
1578 # used is the size of self. Missing indices are filled in with
1579 # nils and any elements past self.size in the other Arrays are
1582 out = Array.new(size) { [] }
1583 others = others.map { |ary| ary.to_a }
1587 slot << @tuple.at(@start + i)
1588 others.each { |ary| slot << ary.at(i) }
1592 out.each { |ary| yield ary }
1607 def unshift(*values)
1608 while values.size > 0 && @start > 0
1611 @tuple.put @start, values.pop
1614 @tuple = @tuple.shifted(values.size)
1616 values.each_with_index do |val, i|
1620 @total += values.size
1624 # Exactly the same as #replace but private
1625 def initialize_copy(other)
1629 private :initialize_copy
1631 # Reallocates the internal Tuple to accommodate at least given size
1632 def reallocate(at_least)
1633 return if at_least < @tuple.size
1635 new_size = @tuple.size * 2
1637 if new_size < at_least
1641 tuple = Tuple.new(new_size)
1643 tuple.copy_from @tuple, @start, 0 # Swap over old data
1651 # Helper to recurse through flattening since the method
1652 # is not allowed to recurse itself. Detects recursive structures.
1653 def recursively_flatten(array, out, recursive_placeholder = Undefined)
1654 if RecursionGuard.inspecting?(array)
1655 if recursive_placeholder.equal? Undefined
1656 raise ArgumentError, "tried to flatten recursive array"
1658 out << recursive_placeholder
1666 RecursionGuard.inspect(array) do
1667 recursively_flatten(o, out, recursive_placeholder)
1678 private :recursively_flatten
1680 def remove_outer_arrays(array=self)
1681 if RecursionGuard.inspecting?(array)
1683 elsif array.size == 1 && array.first.kind_of?(Array)
1685 RecursionGuard.inspect(array) do
1686 new_array = remove_outer_arrays(array.first)
1694 private :remove_outer_arrays
1697 MEDIAN_THRESHOLD = 11
1699 # In-place non-recursive sort between the given indexes.
1701 # Stack stores the indexes that still need sorting
1703 left_end, right_end = @start, (@total - 1)
1705 # We are either processing a 'new' partition or one that
1706 # was saved to stack earlier.
1708 left, right = left_end, (right_end - 1) # Leave room for pivot
1709 eqls_left, eqls_right = (left_end - 1), right_end
1711 # Choose pivot from median
1712 # CAUTION: This is NOT the same as #qsort_block!
1713 middle = left_end + ((right_end - left_end) / 2)
1714 low, mid, hi = @tuple.at(left_end), @tuple.at(middle), @tuple.at(right_end)
1716 segment_size = right_end - left_end
1718 # "Heuristic" to avoid problems with reverse-sorted
1719 if segment_size > 1000 and (low <=> mid) == 1 and (mid <=> hi) == 1
1720 semi_left = @tuple.at(left_end + ((middle - left_end) / 2))
1721 semi_right = @tuple.at(middle + ((right_end - middle) / 2))
1723 if (low <=> semi_left) == 1 and (semi_left <=> mid) == 1 and
1724 (mid <=> semi_right) == 1 and (semi_right <=> hi) == 1
1726 r = segment_size / 4
1728 @tuple.swap(rand(segment_size), rand(segment_size))
1732 middle += (right_end - middle) / 2
1736 # These can be reordered which may help with sorting randomly
1737 # distributed elements at the cost of presorted. Be sure to
1738 # mark down the correct order though..
1739 @tuple.swap(left_end, right_end) if (hi <=> low) < 0
1740 @tuple.swap(left_end, middle) if (mid <=> low) < 0
1741 @tuple.swap(middle, right_end) if (hi <=> mid) < 0
1743 pivot = @tuple.at(middle)
1744 @tuple.swap(right_end, middle) # Known position to help partition three ways
1748 while left < right_end
1749 case @tuple.at(left) <=> pivot
1753 @tuple.swap(left, (eqls_left += 1))
1760 while right > left_end
1761 case @tuple.at(right) <=> pivot
1765 @tuple.swap(right, (eqls_right -= 1))
1772 break if left >= right
1773 @tuple.swap(left, right)
1776 # Move pivot back to the middle
1777 @tuple.swap(left, right_end)
1778 left, right = (left - 1), (left + 1)
1780 # Move the stashed == pivot elements back to the middle
1781 while eqls_left >= left_end
1782 @tuple.swap(eqls_left, left)
1787 unless right >= right_end
1788 while eqls_right < right_end
1789 @tuple.swap(eqls_right, right)
1795 # Continue processing the now smaller partitions or if
1796 # done with this segment, restore a stored one from the
1797 # stack until nothing remains either way.
1798 left_size, right_size = (left - left_end), (right_end - right)
1800 # Insertion sort is faster at anywhere below 7-9 elements
1801 if left_size < ISORT_THRESHOLD
1802 isort! left_end, left
1804 # We can restore next saved if both of these are getting sorted
1805 if right_size < ISORT_THRESHOLD
1806 isort! right, right_end
1807 break if stack.empty? # Completely done, no stored ones left either
1808 left_end, right_end = stack.pop
1813 elsif right_size < ISORT_THRESHOLD
1814 isort! right, right_end
1817 # Save whichever is the larger partition and do the smaller first
1819 if left_size > right_size
1820 stack.push [left_end, left]
1823 stack.push [right, right_end]
1830 # In-place non-recursive sort between the given indexes using a block.
1831 def qsort_block!(block)
1832 # Stack stores the indexes that still need sorting
1834 left_end, right_end = @start, (@total - 1)
1836 # We are either processing a 'new' partition or one that
1837 # was saved to stack earlier.
1839 left, right = left_end, (right_end - 1) # Leave room for pivot
1840 eqls_left, eqls_right = (left_end - 1), right_end
1842 # Choose pivot from median
1843 # CAUTION: This is NOT the same as #qsort!
1844 middle = left_end + ((right_end - left_end) / 2)
1845 low, mid, hi = @tuple.at(left_end), @tuple.at(middle), @tuple.at(right_end)
1847 segment_size = right_end - left_end
1849 # "Heuristic" for reverse-sorted
1850 if segment_size > 1000 and block.call(low, mid) == 1 and block.call(mid, hi) == 1
1851 semi_left = @tuple.at(left_end + ((middle - left_end) / 2))
1852 semi_right = @tuple.at(middle + ((right_end - middle) / 2))
1854 if block.call(low, semi_left) == 1 and block.call(semi_left, mid) == 1 and
1855 block.call(mid, semi_right) == 1 and block.call(semi_right, hi) == 1
1857 r = segment_size / 4
1859 @tuple.swap(rand(segment_size), rand(segment_size))
1863 middle += (right_end - middle) / 2
1867 # These can be reordered which may help with sorting randomly
1868 # distributed elements at the cost of presorted. Be sure to
1869 # mark down the correct order though..
1870 @tuple.swap(left_end, right_end) if block.call(hi, low) < 0
1871 @tuple.swap(left_end, middle) if block.call(mid, low) < 0
1872 @tuple.swap(middle, right_end) if block.call(hi, mid) < 0
1874 pivot = @tuple.at(middle)
1875 @tuple.swap(right_end, middle) # Known position to help partition three ways
1879 while left < right_end
1880 case block.call(@tuple.at(left), pivot)
1884 @tuple.swap(left, (eqls_left += 1))
1891 while right > left_end
1892 case block.call(@tuple.at(right), pivot)
1896 @tuple.swap(right, (eqls_right -= 1))
1903 break if left >= right
1904 @tuple.swap(left, right)
1907 # Move pivot back to the middle
1908 @tuple.swap(left, right_end)
1909 left, right = (left - 1), (left + 1)
1911 # Move the stashed == pivot elements back to the middle
1912 while eqls_left >= left_end
1913 @tuple.swap(eqls_left, left)
1918 unless right >= right_end
1919 while eqls_right < right_end
1920 @tuple.swap(eqls_right, right)
1926 # Continue processing the now smaller partitions or if
1927 # done with this segment, restore a stored one from the
1928 # stack until nothing remains either way.
1929 left_size, right_size = (left - left_end), (right_end - right)
1931 # Insertion sort is faster at anywhere below 7-9 elements
1932 if left_size < ISORT_THRESHOLD
1933 isort_block! left_end, left, block
1935 # We can restore next saved if both of these are getting sorted
1936 if right_size < ISORT_THRESHOLD
1937 isort_block! right, right_end, block
1938 break if stack.empty? # Completely done, no stored ones left either
1939 left_end, right_end = stack.pop
1944 elsif right_size < ISORT_THRESHOLD
1945 isort_block! right, right_end, block
1948 # Save whichever is the larger partition and do the smaller first
1950 if left_size > right_size
1951 stack.push [left_end, left]
1954 stack.push [right, right_end]
1961 # Insertion sort in-place between the given indexes.
1962 def isort!(left, right)
1968 while j > 0 and (@tuple.at(j - 1) <=> @tuple.at(j)) > 0
1969 @tuple.swap(j, (j - 1))
1977 # Insertion sort in-place between the given indexes using a block.
1978 def isort_block!(left, right, block)
1984 while j > 0 and block.call(@tuple.at(j - 1), @tuple.at(j)) > 0
1985 @tuple.swap(j, (j - 1))
1995 private :qsort_block
1996 private :isort_block