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