From 9b0c36b39032cffff3c62a2b0e1fc38fa429f5ea Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Fri, 11 Dec 2020 09:41:12 +0900 Subject: Import fiddle-1.0.4 (#3860) I don't use tool/sync_default_gem.rb because the last sync was incomplete. Co-authored-by: Hiroshi SHIBATA Co-authored-by: Alan Wu Co-authored-by: sinisterchipmunk Co-authored-by: Sutou Kouhei --- ext/fiddle/lib/fiddle/cparser.rb | 115 ++++++++++++++---- ext/fiddle/lib/fiddle/function.rb | 5 + ext/fiddle/lib/fiddle/struct.rb | 249 +++++++++++++++++++++++++++++++------- ext/fiddle/lib/fiddle/version.rb | 2 +- 4 files changed, 303 insertions(+), 68 deletions(-) (limited to 'ext/fiddle/lib/fiddle') diff --git a/ext/fiddle/lib/fiddle/cparser.rb b/ext/fiddle/lib/fiddle/cparser.rb index cd0a64fef5..8a269393c6 100644 --- a/ext/fiddle/lib/fiddle/cparser.rb +++ b/ext/fiddle/lib/fiddle/cparser.rb @@ -35,12 +35,37 @@ module Fiddle def parse_struct_signature(signature, tymap=nil) if signature.is_a?(String) signature = split_arguments(signature, /[,;]/) + elsif signature.is_a?(Hash) + signature = [signature] end mems = [] tys = [] signature.each{|msig| - msig = compact(msig) + msig = compact(msig) if msig.is_a?(String) case msig + when Hash + msig.each do |struct_name, struct_signature| + struct_name = struct_name.to_s if struct_name.is_a?(Symbol) + struct_name = compact(struct_name) + struct_count = nil + if struct_name =~ /^([\w\*\s]+)\[(\d+)\]$/ + struct_count = $2.to_i + struct_name = $1 + end + if struct_signature.respond_to?(:entity_class) + struct_type = struct_signature + else + parsed_struct = parse_struct_signature(struct_signature, tymap) + struct_type = CStructBuilder.create(CStruct, *parsed_struct) + end + if struct_count + ty = [struct_type, struct_count] + else + ty = struct_type + end + mems.push([struct_name, struct_type.members]) + tys.push(ty) + end when /^[\w\*\s]+[\*\s](\w+)$/ mems.push($1) tys.push(parse_ctype(msig, tymap)) @@ -128,50 +153,90 @@ module Fiddle return [parse_ctype(ty[0], tymap), ty[1]] when 'void' return TYPE_VOID - when /^(?:(?:signed\s+)?long\s+long(?:\s+int\s+)?|int64_t)(?:\s+\w+)?$/ - if( defined?(TYPE_LONG_LONG) ) - return TYPE_LONG_LONG - else + when /\A(?:(?:signed\s+)?long\s+long(?:\s+int\s+)?|int64_t)(?:\s+\w+)?\z/ + unless Fiddle.const_defined?(:TYPE_LONG_LONG) raise(RuntimeError, "unsupported type: #{ty}") end - when /^(?:unsigned\s+long\s+long(?:\s+int\s+)?|uint64_t)(?:\s+\w+)?$/ - if( defined?(TYPE_LONG_LONG) ) - return -TYPE_LONG_LONG - else + return TYPE_LONG_LONG + when /\A(?:unsigned\s+long\s+long(?:\s+int\s+)?|uint64_t)(?:\s+\w+)?\z/ + unless Fiddle.const_defined?(:TYPE_LONG_LONG) raise(RuntimeError, "unsupported type: #{ty}") end - when /^(?:signed\s+)?long(?:\s+int\s+)?(?:\s+\w+)?$/ + return -TYPE_LONG_LONG + when /\A(?:signed\s+)?long(?:\s+int\s+)?(?:\s+\w+)?\z/ return TYPE_LONG - when /^unsigned\s+long(?:\s+int\s+)?(?:\s+\w+)?$/ + when /\Aunsigned\s+long(?:\s+int\s+)?(?:\s+\w+)?\z/ return -TYPE_LONG - when /^(?:signed\s+)?int(?:\s+\w+)?$/ + when /\A(?:signed\s+)?int(?:\s+\w+)?\z/ return TYPE_INT - when /^(?:unsigned\s+int|uint)(?:\s+\w+)?$/ + when /\A(?:unsigned\s+int|uint)(?:\s+\w+)?\z/ return -TYPE_INT - when /^(?:signed\s+)?short(?:\s+int\s+)?(?:\s+\w+)?$/ + when /\A(?:signed\s+)?short(?:\s+int\s+)?(?:\s+\w+)?\z/ return TYPE_SHORT - when /^unsigned\s+short(?:\s+int\s+)?(?:\s+\w+)?$/ + when /\Aunsigned\s+short(?:\s+int\s+)?(?:\s+\w+)?\z/ return -TYPE_SHORT - when /^(?:signed\s+)?char(?:\s+\w+)?$/ + when /\A(?:signed\s+)?char(?:\s+\w+)?\z/ return TYPE_CHAR - when /^unsigned\s+char(?:\s+\w+)?$/ + when /\Aunsigned\s+char(?:\s+\w+)?\z/ return -TYPE_CHAR - when /^float(?:\s+\w+)?$/ + when /\Aint8_t(?:\s+\w+)?\z/ + unless Fiddle.const_defined?(:TYPE_INT8_T) + raise(RuntimeError, "unsupported type: #{ty}") + end + return TYPE_INT8_T + when /\Auint8_t(?:\s+\w+)?\z/ + unless Fiddle.const_defined?(:TYPE_INT8_T) + raise(RuntimeError, "unsupported type: #{ty}") + end + return -TYPE_INT8_T + when /\Aint16_t(?:\s+\w+)?\z/ + unless Fiddle.const_defined?(:TYPE_INT16_T) + raise(RuntimeError, "unsupported type: #{ty}") + end + return TYPE_INT16_T + when /\Auint16_t(?:\s+\w+)?\z/ + unless Fiddle.const_defined?(:TYPE_INT16_T) + raise(RuntimeError, "unsupported type: #{ty}") + end + return -TYPE_INT16_T + when /\Aint32_t(?:\s+\w+)?\z/ + unless Fiddle.const_defined?(:TYPE_INT32_T) + raise(RuntimeError, "unsupported type: #{ty}") + end + return TYPE_INT32_T + when /\Auint32_t(?:\s+\w+)?\z/ + unless Fiddle.const_defined?(:TYPE_INT32_T) + raise(RuntimeError, "unsupported type: #{ty}") + end + return -TYPE_INT32_T + when /\Aint64_t(?:\s+\w+)?\z/ + unless Fiddle.const_defined?(:TYPE_INT64_T) + raise(RuntimeError, "unsupported type: #{ty}") + end + return TYPE_INT64_T + when /\Auint64_t(?:\s+\w+)?\z/ + unless Fiddle.const_defined?(:TYPE_INT64_T) + raise(RuntimeError, "unsupported type: #{ty}") + end + return -TYPE_INT64_T + when /\Afloat(?:\s+\w+)?\z/ return TYPE_FLOAT - when /^double(?:\s+\w+)?$/ + when /\Adouble(?:\s+\w+)?\z/ return TYPE_DOUBLE - when /^size_t(?:\s+\w+)?$/ + when /\Asize_t(?:\s+\w+)?\z/ return TYPE_SIZE_T - when /^ssize_t(?:\s+\w+)?$/ + when /\Assize_t(?:\s+\w+)?\z/ return TYPE_SSIZE_T - when /^ptrdiff_t(?:\s+\w+)?$/ + when /\Aptrdiff_t(?:\s+\w+)?\z/ return TYPE_PTRDIFF_T - when /^intptr_t(?:\s+\w+)?$/ + when /\Aintptr_t(?:\s+\w+)?\z/ return TYPE_INTPTR_T - when /^uintptr_t(?:\s+\w+)?$/ + when /\Auintptr_t(?:\s+\w+)?\z/ return TYPE_UINTPTR_T when /\*/, /\[[\s\d]*\]/ return TYPE_VOIDP + when "..." + return TYPE_VARIADIC else ty = ty.split(' ', 2)[0] if( tymap[ty] ) @@ -186,7 +251,7 @@ module Fiddle def split_arguments(arguments, sep=',') return [] if arguments.strip == 'void' - arguments.scan(/([\w\*\s]+\(\*\w*\)\(.*?\)|[\w\*\s\[\]]+)(?:#{sep}\s*|$)/).collect {|m| m[0]} + arguments.scan(/([\w\*\s]+\(\*\w*\)\(.*?\)|[\w\*\s\[\]]+|\.\.\.)(?:#{sep}\s*|\z)/).collect {|m| m[0]} end def compact(signature) diff --git a/ext/fiddle/lib/fiddle/function.rb b/ext/fiddle/lib/fiddle/function.rb index dd5e04e417..0f9913adeb 100644 --- a/ext/fiddle/lib/fiddle/function.rb +++ b/ext/fiddle/lib/fiddle/function.rb @@ -10,6 +10,11 @@ module Fiddle # The name of this function attr_reader :name + # Whether GVL is needed to call this function + def need_gvl? + @need_gvl + end + # The integer memory location of this function def to_i ptr.to_i diff --git a/ext/fiddle/lib/fiddle/struct.rb b/ext/fiddle/lib/fiddle/struct.rb index 259903d25c..a766eba83b 100644 --- a/ext/fiddle/lib/fiddle/struct.rb +++ b/ext/fiddle/lib/fiddle/struct.rb @@ -4,15 +4,72 @@ require 'fiddle/value' require 'fiddle/pack' module Fiddle - # C struct shell + # A base class for objects representing a C structure class CStruct + include Enumerable + # accessor to Fiddle::CStructEntity def CStruct.entity_class CStructEntity end + + def each + return enum_for(__function__) unless block_given? + + self.class.members.each do |name,| + yield(self[name]) + end + end + + def each_pair + return enum_for(__function__) unless block_given? + + self.class.members.each do |name,| + yield(name, self[name]) + end + end + + def to_h + hash = {} + each_pair do |name, value| + hash[name] = unstruct(value) + end + hash + end + + def replace(another) + if another.nil? + self.class.members.each do |name,| + self[name] = nil + end + elsif another.respond_to?(:each_pair) + another.each_pair do |name, value| + self[name] = value + end + else + another.each do |name, value| + self[name] = value + end + end + self + end + + private + def unstruct(value) + case value + when CStruct + value.to_h + when Array + value.collect do |v| + unstruct(v) + end + else + value + end + end end - # C union shell + # A base class for objects representing a C union class CUnion # accessor to Fiddle::CUnionEntity def CUnion.entity_class @@ -27,10 +84,14 @@ module Fiddle def initialize(ptr, type, initial_values) @ptr = ptr @type = type - @align = PackInfo::ALIGN_MAP[type] - @size = Fiddle::PackInfo::SIZE_MAP[type] - @pack_format = Fiddle::PackInfo::PACK_MAP[type] - super(initial_values.collect { |v| unsigned_value(v, type) }) + @is_struct = @type.respond_to?(:entity_class) + if @is_struct + super(initial_values) + else + @size = Fiddle::PackInfo::SIZE_MAP[type] + @pack_format = Fiddle::PackInfo::PACK_MAP[type] + super(initial_values.collect { |v| unsigned_value(v, type) }) + end end def to_ptr @@ -42,8 +103,12 @@ module Fiddle raise IndexError, 'index %d outside of array bounds 0...%d' % [index, size] end - to_ptr[index * @size, @size] = [value].pack(@pack_format) - super(index, value) + if @is_struct + self[index].replace(value) + else + to_ptr[index * @size, @size] = [value].pack(@pack_format) + super(index, value) + end end end @@ -62,7 +127,7 @@ module Fiddle # Fiddle::Importer#struct and Fiddle::Importer#union wrap this functionality in an # easy-to-use manner. # - # Example: + # Examples: # # require 'fiddle/struct' # require 'fiddle/cparser' @@ -73,6 +138,17 @@ module Fiddle # # 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 # ... @@ -82,45 +158,78 @@ module Fiddle # 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) } define_method(:[]=) { |*args| @entity.send(:[]=, *args) } define_method(:to_ptr){ @entity } define_method(:to_i){ @entity.to_i } + define_singleton_method(:types) { types } + define_singleton_method(:members) { members } members.each{|name| + name = name[0] if name.is_a?(Array) # name is a nested struct + next if method_defined?(name) 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) + entity_class = klass.entity_class + alignment = entity_class.alignment(types) + size = entity_class.size(types) + define_singleton_method(:alignment) { alignment } + define_singleton_method(:size) { size } + define_singleton_method(:malloc) do |func=nil, &block| + if block + entity_class.malloc(types, func, size) do |entity| + block.call(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 + def CStructEntity.alignment(types) + max = 1 + types.each do |type, count = 1| + if type.respond_to?(:entity_class) + n = type.alignment + else + n = ALIGN_MAP[type] + end + max = n if n > max + end + max + end + # 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+. @@ -136,9 +245,15 @@ module Fiddle max_align = types.map { |type, count = 1| last_offset = offset - align = PackInfo::ALIGN_MAP[type] + if type.respond_to?(:entity_class) + align = type.alignment + type_size = type.size + else + align = PackInfo::ALIGN_MAP[type] + type_size = PackInfo::SIZE_MAP[type] + end offset = PackInfo.align(last_offset, align) + - (PackInfo::SIZE_MAP[type] * count) + (type_size * count) align }.max @@ -152,13 +267,37 @@ module Fiddle # # 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 # Set the names of the +members+ in this C struct def assign_names(members) - @members = members + @members = [] + @nested_structs = {} + members.each_with_index do |member, index| + if member.is_a?(Array) # nested struct + member_name = member[0] + struct_type, struct_count = @ctypes[index] + if struct_count.nil? + struct = struct_type.new(to_i + @offset[index]) + else + structs = struct_count.times.map do |i| + struct_type.new(to_i + @offset[index] + i * struct_type.size) + end + struct = StructArray.new(to_i + @offset[index], + struct_type, + structs) + end + @nested_structs[member_name] = struct + else + member_name = member + end + @members << member_name + end end # Calculates the offsets and sizes for the given +types+ in the struct. @@ -169,12 +308,18 @@ module Fiddle max_align = types.map { |type, count = 1| orig_offset = offset - align = ALIGN_MAP[type] + if type.respond_to?(:entity_class) + align = type.alignment + type_size = type.size + else + align = ALIGN_MAP[type] + type_size = SIZE_MAP[type] + end offset = PackInfo.align(orig_offset, align) @offset << offset - offset += (SIZE_MAP[type] * count) + offset += (type_size * count) align }.max @@ -203,7 +348,13 @@ module Fiddle end ty = @ctypes[idx] if( ty.is_a?(Array) ) - r = super(@offset[idx], SIZE_MAP[ty[0]] * ty[1]) + if ty.first.respond_to?(:entity_class) + return @nested_structs[name] + else + r = super(@offset[idx], SIZE_MAP[ty[0]] * ty[1]) + end + elsif ty.respond_to?(:entity_class) + return @nested_structs[name] else r = super(@offset[idx], SIZE_MAP[ty.abs]) end @@ -243,6 +394,24 @@ module Fiddle def []=(*args) return super(*args) if args.size > 2 name, val = *args + name = name.to_s if name.is_a?(Symbol) + nested_struct = @nested_structs[name] + if nested_struct + if nested_struct.is_a?(StructArray) + if val.nil? + nested_struct.each do |s| + s.replace(nil) + end + else + val.each_with_index do |v, i| + nested_struct[i] = v + end + end + else + nested_struct.replace(val) + end + return val + end idx = @members.index(name) if( idx.nil? ) raise(ArgumentError, "no such member: #{name}") @@ -261,23 +430,16 @@ module Fiddle 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( @@ -287,7 +449,11 @@ module Fiddle # Fiddle::TYPE_VOIDP ]) #=> 8 def CUnionEntity.size(types) types.map { |type, count = 1| - PackInfo::SIZE_MAP[type] * count + if type.respond_to?(:entity_class) + type.size * count + else + PackInfo::SIZE_MAP[type] * count + end }.max end @@ -300,4 +466,3 @@ module Fiddle end end end - diff --git a/ext/fiddle/lib/fiddle/version.rb b/ext/fiddle/lib/fiddle/version.rb index 40d8300d2f..d8aef1a71b 100644 --- a/ext/fiddle/lib/fiddle/version.rb +++ b/ext/fiddle/lib/fiddle/version.rb @@ -1,3 +1,3 @@ module Fiddle - VERSION = "1.0.2" + VERSION = "1.0.4" end -- cgit v1.2.3