module Rubinius class Tuple include Enumerable def self.new(cnt) Rubinius.primitive :tuple_allocate raise PrimitiveFailure, "Tuple.new primitive failed" end def self.pattern(size, obj) Rubinius.primitive :tuple_pattern raise PrimitiveFailure, "Tuple.pattern primitive failed" end def [](idx) Rubinius.primitive :tuple_at unless idx.kind_of? Fixnum raise TypeError, "Only Fixnums valid for Tuple indices" end raise PrimitiveFailure, "Tuple#[] primitive failed" end alias_method :at, :[] def []=(idx, val) Rubinius.primitive :tuple_put unless idx.kind_of? Fixnum raise TypeError, "Only Fixnums valid for Tuple indices" end raise PrimitiveFailure, "Tuple#[]= primitive failed" end alias_method :put, :[]= def fields Rubinius.primitive :tuple_fields raise PrimitiveFailure, "Tuple#fields primitive failed" end def copy_from(other, start, length, dest) Rubinius.primitive :tuple_copy_from unless other.kind_of? Rubinius::Tuple raise TypeError, "Tuple#copy_from was expecting a Tuple, not a #{other.class}" end start = Rubinius::Type.coerce_to start, Fixnum, :to_i length = Rubinius::Type.coerce_to length, Fixnum, :to_i dest = Rubinius::Type.coerce_to dest, Fixnum, :to_i if start < 0 || start > other.fields raise IndexError, "Start %d is out of bounds %d" % [start, other.fields] end if dest < 0 || dest > self.fields raise IndexError, "Destination %d is out of bounds %d" % [dest, self.fields] end if length < 0 raise IndexError, "length %d must be positive" % [length] end if (start + length) > other.fields raise IndexError, "end index %d can not exceed size of source %d" % [start+length, other.fields] end if length > (self.fields - dest) raise IndexError, "length %d can not exceed space %d in destination" % [length, self.fields - dest] end src = start dst = dest while src < (start + length) put dst, other.at(src) src += 1 dst += 1 end self end def dup Rubinius.primitive :tuple_dup obj = Rubinius::Type.object_class(self).new(self.size) Rubinius.invoke_primitive :object_copy_object, obj, self Rubinius.privately do obj.initialize_copy self end obj end def delete(start, length, object) Rubinius.primitive :tuple_delete_inplace start = Rubinius::Type.coerce_to start, Fixnum, :to_i length = Rubinius::Type.coerce_to length, Fixnum, :to_i lend = start rend = lend + length return 0 if self.fields == 0 if lend < 0 || lend >= self.fields raise IndexError, "Index %d is not within Tuple of length %d" % [lend, self.fields] end if rend < 0 || rend > self.fields raise IndexError, "Index %d is not within Tuple of length %d" % [rend, self.fields] end i = lend while i < rend if at(i) == object j = i i += 1 while i < rend val = at(i) if val != object put j, val j += 1 end i += 1 end # cleanup all the remaining bins in the tuple i = j while i < rend put i, nil i += 1 end return rend - j end i += 1 end return 0 end def reverse!(start, total) Rubinius.primitive :tuple_reverse reverse( Rubinius::Type.coerce_to(start, Fixnum, :to_i), Rubinius::Type.coerce_to(total, Fixnum, :to_i)) end def delete_at_index(index) return if size == 1 s = size - 1 t = self.class.new s if index != 0 t.copy_from self, 0, index, 0 end if index < s t.copy_from self, index+1, s-index, index end t end def insert_at_index(index, value) t = self.class.new size + 1 if index != 0 t.copy_from self, 0, index, 0 end if index < size t.copy_from self, index, size-index, index+1 end t[index] = value t end def self.[](*args) args.to_tuple end def to_s "#<#{self.class}:0x#{object_id.to_s(16)} #{fields} elements>" end def each i = 0 t = fields while i < t yield at(i) i += 1 end self end def ==(tup) return super unless tup.kind_of?(Tuple) t = fields() return false unless t == tup.size i = 0 while i < t return false unless at(i) == tup.at(i) i += 1 end return true end def +(o) t = Tuple.new(size + o.size) t.copy_from(self, 0, size, 0) t.copy_from(o, 0, o.size, size) t end def inspect str = "#<#{self.class}" if fields == 0 str << " empty>" else str << ": #{join(", ", :inspect)}>" end return str end def join(sep, meth=:to_s) join_upto(sep, fields, meth) end def join_upto(sep, count, meth=:to_s) str = "" return str if count == 0 or empty? count = fields if count >= fields count -= 1 i = 0 while i < count str.append at(i).__send__(meth) str.append sep.dup i += 1 end str.append at(count).__send__(meth) return str end def ===(other) return false unless Tuple === other and fields == other.fields i = 0 while i < fields return false unless at(i) === other.at(i) i += 1 end true end def to_a array = Array.allocate m = Rubinius::Mirror.reflect array m.tuple = dup m.total = fields array end alias_method :to_ary, :to_a def shift return self unless fields > 0 t = Tuple.new(fields-1) t.copy_from self, 1, fields-1, 0 return t end # Swap elements of the two indexes. def swap(a, b) temp = at(a) put a, at(b) put b, temp end alias_method :size, :fields alias_method :length, :fields def empty? size == 0 end def first at(0) end def last at(fields - 1) end # Marshal support - _dump / _load are deprecated so eventually we should figure # out a better way. def _dump(depth) # TODO use depth Marshal.dump to_a end def self._load(str) ary = Marshal.load(str) t = new(ary.size) ary.each_with_index { |obj, idx| t[idx] = obj } return t end end class RTuple < Tuple include Enumerable def self.new(cnt) Rubinius.primitive :rtuple_allocate raise PrimitiveFailure, "RTuple.new primitive failed" end def [](idx) Rubinius.primitive :rtuple_at unless idx.kind_of? Fixnum raise TypeError, "Only Fixnums valid for RTuple indices" end raise PrimitiveFailure, "RTuple#[] primitive failed" end alias_method :at, :[] def []=(idx, val) Rubinius.primitive :rtuple_put unless idx.kind_of? Fixnum raise TypeError, "Only Fixnums valid for RTuple indices" end raise PrimitiveFailure, "RTuple#[]= primitive failed" end alias_method :put, :[]= end end