The Artima Developer Community
Sponsored Link

Ruby Buzz Forum
Rubinius' Foreign Function Interface

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
Eric Hodel

Posts: 660
Nickname: drbrain
Registered: Mar, 2006

Eric Hodel is a long-time Rubyist and co-founder of Seattle.rb.
Rubinius' Foreign Function Interface Posted: Jan 15, 2008 2:31 AM
Reply to this message Reply

This post originated from an RSS feed registered with Ruby Buzz by Eric Hodel.
Original Post: Rubinius' Foreign Function Interface
Feed Title: Segment7
Feed URL: http://blog.segment7.net/articles.rss
Feed Description: Posts about and around Ruby, MetaRuby, ruby2c, ZenTest and work at The Robot Co-op.
Latest Ruby Buzz Posts
Latest Ruby Buzz Posts by Eric Hodel
Latest Posts From Segment7

Advertisement

I really, really, really love Rubinius’ Foreign Function Interface (FFI) since it allows you to replace C code with Ruby code. Earlier today I wrote Socket::getaddrinfo in C for Rubinius, and just now I finished a rewrite using FFI and Ruby. I’ve commented the code for clarity.

def self.getaddrinfo(host, service, family = nil, socktype = nil,
                     protocol = nil, flags = nil)
  service = service.to_s

  # MemoryPointer.new is kind-of like malloc(3), but understands what's inside
  hints_p = MemoryPointer.new Socket::Foreign::AddrInfo.size

  # Socket::Foreign::AddrInfo is a struct addrinfo wrapper with friendly accessors
  hints = Socket::Foreign::AddrInfo.new hints_p
  hints[:ai_family] = family || 0
  hints[:ai_socktype] = socktype || 0
  hints[:ai_protocol] = protocol || 0
  hints[:ai_flags] = flags || 0

  # getaddrinfo(3) asks for a struct addrinfo **.
  # This creates a pointer to a pointer
  res_p = MemoryPointer.new :pointer

  # call out to C
  err = Socket::Foreign.getaddrinfo host, service, hints_p, res_p

  # check for errors
  raise SocketError, Socket::Foreign.gai_strerror(err) unless err == 0

  # now we read out the pointer that getaddrinfo() passed us, and cast it
  # to a struct addrinfo *
  res = Socket::Foreign::AddrInfo.new res_p.read_pointer

  addrinfos = []

  loop do
    addrinfo = []

    # Extract data
    addrinfo << Socket::Constants::AF_TO_FAMILY[res[:ai_family]]

    ai_sockaddr = res[:ai_addr].read_string res[:ai_addrlen]

    sockaddr = Socket::Foreign::unpack_sa_ip ai_sockaddr, true

    addrinfo << sockaddr.pop # port
    addrinfo.concat sockaddr # hosts
    addrinfo << res[:ai_family]
    addrinfo << res[:ai_socktype]
    addrinfo << res[:ai_protocol]

    addrinfos << addrinfo

    # struct addrinfo is a linked list, so if we've hit the end, stop
    break unless res[:ai_next]

    # otherwise, down the linked-list
    res = Socket::Foreign::AddrInfo.new res[:ai_next]
  end

  return addrinfos
ensure
  # like a C code, we have to free our MemoryPointer objects
  hints_p.free if hints_p

  if res_p then
    # also, we have to do any C-side cleanup
    Socket::Foreign.freeaddrinfo res_p.read_pointer
    res_p.free
  end
end

getaddrinfo(3), freeaddrinfo(3) and gai_strerror(3) are wrapped up by FFI like this:

attach_function "gai_strerror", :gai_strerror, [:int], :string

attach_function "getaddrinfo", :getaddrinfo,
                [:string, :string, :pointer, :pointer], :int

attach_function "freeaddrinfo", :freeaddrinfo, [:pointer], :void

The first argument is the C function name, the second is the Ruby name, the third is the input arguments, and the fourth is the return type. Currently, FFI can only wrap up C functions with six or fewer args.

The AddrInfo struct is wrapped up like this:

class AddrInfo < FFI::Struct
  config("rbx.platform.addrinfo", :ai_flags, :ai_family, :ai_socktype,
         :ai_protocol, :ai_addrlen, :ai_addr, :ai_canonname, :ai_next)
end

The config method pulls pre-generated struct information out of a Rubinius config file and hooks up accessors to each of the struct’s fields. The accessors know which offset into the struct the data lives at and what type to convert data from and to when working with the struct. The information is collected at Rubinius build time by a small bit of C code.

I still have some confusion between passing an FFI::Struct like Socket::Foreign::AddrInfo vs. passing a MemoryPointer instance (which is what an FFI wrapped function understands) to an FFI-wrapped function, so we’re going to clean up that part of the API to make it more natural. Instead you’ll be able to initialize an FFI::Struct directly and pass it to the FFI-wrapped function. This will make the code quite a bit cleaner.

Read: Rubinius' Foreign Function Interface

Topic: Let's Go Camping - Getting Started w/ Ruby's Model-View-Controller (MVC) Web Microframework Previous Topic   Next Topic Topic: Ruby: Staying Current

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use