blog/published/2010.01.17-working-with-c-style-structs-in-ruby.html
2011-12-02 23:22:41 -08:00

74 lines
3.6 KiB
HTML

Title: Working with C-style structs in Ruby
Date: January 17, 2010
Timestamp: 1263715200
Author: sjs
Tags: ruby, cstruct, compiler
----
<p>This is the beginning of a series on generating Mach-O object files in
Ruby. We start small by introducing some Ruby tools that are useful when
working with binary data. Subsequent articles will cover a subset of the
Mach-O file format, then generating Mach object files suitable for linking
with ld or gcc to produce working executables. A basic knowledge of Ruby and C
are assumed. You can likely wing it on the Ruby side of things if you know any
similar languages.</p>
<p>First we need to read and write structured binary files with Ruby. <a
href="http://ruby-doc.org/core/classes/Array.html#M002222">Array#pack</a> and
<a
href="http://ruby-doc.org/core/classes/String.html#M000760">String#unpack</a>
get the job done at a low level, but every time I use them I have to look up
the documentation. It would also be nice to encapsulate serializing and
deserializing into classes describing the various binary data structures. The
built-in <a href="http://ruby-doc.org/core/classes/Struct.html">Struct
class</a> sounds promising but did not meet my needs, nor was it easily
extended to meet them.</p>
<p>Meet <a
href="http://github.com/samsonjs/compiler/blob/master/asm/cstruct.rb#files">CStruct</a>,
a class that you can use to describe a binary structure, somewhat similar to
how you would do it in C. Subclassing CStruct results in a class whose
instances can be serialized, and unserialized, with little effort. You can
subclass descendants of CStruct to extend them with additional members.
CStruct does not implement much more than is necessary for the compiler. For
example there is no support for floating point. If you want to use this for
more general purpose tasks be warned that it may require some work. Anything
supported by Array#pack is fairly easy to add though.</p>
<p>First a quick example and then we'll get into the CStruct class itself. In
C you may write the following to have one struct "inherit" from another:</p>
<p><script src="http://gist.github.com/279790.js"></script></p>
<p>With CStruct in Ruby that translates to:</p>
<p><script src="http://gist.github.com/279794.js"></script></p>
<p>CStructs act like Ruby's built-in Struct to a certain extent. They are
instantiated the same way, by passing values to #new in the same order they
are defined in the class. You can find out the size (in bytes) of a CStruct
instance using the #bytesize method, or of any member using #sizeof(name).</p>
<p>The most important method (for us) is #serialize, which returns a binary
string representing the contents of the CStruct.</p>
<p>(I know that CStruct.new_from_bin should be called CStruct.unserialize, you
can see where my focus was when I wrote it.)</p>
<p>CStruct#serialize automatically creates a "pack pattern", which is an array
of strings used to pack each member in turn. The pack pattern is mapped to the
result of calling Array#pack on each corresponding member, and then the
resulting strings are joined together. Serializing strings complicates matters
so we cannot build up a pack pattern string and then serialize it in one go,
but conceptually it's quite similar.</p>
<p>Unserializing is the same process in reverse, and was mainly added for
completeness and testing purposes.</p>
<p>That's about all you need to know to use CStruct. The code needs some work
but I decided to just go with what I have already so I can get on with the
more interesting and fun tasks.</p>
<p><i>Next in this series: <a
href="2010.01.18-basics-of-the-mach-o-file-format.html">Basics
of the Mach-O file format</a></i><p>