diff options
-rw-r--r-- | ext/AbstractMemory.c | 11 | ||||
-rw-r--r-- | lib/ffi/ffi.rb | 2 | ||||
-rw-r--r-- | lib/ffi/platform.rb | 2 | ||||
-rw-r--r-- | lib/ffi/struct.rb | 263 | ||||
-rw-r--r-- | samples/gettimeofday.rb | 12 |
5 files changed, 283 insertions, 7 deletions
diff --git a/ext/AbstractMemory.c b/ext/AbstractMemory.c index a9be9f8..2fb7ef1 100644 --- a/ext/AbstractMemory.c +++ b/ext/AbstractMemory.c @@ -63,11 +63,12 @@ memory_get_array_of_##name(VALUE self, VALUE offset, VALUE length) \ return retVal; \ } -#define INT(type, toNative, fromNative) NUM_OP(type, type##_t, toNative, fromNative); \ - NUM_OP(u##type, u_##type##_t, toNative, fromNative) -INT(int8, NUM2INT, INT2FIX); -INT(int16, NUM2INT, INT2FIX); -INT(int32, NUM2INT, INT2FIX); +NUM_OP(int8, int8_t, NUM2INT, INT2NUM); +NUM_OP(uint8, u_int8_t, NUM2UINT, UINT2NUM); +NUM_OP(int16, int16_t, NUM2INT, INT2NUM); +NUM_OP(uint16, u_int16_t, NUM2UINT, UINT2NUM); +NUM_OP(int32, int32_t, NUM2INT, INT2NUM); +NUM_OP(uint32, u_int32_t, NUM2UINT, UINT2NUM); NUM_OP(int64, int64_t, NUM2LL, LL2NUM); NUM_OP(uint64, u_int64_t, NUM2ULL, ULL2NUM); NUM_OP(float32, float, NUM2DBL, rb_float_new); diff --git a/lib/ffi/ffi.rb b/lib/ffi/ffi.rb index e9a1aa8..1d8cd1a 100644 --- a/lib/ffi/ffi.rb +++ b/lib/ffi/ffi.rb @@ -52,9 +52,9 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. require 'ffi.so' -puts "Loading ffi/platform" require 'ffi/platform' require 'ffi/memorypointer' +require 'ffi/struct' module FFI # Specialised error classes diff --git a/lib/ffi/platform.rb b/lib/ffi/platform.rb index f10e0f3..cc86d40 100644 --- a/lib/ffi/platform.rb +++ b/lib/ffi/platform.rb @@ -5,7 +5,6 @@ module FFI OS =~ /#{os}/ ? true : false end public - puts "Platform ruby init" NAME = "#{ARCH}-#{OS}" IS_LINUX = is_os("linux") IS_MAC = is_os("darwin") @@ -21,5 +20,6 @@ module FFI when /(linux|.*bsd)/ '.so' end + end end
\ No newline at end of file diff --git a/lib/ffi/struct.rb b/lib/ffi/struct.rb new file mode 100644 index 0000000..b9bc240 --- /dev/null +++ b/lib/ffi/struct.rb @@ -0,0 +1,263 @@ +require 'ffi/platform' +module FFI + class StructLayout + def initialize(fields, size) + @fields = fields + @size = size + end + + def [](name) + @fields[name] + end + def size + @size + end + end + class StructLayoutBuilder + LONG_ALIGN = Platform::ARCH =~ /sparc.*/ ? 64 : Platform::LONG_SIZE + ADDRESS_ALIGN = Platform::ARCH =~ /sparc.*/ ? 64 : Platform::ADDRESS_SIZE + FLOAT_ALIGN = Platform::ARCH =~ /sparc.*/ ? 64 : Platform::ADDRESS_SIZE + DOUBLE_ALIGN = Platform::ARCH =~ /sparc.*/ ? 64 : Platform::ADDRESS_SIZE + class Field + def initialize(off) + @off = off + end + + def offset + @off + end + def size + self.size + end + def align + self.align + end + def self.align + self.size + end + end + class Signed8 < Field + def self.size; 8; end + def set(ptr, val) + ptr.set_int8(@off, val) + end + def get(ptr) + ptr.get_int8(@off) + end + end + class Unsigned8 < Field + def self.size; 8; end + def set(ptr, val) + ptr.set_uint8(@off, val) + end + def get(ptr) + ptr.get_uint8(@off) + end + end + class Signed16 < Field + def self.size; 16; end + def set(ptr, val) + ptr.set_int16(@off, val) + end + def get(ptr) + ptr.get_int16(@off) + end + end + class Unsigned16 < Field + def self.size; 16; end + def set(ptr, val) + ptr.set_uint16(@off, val) + end + def get(ptr) + ptr.get_uint16(@off) + end + end + class Signed32 < Field + def self.size; 32; end + def set(ptr, val) + ptr.set_int32(@off, val) + end + def get(ptr) + ptr.get_int32(@off) + end + end + class Unsigned32 < Field + def self.size; 32; end + def set(ptr, val) + ptr.set_uint32(@off, val) + end + def get(ptr) + ptr.get_uint32(@off) + end + end + class Signed64 < Field + def self.size; 64; end + def self.align; LONG_ALIGN; end + def set(ptr, val) + ptr.set_int64(@off, val) + end + def get(ptr) + ptr.get_int64(@off) + end + end + class Unsigned64 < Field + def self.size; 64; end + def self.align; LONG_ALIGN; end + def set(ptr, val) + ptr.set_uint64(@off, val) + end + def get(ptr) + ptr.get_uint64(@off) + end + end + def initialize + @fields = {} + @size = 0 + end + def add_field(name, type, offset=nil) + field_class = case type + when :char, NativeType::INT8 + Signed8 + when :uchar, NativeType::UINT8 + Unsigned8 + when :short, NativeType::INT16 + Signed16 + when :ushort, NativeType::UINT16 + Unsigned16 + when :long, NativeType::LONG + FFI::Platform::LONG_SIZE == 32 ? Signed32 : Signed64 + when :ulong, NativeType::ULONG + FFI::Platform::LONG_SIZE == 32 ? Unsigned32 : Unsigned64 + when :int, NativeType::INT32 + Signed32 + when :uint, NativeType::UINT32 + Unsigned32 + when :long_long, NativeType::INT64 + Signed64 + when :ulong_long, NativeType::UINT64 + Unsigned64 + else + raise ArgumentError, "Unknown type: #{type}" + end + + size = field_class.size / 8 + off = offset ? offset.to_i : align(@size, field_class.align) + @fields[name] = field_class.new(off) + @size = off + size + end + def build + StructLayout.new @fields, @size + end + def align(offset, bits) + bytes = bits / 8 + mask = bytes - 1; + off = offset; + ((off & mask) != 0) ? (off & ~mask) + bytes : off + end + end + class BaseStruct + Buffer = FFI::MemoryPointer + attr_reader :pointer + + def initialize(pointer = nil, *spec) + @cspec = self.class.layout(*spec) + + if pointer then + @pointer = pointer + else + @pointer = MemoryPointer.new size + end + end + def self.alloc_inout(clear = true) + self.new(Buffer.alloc_inout(@size, 1, clear)) + end + def self.alloc_in(clear = true) + self.new(Buffer.alloc_in(@size, 1, clear)) + end + def self.alloc_out(clear = true) + self.new(Buffer.alloc_out(@size, 1, clear)) + end + def self.size + @size + end + def self.members + @layout.members + end + def size + self.class.size + end + def [](field) + @cspec[field].get(@pointer) + end + def []=(field, val) + @cspec[field].put(@pointer, val) + end + def members + @cspec.members + end + def values + @cspec.members.map { |m| self[m] } + end + def clear + @pointer.clear + self + end + end + class Struct < BaseStruct + def self.jruby_layout(spec) + raise "Ruby version not supported" if RUBY_VERSION =~ /1.8.*/ + builder = FFI::StructLayoutBuilder.new + spec[0].each do |name,type| + builder.add_field(name, FFI.find_type(type)) + end + builder.build + end + def self.rubinius_layout(spec) + builder = FFI::StructLayoutBuilder.new + i = 0 + while i < spec.size + name, type, offset = spec[i, 3] + + code = FFI.find_type(type) + builder.add_field(name, code, offset) + i += 3 + end + builder.build + end + def self.layout(*spec) + + return @layout if spec.size == 0 + cspec = spec[0].kind_of?(Hash) ? jruby_layout(spec) : rubinius_layout(spec) + + @layout = cspec unless self == FFI::Struct + @size = cspec.size + return cspec + end + def self.config(base, *fields) + config = Config::CONFIG + @size = config["#{base}.sizeof"] + + builder = StructLayoutBuilder.new + + fields.each do |field| + offset = config["#{base}.#{field}.offset"] + size = config["#{base}.#{field}.size"] + type = config["#{base}.#{field}.type"] + type = type ? type.to_sym : FFI.size_to_type(size) + + code = FFI.find_type type + if (code == NativeType::CHAR_ARRAY) + builder.add_char_array(field.to_s, size, offset) + else + builder.add_field(field.to_s, code, offset) + end + end + cspec = builder.build + + @layout = cspec + @size = cspec.size if @size < cspec.size + + return cspec + end + end +end diff --git a/samples/gettimeofday.rb b/samples/gettimeofday.rb new file mode 100644 index 0000000..f9835b4 --- /dev/null +++ b/samples/gettimeofday.rb @@ -0,0 +1,12 @@ +require 'ffi' +class Timeval < FFI::Struct +# layout :tv_sec => :ulong, :tv_usec => :ulong + layout :tv_sec, :ulong, 0, :tv_usec, :ulong, 4 +end +module LibC + extend FFI::Library + attach_function :gettimeofday, [ :pointer, :pointer ], :int +end +t = Timeval.new +LibC.gettimeofday(t.pointer, nil) +puts "t.tv_sec=#{t[:tv_sec]} t.tv_usec=#{t[:tv_usec]}" |