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. (howewver 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 seperate 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
Copyright © 2011 Eric Monti. See LICENSE.txt for further details.