secret
Last active

Some examples custom types for use in FFI libraries

  • Download Gist
byte_string.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
# From: https://github.com/Burgestrand/spotify/blob/master/lib/spotify/types/byte_string.rb
 
module Spotify
module ByteString
extend FFI::DataConverter
native_type FFI::Type::POINTER
 
class << self
# Given either a String or nil, make an actual FFI::Pointer
# of that value, without an ending NULL-byte.
#
# @param [#to_str, nil] value
# @param ctx
# @return [FFI::Pointer]
def to_native(value, ctx)
value && begin
value = value.to_str
 
pointer = FFI::MemoryPointer.new(:char, value.bytesize)
pointer.write_string(value)
end
end
# no from_native, as we must return a pointer because we simply
# don’t know the length of a ByteString just by looking at the pointer.
 
def reference_required?
true
end
end
end
end
image_id.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
# From: https://github.com/Burgestrand/spotify/blob/master/lib/spotify/types/image_id.rb
 
module Spotify
# A custom data type for Spotify image IDs.
#
# It will convert strings to image ID pointers when handling
# values from Ruby to C, and it will convert pointers to Ruby
# strings when handling values from C to Ruby.
#
# Spotify image IDs are pointers to an array of 20 bytes, but
# in Ruby we treat them as strings.
module ImageID
extend FFI::DataConverter
native_type FFI::Type::POINTER
 
class << self
# @return [Integer] bytesize of image ID pointers.
def size
20
end
 
# Given a string, convert it to an image ID pointer.
#
# @param [#to_str, nil] value image id as a string
# @param ctx
# @return [FFI::Pointer] pointer to the image ID
def to_native(value, ctx)
value && begin
value = value.to_str
 
if value.bytesize != size
raise ArgumentError, "image id bytesize must be #{size}, was #{value.bytesize}"
end
 
pointer = FFI::MemoryPointer.new(:char, size)
pointer.write_string(value)
end
end
 
# Given a pointer, read a {.size}-byte image ID from it.
#
# @param [FFI::Pointer] value
# @param ctx
# @return [String, nil] the image ID as a string, or nil
def from_native(value, ctx)
value.read_string(size) unless value.null?
end
 
# FFI needs this to know if a reference to the native object
# should be kept. Since we allocate memory using MemoryPointer,
# we want the refernce to be kept.
#
# This is used if you assign the object to a struct, for exmaple.
def reference_required?
true
end
end
end
end
type_class.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
# From: https://github.com/Burgestrand/plaything/blob/master/lib/plaything/support/type_class.rb
 
class Plaything
module OpenAL
# TypeClass will return an FFI::DataConverter for a built-in FFI
# type. Since you can’t subclass FFI::Type::INT, for example, this
# method will return a class for a given type that you *can* subclass.
#
# Example usage:
#
# class Source < TypeClass(FFI::Type::INT)
# end
def self.TypeClass(type)
Class.new do
extend FFI::DataConverter
 
define_singleton_method(:type) do
type
end
 
class << self
def inherited(other)
other.native_type(type)
end
 
def to_native(source, ctx)
source.value
end
 
def from_native(value, ctx)
new(value)
end
 
def size
type.size
end
end
 
def initialize(value)
@value = value
end
 
def ==(other)
other.is_a?(self.class) and other.value == value
end
 
attr_reader :value
end
end
end
end

To any elabsians wondering why this is here, I created it for the upcoming blog post on FFI.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.