module FFI::Library

def self.extended(mod)

Raises:
  • (RuntimeError) - if +mod+ is not a Module

Returns:
  • (nil) -

Parameters:
  • mod () -- extended object
def self.extended(mod)
  raise RuntimeError.new("must only be extended by module") unless mod.kind_of?(::Module)
end

def attach_function(name, func, args, returns = nil, options = nil)

Raises:
  • (FFI::NotFoundError) - if +func+ cannot be found in the attached libraries (see {#ffi_lib})

Returns:
  • (FFI::VariadicInvoker) -

Options Hash: (**options)
  • :type_map (Hash) --
  • :enums (FFI::Enums) --
  • :convention (Symbol) -- calling convention (see {#ffi_convention})
  • :blocking (Boolean) -- set to true if the C function is a blocking call

Parameters:
  • returns (Symbol) -- type of return value
  • args (Array) -- an array of types
  • func (#to_s) -- name of C function to attach
  • name (#to_s) -- name of ruby method to attach as

Other tags:
    Example: attach function with an explicit name -
    Example: attach function without an explicit name -

Overloads:
  • attach_function(name, func, args, returns, options = {})
  • attach_function(func, args, returns, options = {})
def attach_function(name, func, args, returns = nil, options = nil)
  mname, a2, a3, a4, a5 = name, func, args, returns, options
  cname, arg_types, ret_type, opts = (a4 && (a2.is_a?(String) || a2.is_a?(Symbol))) ? [ a2, a3, a4, a5 ] : [ mname.to_s, a2, a3, a4 ]
  # Convert :foo to the native type
  arg_types = arg_types.map { |e| find_type(e) }
  options = {
    :convention => ffi_convention,
    :type_map => defined?(@ffi_typedefs) ? @ffi_typedefs : nil,
    :blocking => defined?(@blocking) && @blocking,
    :enums => defined?(@ffi_enums) ? @ffi_enums : nil,
  }
  @blocking = false
  options.merge!(opts) if opts && opts.is_a?(Hash)
  # Try to locate the function in any of the libraries
  invokers = []
  ffi_libraries.each do |lib|
    if invokers.empty?
      begin
        function = nil
        function_names(cname, arg_types).find do |fname|
          function = lib.find_function(fname)
        end
        raise LoadError unless function
        invokers << if arg_types[-1] == FFI::NativeType::VARARGS
          VariadicInvoker.new(function, arg_types, find_type(ret_type), options)
        else
          Function.new(find_type(ret_type), arg_types, function, options)
        end
      rescue LoadError
      end
    end
  end
  invoker = invokers.compact.shift
  raise FFI::NotFoundError.new(cname.to_s, ffi_libraries.map { |lib| lib.name }) unless invoker
  invoker.attach(self, mname.to_s)
  invoker
end

def attach_variable(mname, a1, a2 = nil)

Raises:
  • (FFI::NotFoundError) - if +cname+ cannot be found in libraries

Returns:
  • (DynamicLibrary::Symbol) -

Parameters:
  • type (DataConverter, Struct, Symbol, Type) -- C variable's type
  • mname (#to_s) -- name of ruby method to attach as
  • type (DataConverter, Struct, Symbol, Type) -- C variable's type
  • cname (#to_s) -- name of C variable to attach
  • mname (#to_s) -- name of ruby method to attach as

Overloads:
  • attach_variable(cname, type)
  • attach_variable(mname, cname, type)
def attach_variable(mname, a1, a2 = nil)
  cname, type = a2 ? [ a1, a2 ] : [ mname.to_s, a1 ]
  mname = mname.to_sym
  address = nil
  ffi_libraries.each do |lib|
    begin
      address = lib.find_variable(cname.to_s)
      break unless address.nil?
    rescue LoadError
    end
  end
  raise FFI::NotFoundError.new(cname, ffi_libraries) if address.nil? || address.null?
  if type.is_a?(Class) && type < FFI::Struct
    # If it is a global struct, just attach directly to the pointer
    s = s = type.new(address) # Assigning twice to suppress unused variable warning
    self.module_eval <<-code, __FILE__, __LINE__
      @ffi_gsvars = {} unless defined?(@ffi_gsvars)
      @ffi_gsvars[#{mname.inspect}] = s
      def self.#{mname}
        @ffi_gsvars[#{mname.inspect}]
      end
    code
  else
    sc = Class.new(FFI::Struct)
    sc.layout :gvar, find_type(type)
    s = sc.new(address)
    #
    # Attach to this module as mname/mname=
    #
    self.module_eval <<-code, __FILE__, __LINE__
      @ffi_gvars = {} unless defined?(@ffi_gvars)
      @ffi_gvars[#{mname.inspect}] = s
      def self.#{mname}
        @ffi_gvars[#{mname.inspect}][:gvar]
      end
      def self.#{mname}=(value)
        @ffi_gvars[#{mname.inspect}][:gvar] = value
      end
    code
  end
  address
end

def attached_functions

Returns:
  • (Hash< Symbol => [FFI::Function, FFI::VariadicInvoker] >) -
def attached_functions
  @ffi_functions || {}
end

def attached_variables

Returns:
  • (Hash< Symbol => ffi_type >) -
def attached_variables
  (
    (@ffi_gsvars || {}).map do |name, gvar|
      [name, gvar.class]
    end +
    (@ffi_gvars || {}).map do |name, gvar|
      [name, gvar.layout[:gvar].type]
    end
  ).to_h
end

def bitmask(*args)

Returns:
  • (FFI::Bitmask) -

Parameters:
  • values (Array) -- for new bitmask
  • native_type (FFI::Type) -- native type for new bitmask
  • args (Symbol, Integer) -- values for new bitmask
  • native_type (FFI::Type) -- native type for new bitmask
  • values (Array) -- for new bitmask
  • name (Symbol) -- for new bitmask
  • native_type (FFI::Type) -- native type for new bitmask
  • values (Array) -- for new bitmask
  • args (Symbol, Integer) -- values for new bitmask
  • values (Array) -- for new bitmask
  • name (Symbol) -- for new bitmask

Overloads:
  • bitmask(native_type, values)
  • bitmask(native_type, *args)
  • bitmask(native_type, name, values)
  • bitmask(values)
  • bitmask(*args)
  • bitmask(name, values)
def bitmask(*args)
  generic_enum(FFI::Bitmask, *args)
end

def callback(*args)

Returns:
  • (FFI::CallbackInfo) -

Parameters:
  • ret (DataConverter, Struct, Symbol, Type) -- callback return type
  • params (Array) -- array of parameters' types
  • ret (DataConverter, Struct, Symbol, Type) -- callback return type
  • params (Array) -- array of parameters' types
  • name () -- callback name to add to type map

Overloads:
  • callback(params, ret)
  • callback(name, params, ret)
def callback(*args)
  raise ArgumentError, "wrong number of arguments" if args.length < 2 || args.length > 3
  name, params, ret = if args.length == 3
    args
  else
    [ nil, args[0], args[1] ]
  end
  native_params = params.map { |e| find_type(e) }
  raise ArgumentError, "callbacks cannot have variadic parameters" if native_params.include?(FFI::Type::VARARGS)
  options = Hash.new
  options[:convention] = ffi_convention
  options[:enums] = @ffi_enums if defined?(@ffi_enums)
  ret_type = find_type(ret)
  if ret_type == Type::STRING
    raise TypeError, ":string is not allowed as return type of callbacks"
  end
  cb = FFI::CallbackInfo.new(ret_type, native_params, options)
  # Add to the symbol -> type map (unless there was no name)
  unless name.nil?
    typedef cb, name
  end
  cb
end

def enum(*args)

Returns:
  • (FFI::Enum) -

Parameters:
  • values (Array) -- values for enum
  • native_type (FFI::Type) -- native type for new enum
  • args () -- values for enum
  • native_type (FFI::Type) -- native type for new enum
  • values (Array) -- values for enum
  • name (Symbol) -- name for new enum
  • native_type (FFI::Type) -- native type for new enum
  • values (Array) -- values for enum
  • args () -- values for enum
  • values (Array) -- values for enum
  • name (Symbol) -- name for new enum

Overloads:
  • enum(native_type, values)
  • enum(native_type, *args)
  • enum(native_type, name, values)
  • enum(values)
  • enum(*args)
  • enum(name, values)
def enum(*args)
  generic_enum(FFI::Enum, *args)
end

def enum_type(name)

Returns:
  • (FFI::Enum) -

Parameters:
  • name () --
def enum_type(name)
  @ffi_enums.find(name) if defined?(@ffi_enums)
end

def enum_value(symbol)

Returns:
  • (FFI::Enum) -

Parameters:
  • symbol () --
def enum_value(symbol)
  @ffi_enums.__map_symbol(symbol)
end

def ffi_convention(convention = nil)

Returns:
  • (Symbol) - the new calling convention

Parameters:
  • convention (Symbol) -- one of +:default+, +:stdcall+

Other tags:
    Note: - +:stdcall+ is typically used for attaching Windows API functions

Other tags:
    See: http://en.wikipedia.org/wiki/Stdcall#stdcall -
def ffi_convention(convention = nil)
  @ffi_convention ||= :default
  @ffi_convention = convention if convention
  @ffi_convention
end

def ffi_lib(*names)

Raises:
  • (LoadError) - if a library cannot be opened

Returns:
  • (Array) -

Parameters:
  • names (Array) -- names of libraries to load
def ffi_lib(*names)
  raise LoadError.new("library names list must not be empty") if names.empty?
  lib_flags = defined?(@ffi_lib_flags) && @ffi_lib_flags
  @ffi_libs = names.map do |name|
    FFI::DynamicLibrary.send(:load_library, name, lib_flags)
  end
end

def ffi_lib_flags(*flags)

Returns:
  • (Fixnum) - the new value

Parameters:
  • flags (Symbol, …) -- (see {FlagsMap})
def ffi_lib_flags(*flags)
  @ffi_lib_flags = flags.inject(0) { |result, f| result | FlagsMap[f] }
end

def ffi_libraries

Raises:
  • (LoadError) - if no libraries have been loaded (using {#ffi_lib})

Returns:
  • (Array) - array of currently loaded FFI libraries

Other tags:
    See: #ffi_lib -
def ffi_libraries
  raise LoadError.new("no library specified") if !defined?(@ffi_libs) || @ffi_libs.empty?
  @ffi_libs
end

def find_type(t)

Returns:
  • (Type) -

Parameters:
  • t (DataConverter, Type, Struct, Symbol) -- type to find
def find_type(t)
  if t.kind_of?(Type)
    t
  elsif defined?(@ffi_typedefs) && @ffi_typedefs.has_key?(t)
    @ffi_typedefs[t]
  elsif t.is_a?(Class) && t < Struct
    Type::POINTER
  elsif t.is_a?(DataConverter)
    # Add a typedef so next time the converter is used, it hits the cache
    typedef Type::Mapped.new(t), t
  end || FFI.find_type(t)
end

def freeze

No further methods or variables can be attached and no further enums or typedefs can be created in this module afterwards.
This freezes the module's definitions, so that it can be used in a Ractor.

Freeze all definitions of the module
def freeze
  instance_variables.each do |name|
    var = instance_variable_get(name)
    FFI.make_shareable(var)
  end
  nil
end

def function_names(name, arg_types)

Other tags:
    Note: - Function names on windows may be decorated if they are using stdcall. See

Returns:
  • (Array) -

Parameters:
  • arg_types (Array) -- function's argument types
  • name (#to_s) -- function name
def function_names(name, arg_types)
  result = [name.to_s]
  if ffi_convention == :stdcall
    # Get the size of each parameter
    size = arg_types.inject(0) do |mem, arg|
      size = arg.size
      # The size must be a multiple of 4
      size += (4 - size) % 4
      mem + size
    end
    result << "_#{name.to_s}@#{size}" # win32
    result << "#{name.to_s}@#{size}" # win64
  end
  result
end

def generic_enum(klass, *args)

Parameters:
  • args (see #enum or #bitmask) --
  • klass (Class) -- can be one of FFI::Enum or FFI::Bitmask
def generic_enum(klass, *args)
  native_type = args.first.kind_of?(FFI::Type) ? args.shift : nil
  name, values = if args[0].kind_of?(Symbol) && args[1].kind_of?(Array)
    [ args[0], args[1] ]
  elsif args[0].kind_of?(Array)
    [ nil, args[0] ]
  else
    [ nil, args ]
  end
  @ffi_enums = FFI::Enums.new unless defined?(@ffi_enums)
  @ffi_enums << (e = native_type ? klass.new(native_type, values, name) : klass.new(values, name))
  # If called with a name, add a typedef alias
  typedef(e, name) if name
  e
end

def typedef(old, add, info=nil)

Returns:
  • (FFI::Enum, FFI::Type) -

Parameters:
  • info (Symbol) --
  • add (Symbol) --
  • old (DataConverter, Symbol, Type) --
def typedef(old, add, info=nil)
  @ffi_typedefs = Hash.new unless defined?(@ffi_typedefs)
  @ffi_typedefs[add] = if old.kind_of?(FFI::Type)
    old
  elsif @ffi_typedefs.has_key?(old)
    @ffi_typedefs[old]
  elsif old.is_a?(DataConverter)
    FFI::Type::Mapped.new(old)
  elsif old == :enum
    if add.kind_of?(Array)
      self.enum(add)
    else
      self.enum(info, add)
    end
  else
    FFI.find_type(old)
  end
end