00001 #include "typelib.hh"
00002 #include <ruby.h>
00003
00004 #include <sstream>
00005 #include <limits>
00006 #include <typelib/value.hh>
00007 #include <typelib/registry.hh>
00008 #include <typelib/typevisitor.hh>
00009 #include <typelib/csvoutput.hh>
00010 #include <typelib/endianness.hh>
00011 #include <typelib/value_ops.hh>
00012 #include <typelib/typename.hh>
00013
00014 #include <iostream>
00015
00016 using namespace Typelib;
00017 using std::numeric_limits;
00018 using std::vector;
00019 using namespace typelib_ruby;
00020
00021 void* value_root_ptr(VALUE value)
00022 {
00023 VALUE parent = Qnil;
00024 while (RTEST(value))
00025 {
00026 parent = value;
00027 value = rb_iv_get(value, "@parent");
00028 }
00029 if (RTEST(parent))
00030 {
00031 Value v = rb2cxx::object<Value>(parent);
00032 return v.getData();
00033 }
00034 else return 0;
00035 }
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051 VALUE cxx2rb::value_wrap(Value v, VALUE registry, VALUE parent)
00052 {
00053 VALUE type = type_wrap(v.getType(), registry);
00054 # ifdef VERBOSE
00055 Value parent_value = rb2cxx::object<Value>(parent);
00056 fprintf(stderr, "wrapping Typelib::Value %p from C++, type=%s, parent=%p and root=%p\n", v.getData(), v.getType().getName().c_str(), parent_value.getData(), value_root_ptr(parent));
00057 # endif
00058 VALUE ptr = memory_wrap(v.getData(), false, value_root_ptr(parent));
00059 VALUE wrapper = rb_funcall(type, rb_intern("wrap"), 1, ptr);
00060
00061 rb_iv_set(wrapper, "@parent", parent);
00062 return wrapper;
00063 }
00064
00065 static VALUE value_allocate(Type const& type, VALUE registry)
00066 {
00067 VALUE rb_type = cxx2rb::type_wrap(type, registry);
00068 # ifdef VERBOSE
00069 fprintf(stderr, "allocating new value of type %s\n", type.getName().c_str());
00070 # endif
00071 VALUE ptr = memory_allocate(type.getSize());
00072 memory_init(ptr, rb_type);
00073 VALUE wrapper = rb_funcall(rb_type, rb_intern("wrap"), 1, ptr);
00074 return wrapper;
00075 }
00076
00077 namespace typelib_ruby {
00078 VALUE cType = Qnil;
00079 VALUE cNumeric = Qnil;
00080 VALUE cOpaque = Qnil;
00081 VALUE cNull = Qnil;
00082 VALUE cIndirect = Qnil;
00083 VALUE cPointer = Qnil;
00084 VALUE cArray = Qnil;
00085 VALUE cCompound = Qnil;
00086 VALUE cEnum = Qnil;
00087 VALUE cContainer = Qnil;
00088 }
00089
00090 VALUE cxx2rb::class_of(Typelib::Type const& type)
00091 {
00092 using Typelib::Type;
00093 switch(type.getCategory()) {
00094 case Type::Numeric: return cNumeric;
00095 case Type::Compound: return cCompound;
00096 case Type::Pointer: return cPointer;
00097 case Type::Array: return cArray;
00098 case Type::Enum: return cEnum;
00099 case Type::Container: return cContainer;
00100 case Type::Opaque: return cOpaque;
00101 case Type::NullType: return cNull;
00102 default: return cType;
00103 }
00104 }
00105
00106 VALUE cxx2rb::type_wrap(Type const& type, VALUE registry)
00107 {
00108
00109
00110 WrapperMap& wrappers = rb2cxx::object<RbRegistry>(registry).wrappers;
00111
00112 WrapperMap::const_iterator it = wrappers.find(&type);
00113 if (it != wrappers.end())
00114 return it->second.second;
00115
00116 VALUE base = class_of(type);
00117 VALUE klass = rb_funcall(rb_cClass, rb_intern("new"), 1, base);
00118 VALUE rb_type = Data_Wrap_Struct(rb_cObject, 0, 0, const_cast<Type*>(&type));
00119 rb_iv_set(klass, "@registry", registry);
00120 rb_iv_set(klass, "@type", rb_type);
00121 rb_iv_set(klass, "@name", rb_str_new2(type.getName().c_str()));
00122 rb_iv_set(klass, "@null", (type.getCategory() == Type::NullType) ? Qtrue : Qfalse);
00123 rb_iv_set(klass, "@opaque", (type.getCategory() == Type::Opaque) ? Qtrue : Qfalse);
00124
00125 if (rb_respond_to(klass, rb_intern("subclass_initialize")))
00126 rb_funcall(klass, rb_intern("subclass_initialize"), 0);
00127
00128 wrappers.insert(std::make_pair(&type, std::make_pair(false, klass)));
00129 return klass;
00130 }
00131
00132
00133
00134
00135
00152 static VALUE type_to_csv(int argc, VALUE* argv, VALUE rbself)
00153 {
00154 VALUE basename = Qnil;
00155 VALUE separator = Qnil;
00156 rb_scan_args(argc, argv, "02", &basename, &separator);
00157
00158 std::string bname = "", sep = " ";
00159 if (!NIL_P(basename)) bname = StringValuePtr(basename);
00160 if (!NIL_P(separator)) sep = StringValuePtr(separator);
00161
00162 Type const& self(rb2cxx::object<Type>(rbself));
00163 std::ostringstream stream;
00164 stream << csv_header(self, bname, sep);
00165 std::string str = stream.str();
00166 return rb_str_new(str.c_str(), str.length());
00167 }
00168
00169
00170
00171
00172
00173
00174
00175 static VALUE typelib_do_basename(VALUE mod, VALUE name)
00176 {
00177 std::string result = Typelib::getTypename(StringValuePtr(name));
00178 return rb_str_new(result.c_str(), result.length());
00179 }
00180
00181
00182 static VALUE typelib_do_namespace(VALUE mod, VALUE name)
00183 {
00184 std::string result = Typelib::getNamespace(StringValuePtr(name));
00185 return rb_str_new(result.c_str(), result.length());
00186 }
00187
00188
00189 static VALUE typelib_do_split_name(VALUE mod, VALUE name)
00190 {
00191 std::list<std::string> splitted = Typelib::splitTypename(StringValuePtr(name));
00192 VALUE result = rb_ary_new();
00193 for (std::list<std::string>::const_iterator it = splitted.begin(); it != splitted.end(); ++it)
00194 rb_ary_push(result, rb_str_new(it->c_str(), it->length()));
00195 return result;
00196 }
00197
00198
00199
00200
00201
00202
00203
00204 static VALUE type_equal_operator(VALUE rbself, VALUE rbwith)
00205 {
00206 if (! rb_respond_to(rbwith, rb_intern("superclass")))
00207 return Qfalse;
00208 if (rb_funcall(rbself, rb_intern("superclass"), 0) != rb_funcall(rbwith, rb_intern("superclass"), 0))
00209 return Qfalse;
00210 if (rbself == rbwith)
00211 return Qtrue;
00212
00213 Type const& self(rb2cxx::object<Type>(rbself));
00214 Type const& with(rb2cxx::object<Type>(rbwith));
00215 bool result = (self == with) || self.isSame(with);
00216 return result ? Qtrue : Qfalse;
00217 }
00218
00219
00220
00221
00222
00223
00224 static VALUE type_size(VALUE self)
00225 {
00226 Type const& type(rb2cxx::object<Type>(self));
00227 return INT2FIX(type.getSize());
00228 }
00229
00230
00231
00232
00233
00234
00235
00236 static VALUE type_dependencies(VALUE self)
00237 {
00238 Type const& type(rb2cxx::object<Type>(self));
00239
00240 typedef std::set<Type const*> TypeSet;
00241 TypeSet dependencies = type.dependsOn();
00242 VALUE registry = type_get_registry(self);
00243
00244 VALUE result = rb_ary_new();
00245 for (TypeSet::const_iterator it = dependencies.begin(); it != dependencies.end(); ++it)
00246 rb_ary_push(result, cxx2rb::type_wrap(**it, registry));
00247 return result;
00248 }
00249
00250
00251
00252
00253
00254
00255
00256 static VALUE type_can_cast_to(VALUE self, VALUE to)
00257 {
00258 Type const& from_type(rb2cxx::object<Type>(self));
00259 Type const& to_type(rb2cxx::object<Type>(to));
00260 return from_type.canCastTo(to_type) ? Qtrue : Qfalse;
00261 }
00262
00263
00264
00265
00266
00267
00268
00269
00270 static VALUE type_memory_layout(VALUE self, VALUE pointers, VALUE opaques, VALUE merge, VALUE remove_trailing_skips)
00271 {
00272 Type const& type(rb2cxx::object<Type>(self));
00273 VALUE registry = type_get_registry(self);
00274
00275 VALUE result = rb_ary_new();
00276
00277 VALUE rb_memcpy = ID2SYM(rb_intern("FLAG_MEMCPY"));
00278 VALUE rb_skip = ID2SYM(rb_intern("FLAG_SKIP"));
00279 VALUE rb_array = ID2SYM(rb_intern("FLAG_ARRAY"));
00280 VALUE rb_end = ID2SYM(rb_intern("FLAG_END"));
00281 VALUE rb_container = ID2SYM(rb_intern("FLAG_CONTAINER"));
00282
00283 try {
00284 MemoryLayout layout = Typelib::layout_of(type, RTEST(pointers), RTEST(opaques), RTEST(merge), RTEST(remove_trailing_skips));
00285
00286
00287 for (MemoryLayout::const_iterator it = layout.begin(); it != layout.end(); ++it)
00288 {
00289 switch(*it)
00290 {
00291 case MemLayout::FLAG_MEMCPY:
00292 rb_ary_push(result, rb_memcpy);
00293 rb_ary_push(result, LONG2NUM(*(++it)));
00294 break;
00295 case MemLayout::FLAG_SKIP:
00296 rb_ary_push(result, rb_skip);
00297 rb_ary_push(result, LONG2NUM(*(++it)));
00298 break;
00299 case MemLayout::FLAG_ARRAY:
00300 rb_ary_push(result, rb_array);
00301 rb_ary_push(result, LONG2NUM(*(++it)));
00302 break;
00303 case MemLayout::FLAG_END:
00304 rb_ary_push(result, rb_end);
00305 break;
00306 case MemLayout::FLAG_CONTAINER:
00307 rb_ary_push(result, rb_container);
00308 rb_ary_push(result, cxx2rb::type_wrap(*reinterpret_cast<Container*>(*(++it)), registry));
00309 break;
00310 default:
00311 rb_raise(rb_eArgError, "error encountered while parsing memory layout");
00312 }
00313 }
00314
00315 } catch(std::exception const& e) {
00316 rb_raise(rb_eArgError, "%s", e.what());
00317 }
00318
00319 return result;
00320 }
00321
00322
00323 static VALUE type_is_assignable(Type const& type)
00324 {
00325 switch(type.getCategory())
00326 {
00327 case Type::Numeric:
00328 return INT2FIX(1);
00329 case Type::Pointer:
00330 return type_is_assignable( dynamic_cast<Pointer const&>(type).getIndirection());
00331 case Type::Enum:
00332 return INT2FIX(1);
00333 default:
00334 return INT2FIX(0);
00335 }
00336
00337 }
00338
00339 VALUE typelib_ruby::type_get_registry(VALUE self)
00340 {
00341 return rb_iv_get(self, "@registry");
00342 }
00343
00344
00345
00346
00347
00348
00349
00350
00351 static void value_delete(void* self) { delete reinterpret_cast<Value*>(self); }
00352
00353 static VALUE value_alloc(VALUE klass)
00354 { return Data_Wrap_Struct(klass, 0, value_delete, new Value); }
00355
00356 static
00357 VALUE value_initialize(VALUE self, VALUE ptr)
00358 {
00359 Type const& t(rb2cxx::object<Type>(rb_class_of(self)));
00360
00361 if (NIL_P(ptr) || rb_obj_is_kind_of(ptr, rb_cString))
00362 {
00363 # ifdef VERBOSE
00364 fprintf(stderr, "allocating new value of type %s to copy an existing buffer\n", t.getName().c_str());
00365 # endif
00366 VALUE buffer = memory_allocate(t.getSize());
00367 memory_init(buffer, rb_class_of(self));
00368 if (! NIL_P(ptr))
00369 {
00370 char* ruby_buffer = StringValuePtr(ptr);
00371 vector<uint8_t> cxx_buffer(ruby_buffer, ruby_buffer + RSTRING_LEN(ptr));
00372 try { Typelib::load(Value(memory_cptr(buffer), t), cxx_buffer); }
00373 catch(std::exception const& e)
00374 { rb_raise(rb_eArgError, "%s", e.what()); }
00375 }
00376
00377 ptr = buffer;
00378 }
00379
00380
00381 rb_iv_set(self, "@ptr", ptr);
00382 Value& value = rb2cxx::object<Value>(self);
00383 value = Value(memory_cptr(ptr), t);
00384 # ifdef VERBOSE
00385 fprintf(stderr, "object %llu uses memory zone %p\n", NUM2ULL(rb_obj_id(self)), value.getData());
00386 # endif
00387 return self;
00388 }
00389
00390 static
00391 VALUE value_address(VALUE self)
00392 {
00393 Value value = rb2cxx::object<Value>(self);
00394 return LONG2NUM((long)value.getData());
00395 }
00396
00397 static
00398 VALUE value_endian_swap(VALUE self)
00399 {
00400 Value& value = rb2cxx::object<Value>(self);
00401 CompileEndianSwapVisitor compiled;
00402 compiled.apply(value.getType());
00403
00404 VALUE registry = value_get_registry(self);
00405 VALUE result = value_allocate(value.getType(), registry);
00406 compiled.swap(value, rb2cxx::object<Value>(result));
00407 return result;
00408 }
00409
00410 static
00411 VALUE value_endian_swap_b(VALUE self, VALUE rb_compile)
00412 {
00413 Value& value = rb2cxx::object<Value>(self);
00414 endian_swap(value);
00415 return self;
00416 }
00417
00418 static
00419 VALUE value_do_cast(VALUE self, VALUE target_type)
00420 {
00421 Value& value = rb2cxx::object<Value>(self);
00422 Type const& to_type(rb2cxx::object<Type>(target_type));
00423
00424 if (value.getType() == to_type)
00425 return self;
00426
00427 VALUE registry = rb_iv_get(target_type, "@registry");
00428 Value casted(value.getData(), to_type);
00429 # ifdef VERBOSE
00430 fprintf(stderr, "wrapping casted value\n");
00431 # endif
00432
00433 return cxx2rb::value_wrap(casted, registry, self);
00434 }
00435
00436 static
00437 VALUE value_invalidate(VALUE self)
00438 {
00439 if (NIL_P(rb_iv_get(self, "@parent")))
00440 rb_raise(rb_eArgError, "cannot invalidate a toplevel value");
00441
00442 Value& value = rb2cxx::object<Value>(self);
00443 #ifdef VERBOSE
00444 fprintf(stderr, "invalidating %llu, ptr=%p\n", NUM2ULL(rb_obj_id(self)), value.getData());
00445 #endif
00446 value = Value(0, value.getType());
00447 rb_funcall(rb_iv_get(self, "@ptr"), rb_intern("invalidate"), 0);
00448 return Qnil;
00449 }
00450
00451 static
00452 VALUE value_do_byte_array(VALUE self, VALUE pointers, VALUE opaques, VALUE merge, VALUE remove_trailing_skips)
00453 {
00454 Value& value = rb2cxx::object<Value>(self);
00455 MemoryLayout layout = Typelib::layout_of(value.getType(), RTEST(pointers), RTEST(opaques), RTEST(merge), RTEST(remove_trailing_skips));
00456
00457 vector<uint8_t> buffer;
00458 Typelib::dump(value, buffer, layout);
00459 return rb_str_new(reinterpret_cast<char*>(&buffer[0]), buffer.size());
00460 }
00461
00462
00463
00464
00465
00466
00467
00468 static
00469 VALUE value_marshalling_size(VALUE self)
00470 {
00471 Value& value = rb2cxx::object<Value>(self);
00472 try { return INT2NUM(Typelib::getDumpSize(value)); }
00473 catch(Typelib::NoLayout)
00474 { return Qnil; }
00475 }
00476
00477 VALUE value_memory_eql_p(VALUE rbself, VALUE rbwith)
00478 {
00479 Value& self = rb2cxx::object<Value>(rbself);
00480 Value& with = rb2cxx::object<Value>(rbwith);
00481
00482 if (self.getData() == with.getData())
00483 return Qtrue;
00484
00485
00486 Type const& type = self.getType();
00487 return memcmp(self.getData(), with.getData(), type.getSize()) == 0 ? Qtrue : Qfalse;
00488 }
00489
00490 VALUE typelib_ruby::value_get_registry(VALUE self)
00491 {
00492 VALUE type = rb_funcall(self, rb_intern("class"), 0);
00493 return rb_iv_get(type, "@registry");
00494 }
00495
00503 static VALUE value_to_csv(int argc, VALUE* argv, VALUE self)
00504 {
00505 VALUE separator = Qnil;
00506 rb_scan_args(argc, argv, "01", &separator);
00507
00508 Value const& value(rb2cxx::object<Value>(self));
00509 std::string sep = " ";
00510 if (!NIL_P(separator)) sep = StringValuePtr(separator);
00511
00512 std::ostringstream stream;
00513 stream << csv(value.getType(), value.getData(), sep);
00514 std::string str = stream.str();
00515 return rb_str_new(str.c_str(), str.length());
00516 }
00517
00518
00519 static VALUE value_zero(VALUE self)
00520 {
00521 Value const& value(rb2cxx::object<Value>(self));
00522 Typelib::zero(value);
00523 return self;
00524 }
00525
00526 static VALUE typelib_do_copy(VALUE, VALUE to, VALUE from)
00527 {
00528 Value v_from = rb2cxx::object<Value>(from);
00529 Value v_to = rb2cxx::object<Value>(to);
00530
00531 if (v_from.getType() != v_to.getType())
00532 {
00533
00534 if (!v_from.getType().canCastTo(v_to.getType()))
00535 rb_raise(rb_eArgError, "cannot copy: types are not compatible");
00536 }
00537 Typelib::copy(v_to.getData(), v_from.getData(), v_from.getType());
00538 return to;
00539 }
00540
00541
00542
00543
00544
00545
00546
00547 static VALUE typelib_compare(VALUE, VALUE to, VALUE from)
00548 {
00549 Value v_from = rb2cxx::object<Value>(from);
00550 Value v_to = rb2cxx::object<Value>(to);
00551
00552 if (v_from.getType() != v_to.getType())
00553 {
00554
00555 if (!v_from.getType().canCastTo(v_to.getType()))
00556 rb_raise(rb_eArgError, "cannot compare: %s and %s are not compatible types",
00557 v_from.getType().getName().c_str(),
00558 v_to.getType().getName().c_str());
00559 }
00560 try {
00561 bool result = Typelib::compare(v_to.getData(), v_from.getData(), v_from.getType());
00562 return result ? Qtrue : Qfalse;
00563 } catch(std::exception const& e) {
00564 rb_raise(rb_eArgError, "%s", e.what());
00565 }
00566 }
00567
00568
00569 void typelib_ruby::Typelib_init_values()
00570 {
00571 VALUE mTypelib = rb_define_module("Typelib");
00572 rb_define_singleton_method(mTypelib, "do_copy", RUBY_METHOD_FUNC(typelib_do_copy), 2);
00573 rb_define_singleton_method(mTypelib, "compare", RUBY_METHOD_FUNC(typelib_compare), 2);
00574
00575 cType = rb_define_class_under(mTypelib, "Type", rb_cObject);
00576 rb_define_alloc_func(cType, value_alloc);
00577 rb_define_singleton_method(cType, "==", RUBY_METHOD_FUNC(type_equal_operator), 1);
00578 rb_define_singleton_method(cType, "size", RUBY_METHOD_FUNC(&type_size), 0);
00579 rb_define_singleton_method(cType, "do_memory_layout", RUBY_METHOD_FUNC(&type_memory_layout), 4);
00580 rb_define_singleton_method(cType, "do_dependencies", RUBY_METHOD_FUNC(&type_dependencies), 0);
00581 rb_define_singleton_method(cType, "casts_to?", RUBY_METHOD_FUNC(&type_can_cast_to), 1);
00582 rb_define_method(cType, "__initialize__", RUBY_METHOD_FUNC(&value_initialize), 1);
00583 rb_define_method(cType, "zero!", RUBY_METHOD_FUNC(&value_zero), 0);
00584 rb_define_method(cType, "memory_eql?", RUBY_METHOD_FUNC(&value_memory_eql_p), 1);
00585 rb_define_method(cType, "endian_swap", RUBY_METHOD_FUNC(&value_endian_swap), 0);
00586 rb_define_method(cType, "endian_swap!", RUBY_METHOD_FUNC(&value_endian_swap_b), 0);
00587 rb_define_method(cType, "zone_address", RUBY_METHOD_FUNC(&value_address), 0);
00588 rb_define_method(cType, "do_cast", RUBY_METHOD_FUNC(&value_do_cast), 1);
00589 rb_define_method(cType, "do_invalidate", RUBY_METHOD_FUNC(&value_invalidate), 0);
00590
00591 rb_define_singleton_method(mTypelib, "do_basename", RUBY_METHOD_FUNC(typelib_do_basename), 1);
00592 rb_define_singleton_method(mTypelib, "do_namespace", RUBY_METHOD_FUNC(typelib_do_namespace), 1);
00593 rb_define_singleton_method(mTypelib, "split_typename", RUBY_METHOD_FUNC(typelib_do_split_name), 1);
00594
00595 rb_define_singleton_method(cType, "to_csv", RUBY_METHOD_FUNC(type_to_csv), -1);
00596 rb_define_method(cType, "to_csv", RUBY_METHOD_FUNC(value_to_csv), -1);
00597 rb_define_method(cType, "do_byte_array", RUBY_METHOD_FUNC(value_do_byte_array), 4);
00598 rb_define_method(cType, "marshalling_size", RUBY_METHOD_FUNC(value_marshalling_size), 0);
00599
00600 Typelib_init_specialized_types();
00601 }
00602