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 NULL;
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 rb_iv_set(wrapper, "@__typelib_invalidated", Qfalse);
00063 return wrapper;
00064 }
00065
00066 static VALUE value_allocate(Type const& type, VALUE registry)
00067 {
00068 VALUE rb_type = cxx2rb::type_wrap(type, registry);
00069 # ifdef VERBOSE
00070 fprintf(stderr, "allocating new value of type %s\n", type.getName().c_str());
00071 # endif
00072 VALUE ptr = memory_allocate(type.getSize());
00073 memory_init(ptr, rb_type);
00074 VALUE wrapper = rb_funcall(rb_type, rb_intern("wrap"), 1, ptr);
00075 return wrapper;
00076 }
00077
00078 namespace typelib_ruby {
00079 VALUE cType = Qnil;
00080 VALUE cNumeric = Qnil;
00081 VALUE cOpaque = Qnil;
00082 VALUE cNull = Qnil;
00083 VALUE cIndirect = Qnil;
00084 VALUE cPointer = Qnil;
00085 VALUE cArray = Qnil;
00086 VALUE cCompound = Qnil;
00087 VALUE cEnum = Qnil;
00088 VALUE cContainer = Qnil;
00089 }
00090
00091 VALUE cxx2rb::class_of(Typelib::Type const& type)
00092 {
00093 using Typelib::Type;
00094 switch(type.getCategory()) {
00095 case Type::Numeric: return cNumeric;
00096 case Type::Compound: return cCompound;
00097 case Type::Pointer: return cPointer;
00098 case Type::Array: return cArray;
00099 case Type::Enum: return cEnum;
00100 case Type::Container: return cContainer;
00101 case Type::Opaque: return cOpaque;
00102 case Type::NullType: return cNull;
00103 default: return cType;
00104 }
00105 }
00106
00107 VALUE cxx2rb::type_wrap(Type const& type, VALUE registry)
00108 {
00109
00110
00111 WrapperMap& wrappers = rb2cxx::object<RbRegistry>(registry).wrappers;
00112
00113 WrapperMap::const_iterator it = wrappers.find(&type);
00114 if (it != wrappers.end())
00115 return it->second.second;
00116
00117 VALUE base = class_of(type);
00118 VALUE klass = rb_funcall(rb_cClass, rb_intern("new"), 1, base);
00119 VALUE rb_type = Data_Wrap_Struct(rb_cObject, 0, 0, const_cast<Type*>(&type));
00120 rb_iv_set(klass, "@registry", registry);
00121 rb_iv_set(klass, "@type", rb_type);
00122 rb_iv_set(klass, "@name", rb_str_new2(type.getName().c_str()));
00123 rb_iv_set(klass, "@null", (type.getCategory() == Type::NullType) ? Qtrue : Qfalse);
00124 rb_iv_set(klass, "@opaque", (type.getCategory() == Type::Opaque) ? Qtrue : Qfalse);
00125 rb_iv_set(klass, "@metadata", cxx2rb::metadata_wrap(type.getMetaData()));
00126
00127 if (rb_respond_to(klass, rb_intern("subclass_initialize")))
00128 rb_funcall(klass, rb_intern("subclass_initialize"), 0);
00129
00130 wrappers.insert(std::make_pair(&type, std::make_pair(false, klass)));
00131 return klass;
00132 }
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
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
00176 static VALUE typelib_do_basename(VALUE mod, VALUE name)
00177 {
00178 std::string result = Typelib::getTypename(StringValuePtr(name));
00179 return rb_str_new(result.c_str(), result.length());
00180 }
00181
00182
00183 static VALUE typelib_do_namespace(VALUE mod, VALUE name)
00184 {
00185 std::string result = Typelib::getNamespace(StringValuePtr(name));
00186 return rb_str_new(result.c_str(), result.length());
00187 }
00188
00189
00190
00191
00192
00193
00194
00195 static VALUE typelib_do_split_name(VALUE mod, VALUE name)
00196 {
00197 std::list<std::string> splitted = Typelib::splitTypename(StringValuePtr(name));
00198 VALUE result = rb_ary_new();
00199 for (std::list<std::string>::const_iterator it = splitted.begin(); it != splitted.end(); ++it)
00200 rb_ary_push(result, rb_str_new(it->c_str(), it->length()));
00201 return result;
00202 }
00203
00204
00205
00206
00207
00208
00209
00210
00211 static VALUE type_equal_operator(VALUE rbself, VALUE rbwith)
00212 {
00213 if (rbself == rbwith)
00214 return Qtrue;
00215 if (!rb_obj_is_kind_of(rbwith, rb_cClass))
00216 return Qfalse;
00217 if ((rbwith == cType) || !RTEST(rb_funcall(rbwith, rb_intern("<"), 1, cType)))
00218 return Qfalse;
00219
00220 Type const& self(rb2cxx::object<Type>(rbself));
00221 Type const& with(rb2cxx::object<Type>(rbwith));
00222 bool result = (self == with) || self.isSame(with);
00223 return result ? Qtrue : Qfalse;
00224 }
00225
00226
00227
00228
00229
00230 static VALUE type_size(VALUE self)
00231 {
00232 Type const& type(rb2cxx::object<Type>(self));
00233 return INT2FIX(type.getSize());
00234 }
00235
00236
00237
00238
00239 static VALUE type_dependencies(VALUE self)
00240 {
00241 Type const& type(rb2cxx::object<Type>(self));
00242
00243 typedef std::set<Type const*> TypeSet;
00244 TypeSet dependencies = type.dependsOn();
00245 VALUE registry = type_get_registry(self);
00246
00247 VALUE result = rb_ary_new();
00248 for (TypeSet::const_iterator it = dependencies.begin(); it != dependencies.end(); ++it)
00249 rb_ary_push(result, cxx2rb::type_wrap(**it, registry));
00250 return result;
00251 }
00252
00253
00254
00255
00256
00257
00258 static VALUE type_can_cast_to(VALUE self, VALUE to)
00259 {
00260 Type const& from_type(rb2cxx::object<Type>(self));
00261 Type const& to_type(rb2cxx::object<Type>(to));
00262 return from_type.canCastTo(to_type) ? Qtrue : Qfalse;
00263 }
00264
00265
00266
00267
00268
00269
00270
00271
00272 static VALUE type_memory_layout(VALUE self, VALUE pointers, VALUE opaques, VALUE merge, VALUE remove_trailing_skips)
00273 {
00274 Type const& type(rb2cxx::object<Type>(self));
00275 VALUE registry = type_get_registry(self);
00276
00277 VALUE result = rb_ary_new();
00278
00279 VALUE rb_memcpy = ID2SYM(rb_intern("FLAG_MEMCPY"));
00280 VALUE rb_skip = ID2SYM(rb_intern("FLAG_SKIP"));
00281 VALUE rb_array = ID2SYM(rb_intern("FLAG_ARRAY"));
00282 VALUE rb_end = ID2SYM(rb_intern("FLAG_END"));
00283 VALUE rb_container = ID2SYM(rb_intern("FLAG_CONTAINER"));
00284
00285 try {
00286 MemoryLayout layout = Typelib::layout_of(type, RTEST(pointers), RTEST(opaques), RTEST(merge), RTEST(remove_trailing_skips));
00287
00288
00289 for (MemoryLayout::const_iterator it = layout.begin(); it != layout.end(); ++it)
00290 {
00291 switch(*it)
00292 {
00293 case MemLayout::FLAG_MEMCPY:
00294 rb_ary_push(result, rb_memcpy);
00295 rb_ary_push(result, LONG2NUM(*(++it)));
00296 break;
00297 case MemLayout::FLAG_SKIP:
00298 rb_ary_push(result, rb_skip);
00299 rb_ary_push(result, LONG2NUM(*(++it)));
00300 break;
00301 case MemLayout::FLAG_ARRAY:
00302 rb_ary_push(result, rb_array);
00303 rb_ary_push(result, LONG2NUM(*(++it)));
00304 break;
00305 case MemLayout::FLAG_END:
00306 rb_ary_push(result, rb_end);
00307 break;
00308 case MemLayout::FLAG_CONTAINER:
00309 rb_ary_push(result, rb_container);
00310 rb_ary_push(result, cxx2rb::type_wrap(*reinterpret_cast<Container*>(*(++it)), registry));
00311 break;
00312 default:
00313 rb_raise(rb_eArgError, "error encountered while parsing memory layout");
00314 }
00315 }
00316
00317 } catch(std::exception const& e) {
00318 rb_raise(rb_eArgError, "%s", e.what());
00319 }
00320
00321 return result;
00322 }
00323
00324
00325 static VALUE type_is_assignable(Type const& type)
00326 {
00327 switch(type.getCategory())
00328 {
00329 case Type::Numeric:
00330 return INT2FIX(1);
00331 case Type::Pointer:
00332 return type_is_assignable( dynamic_cast<Pointer const&>(type).getIndirection());
00333 case Type::Enum:
00334 return INT2FIX(1);
00335 default:
00336 return INT2FIX(0);
00337 }
00338
00339 }
00340
00341 VALUE typelib_ruby::type_get_registry(VALUE self)
00342 {
00343 return rb_iv_get(self, "@registry");
00344 }
00345
00346
00347
00348
00349
00350
00351
00352
00353 static void value_delete(void* self) { delete reinterpret_cast<Value*>(self); }
00354
00355 static VALUE value_alloc(VALUE klass)
00356 { return Data_Wrap_Struct(klass, 0, value_delete, new Value); }
00357
00361 static
00362 VALUE value_create_empty(VALUE klass)
00363 {
00364 Type const& t(rb2cxx::object<Type>(klass));
00365 VALUE value = Data_Wrap_Struct(klass, 0, value_delete, new Value(NULL, t));
00366 rb_iv_set(value, "@parent", Qnil);
00367 return value;
00368 }
00369
00370 static
00371 void value_call_typelib_initialize(VALUE obj)
00372 {
00373 rb_iv_set(obj, "@__typelib_invalidated", Qfalse);
00374 rb_iv_set(obj, "@parent", Qnil);
00375 rb_funcall(obj, rb_intern("typelib_initialize"), 0);
00376 }
00377
00378
00379
00380
00381
00382
00383
00384
00385 static
00386 VALUE value_from_memory_zone(VALUE klass, VALUE ptr)
00387 {
00388 VALUE result = value_create_empty(klass);
00389 Value& value = rb2cxx::object<Value>(result);
00390
00391
00392 rb_iv_set(result, "@ptr", ptr);
00393 value = Value(memory_cptr(ptr), value.getType());
00394 # ifdef VERBOSE
00395 fprintf(stderr, "object %llu wraps memory zone %p\n", NUM2ULL(rb_obj_id(result)), value.getData());
00396 # endif
00397 value_call_typelib_initialize(result);
00398 return result;
00399 }
00400
00401
00402
00403
00404
00405
00406
00407 static
00408 VALUE value_new(VALUE klass)
00409 {
00410 Type const& type(rb2cxx::object<Type>(klass));
00411 # ifdef VERBOSE
00412 fprintf(stderr, "allocating new value of type %s\n", type.getName().c_str());
00413 # endif
00414 VALUE buffer = memory_allocate(type.getSize());
00415 memory_init(buffer, klass);
00416
00417 return value_from_memory_zone(klass, buffer);
00418 }
00419
00420 static
00421 VALUE value_do_from_buffer(VALUE rbself, VALUE string, VALUE pointers, VALUE opaques, VALUE merge, VALUE remove_trailing_skips)
00422 {
00423 Value value = rb2cxx::object<Value>(rbself);
00424
00425 MemoryLayout layout = Typelib::layout_of(value.getType(),
00426 RTEST(pointers), RTEST(opaques), RTEST(merge), RTEST(remove_trailing_skips));
00427
00428 char* ruby_buffer = StringValuePtr(string);
00429 vector<uint8_t> cxx_buffer(ruby_buffer, ruby_buffer + RSTRING_LEN(string));
00430 try { Typelib::load(value, cxx_buffer, layout); }
00431 catch(std::exception const& e)
00432 { rb_raise(rb_eArgError, "%s", e.what()); }
00433 return rbself;
00434 }
00435
00436
00437
00438
00439
00440
00441
00442
00443 static
00444 VALUE value_from_address(VALUE klass, VALUE address)
00445 {
00446 VALUE result = value_create_empty(klass);
00447 Value& value = rb2cxx::object<Value>(result);
00448 value = Value(reinterpret_cast<void*>(NUM2ULL(address)), value.getType());
00449 rb_iv_set(result, "@ptr", memory_wrap(value.getData(), false, NULL));
00450 value_call_typelib_initialize(result);
00451 return result;
00452 }
00453
00454
00455
00456
00457
00458 static
00459 VALUE value_address(VALUE self)
00460 {
00461 Value value = rb2cxx::object<Value>(self);
00462 return LONG2NUM((long)value.getData());
00463 }
00464
00465
00466
00467
00468
00469
00470
00471
00472 static
00473 VALUE value_endian_swap(VALUE self)
00474 {
00475 Value& value = rb2cxx::object<Value>(self);
00476 CompileEndianSwapVisitor compiled;
00477 compiled.apply(value.getType());
00478
00479 VALUE registry = value_get_registry(self);
00480 VALUE result = value_allocate(value.getType(), registry);
00481 compiled.swap(value, rb2cxx::object<Value>(result));
00482 return result;
00483 }
00484
00485
00486
00487
00488
00489
00490
00491
00492 static
00493 VALUE value_endian_swap_b(VALUE self, VALUE rb_compile)
00494 {
00495 Value& value = rb2cxx::object<Value>(self);
00496 endian_swap(value);
00497 return self;
00498 }
00499
00500 static
00501 VALUE value_do_cast(VALUE self, VALUE target_type)
00502 {
00503 Value& value = rb2cxx::object<Value>(self);
00504 Type const& to_type(rb2cxx::object<Type>(target_type));
00505
00506 if (value.getType() == to_type)
00507 return self;
00508
00509 VALUE registry = rb_iv_get(target_type, "@registry");
00510 Value casted(value.getData(), to_type);
00511 # ifdef VERBOSE
00512 fprintf(stderr, "wrapping casted value\n");
00513 # endif
00514
00515 return cxx2rb::value_wrap(casted, registry, self);
00516 }
00517
00518 static
00519 VALUE value_invalidate(VALUE self)
00520 {
00521 if (NIL_P(rb_iv_get(self, "@parent")))
00522 rb_raise(rb_eArgError, "cannot invalidate a toplevel value");
00523
00524 Value& value = rb2cxx::object<Value>(self);
00525 #ifdef VERBOSE
00526 fprintf(stderr, "invalidating %llu, ptr=%p\n", NUM2ULL(rb_obj_id(self)), value.getData());
00527 #endif
00528 value = Value(0, value.getType());
00529 rb_funcall(rb_iv_get(self, "@ptr"), rb_intern("invalidate"), 0);
00530 return Qnil;
00531 }
00532
00533 static
00534 VALUE value_do_byte_array(VALUE self, VALUE pointers, VALUE opaques, VALUE merge, VALUE remove_trailing_skips)
00535 {
00536 Value& value = rb2cxx::object<Value>(self);
00537 MemoryLayout layout = Typelib::layout_of(value.getType(), RTEST(pointers), RTEST(opaques), RTEST(merge), RTEST(remove_trailing_skips));
00538
00539 vector<uint8_t> buffer;
00540 Typelib::dump(value, buffer, layout);
00541 return rb_str_new(reinterpret_cast<char*>(&buffer[0]), buffer.size());
00542 }
00543
00544
00545
00546
00547
00548
00549
00550 static
00551 VALUE value_marshalling_size(VALUE self)
00552 {
00553 Value& value = rb2cxx::object<Value>(self);
00554 try { return INT2NUM(Typelib::getDumpSize(value)); }
00555 catch(Typelib::NoLayout)
00556 { return Qnil; }
00557 }
00558
00559 VALUE value_memory_eql_p(VALUE rbself, VALUE rbwith)
00560 {
00561 Value& self = rb2cxx::object<Value>(rbself);
00562 Value& with = rb2cxx::object<Value>(rbwith);
00563
00564 if (self.getData() == with.getData())
00565 return Qtrue;
00566
00567
00568 Type const& type = self.getType();
00569 return memcmp(self.getData(), with.getData(), type.getSize()) == 0 ? Qtrue : Qfalse;
00570 }
00571
00572 VALUE typelib_ruby::value_get_registry(VALUE self)
00573 {
00574 VALUE type = rb_funcall(self, rb_intern("class"), 0);
00575 return rb_iv_get(type, "@registry");
00576 }
00577
00585 static VALUE value_to_csv(int argc, VALUE* argv, VALUE self)
00586 {
00587 VALUE separator = Qnil;
00588 rb_scan_args(argc, argv, "01", &separator);
00589
00590 Value const& value(rb2cxx::object<Value>(self));
00591 std::string sep = " ";
00592 if (!NIL_P(separator)) sep = StringValuePtr(separator);
00593
00594 std::ostringstream stream;
00595 stream << csv(value.getType(), value.getData(), sep);
00596 std::string str = stream.str();
00597 return rb_str_new(str.c_str(), str.length());
00598 }
00599
00600
00601 static VALUE value_zero(VALUE self)
00602 {
00603 Value const& value(rb2cxx::object<Value>(self));
00604 Typelib::zero(value);
00605 return self;
00606 }
00607
00608 static VALUE typelib_do_copy(VALUE, VALUE to, VALUE from)
00609 {
00610 Value v_from = rb2cxx::object<Value>(from);
00611 Value v_to = rb2cxx::object<Value>(to);
00612
00613 if (v_from.getType() != v_to.getType())
00614 {
00615
00616 if (!v_from.getType().canCastTo(v_to.getType()))
00617 rb_raise(rb_eArgError, "cannot copy: types are not compatible");
00618 }
00619 Typelib::copy(v_to.getData(), v_from.getData(), v_from.getType());
00620 return to;
00621 }
00622
00623
00624
00625
00626
00627
00628
00629 static VALUE typelib_compare(VALUE, VALUE to, VALUE from)
00630 {
00631 Value v_from = rb2cxx::object<Value>(from);
00632 Value v_to = rb2cxx::object<Value>(to);
00633
00634 if (v_from.getType() != v_to.getType())
00635 {
00636
00637 if (!v_from.getType().canCastTo(v_to.getType()))
00638 rb_raise(rb_eArgError, "cannot compare: %s and %s are not compatible types",
00639 v_from.getType().getName().c_str(),
00640 v_to.getType().getName().c_str());
00641 }
00642 try {
00643 bool result = Typelib::compare(v_to.getData(), v_from.getData(), v_from.getType());
00644 return result ? Qtrue : Qfalse;
00645 } catch(std::exception const& e) {
00646 rb_raise(rb_eArgError, "%s", e.what());
00647 }
00648 }
00649
00650
00651 void typelib_ruby::Typelib_init_values()
00652 {
00653 VALUE mTypelib = rb_define_module("Typelib");
00654 rb_define_singleton_method(mTypelib, "do_copy", RUBY_METHOD_FUNC(typelib_do_copy), 2);
00655 rb_define_singleton_method(mTypelib, "compare", RUBY_METHOD_FUNC(typelib_compare), 2);
00656
00657 cType = rb_define_class_under(mTypelib, "Type", rb_cObject);
00658 rb_define_alloc_func(cType, value_alloc);
00659 rb_define_singleton_method(cType, "==", RUBY_METHOD_FUNC(type_equal_operator), 1);
00660 rb_define_singleton_method(cType, "size", RUBY_METHOD_FUNC(&type_size), 0);
00661 rb_define_singleton_method(cType, "do_memory_layout", RUBY_METHOD_FUNC(&type_memory_layout), 4);
00662 rb_define_singleton_method(cType, "do_dependencies", RUBY_METHOD_FUNC(&type_dependencies), 0);
00663 rb_define_singleton_method(cType, "casts_to?", RUBY_METHOD_FUNC(&type_can_cast_to), 1);
00664 rb_define_singleton_method(cType, "value_new", RUBY_METHOD_FUNC(&value_new), 0);
00665 rb_define_singleton_method(cType, "from_memory_zone", RUBY_METHOD_FUNC(&value_from_memory_zone), 1);
00666 rb_define_singleton_method(cType, "from_address", RUBY_METHOD_FUNC(&value_from_address), 1);
00667
00668 rb_define_method(cType, "do_from_buffer", RUBY_METHOD_FUNC(&value_do_from_buffer), 5);
00669 rb_define_method(cType, "zero!", RUBY_METHOD_FUNC(&value_zero), 0);
00670 rb_define_method(cType, "memory_eql?", RUBY_METHOD_FUNC(&value_memory_eql_p), 1);
00671 rb_define_method(cType, "endian_swap", RUBY_METHOD_FUNC(&value_endian_swap), 0);
00672 rb_define_method(cType, "endian_swap!", RUBY_METHOD_FUNC(&value_endian_swap_b), 0);
00673 rb_define_method(cType, "zone_address", RUBY_METHOD_FUNC(&value_address), 0);
00674 rb_define_method(cType, "do_cast", RUBY_METHOD_FUNC(&value_do_cast), 1);
00675 rb_define_method(cType, "do_invalidate", RUBY_METHOD_FUNC(&value_invalidate), 0);
00676
00677 rb_define_singleton_method(mTypelib, "do_basename", RUBY_METHOD_FUNC(typelib_do_basename), 1);
00678 rb_define_singleton_method(mTypelib, "do_namespace", RUBY_METHOD_FUNC(typelib_do_namespace), 1);
00679 rb_define_singleton_method(mTypelib, "split_typename", RUBY_METHOD_FUNC(typelib_do_split_name), 1);
00680
00681 rb_define_singleton_method(cType, "to_csv", RUBY_METHOD_FUNC(type_to_csv), -1);
00682 rb_define_method(cType, "to_csv", RUBY_METHOD_FUNC(value_to_csv), -1);
00683 rb_define_method(cType, "do_byte_array", RUBY_METHOD_FUNC(value_do_byte_array), 4);
00684 rb_define_method(cType, "marshalling_size", RUBY_METHOD_FUNC(value_marshalling_size), 0);
00685
00686 Typelib_init_specialized_types();
00687 }
00688