diff --git a/shotgun/lib/string.c b/shotgun/lib/string.c index 5a15df6..08a5b6a 100644 --- a/shotgun/lib/string.c +++ b/shotgun/lib/string.c @@ -184,6 +184,31 @@ double string_to_double(STATE, OBJECT self) { return value; } +OBJECT string_cstr_overwrite(STATE, OBJECT self, char *str, int len) { + OBJECT nd, cur; + int tmp; + char *ba; + + xassert(STRING_P(self)); + string_unshare(state, self); + + cur = string_get_data(self); + tmp = bytearray_bytes(state, cur); + if(len+1 > tmp) { + int extra = len * 0.01; + if(extra < 10) extra = 10; + nd = bytearray_new_dirty(state, len + extra); + ba = bytearray_byte_address(state, nd); + string_set_data(self, nd); + } else { + ba = bytearray_byte_address(state, cur); + } + memcpy(ba, str, len); + ba[len] = 0; + string_set_bytes(self, I2N(len)); + return self; +} + static OBJECT tr_replace(STATE, OBJECT string, int bytes, unsigned char *str, unsigned char *data, int size, int steps) { if(size > bytes || RTEST(string_get_shared(string))) { diff --git a/shotgun/lib/string.h b/shotgun/lib/string.h index 5483fb7..5cb44a6 100644 --- a/shotgun/lib/string.h +++ b/shotgun/lib/string.h @@ -35,6 +35,7 @@ unsigned int string_hash_str(unsigned char *bp, unsigned int sz); OBJECT string_newfrombstr(STATE, bstring output); int string_equal_p(STATE, OBJECT self, OBJECT other); OBJECT string_tr_expand(STATE, OBJECT string, OBJECT limit); +OBJECT string_cstr_overwrite(STATE, OBJECT self, char *str, int len); #define string_unshare(state, cur) \ if(string_get_shared(cur) == Qtrue) { string_set_data(cur, bytearray_dup(state, string_get_data(cur))); string_set_shared(cur, Qnil); } diff --git a/shotgun/lib/subtend/handle.c b/shotgun/lib/subtend/handle.c index a061c3f..d2f21c6 100644 --- a/shotgun/lib/subtend/handle.c +++ b/shotgun/lib/subtend/handle.c @@ -1,4 +1,13 @@ #include "shotgun/lib/shotgun.h" +#include "shotgun/lib/string.h" + +/* Copied from ruby.h to avoid including it here. */ +struct RString { + char *ptr; + int len; +}; + +typedef struct RString RString; rni_handle_table *handle_table_new() { rni_handle_table *tbl; @@ -18,6 +27,7 @@ rni_handle *handle_allocate() { h->flags = 0; h->handle_id = 0; h->table_idx = 0; + h->data = 0; return h; } @@ -35,6 +45,7 @@ rni_handle *handle_new(rni_handle_table *tbl, OBJECT obj) { e = ALLOC(rni_ht_entry); e->handle_id = h->handle_id; e->object = obj; + e->rh = h; /* Put the entry in the table. */ tbl->entries[h->table_idx] = e; @@ -109,6 +120,38 @@ rni_handle *handle_detached_array(rni_handle_table *tbl, int len) { */ +/* Check if the handle has RStruct data in it. + If it does we have to copy RStruct data back to the object. */ +void check_rstruct_data(STATE, rni_handle *h, OBJECT o) { + if(!h->data) { + return; + } + if (STRING_P(o)) { + RString *rs = (RString *)h->data; + string_cstr_overwrite(state, o, rs->ptr, rs->len); + XFREE(rs); + h->data = 0; + } else if (ARRAY_P(o)) { + //TODO: overwrite array in object + } +} + +/* Check all created handles looking for RStruct data on them. */ +void check_rstruct_data_in_handles(STATE, rni_handle_table *tbl) { + rni_ht_entry *e; + rni_handle *h; + int i; + + for(i = 0; i < tbl->total; ++i) { + e = tbl->entries[i]; + if(!e) { + continue; + } + h = e->rh; + check_rstruct_data(state, h, e->object); + } +} + OBJECT handle_to_object(STATE, rni_handle_table *tbl, rni_handle *h) { rni_ht_entry *e; @@ -141,6 +184,8 @@ OBJECT handle_to_object(STATE, rni_handle_table *tbl, rni_handle *h) { return Qnil; } + check_rstruct_data(state, h, e->object); + return e->object; } diff --git a/shotgun/lib/subtend/handle.h b/shotgun/lib/subtend/handle.h index 30a0e48..638d4bf 100644 --- a/shotgun/lib/subtend/handle.h +++ b/shotgun/lib/subtend/handle.h @@ -9,6 +9,7 @@ typedef struct rni_handle rni_handle; struct rni_ht_entry { int handle_id; + rni_handle *rh; OBJECT object; }; @@ -41,4 +42,5 @@ void handle_delete(rni_handle *h); OBJECT handle_to_object(STATE, rni_handle_table *tbl, rni_handle *h); void handle_make_global(rni_handle *h); void handle_clear_global(rni_handle *h); +void check_rstruct_data_in_handles(STATE, rni_handle_table *tbl); diff --git a/shotgun/lib/subtend/nmc.c b/shotgun/lib/subtend/nmc.c index c0f7e97..2e8d1e0 100644 --- a/shotgun/lib/subtend/nmc.c +++ b/shotgun/lib/subtend/nmc.c @@ -253,7 +253,9 @@ void _nmc_start() { abort(); } } - + + check_rstruct_data_in_handles(state, global_context->state->handle_tbl); + /* if(args) XFREE(args); if(va) XFREE(va); diff --git a/shotgun/lib/subtend/ruby.c b/shotgun/lib/subtend/ruby.c index e4746a8..3e925eb 100644 --- a/shotgun/lib/subtend/ruby.c +++ b/shotgun/lib/subtend/ruby.c @@ -620,6 +620,20 @@ void rb_string_value(VALUE *val) { *val = rb_obj_as_string(*val); } +RString* RSTRING(VALUE arg) { + RString *ret; + CTX; + + ret = (RString *)AS_HNDL(arg)->data; + if(!ret) { + ret = ALLOC(RString); + ret->ptr = rbx_string_as_cstr(ctx->state, HNDL(arg)); + ret->len = strlen(ret->ptr); + AS_HNDL(arg)->data = (void *)ret; + } + return ret; +} + VALUE rb_inspect(VALUE obj) { return rb_funcall(obj, rb_intern("inspect"), 0, 0); } diff --git a/shotgun/lib/subtend/ruby.h b/shotgun/lib/subtend/ruby.h index a848145..5529ffe 100644 --- a/shotgun/lib/subtend/ruby.h +++ b/shotgun/lib/subtend/ruby.h @@ -195,6 +195,15 @@ void rb_string_value(VALUE *obj); /* HACK ? */ #define STR2CSTR StringValuePtr +struct RString { + char *ptr; + int len; +}; + +typedef struct RString RString; + +RString* RSTRING(VALUE obj); + VALUE rb_inspect(VALUE obj); /* Hash */ diff --git a/spec/subtend/ext/subtend_string.c b/spec/subtend/ext/subtend_string.c index c713735..e9d3ee2 100644 --- a/spec/subtend/ext/subtend_string.c +++ b/spec/subtend/ext/subtend_string.c @@ -62,6 +62,38 @@ VALUE ss_str_substr(VALUE self, VALUE str, VALUE beg, VALUE len) { return rb_str_substr(str, FIX2INT(beg), FIX2INT(len)); } +VALUE ss_rstring_see(VALUE self, VALUE str) { + return rb_str_new2(RSTRING(str)->ptr); +} + +VALUE ss_rstring_assign_foo(VALUE self, VALUE str) { + RSTRING(str)->len = 3; + RSTRING(str)->ptr = ALLOC_N(char, 4); + memcpy(RSTRING(str)->ptr, "foo", 4); + return Qnil; +} + +VALUE ss_rstring_assign_global_foobar(VALUE self) { + VALUE var = rb_gv_get("$global_rstring_test"); + RSTRING(var)->len = 6; + RSTRING(var)->ptr = ALLOC_N(char, 7); + memcpy(RSTRING(var)->ptr, "foobar", 7); + return Qnil; +} + +VALUE ss_rstring_set_len(VALUE self, VALUE str, VALUE len) { + RSTRING(str)->len = NUM2INT(len); + return Qnil; +} + +VALUE ss_rstring_assign_foo_and_upcase(VALUE self, VALUE str) { + RSTRING(str)->len = 3; + RSTRING(str)->ptr = ALLOC_N(char, 4); + memcpy(RSTRING(str)->ptr, "foo", 4); + rb_funcall(str, rb_intern("upcase!"), 0, 0); + return Qnil; +} + void Init_subtend_string() { VALUE cls; cls = rb_define_class("SubtendString", rb_cObject); @@ -79,4 +111,9 @@ void Init_subtend_string() { rb_define_method(cls, "rb_str2inum", ss_str2inum, 2); rb_define_method(cls, "rb_cstr2inum", ss_cstr2inum, 1); rb_define_method(cls, "rb_str_substr", ss_str_substr, 3); + rb_define_method(cls, "rb_rstring_see", ss_rstring_see, 1); + rb_define_method(cls, "rb_rstring_assign_foo", ss_rstring_assign_foo, 1); + rb_define_method(cls, "rb_rstring_assign_global_foobar", ss_rstring_assign_global_foobar, 0); + rb_define_method(cls, "rb_rstring_set_len", ss_rstring_set_len, 2); + rb_define_method(cls, "rb_rstring_assign_foo_and_upcase", ss_rstring_assign_foo_and_upcase, 1); } diff --git a/spec/subtend/string_spec.rb b/spec/subtend/string_spec.rb index be2865f..8d6d818 100644 --- a/spec/subtend/string_spec.rb +++ b/spec/subtend/string_spec.rb @@ -77,4 +77,28 @@ describe "SubtendString" do end end + it "RSTRING(str)->ptr should return the string on the object" do + @s.rb_rstring_see("foo").should == "foo" + end + + it "Changing RSTRING(str)->ptr should change the string" do + t = "a" + @s.rb_rstring_assign_foo(t) + t.should == "foo" + $global_rstring_test = "b" + @s.rb_rstring_assign_global_foobar() + $global_rstring_test.should == "foobar" + end + + it "Reducing RSTRING(str)->len should cut the string" do + t = "12345" + @s.rb_rstring_set_len(t, 3) + t.should == "123" + end + + it "Changing RSTRING(str)->ptr and calling upcase! should change the string and upcase it" do + t = "" + @s.rb_rstring_assign_foo_and_upcase(t) + t.should == "FOO" + end end