Skip to content

Commit

Permalink
Added Mirror::String#character_index. Fixed 1.9 String#index.
Browse files Browse the repository at this point in the history
  • Loading branch information
brixen committed Dec 5, 2012
1 parent 8fc9829 commit 7f7c590
Show file tree
Hide file tree
Showing 11 changed files with 221 additions and 53 deletions.
12 changes: 12 additions & 0 deletions kernel/common/mirror.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
module Rubinius
class Mirror
def self.subject=(klass)
@subject = klass
end

def self.subject
@subject
end

def self.reflect(obj)
klass = Rubinius.invoke_primitive :module_mirror, obj
klass.new obj if klass
Expand All @@ -8,5 +16,9 @@ def self.reflect(obj)
def initialize(obj)
@object = obj
end

class Object < Mirror
subject = ::Object
end
end
end
4 changes: 2 additions & 2 deletions kernel/common/regexp19.rb
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ def begin(idx)
else
start = @region.at(idx - 1).at(0)
end
Rubinius.invoke_primitive :string_character_index, @source, start, 0
Rubinius.invoke_primitive :string_byte_character_index, @source, start, 0
end

def end(idx)
Expand All @@ -176,7 +176,7 @@ def end(idx)
else
fin = @region.at(idx - 1).at(1)
end
Rubinius.invoke_primitive :string_character_index, @source, fin, 0
Rubinius.invoke_primitive :string_byte_character_index, @source, fin, 0
end

def offset(idx)
Expand Down
36 changes: 0 additions & 36 deletions kernel/common/string.rb
Original file line number Diff line number Diff line change
Expand Up @@ -374,42 +374,6 @@ def include?(needle)
!!find_string(str_needle, 0)
end

def index(needle, offset=0)
offset = Rubinius::Type.coerce_to offset, Integer, :to_int
offset = @num_bytes + offset if offset < 0

return nil if offset < 0 || offset > @num_bytes

needle = needle.to_str if !needle.instance_of?(String) && needle.respond_to?(:to_str)

# What are we searching for?
case needle
when Fixnum
return nil if needle > 255 or needle < 0
return find_string(needle.chr, offset)
when String
return offset if needle == ""

needle_size = needle.size

max = @num_bytes - needle_size
return if max < 0 # <= 0 maybe?

return find_string(needle, offset)
when Regexp
if match = needle.match_from(self, offset)
Regexp.last_match = match
return match.begin(0)
else
Regexp.last_match = nil
end
else
raise TypeError, "type mismatch: #{needle.class} given"
end

return nil
end

def insert(index, other)
other = StringValue(other)
index = Rubinius::Type.coerce_to index, Fixnum, :to_int
Expand Down
35 changes: 35 additions & 0 deletions kernel/common/string18.rb
Original file line number Diff line number Diff line change
Expand Up @@ -747,4 +747,39 @@ def justify(width, direction, padstr=" ")
str
end

def index(needle, offset=0)
offset = Rubinius::Type.coerce_to offset, Integer, :to_int
offset = @num_bytes + offset if offset < 0

return nil if offset < 0 || offset > @num_bytes

needle = needle.to_str if !needle.instance_of?(String) && needle.respond_to?(:to_str)

# What are we searching for?
case needle
when Fixnum
return nil if needle > 255 or needle < 0
return find_string(needle.chr, offset)
when String
return offset if needle == ""

needle_size = needle.size

max = @num_bytes - needle_size
return if max < 0 # <= 0 maybe?

return find_string(needle, offset)
when Regexp
if match = needle.match_from(self, offset)
Regexp.last_match = match
return match.begin(0)
else
Regexp.last_match = nil
end
else
raise TypeError, "type mismatch: #{needle.class} given"
end

return nil
end
end
36 changes: 34 additions & 2 deletions kernel/common/string19.rb
Original file line number Diff line number Diff line change
Expand Up @@ -955,8 +955,7 @@ def []=(index, count_or_replacement, replacement=undefined)

splice! bi, bs, replacement
when String
# TODO: fix String#index
unless start = self.index(index)
unless start = byteindex(index)
raise IndexError, "string not matched"
end

Expand Down Expand Up @@ -1142,4 +1141,37 @@ def rjust(width, padding=" ")
str.taint if tainted? or padding.tainted?
str.force_encoding enc
end

def index(str, start=undefined)
if start.equal? undefined
start = 0
else
start = Rubinius::Type.coerce_to start, Fixnum, :to_int

start += size if start < 0
return if start < 0 or start > size
end

if str.kind_of? Regexp
Rubinius::Type.compatible_encoding self, str

if match = str.match_from(self, start)
Regexp.last_match = match
return match.begin(0)
else
Regexp.last_match = nil
return
end
end

str = StringValue(str)
return start if str == ""

Rubinius::Type.compatible_encoding self, str

return if str.size > size

m = Rubinius::Mirror.reflect self
m.character_index str, start
end
end
5 changes: 5 additions & 0 deletions kernel/common/string_mirror.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
module Rubinius
class Mirror
class String < Mirror
subject = ::String

def character_index(str, start)
Rubinius.invoke_primitive :string_character_index, @object, str, start
end
end
end
end
20 changes: 20 additions & 0 deletions spec/core/mirror/reflect_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
require File.expand_path('../../../spec_helper', __FILE__)

describe "Rubinius::Mirror.reflect" do
it "returns a Mirror::String instance for a String" do
Rubinius::Mirror.reflect("a").should be_an_instance_of(Rubinius::Mirror::String)
end

it "returns a Mirror::String instance for an anonymous String subclass" do
klass = Class.new String
obj = klass.new
Rubinius::Mirror.reflect(obj).should be_an_instance_of(Rubinius::Mirror::String)
end

it "returns a Mirror::String instance for a String with a singleton class" do
str = "a"
def str.some_method; end

Rubinius::Mirror.reflect(str).should be_an_instance_of(Rubinius::Mirror::String)
end
end
43 changes: 32 additions & 11 deletions vm/builtin/module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -382,21 +382,42 @@ namespace rubinius {
}

Class* Module::mirror(STATE, Object* obj) {
if(!mirror_->nil_p()) return mirror_;
Class* object_class = obj->class_object(state);
Class* mirror = object_class->mirror();

Symbol* name = module_name();
if(name->nil_p()) return nil<Class>();
if(!mirror->nil_p()) return mirror;

std::string class_name = name->cpp_str(state);
size_t k = class_name.rfind("::");
if(k != std::string::npos) {
class_name = class_name.substr(k);
}
Class* klass = object_class;

bool found;
Object* klass = G(rubinius)->get_const(state, state->symbol(class_name), &found);
do {
Symbol* name = klass->module_name();

if(!name->nil_p()) {
std::string class_name = name->cpp_str(state);
size_t k = class_name.rfind("::");
if(k != std::string::npos) {
class_name = class_name.substr(k);
}

bool found;
Object* obj = G(mirror)->get_const(state, state->symbol(class_name), &found);

if(found) return as<Class>(klass);
if(found) {
if(Class* mirror_class = try_as<Class>(obj)) {
object_class->mirror(state, mirror_class);
return mirror_class;
}
}
}

Module* ancestor = klass->superclass();
klass = 0;

while(!ancestor->nil_p()) {
if((klass = try_as<Class>(ancestor))) break;
ancestor = ancestor->superclass();
}
} while(klass);

return nil<Class>();
}
Expand Down
2 changes: 1 addition & 1 deletion vm/builtin/module.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ namespace rubinius {
Object* cvar_remove(STATE, Symbol* name);

// Rubinius.primitive :module_mirror
Class* mirror(STATE, Object* obj);
static Class* mirror(STATE, Object* obj);

void setup(STATE);
void setup(STATE, std::string name, Module* under = NULL);
Expand Down
76 changes: 76 additions & 0 deletions vm/builtin/string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1505,6 +1505,82 @@ namespace rubinius {
}
}

Fixnum* String::character_index(STATE, String* pattern, Fixnum* start) {
native_int offset = start->to_native();
if(offset < 0) return nil<Fixnum>();

native_int total = byte_size();

uint8_t* p = byte_address();
uint8_t* e = byte_address() + total;
uint8_t* pp = pattern->byte_address();
uint8_t* pe = pp + pattern->byte_size();
uint8_t* s;
uint8_t* ss;

if(byte_compatible_p(encoding())) {
for(s = p += offset, ss = pp; p < e; s = ++p) {
if(*p != *pp) continue;

while(p < e && pp < pe && *(++p) == *(++pp))
; // memcmp

if(pp < pe) {
p = s;
pp = ss;
} else {
return Fixnum::from(s - byte_address());
}
}

return nil<Fixnum>();
}

OnigEncodingType* enc = encoding(state)->get_encoding();
native_int sindex, index = 0;
int c;

while(p < e && index < offset) {
c = Encoding::precise_mbclen(p, e, enc);

if(ONIGENC_MBCLEN_CHARFOUND_P(c)) {
p += c;
index++;
} else {
return nil<Fixnum>();
}
}

for(sindex = index, s = p, ss = pp; p < e; s = p += c, sindex = ++index) {
c = Encoding::precise_mbclen(p, e, enc);
if(!ONIGENC_MBCLEN_CHARFOUND_P(c)) return nil<Fixnum>();

if(*p != *pp) continue;

while(p < e && pp < pe) {
for(uint8_t* pc = p + c; p < e && p < pc && pp < pe; ) {
if(*(++p) != *(++pp)) goto next_search;
}

c = Encoding::precise_mbclen(p, e, enc);
if(!ONIGENC_MBCLEN_CHARFOUND_P(c)) break;

index++;
}

next_search:

if(pp < pe) {
p = s;
pp = ss;
} else {
return Fixnum::from(sindex);
}
}

return nil<Fixnum>();
}

Fixnum* String::rindex(STATE, String* pattern, Fixnum* start) {
native_int total = byte_size();
native_int match_size = pattern->byte_size();
Expand Down
5 changes: 4 additions & 1 deletion vm/builtin/string.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,12 +233,15 @@ namespace rubinius {
native_int find_character_byte_index(STATE, native_int index, native_int start = 0);
native_int find_byte_character_index(STATE, native_int index, native_int start = 0);

// Rubinius.primitive :string_character_index
// Rubinius.primitive :string_byte_character_index
Fixnum* find_byte_character_index_prim(STATE, Fixnum* index, Fixnum* start);

// Rubinius.primitive :string_index
Fixnum* index(STATE, String* pattern, Fixnum* start);

// Rubinius.primitive :string_character_index
Fixnum* character_index(STATE, String* pattern, Fixnum* start);

// Rubinius.primitive :string_rindex
Fixnum* rindex(STATE, String* pattern, Fixnum* start);

Expand Down

0 comments on commit 7f7c590

Please sign in to comment.