Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions ext/fiddle/pointer.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,13 @@ rb_fiddle_ptr_new(void *ptr, long size, freefunc_t func)
}

static VALUE
rb_fiddle_ptr_malloc(long size, freefunc_t func)
rb_fiddle_ptr_malloc(VALUE klass, long size, freefunc_t func)
{
void *ptr;

ptr = ruby_xmalloc((size_t)size);
memset(ptr,0,(size_t)size);
return rb_fiddle_ptr_new(ptr, size, func);
return rb_fiddle_ptr_new2(klass, ptr, size, func);
}

static void *
Expand Down Expand Up @@ -268,7 +268,7 @@ rb_fiddle_ptr_s_malloc(int argc, VALUE argv[], VALUE klass)
rb_bug("rb_fiddle_ptr_s_malloc");
}

obj = rb_fiddle_ptr_malloc(s,f);
obj = rb_fiddle_ptr_malloc(klass, s,f);
if (wrap) RPTR_DATA(obj)->wrap[1] = wrap;

if (rb_block_given_p()) {
Expand Down
77 changes: 48 additions & 29 deletions lib/fiddle/struct.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
require 'fiddle/pack'

module Fiddle
# C struct shell
# A base class for objects representing a C structure
class CStruct
# accessor to Fiddle::CStructEntity
def CStruct.entity_class
CStructEntity
end
end

# C union shell
# A base class for objects representing a C union
class CUnion
# accessor to Fiddle::CUnionEntity
def CUnion.entity_class
Expand Down Expand Up @@ -62,7 +62,7 @@ module CStructBuilder
# Fiddle::Importer#struct and Fiddle::Importer#union wrap this functionality in an
# easy-to-use manner.
#
# Example:
# Examples:
#
# require 'fiddle/struct'
# require 'fiddle/cparser'
Expand All @@ -73,6 +73,17 @@ module CStructBuilder
#
# MyStruct = Fiddle::CStructBuilder.create(Fiddle::CUnion, types, members)
#
# MyStruct.malloc(Fiddle::RUBY_FREE) do |obj|
# ...
# end
#
# obj = MyStruct.malloc(Fiddle::RUBY_FREE)
# begin
# ...
# ensure
# obj.call_free
# end
#
# obj = MyStruct.malloc
# begin
# ...
Expand All @@ -82,8 +93,12 @@ module CStructBuilder
#
def create(klass, types, members)
new_class = Class.new(klass){
define_method(:initialize){|addr|
@entity = klass.entity_class.new(addr, types)
define_method(:initialize){|addr, func = nil|
if addr.is_a?(self.class.entity_class)
@entity = addr
else
@entity = self.class.entity_class.new(addr, types, func)
end
@entity.assign_names(members)
}
define_method(:[]) { |*args| @entity.send(:[], *args) }
Expand All @@ -94,33 +109,42 @@ def create(klass, types, members)
define_method(name){ @entity[name] }
define_method(name + "="){|val| @entity[name] = val }
}
}
size = klass.entity_class.size(types)
new_class.module_eval(<<-EOS, __FILE__, __LINE__+1)
def new_class.size()
#{size}
end
def new_class.malloc()
addr = Fiddle.malloc(#{size})
new(addr)
size = klass.entity_class.size(types)
define_singleton_method(:size) { size }
define_singleton_method(:malloc) do |func=nil|
if block_given?
entity_class.malloc(types, func, size) do |entity|
yield new(entity)
end
else
new(entity_class.malloc(types, func, size))
end
end
EOS
}
return new_class
end
module_function :create
end

# A C struct wrapper
# A pointer to a C structure
class CStructEntity < Fiddle::Pointer
include PackInfo
include ValueUtil

# Allocates a C struct with the +types+ provided.
#
# See Fiddle::Pointer.malloc for memory management issues.
def CStructEntity.malloc(types, func = nil)
addr = Fiddle.malloc(CStructEntity.size(types))
CStructEntity.new(addr, types, func)
def CStructEntity.malloc(types, func = nil, size = size(types), &block)
if block_given?
super(size, func) do |struct|
struct.set_ctypes types
yield struct
end
else
struct = super(size, func)
struct.set_ctypes types
struct
end
end

# Returns the offset for the packed sizes for the given +types+.
Expand Down Expand Up @@ -152,6 +176,9 @@ def CStructEntity.size(types)
#
# See also Fiddle::Pointer.new
def initialize(addr, types, func = nil)
if func && addr.is_a?(Pointer) && addr.free
raise ArgumentError, 'free function specified on both underlying struct Pointer and when creating a CStructEntity - who do you want to free this?'
end
set_ctypes(types)
super(addr, @size, func)
end
Expand Down Expand Up @@ -261,23 +288,16 @@ def []=(*args)
end
end

undef_method :size=
def to_s() # :nodoc:
super(@size)
end
end

# A C union wrapper
# A pointer to a C union
class CUnionEntity < CStructEntity
include PackInfo

# Allocates a C union the +types+ provided.
#
# See Fiddle::Pointer.malloc for memory management issues.
def CUnionEntity.malloc(types, func=nil)
addr = Fiddle.malloc(CUnionEntity.size(types))
CUnionEntity.new(addr, types, func)
end

# Returns the size needed for the union with the given +types+.
#
# Fiddle::CUnionEntity.size(
Expand All @@ -300,4 +320,3 @@ def set_ctypes(types)
end
end
end

127 changes: 106 additions & 21 deletions test/fiddle/test_c_struct_entry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,37 +43,122 @@ def test_class_size_with_count
end

def test_set_ctypes
union = CStructEntity.malloc [TYPE_INT, TYPE_LONG], Fiddle::RUBY_FREE
union.assign_names %w[int long]
CStructEntity.malloc([TYPE_INT, TYPE_LONG], Fiddle::RUBY_FREE) do |struct|
struct.assign_names %w[int long]

# this test is roundabout because the stored ctypes are not accessible
union['long'] = 1
union['int'] = 2
# this test is roundabout because the stored ctypes are not accessible
struct['long'] = 1
struct['int'] = 2

assert_equal 1, union['long']
assert_equal 2, union['int']
assert_equal 1, struct['long']
assert_equal 2, struct['int']
end
end

def test_aref_pointer_array
team = CStructEntity.malloc([[TYPE_VOIDP, 2]], Fiddle::RUBY_FREE)
team.assign_names(["names"])
Fiddle::Pointer.malloc(6, Fiddle::RUBY_FREE) do |alice|
alice[0, 6] = "Alice\0"
Fiddle::Pointer.malloc(4, Fiddle::RUBY_FREE) do |bob|
bob[0, 4] = "Bob\0"
team["names"] = [alice, bob]
assert_equal(["Alice", "Bob"], team["names"].map(&:to_s))
CStructEntity.malloc([[TYPE_VOIDP, 2]], Fiddle::RUBY_FREE) do |team|
team.assign_names(["names"])
Fiddle::Pointer.malloc(6, Fiddle::RUBY_FREE) do |alice|
alice[0, 6] = "Alice\0"
Fiddle::Pointer.malloc(4, Fiddle::RUBY_FREE) do |bob|
bob[0, 4] = "Bob\0"
team["names"] = [alice, bob]
assert_equal(["Alice", "Bob"], team["names"].map(&:to_s))
end
end
end
end

def test_aref_pointer
user = CStructEntity.malloc([TYPE_VOIDP], Fiddle::RUBY_FREE)
user.assign_names(["name"])
Fiddle::Pointer.malloc(6, Fiddle::RUBY_FREE) do |alice|
alice[0, 6] = "Alice\0"
user["name"] = alice
assert_equal("Alice", user["name"].to_s)
CStructEntity.malloc([TYPE_VOIDP], Fiddle::RUBY_FREE) do |user|
user.assign_names(["name"])
Fiddle::Pointer.malloc(6, Fiddle::RUBY_FREE) do |alice|
alice[0, 6] = "Alice\0"
user["name"] = alice
assert_equal("Alice", user["name"].to_s)
end
end
end

def test_new_double_free
types = [TYPE_INT]
Pointer.malloc(CStructEntity.size(types), Fiddle::RUBY_FREE) do |pointer|
assert_raise ArgumentError do
CStructEntity.new(pointer, types, Fiddle::RUBY_FREE)
end
end
end

def test_malloc_block
escaped_struct = nil
returned = CStructEntity.malloc([TYPE_INT], Fiddle::RUBY_FREE) do |struct|
assert_equal Fiddle::SIZEOF_INT, struct.size
assert_equal Fiddle::RUBY_FREE, struct.free.to_i
escaped_struct = struct
:returned
end
assert_equal :returned, returned
assert escaped_struct.freed?
end

def test_malloc_block_no_free
assert_raise ArgumentError do
CStructEntity.malloc([TYPE_INT]) { |struct| }
end
end

def test_free
struct = CStructEntity.malloc([TYPE_INT])
begin
assert_nil struct.free
ensure
Fiddle.free struct
end
end

def test_free_with_func
struct = CStructEntity.malloc([TYPE_INT], Fiddle::RUBY_FREE)
refute struct.freed?
struct.call_free
assert struct.freed?
struct.call_free # you can safely run it again
assert struct.freed?
GC.start # you can safely run the GC routine
assert struct.freed?
end

def test_free_with_no_func
struct = CStructEntity.malloc([TYPE_INT])
refute struct.freed?
struct.call_free
refute struct.freed?
struct.call_free # you can safely run it again
refute struct.freed?
end

def test_freed?
struct = CStructEntity.malloc([TYPE_INT], Fiddle::RUBY_FREE)
refute struct.freed?
struct.call_free
assert struct.freed?
end

def test_null?
struct = CStructEntity.malloc([TYPE_INT], Fiddle::RUBY_FREE)
refute struct.null?
end

def test_size
CStructEntity.malloc([TYPE_INT], Fiddle::RUBY_FREE) do |struct|
assert_equal Fiddle::SIZEOF_INT, struct.size
end
end

def test_size=
CStructEntity.malloc([TYPE_INT], Fiddle::RUBY_FREE) do |struct|
assert_raise NoMethodError do
struct.size = 1
end
end
end
end
Expand Down
15 changes: 8 additions & 7 deletions test/fiddle/test_c_union_entity.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,16 @@ def test_class_size_with_count
end

def test_set_ctypes
union = CUnionEntity.malloc [TYPE_INT, TYPE_LONG], Fiddle::RUBY_FREE
union.assign_names %w[int long]
CUnionEntity.malloc([TYPE_INT, TYPE_LONG], Fiddle::RUBY_FREE) do |union|
union.assign_names %w[int long]

# this test is roundabout because the stored ctypes are not accessible
union['long'] = 1
assert_equal 1, union['long']
# this test is roundabout because the stored ctypes are not accessible
union['long'] = 1
assert_equal 1, union['long']

union['int'] = 1
assert_equal 1, union['int']
union['int'] = 1
assert_equal 1, union['int']
end
end
end
end if defined?(Fiddle)
Loading