Class: VirtualBox::COM::FFI::Util

Inherits:
Object
  • Object
show all
Defined in:
lib/virtualbox/com/ffi/util.rb

Overview

Class which contains many class-level utility methods to assist with the FFI interface. These functions range from converting a function spec to a FFI parameter list to dereferencing pointers.

Class Method Summary collapse

Class Method Details

.camelize(string) ⇒ String

An “almost complete” camel-caser. Camel cases a string with a few exceptions. For example: ‘get_foo` becomes `GetFoo`, but `get_os_type` becomes `GetOSType` since `os` is a special case.

Parameters:

  • string (String)

    The string to camel case

Returns:

  • (String)


109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/virtualbox/com/ffi/util.rb', line 109

def camelize(string)
  special_cases = {
    "api" => "API",
    "os" => "OS",
    "dhcp" => "DHCP",
    "dvd" => "DVD",
    "usb" => "USB",
    "vram" => "VRAM",
    "3d" => "3D",
    "bios" => "BIOS",
    "vrdp" => "VRDP",
    "vrde" => "VRDE",
    "hw" => "HW",
    "png" => "PNG",
    "io" => "IO",
    "apic" => "APIC",
    "acpi" => "ACPI",
    "pxe" => "PXE",
    "nat" => "NAT",
    "ide" => "IDE",
    "vfs" => "VFS",
    "ip" => "IP",
    "vdi" => "VDI",
    "cpu" => "CPU",
    "ram" => "RAM",
    "hdd" => "HDD",
    "rtc" => "RTC",
    "utc" => "UTC",
    "io" => "IO",
    "vm" => "VM"
  }

  parts = string.to_s.split(/_/).collect do |part|
    special_cases[part] || part.capitalize
  end

  parts.join("")
end

.interface_klass(type) ⇒ Class

Finds and returns the ‘COM::Interface` class associated with the type. If the class does not exist, a `NameError` will be raised.

Returns:

  • (Class)


13
14
15
# File 'lib/virtualbox/com/ffi/util.rb', line 13

def interface_klass(type)
  ::VirtualBox::COM::Util.versioned_interface(type)
end

.load_interface(interface) ⇒ Object

Loads an interface with the current version.



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/virtualbox/com/ffi/util.rb', line 36

def load_interface(interface)
  @current_constant ||= 1

  # Create a new class that is an interface
  klass = Class.new(Interface)

  # Note that the klass must exist in a real constant path
  # because FFI gem exists by climbing up the full path of the
  # class. Dumb.
  const_set("FFIClass#{@current_constant}", klass)
  @current_constant += 1

  # Create the actual interface on this empty class and return!
  klass.com_interface(interface)
  klass
end

.loaded_interfacesObject

This keeps a hash of all the loaded interface classes by both version and name. Example:

loaded_interfaces["4.0.x"][:VirtualBox]


31
32
33
# File 'lib/virtualbox/com/ffi/util.rb', line 31

def loaded_interfaces
  @loaded_interfaces ||= {}
end

.spec_to_ffi(spec) ⇒ Array

Converts a function spec from AbstractInterface to an FFI function spec. This handles custom types (unicode strings, arrays, and out-parameters) and will return a perfectly valid array ready to be passed into ‘callback`.

Parameters:

  • spec (Array)

    The function spec

Returns:

  • (Array)


60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/virtualbox/com/ffi/util.rb', line 60

def spec_to_ffi(spec)
  spec = spec.collect do |item|
    if item.is_a?(Array) && item[0] == :out
      if item[1].is_a?(Array)
        # The out is an array of items, so we add in two pointers:
        # one for size and one for the array
        [:pointer, :pointer]
      else
        # A regular out parameter is just a single pointer
        :pointer
      end
    elsif item.is_a?(Array) && item.length == 1
      # The parameter is an array of somethings
      [T_UINT32, :pointer]
    elsif item == WSTRING
      # Unicode strings are simply pointers
      :pointer
    elsif item.to_s[0,1] == item.to_s[0,1].upcase
      begin
        # Try to get the class from the interfaces
        interface = interface_klass(item.to_sym)

        if interface.superclass == COM::AbstractInterface
          :pointer
        elsif interface.superclass == COM::AbstractEnum
          T_UINT32
        end
      rescue NameError,LoadError
        # Default to a pointer, since not all interfaces are implemented
        :pointer
      end
    else
      # Unknown items are simply passed as-is, hopefully FFI
      # will catch any problems
      item
    end
  end

  # Prepend a :pointer to represent the `this` parameter required
  # for the FFI parameter lists
  spec.unshift(:pointer).flatten
end

.versioned_interface(interface) ⇒ Class

Finds the versioned interface for the FFI module.

Returns:

  • (Class)


20
21
22
23
24
# File 'lib/virtualbox/com/ffi/util.rb', line 20

def versioned_interface(interface)
  version = ::VirtualBox::COM::Util.version
  loaded_interfaces[version] ||= {}
  loaded_interfaces[version][interface] ||= load_interface(interface)
end