Skip to content

Commit

Permalink
Clean Array by using a Reflector (simple mirror)
Browse files Browse the repository at this point in the history
Array left it's guts exposed to deal with the way that data has to be
exposed in ruby. To clean things up, I've introduce a Reflector class
which is a simple mirror. This can easily access the internal data of an
object. Additionally, rather than using instance_variable_[get|set] the
JIT can make access quite fast because it doesn't have to deal with any
edgecases.
  • Loading branch information
evanphx committed Dec 5, 2012
1 parent 2dd95c3 commit 0b96773
Show file tree
Hide file tree
Showing 15 changed files with 187 additions and 46 deletions.
3 changes: 1 addition & 2 deletions kernel/bootstrap/array.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# -*- encoding: us-ascii -*-

class Array
attr_accessor :tuple
attr_accessor :start
attr_reader :tuple

attr_reader_specific :total, :size
alias_method :length, :size
Expand Down
8 changes: 3 additions & 5 deletions kernel/bootstrap/array18.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,19 @@ class Array
# each element in self to the supplied block.
def map
return dup unless block_given?
out = Array.new size
tup = Rubinius::Tuple.new size

i = @start
total = i + @total
tuple = @tuple

out_tuple = out.tuple

j = 0
while i < total
out_tuple[j] = yield tuple.at(i)
tup[j] = yield tuple.at(i)
i += 1
j += 1
end

out
Array.wrap tup, size
end
end
8 changes: 3 additions & 5 deletions kernel/bootstrap/array19.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,19 @@ class Array
# each element in self to the supplied block.
def map
return to_enum :map unless block_given?
out = Array.new size
out = Rubinius::Tuple.new size

i = @start
total = i + @total
tuple = @tuple

out_tuple = out.tuple

j = 0
while i < total
out_tuple[j] = yield tuple.at(i)
out[j] = yield tuple.at(i)
i += 1
j += 1
end

out
Array.wrap out, size
end
end
1 change: 1 addition & 0 deletions kernel/bootstrap/load_order18.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ process.rbc
regexp.rbc
rubinius.rbc
constant_scope.rbc
reflector.rbc
string.rbc
symbol.rbc
thread.rbc
Expand Down
1 change: 1 addition & 0 deletions kernel/bootstrap/load_order19.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ regexp.rbc
regexp19.rbc
rubinius.rbc
constant_scope.rbc
reflector.rbc
string.rbc
string19.rbc
symbol.rbc
Expand Down
19 changes: 19 additions & 0 deletions kernel/bootstrap/reflector.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module Rubinius
class Reflector

def self.new(target)
Rubinius.primitive :reflector_create
raise PrimitiveFailure, "Reflector.new failed"
end

def get(ivar)
Rubinius.primitive :reflector_get
raise PrimitiveFailure, "Reflector#get failed"
end

def set(ivar, name)
Rubinius.primitive :reflector_set
raise TypeError, "'#{name}' is not a ivar name"
end
end
end
24 changes: 16 additions & 8 deletions kernel/common/array.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ def initialize(size_or_array=undefined, obj=undefined)
if size_or_array.respond_to? :to_ary
ary = Rubinius::Type.coerce_to size_or_array, Array, :to_ary

@tuple = ary.tuple.dup
@start = ary.start
ref = Rubinius::Reflector.new ary

@tuple = ref.get(:@tuple).dup
@start = ref.get :@start
@total = ary.size

return self
Expand Down Expand Up @@ -70,9 +72,11 @@ def replace(other)

other = Rubinius::Type.coerce_to other, Array, :to_ary

@tuple = other.tuple.dup
ref = Rubinius::Reflector.new other

@tuple = ref.get(:@tuple).dup
@start = ref.get(:@start)
@total = other.size
@start = other.start

Rubinius::Type.infect(self, other)
self
Expand Down Expand Up @@ -249,11 +253,13 @@ def ==(other)
return false unless size == other.size

Thread.detect_recursion self, other do
ref = Rubinius::Reflector.new other

md = @tuple
od = other.tuple
od = ref.get :@tuple

i = @start
j = other.start
j = ref.get :@start

total = i + @total

Expand Down Expand Up @@ -1077,9 +1083,11 @@ def recursively_flatten(array, out, max_levels = -1)

max_levels -= 1
recursion = Thread.detect_recursion(array) do
i = array.start
ref = Rubinius::Reflector.new array

i = ref.get :@start
total = i + array.size
tuple = array.tuple
tuple = ref.get :@tuple

while i < total
o = tuple.at i
Expand Down
35 changes: 24 additions & 11 deletions kernel/common/array18.rb
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ def set_index(index, ent, fin=undefined)
when 0
# nothing
else
new_tuple.copy_from(replacement.tuple, replacement.start,
ref = Rubinius::Reflector.new replacement
new_tuple.copy_from(ref.get(:@tuple), ref.get(:@start),
replace_count, index)
end

Expand Down Expand Up @@ -150,7 +151,8 @@ def set_index(index, ent, fin=undefined)
when 0
# nothing
else
@tuple.copy_from(replacement.tuple, replacement.start,
ref = Rubinius::Reflector.new replacement
@tuple.copy_from(ref.get(:@tuple), ref.get(:@start),
replace_count, @start + index)
end

Expand Down Expand Up @@ -466,8 +468,11 @@ def uniq!
Rubinius.check_frozen

array = im.to_array
@tuple = array.tuple
@start = array.start

ref = Rubinius::Reflector.new array

@tuple = ref.get :@tuple
@start = ref.get :@start
@total = array.size

self
Expand Down Expand Up @@ -496,19 +501,27 @@ def unshift(*values)

Rubinius.check_frozen

if @start > values.size
sz = values.size

if @start > sz
# fit the new values in between 0 and @start if possible
@start -= values.size
@tuple.copy_from(values.tuple, 0, values.size, @start)
@start -= sz
if sz == 1
@tuple.put @start, values.at(0)
else
ref = Rubinius::Reflector.new values
@tuple.copy_from(ref.get(:@tuple), 0, sz, @start)
end
else
new_tuple = Rubinius::Tuple.new @total + values.size
new_tuple.copy_from values.tuple, 0, values.size, 0
new_tuple.copy_from @tuple, @start, @total, values.size
ref = Rubinius::Reflector.new values
new_tuple = Rubinius::Tuple.new @total + sz
new_tuple.copy_from ref.get(:@tuple), 0, sz, 0
new_tuple.copy_from @tuple, @start, @total, sz
@start = 0
@tuple = new_tuple
end

@total += values.size
@total += sz
self
end
end
42 changes: 30 additions & 12 deletions kernel/common/array19.rb
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ def set_index(index, ent, fin=undefined)
when 0
# nothing
else
new_tuple.copy_from(replacement.tuple, replacement.start,
ref = Rubinius::Reflector.new replacement

new_tuple.copy_from(ref.get(:@tuple), ref.get(:@start),
replace_count, index)
end

Expand Down Expand Up @@ -125,7 +127,9 @@ def set_index(index, ent, fin=undefined)
when 0
# nothing
else
@tuple.copy_from(replacement.tuple, replacement.start,
ref = Rubinius::Reflector.new(replacement)

@tuple.copy_from(ref.get(:@tuple), ref.get(:@start),
replace_count, @start + index)
end

Expand Down Expand Up @@ -461,11 +465,14 @@ def sample(*args)
return at(random_generator.rand(size)) unless n

n = size if n > size

result = Array.new(self)
ref = Rubinius::Reflector.new(result)
tuple = ref.get(:@tuple)

n.times do |i|
r = i + random_generator.rand(size - i)
result.tuple.swap(i, r)
tuple.swap(i, r)
end

result[n..size] = []
Expand Down Expand Up @@ -629,8 +636,11 @@ def uniq!(&block)
return if im.size == size

array = im.to_array
@tuple = array.tuple
@start = array.start

ref = Rubinius::Reflector.new array

@tuple = ref.get(:@tuple)
@start = ref.get(:@start)
@total = array.size

self
Expand Down Expand Up @@ -667,19 +677,27 @@ def unshift(*values)

return self if values.empty?

if @start > values.size
sz = values.size

if @start > sz
# fit the new values in between 0 and @start if possible
@start -= values.size
@tuple.copy_from(values.tuple, 0, values.size, @start)
@start -= sz
if sz == 1
@tuple.put @start, values.at(0)
else
ref = Rubinius::Reflector.new values
@tuple.copy_from(ref.get(:@tuple), 0, sz, @start)
end
else
new_tuple = Rubinius::Tuple.new @total + values.size
new_tuple.copy_from values.tuple, 0, values.size, 0
new_tuple.copy_from @tuple, @start, @total, values.size
ref = Rubinius::Reflector.new values
new_tuple = Rubinius::Tuple.new @total + sz
new_tuple.copy_from ref.get(:@tuple), 0, sz, 0
new_tuple.copy_from @tuple, @start, @total, sz
@start = 0
@tuple = new_tuple
end

@total += values.size
@total += sz
self
end
end
7 changes: 5 additions & 2 deletions kernel/common/tuple.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ class Tuple
include Enumerable

def self.[](*args)
start = args.start
ref = Rubinius::Reflector.new args
start = ref.get(:@start)
tuple = ref.get(:@tuple)

tot = args.size
return new(tot).copy_from(args.tuple, start, tot, 0)
return new(tot).copy_from(tuple, start, tot, 0)
end

def to_s
Expand Down
1 change: 1 addition & 0 deletions rakelib/vm.rake
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ field_extract_headers = %w[
vm/builtin/cache.hpp
vm/builtin/atomic.hpp
vm/builtin/character.hpp
vm/builtin/reflector.hpp
]

transcoders_src_dir = File.expand_path "../../vendor/oniguruma/enc/trans", __FILE__
Expand Down
41 changes: 41 additions & 0 deletions vm/builtin/reflector.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include "vm.hpp"
#include "vm/object_utils.hpp"

#include "builtin/object.hpp"

#include "builtin/reflector.hpp"

#include "ontology.hpp"
#include "builtin/class.hpp"
#include "builtin/symbol.hpp"
#include "primitives.hpp"

namespace rubinius {
void Reflector::init(STATE) {
GO(reflector).set(ontology::new_class(state, "Reflector",
G(object), G(rubinius)));
G(reflector)->set_object_type(state, Reflector::type);
}

Reflector* Reflector::create(STATE, Object* target) {
Reflector* ref = state->new_object<Reflector>(G(reflector));
ref->target(state, target);
return ref;
}

Object* Reflector::get(STATE, Symbol* name) {
if(name->is_ivar_p(state)->false_p()) return cNil;
return target_->get_ivar(state, name);
}

Object* Reflector::set(STATE, Symbol* name, Object* val) {
if(name->is_ivar_p(state)->false_p()) {
return Primitives::failure();
}

target_->check_frozen(state);
target_->set_ivar(state, name, val);

return val;
}
}

0 comments on commit 0b96773

Please sign in to comment.