Socket
Book a DemoInstallSign in
Socket

emonti-ffi_dry

Package Overview
Dependencies
Maintainers
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

emonti-ffi_dry

0.1.2
bundlerRubygems
Version published
Maintainers
1
Created
Source

= ffi_dry

Helpers, sugar methods, and new features over Ruby FFI to do some common things and add support for some uncommon ones.

== Requirements

  • ffi-ffi (>= 0.5.0) - github.com/ffi/ffi

== Synopsis

(samples/ in the package for code)

A major feature is a DSL"-like" syntax for declaring structure members in FFI::Struct or FFI::ManagedStruct definitions.

require 'rubygems'
require 'ffi'
require 'ffi/dry'

class SomeStruct < FFI::Struct
  include FFI::DRY::StructHelper

  # we get a new way of specifying layouts with a 'dsl'-like syntax
  # The hash containing {:desc => ... } can contain arbitrary keys which 
  # can be used however we like. dsl_metadata will contain all these
  # in the class and instance.
  dsl_layout do
    field   :field1,  :uint16, :desc => 'this is field 1'
    field   :field2,  :uint16, :desc => 'this is field 2'
  end
end

ss0=SomeStruct.new

With the declarations above, we specified :desc hash value in metadata Let's check out in our instance.

pp ss0.dsl_metadata 
[{:type=>:uint16, :name=>:field1, :desc=>"this is field 1"},
 {:type=>:uint16, :name=>:field2, :desc=>"this is field 2"}]
# => nil

Or class.

pp SomeStruct.dsl_metadata
#...

We get some additional ways of instantiating and declaring values for free during initialization. (The FFI standard ways still work too)

raw_data = "\x00\x00\xff\xff"

ss1=SomeStruct.new :raw => raw_data
ss2=SomeStruct.new :raw => raw_data, :field1 => 1, :field2 => 2
ss3=SomeStruct.new {|x| x.field1=1 }
ss4=SomeStruct.new(:raw => raw_data) {|x| x.field1=1 }

[ ss0, 
  ss1, 
  ss2, 
  ss3, 
  ss4 ].each_with_index {|x,i| p ["ss#{i}",[x.field1, x.field2]]}

# which will produce...
# ["ss0", [0, 0]]
# ["ss1", [0, 65535]]
# ["ss2", [1, 2]]
# ["ss3", [1, 0]]
# ["ss4", [1, 65535]]

Here's a broader example which utilizes that arbitrary ':desc' parameter in a "neighborly" way. This also demonstrates superclasses to add common struct features, declaring array fields, as well as nesting other structs.

require 'rubygems'
require 'ffi'
require 'ffi/dry'

class NeighborlyStruct < ::FFI::Struct
  include ::FFI::DRY::StructHelper

  def self.describe
    print "Struct: #{self.name}"
    dsl_metadata().each_with_index do |spec, i|
      print "  Field #{i}\n"
      print "    name:  #{spec[:name].inspect}\n"
      print "    type:  #{spec[:type].inspect}\n"
      print "    desc:  #{spec[:desc]}\n\n"
    end
    print "\n"
  end
  def describe;  self.class.describe;  end
end

class TestStruct < NeighborlyStruct
  dsl_layout do
    field   :field1,  :uint8,  :desc => "test field 1"
    field   :field2,  :uint8,  :desc => "test field 2"
  end
end

class SomeStruct < NeighborlyStruct
  dsl_layout do
    field  :kind, :uint8,      :desc => "a type identifier"
    struct :tst,  TestStruct,  :desc => "a nested TestStruct"
    field  :len,  :uint8,      :desc => "8-bit size value (>= self.size+2)"
    array  :str,  [:char,255], 
        :desc => "a string up to 255 bytes bound by :len"
  end

  # override kind getter method with our own
  # resolves kind to some kind of type array for example...
  def kind
    [:default, :bar, :baz][ self[:kind] ]
  end
end

s1=TestStruct.new
s2=SomeStruct.new

# check out that 'kind' override:
s2.kind
# => :default

# oh and the regular FFI way is always intact
s2[:kind]
# => 0

s2[:kind]=1
s2.kind
# => :bar

s2.kind=3
s2.kind
# => :baz

puts "*"*70
s1.describe
## we get a dump of metadata
# **********************************************************************
# Struct: TestStruct  
# Field 0
#   name:  :field1
#   type:  :uint8
#   desc:  test field 1
#
# Field 1
#   name:  :field2
#   type:  :uint8
#   desc:  test field 2

puts "*"*70
s2.describe
## we get a dump of metadata
# Struct: SomeStruct  Field 0
#     name:  :kind
#     type:  :uint8
#     desc:  a type identifier
#
#   Field 1
#     name:  :tst
#     type:  TestStruct
#     desc:  a nested TestStruct
#
#   Field 2
#     name:  :len
#     type:  :uint8
#     desc:  8-bit size value (>= self.size+2)
#
#   Field 3
#     name:  :str
#     type:  [:char, 255]
#     desc:  a string up to 255 bytes bound by :len

puts "*"*70
s2.tst.describe
## same as s1.describe
# **********************************************************************
# Struct: TestStruct  
# Field 0
#   name:  :field1
#   type:  :uint8
#   desc:  test field 1
#
# Field 1
#   name:  :field2
#   type:  :uint8
#   desc:  test field 2

There's also some helpers for collecting lookup maps for constants, a common and handy thing when porting various libraries. We use Socket here just for example purposes, you can 'slurp' constants form any namespace this way.

require 'ffi/dry'
require 'socket'

module AddressFamily
  include FFI::DRY::ConstMap
  slurp_constants ::Socket, "AF_"
  def list ; @@list ||= super() ; end  # only generate the hash once
end

AddressFamily now has all the constants it found for Socket::AF_* minus the prefix.

AddressFamily::INET AddressFamily::LINK AddressFamily::INET6

etc...

We can do type or value lookups using []

AddressFamily[2]      # => "INET"
AddressFamily["INET"] # => 2

We can get a hash of all constant->value pairs with .list

AddressFamily.list
# => {"NATM"=>31, "DLI"=>13, "UNIX"=>1, "NETBIOS"=>33,  ...}

... and invert for a reverse mapping

AddressFamily.list.invert
# => {16=>"APPLETALK", 5=>"CHAOS", 27=>"NDRV", 0=>"UNSPEC", ...}

== License

Copyright (c) 2009 Eric Monti. See LICENSE for details.

FAQs

Package last updated on 11 Aug 2014

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

About

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.

  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc

U.S. Patent No. 12,346,443 & 12,314,394. Other pending.