ruby: add manual c++ object allocator

develop
jj 2012-04-25 19:22:04 +02:00
parent b0acad6d59
commit bd2e6e74de
3 changed files with 146 additions and 88 deletions

@ -17,6 +17,13 @@ class Compound < MemStruct
define_method(name) { struct._at(@_memaddr+offset)._get }
define_method("#{name}=") { |v| struct._at(@_memaddr+offset)._set(v) }
end
def _fields_ancestors
if superclass.respond_to?(:_fields_ancestors)
superclass._fields_ancestors + _fields.to_a
else
_fields.to_a
end
end
def number(bits, signed)
Number.new(bits, signed)
@ -86,22 +93,41 @@ class Compound < MemStruct
def sizeof(n)
@_sizeof = n
end
# allocate a new c++ object, return its ruby wrapper
def cpp_new
ptr = DFHack.malloc(_sizeof)
if _rtti_classname and vt = DFHack.rtti_getvtable(_rtti_classname)
DFHack.memory_write_int32(ptr, vt)
# TODO call constructor
end
new._at(ptr)._cpp_init
end
end
def _cpp_init
_fields.each { |n, o, s|
case s
when StlString
DFHack.memory_stlstring_init(@_memaddr+o)
when StlVector32
# englobes all vectors
DFHack.memory_vector_init(@_memaddr+o)
when Compound
send(n)._cpp_init
end
}
self
end
def _set(h) ; h.each { |k, v| send("_#{k}=", v) } ; end
def _fields ; self.class._fields.to_a ; end
def _fields_ancestors ; self.class._fields_ancestors.to_a ; end
def _rtti_classname ; self.class._rtti_classname ; end
def _sizeof ; self.class._sizeof ; end
def inspect
cn = self.class.name.sub(/^DFHack::/, '')
cn << ' @' << ('0x%X' % _memaddr) if cn != ''
out = "#<#{cn}"
fields = _fields
cls = self.class.superclass
while cls.respond_to?(:_fields)
fields += cls._fields.to_a
cls = cls.superclass
end
fields.each { |n, o, s|
_fields_ancestors.each { |n, o, s|
out << ' ' if out.length != 0 and out[-1, 1] != ' '
if out.length > 1024
out << '...'
@ -209,6 +235,17 @@ class Pointer < MemStruct
@_tg._at(addr)._get
end
# XXX shaky...
def _set(v)
if v.kind_of?(Pointer)
DFHack.memory_write_int32(@_memaddr, v._getv)
elsif v.kind_of?(MemStruct)
DFHack.memory_write_int32(@_memaddr, v._memaddr)
else
_getv._set(v)
end
end
# these ruby Object methods should be forwarded to the ptr
undef id
undef type

@ -413,7 +413,11 @@ static VALUE rb_dfget_vtable_ptr(VALUE self, VALUE objptr)
static VALUE rb_dfmalloc(VALUE self, VALUE len)
{
return rb_uint2inum((long)malloc(FIX2INT(len)));
char *ptr = (char*)malloc(FIX2INT(len));
if (!ptr)
rb_raise(rb_eRuntimeError, "no memory");
memset(ptr, 0, FIX2INT(len));
return rb_uint2inum((long)ptr);
}
static VALUE rb_dffree(VALUE self, VALUE ptr)
@ -422,17 +426,11 @@ static VALUE rb_dffree(VALUE self, VALUE ptr)
return Qtrue;
}
// memory reading (buffer)
static VALUE rb_dfmemory_read(VALUE self, VALUE addr, VALUE len)
{
return rb_str_new((char*)rb_num2ulong(addr), rb_num2ulong(len));
}
static VALUE rb_dfmemory_read_stlstring(VALUE self, VALUE addr)
{
std::string *s = (std::string*)rb_num2ulong(addr);
return rb_str_new(s->c_str(), s->length());
}
// memory reading (integers/floats)
static VALUE rb_dfmemory_read_int8(VALUE self, VALUE addr)
@ -464,13 +462,6 @@ static VALUE rb_dfmemory_write(VALUE self, VALUE addr, VALUE raw)
return Qtrue;
}
static VALUE rb_dfmemory_write_stlstring(VALUE self, VALUE addr, VALUE val)
{
std::string *s = (std::string*)rb_num2ulong(addr);
int strlen = FIX2INT(rb_funcall(val, rb_intern("length"), 0));
s->assign(rb_string_value_ptr(&val), strlen);
return Qtrue;
}
// memory writing (integers/floats)
static VALUE rb_dfmemory_write_int8(VALUE self, VALUE addr, VALUE val)
@ -496,7 +487,35 @@ static VALUE rb_dfmemory_write_float(VALUE self, VALUE addr, VALUE val)
}
// stl::string
static VALUE rb_dfmemory_stlstring_init(VALUE self, VALUE addr)
{
// XXX THIS IS TERRIBLE
std::string *ptr = new std::string;
memcpy((void*)rb_num2ulong(addr), (void*)ptr, sizeof(*ptr));
return Qtrue;
}
static VALUE rb_dfmemory_read_stlstring(VALUE self, VALUE addr)
{
std::string *s = (std::string*)rb_num2ulong(addr);
return rb_str_new(s->c_str(), s->length());
}
static VALUE rb_dfmemory_write_stlstring(VALUE self, VALUE addr, VALUE val)
{
std::string *s = (std::string*)rb_num2ulong(addr);
int strlen = FIX2INT(rb_funcall(val, rb_intern("length"), 0));
s->assign(rb_string_value_ptr(&val), strlen);
return Qtrue;
}
// vector access
static VALUE rb_dfmemory_vec_init(VALUE self, VALUE addr)
{
std::vector<uint8_t> *ptr = new std::vector<uint8_t>;
memcpy((void*)rb_num2ulong(addr), (void*)ptr, sizeof(*ptr));
return Qtrue;
}
// vector<uint8>
static VALUE rb_dfmemory_vec8_length(VALUE self, VALUE addr)
{
@ -646,19 +665,21 @@ static void ruby_bind_dfhack(void) {
rb_define_const(rb_cDFHack, "REBASE_DELTA", rb_dfrebase_delta());
rb_define_singleton_method(rb_cDFHack, "memory_read", RUBY_METHOD_FUNC(rb_dfmemory_read), 2);
rb_define_singleton_method(rb_cDFHack, "memory_read_stlstring", RUBY_METHOD_FUNC(rb_dfmemory_read_stlstring), 1);
rb_define_singleton_method(rb_cDFHack, "memory_read_int8", RUBY_METHOD_FUNC(rb_dfmemory_read_int8), 1);
rb_define_singleton_method(rb_cDFHack, "memory_read_int16", RUBY_METHOD_FUNC(rb_dfmemory_read_int16), 1);
rb_define_singleton_method(rb_cDFHack, "memory_read_int32", RUBY_METHOD_FUNC(rb_dfmemory_read_int32), 1);
rb_define_singleton_method(rb_cDFHack, "memory_read_float", RUBY_METHOD_FUNC(rb_dfmemory_read_float), 1);
rb_define_singleton_method(rb_cDFHack, "memory_write", RUBY_METHOD_FUNC(rb_dfmemory_write), 2);
rb_define_singleton_method(rb_cDFHack, "memory_write_stlstring", RUBY_METHOD_FUNC(rb_dfmemory_write_stlstring), 2);
rb_define_singleton_method(rb_cDFHack, "memory_write_int8", RUBY_METHOD_FUNC(rb_dfmemory_write_int8), 2);
rb_define_singleton_method(rb_cDFHack, "memory_write_int16", RUBY_METHOD_FUNC(rb_dfmemory_write_int16), 2);
rb_define_singleton_method(rb_cDFHack, "memory_write_int32", RUBY_METHOD_FUNC(rb_dfmemory_write_int32), 2);
rb_define_singleton_method(rb_cDFHack, "memory_write_float", RUBY_METHOD_FUNC(rb_dfmemory_write_float), 2);
rb_define_singleton_method(rb_cDFHack, "memory_stlstring_init", RUBY_METHOD_FUNC(rb_dfmemory_stlstring_init), 1);
rb_define_singleton_method(rb_cDFHack, "memory_read_stlstring", RUBY_METHOD_FUNC(rb_dfmemory_read_stlstring), 1);
rb_define_singleton_method(rb_cDFHack, "memory_write_stlstring", RUBY_METHOD_FUNC(rb_dfmemory_write_stlstring), 2);
rb_define_singleton_method(rb_cDFHack, "memory_vector_init", RUBY_METHOD_FUNC(rb_dfmemory_vec_init), 1);
rb_define_singleton_method(rb_cDFHack, "memory_vector8_length", RUBY_METHOD_FUNC(rb_dfmemory_vec8_length), 1);
rb_define_singleton_method(rb_cDFHack, "memory_vector8_ptrat", RUBY_METHOD_FUNC(rb_dfmemory_vec8_ptrat), 2);
rb_define_singleton_method(rb_cDFHack, "memory_vector8_insert", RUBY_METHOD_FUNC(rb_dfmemory_vec8_insert), 3);

@ -31,11 +31,11 @@ module DFHack
nil
end
def p(*a)
a.each { |e|
puts e.inspect
}
end
def p(*a)
a.each { |e|
puts_err e.inspect
}
end
end
# register a callback to be called every gframe or more
@ -114,61 +114,61 @@ module DFHack
end
end
def map_designation_at(x, y=nil, z=nil)
x = x.pos if x.respond_to?(:pos)
x, y, z = x.x, x.y, x.z if x.respond_to?(:x)
if b = map_block_at(x, y, z)
b.designation[x%16][y%16]
end
end
def map_designation_at(x, y=nil, z=nil)
x = x.pos if x.respond_to?(:pos)
x, y, z = x.x, x.y, x.z if x.respond_to?(:x)
if b = map_block_at(x, y, z)
b.designation[x%16][y%16]
end
end
def map_occupancy_at(x, y=nil, z=nil)
x = x.pos if x.respond_to?(:pos)
x, y, z = x.x, x.y, x.z if x.respond_to?(:x)
if b = map_block_at(x, y, z)
b.occupancy[x%16][y%16]
end
end
def map_occupancy_at(x, y=nil, z=nil)
x = x.pos if x.respond_to?(:pos)
x, y, z = x.x, x.y, x.z if x.respond_to?(:x)
if b = map_block_at(x, y, z)
b.occupancy[x%16][y%16]
end
end
# yields every map block
def each_map_block
(0...world.map.x_count_block).each { |xb|
xl = world.map.block_index[xb]
(0...world.map.y_count_block).each { |yb|
yl = xl[yb]
(0...world.map.z_count_block).each { |z|
p = yl[z]
yield p._getv if p._getp != 0
}
}
}
end
# yields every map block
def each_map_block
(0...world.map.x_count_block).each { |xb|
xl = world.map.block_index[xb]
(0...world.map.y_count_block).each { |yb|
yl = xl[yb]
(0...world.map.z_count_block).each { |z|
p = yl[z]
yield p._getv if p._getp != 0
}
}
}
end
# yields every map block for a given z level
def each_map_block_z(z)
(0...world.map.x_count_block).each { |xb|
xl = world.map.block_index[xb]
(0...world.map.y_count_block).each { |yb|
p = xl[yb][z]
yield p._getv if p._getp != 0
}
}
end
# yields every map block for a given z level
def each_map_block_z(z)
(0...world.map.x_count_block).each { |xb|
xl = world.map.block_index[xb]
(0...world.map.y_count_block).each { |yb|
p = xl[yb][z]
yield p._getv if p._getp != 0
}
}
end
# return true if the argument is under the cursor
def at_cursor?(obj)
same_pos?(obj, cursor)
end
# return true if the argument is under the cursor
def at_cursor?(obj)
same_pos?(obj, cursor)
end
# returns true if both arguments are at the same x/y/z
def same_pos?(pos1, pos2)
pos1 = pos1.pos if pos1.respond_to?(:pos)
pos2 = pos2.pos if pos2.respond_to?(:pos)
pos1.x == pos2.x and pos1.y == pos2.y and pos1.z == pos2.z
end
# returns true if both arguments are at the same x/y/z
def same_pos?(pos1, pos2)
pos1 = pos1.pos if pos1.respond_to?(:pos)
pos2 = pos2.pos if pos2.respond_to?(:pos)
pos1.x == pos2.x and pos1.y == pos2.y and pos1.z == pos2.z
end
# center the DF screen on something
# updates the cursor position if visible
# center the DF screen on something
# updates the cursor position if visible
def center_viewscreen(x, y=nil, z=nil)
x = x.pos if x.respond_to?(:pos)
x, y, z = x.x, x.y, x.z if x.respond_to?(:x)
@ -207,10 +207,10 @@ module DFHack
# add an announcement
# color = integer, bright = bool
def add_announcement(str, color=0, bright=false)
def add_announcement(str, color=7, bright=false)
cont = false
while str.length > 0
rep = Report.cpp_alloc
rep = Report.cpp_new
rep.color = color
rep.bright = ((bright && bright != 0) ? 1 : 0)
rep.year = cur_year
@ -228,15 +228,15 @@ module DFHack
end
end
# try to match a user-specified name to one from the raws
# uses case-switching and substring matching
# eg match_rawname('coal', ['COAL_BITUMINOUS', 'BAUXITE']) => 'COAL_BITUMINOUS'
def match_rawname(name, rawlist)
rawlist.each { |r| return r if name == r }
rawlist.each { |r| return r if name.downcase == r.downcase }
may = rawlist.find_all { |r| r.downcase.index(name.downcase) }
may.first if may.length == 1
end
# try to match a user-specified name to one from the raws
# uses case-switching and substring matching
# eg match_rawname('coal', ['COAL_BITUMINOUS', 'BAUXITE']) => 'COAL_BITUMINOUS'
def match_rawname(name, rawlist)
rawlist.each { |r| return r if name == r }
rawlist.each { |r| return r if name.downcase == r.downcase }
may = rawlist.find_all { |r| r.downcase.index(name.downcase) }
may.first if may.length == 1
end
def test
puts "starting"