rstruct

Rstruct is yet another ruby binary struct parser/builder framework based around a declarative syntax. Rstruct is designed with simplicity in mind and with a a slightly less active focus on performance.

The goal of Rstruct is is to provide a system that lets you define structures once, and use them in many ways as you see fit. Kind-of like… C structs in system/API header files.

The structure declaration syntax emulates C structures syntax, although Rstruct is not intended to be able to parse C structures from C header files. (however an addon could conceivably be built to do so with minimal fuss)

Multiple approaches to parsing and building are supported and more can be added with minimal fuss. For example, you might not wish to use the same interface to build or parse a structure on an IO object as you would a String object. In other words, it’s nice to have something that can work easily with streamed IO as well as buffers.

Rstruct was written because the authors wanted something comparable to C structures without having a strong need for extra ‘magic’ in parsing or building structures. While there exist numerous options in this space, they all seem to have suffered from a combination of performance and interface issues which limit their potential (and in some cases, spotty maintenance). Having, tried pretty much all of these alternatives (and even contributed to a few) in previous projects, the author still decided to write rstruct.

That said, the author does not claim Rstruct to be superior to any others, it’s just another approach. There are several other excellent binary structure library options out there such as BinData, BitStruct, or Ruckus. Some of these support variable length structures which, at this time, Rstruct does not. If you are looking for something to parse variable length data automatically, you are may be better off checking out one of these alterntives.

Installation

(sudo)? gem install rstruct

Synopsis

Here is a trivial example defining and packing a raw structure

require 'rstruct'
extend Rstruct::ClassMethods

example = struct(:example) {
  uint32 :a
  uint32 :b
  uint32 :c
}.instance

example.a = 0x41
example.b = 0x42
example.c = 0x43

raw = example.write()
# => "A\x00\x00\x00B\x00\x00\x00C\x00\x00\x00" # on a little endian machine
# => "\x00\x00\x00A\x00\x00\x00B\x00\x00\x00C" # on a big endian machine

Here is a fully functional Rstruct parser example using Apple’s FAT file structure.

# To compare the structs to their C counterparts, see:
#   http://fxr.watson.org/fxr/source/EXTERNAL_HEADERS/mach-o/fat.h?v=xnu-1228

require 'rstruct'
extend Rstruct::ClassMethods

FAT_MAGIC = 0xcafebabe
FAT_CIGAM = 0xbebafeca

struct(:fat_header) {
  uint32be  :magic;      # FAT_MAGIC
  uint32be  :nfat_arch;  # number of structs that follow
}

typedef :uint32be, :cpu_type_t
typedef :uint32be, :cpu_subtype_t

struct(:fat_arch) {
  cpu_type_t     :cputype    # cpu specifier (int)
  cpu_subtype_t  :cpusubtype # machine specifier (int)
  uint32be       :offset     # file offset to this object file
  uint32be       :size       # size of this object file
  uint32be       :align      # alignment as a power of 2
}

# a basic helper to produce textual dumps of fields
def dump(struct)
  struct.each_pair.map {|k,v| "  #{k} = 0x%0.8x" % v}
end

File.open('ls.from_a_mac','r') do |f|
  # Read and dump the FAT header from the file
  head = get_type(:fat_header).read(f)
  puts "FAT header:", dump(head)
  puts

  # Read and dump the architectures after the header

  fat_arch = get_type(:fat_arch)
  (head.nfat_arch).times do |i|
    arch = fat_arch.read(f) # note, it reads to a new object on each arch
    puts "  Architecture #{i}:"
    puts "  " << dump(arch).join("\n  ")
    puts
  end
end

.. which should produce output something like the following:

FAT header:
  magic = 0xcafebabe
  nfat_arch = 0x00000002

  Architecture 0:
    cputype = 0x01000007
    cpusubtype = 0x80000003
    offset = 0x00001000
    size = 0x00009ab0
    align = 0x0000000c

  Architecture 1:
    cputype = 0x00000007
    cpusubtype = 0x00000003
    offset = 0x0000b000
    size = 0x00008b30
    align = 0x0000000c

Please refer to rdoc, the samples/ directory, and unit tests for more information.

Copyright © 2011 Eric Monti. See LICENSE.txt for further details.