00001 #include "typelib.hh"
00002 #include <typelib/value_ops.hh>
00003 #include <ruby.h>
00004 #if !defined(RUBY_19) && !defined(RUBY_191)
00005 extern "C" {
00006 #include <st.h>
00007 }
00008 #endif
00009
00010 using namespace Typelib;
00011 using namespace std;
00012 using namespace typelib_ruby;
00013
00014 static VALUE cMemoryZone;
00015 static st_table* MemoryTable;
00016
00017
00018
00019
00020 static int memory_table_compare(void* a, void* b)
00021 {
00022 return (a != b);
00023 }
00024
00025 #if !defined(RUBY_19) && !defined(RUBY_191)
00026 typedef long st_index_t;
00027 #endif
00028
00029 static st_index_t memory_table_hash(void* a)
00030 {
00031
00032
00033 return (st_index_t)a;
00034 }
00035
00036 #if defined(RUBY_19)
00037 static struct st_hash_type memory_table_type = {
00038 (int (*)(...))memory_table_compare,
00039 (st_index_t (*)(...))memory_table_hash
00040 };
00041 #elif defined(RUBY_191)
00042 static struct st_hash_type memory_table_type = {
00043 (int (*)(...))memory_table_compare,
00044 (int (*)(...))memory_table_hash
00045 };
00046 #else
00047 static struct st_hash_type memory_table_type = {
00048 (int (*)())memory_table_compare,
00049 (int (*)())memory_table_hash
00050 };
00051 #endif
00052
00053 struct MemoryTableEntry
00054 {
00055 int refcount;
00056 VALUE object;
00057 bool owned;
00058 void* root_ptr;
00059 MemoryTableEntry(VALUE object, bool owned, void* root_ptr = 0)
00060 : refcount(1), object(object), owned(owned), root_ptr(root_ptr)
00061 {
00062 if (root_ptr && owned)
00063 rb_raise(rb_eArgError, "given both a root pointer and owned=true for object %llu", NUM2ULL(rb_obj_id(object)));
00064 }
00065 };
00066
00067 struct RbMemoryLayout
00068 {
00069 int refcount;
00070 MemoryLayout layout;
00071 boost::shared_ptr<Registry> registry;
00072
00073 RbMemoryLayout()
00074 : refcount(0) {}
00075 RbMemoryLayout(MemoryLayout const& layout, boost::shared_ptr<Registry> registry)
00076 : refcount(0), layout(layout), registry(registry) {}
00077 };
00078
00079
00080
00081
00082 typedef std::map< void const*, void const* > MemoryTypes;
00083 typedef std::map< void const*, RbMemoryLayout > TypeLayouts;
00084 MemoryTypes memory_types;
00085 TypeLayouts memory_layouts;
00086
00087 static int
00088 memory_touch_i(volatile void* ptr, MemoryTableEntry* entry, st_data_t)
00089 {
00090 char value = *((char*)ptr);
00091 fprintf(stderr, "touching %p (refcount=%i)\n", ptr, entry->refcount);
00092 *((char*)ptr) = value;
00093 return ST_CONTINUE;
00094 }
00095
00096 static void
00097 memory_touch_all()
00098 {
00099 st_foreach(MemoryTable, (int(*)(ANYARGS))memory_touch_i, (st_data_t)0);
00100
00101 }
00102
00103 void
00104 typelib_ruby::memory_unref(void *ptr)
00105 {
00106 # ifdef VERBOSE
00107 fprintf(stderr, "%p: unref\n", ptr);
00108 # endif
00109
00110
00111
00112 if (!ptr)
00113 return;
00114
00115 MemoryTableEntry* entry = 0;
00116 if (!st_lookup(MemoryTable, (st_data_t)ptr, (st_data_t*)&entry))
00117 rb_raise(rb_eArgError, "cannot find %p in memory table", ptr);
00118 --(entry->refcount);
00119 if (!entry->refcount)
00120 {
00121 if (entry->owned)
00122 memory_delete(ptr);
00123 if (entry->root_ptr)
00124 memory_unref(entry->root_ptr);
00125
00126 # ifdef VERBOSE
00127 fprintf(stderr, "%p: deregister\n", ptr);
00128 # endif
00129 delete entry;
00130 st_delete(MemoryTable, (st_data_t*)&ptr, 0);
00131 }
00132
00133 MemoryTypes::iterator type_it = memory_types.find(ptr);
00134 if (type_it != memory_types.end())
00135 {
00136 TypeLayouts::iterator layout_it = memory_layouts.find(type_it->second);
00137 RbMemoryLayout& layout = layout_it->second;
00138 if (0 == --layout.refcount)
00139 memory_layouts.erase(layout_it);
00140 memory_types.erase(type_it);
00141 }
00142 }
00143
00144 void
00145 typelib_ruby::memory_delete(void *ptr)
00146 {
00147 MemoryTypes::iterator it = memory_types.find(ptr);
00148 if (it != memory_types.end())
00149 {
00150 TypeLayouts::iterator layout_it = memory_layouts.find(it->second);
00151 if (layout_it != memory_layouts.end())
00152 {
00153 RbMemoryLayout& layout = layout_it->second;
00154 Typelib::destroy(
00155 static_cast<uint8_t*>(ptr),
00156 layout.layout);
00157 }
00158 }
00159
00160 # ifdef VERBOSE
00161 fprintf(stderr, "%p: deallocated\n", ptr);
00162 # endif
00163 free(ptr);
00164 }
00165
00166 static VALUE
00167 memory_aref(void *ptr)
00168 {
00169 MemoryTableEntry* entry;
00170 if (!st_lookup(MemoryTable, (st_data_t)ptr, (st_data_t*)&entry)) {
00171 return Qnil;
00172 }
00173 if ((VALUE)entry == Qundef)
00174 rb_bug("found undef in memory table");
00175
00176 return entry->object;
00177 }
00178
00179 static void
00180 memory_aset(void *ptr, VALUE obj, bool owned, void* root_ptr)
00181 {
00182 if (! NIL_P(memory_aref(ptr)))
00183 rb_raise(rb_eArgError, "there is already a wrapper for %p", ptr);
00184
00185 if (ptr == root_ptr)
00186 rb_raise(rb_eArgError, "pointer and root pointer are equal");
00187
00188 MemoryTableEntry* entry = 0;
00189 if (st_lookup(MemoryTable, (st_data_t)ptr, (st_data_t*)&entry))
00190 {
00191 ++(entry->refcount);
00192 }
00193 else
00194 {
00195 entry = new MemoryTableEntry(obj, owned, root_ptr);
00196 st_insert(MemoryTable, (st_data_t)ptr, (st_data_t)entry);
00197
00198 if (root_ptr)
00199 {
00200 MemoryTableEntry* root_entry = 0;
00201 if (!st_lookup(MemoryTable, (st_data_t)root_ptr, (st_data_t*)&root_entry))
00202 rb_raise(rb_eArgError, "%p given as root pointer for %p but is not registered", root_ptr, ptr);
00203 ++(root_entry->refcount);
00204 }
00205 }
00206 }
00207
00208 VALUE
00209 typelib_ruby::memory_allocate(size_t size)
00210 {
00211 # ifdef VERBOSE
00212 memory_touch_all();
00213 # endif
00214
00215 void* ptr = malloc(size);
00216 VALUE zone = Data_Wrap_Struct(cMemoryZone, 0, &memory_unref, ptr);
00217 # ifdef VERBOSE
00218 fprintf(stderr, "%p: new allocated zone of size %lu\n", ptr, size);
00219 # endif
00220 memory_aset(ptr, zone, true, 0);
00221 return zone;
00222 }
00223
00224 void
00225 typelib_ruby::memory_init(VALUE ptr, VALUE type)
00226 {
00227 try {
00228 void* cptr = memory_cptr(ptr);
00229 MemoryTypes::iterator it = memory_types.find(cptr);
00230 if (it != memory_types.end())
00231 rb_raise(rb_eArgError, "memory zone already initialized");
00232
00233
00234 Type const& t(rb2cxx::object<Type>(type));
00235 TypeLayouts::iterator layout_it = memory_layouts.find(&t);
00236 if (layout_it == memory_layouts.end())
00237 {
00238 cxx2rb::RbRegistry& registry = rb2cxx::object<cxx2rb::RbRegistry>(type_get_registry(type));
00239 layout_it = memory_layouts.insert(
00240 make_pair( &t, RbMemoryLayout(layout_of(t, true), registry.registry) )
00241 ).first;
00242 }
00243 RbMemoryLayout& layout = layout_it->second;
00244 ++layout.refcount;
00245
00246 memory_types.insert( make_pair(cptr, &t) );
00247 Typelib::init(static_cast<uint8_t*>(cptr), layout.layout);
00248 } catch(std::exception const& e) {
00249 rb_raise(rb_eArgError, "internal error: %s", e.what());
00250 }
00251 }
00252
00253 VALUE
00254 typelib_ruby::memory_wrap(void* ptr, bool take_ownership, void* root_ptr)
00255 {
00256 VALUE zone = memory_aref(ptr);
00257 if (NIL_P(zone))
00258 {
00259 # ifdef VERBOSE
00260 fprintf(stderr, "%p: wrapping new memory zone\n", ptr);
00261 # endif
00262
00263 zone = Data_Wrap_Struct(cMemoryZone, 0, &memory_unref, ptr);
00264 memory_aset(ptr, zone, take_ownership, root_ptr);
00265 }
00266 else
00267 {
00268 # ifdef VERBOSE
00269 fprintf(stderr, "%p: already known memory zone\n", ptr);
00270 # endif
00271 }
00272
00273 return zone;
00274 }
00275
00276 void*
00277 typelib_ruby::memory_cptr(VALUE ptr)
00278 {
00279 void* cptr;
00280 Data_Get_Struct(ptr, void, cptr);
00281 return cptr;
00282 }
00283
00284 static VALUE
00285 memory_zone_address(VALUE self)
00286 {
00287 void* ptr = memory_cptr(self);
00288 return ULL2NUM(reinterpret_cast<uint64_t>(ptr));
00289 }
00290
00291 static VALUE
00292 memory_zone_to_ptr(VALUE self)
00293 {
00294 # ifdef VERBOSE
00295 fprintf(stderr, "allocating void* to create a pointer-to-memory\n");
00296 # endif
00297
00298 VALUE result = memory_allocate(sizeof(void*));
00299
00300 void* newptr = memory_cptr(result);
00301 void* ptr = memory_cptr(self);
00302
00303 *reinterpret_cast<void**>(newptr) = ptr;
00304 rb_iv_set(result, "@pointed_to_memory", self);
00305 return result;
00306 }
00307
00308 static VALUE
00309 string_to_memory_ptr(VALUE self)
00310 {
00311 rb_str_modify(self);
00312 # ifdef VERBOSE
00313 fprintf(stderr, "wrapping string value from Ruby\n");
00314 # endif
00315 VALUE ptr = memory_wrap(StringValuePtr(self), false, 0);
00316 rb_iv_set(ptr, "@buffer_string", self);
00317 return ptr;
00318 }
00319
00320 static VALUE
00321 memory_zone_table_size(VALUE self)
00322 {
00323 return INT2NUM(MemoryTable->num_entries);
00324 }
00325
00326 void typelib_ruby::Typelib_init_memory()
00327 {
00328 VALUE mTypelib = rb_define_module("Typelib");
00329 MemoryTable = st_init_table(&memory_table_type);
00330 VALUE table = Data_Wrap_Struct(rb_cObject, 0, 0, MemoryTable);
00331 rb_iv_set(mTypelib, "@__memory_table__", table);
00332
00333 cMemoryZone = rb_define_class_under(mTypelib, "MemoryZone", rb_cObject);
00334 rb_define_method(cMemoryZone, "zone_address", RUBY_METHOD_FUNC(memory_zone_address), 0);
00335 rb_define_method(cMemoryZone, "to_ptr", RUBY_METHOD_FUNC(memory_zone_to_ptr), 0);
00336 rb_define_singleton_method(cMemoryZone, "table_size", RUBY_METHOD_FUNC(memory_zone_table_size), 0);
00337
00338 rb_define_method(rb_cString, "to_memory_ptr", RUBY_METHOD_FUNC(string_to_memory_ptr), 0);
00339 }
00340