[ANN] BitStruct

J

Joel VanderWerf

(Exerpt from the docs at the project page,
http://redshift.sourceforge.net/bit-struct/.)

= BitStruct

Class for packed binary data stored in ruby Strings. BitStruct
accessors, generated from user declared fields, use pack/unpack to treat
substrings as fields with a specified portable format.

Field types include

* signed and unsigned integer (1..8 bits, or 16 or 32 bits)

* fixed point, with arbitrary scale factor

* fixed length character array

* null-terminated character array for printable text

* octets (hex and decimal representation options; useful for IP and MAC
addrs)

* float

* nested BitStruct

* free-form "rest" field (e.g., for the variable-size payload of a packet)

== Uses

BitStruct is useful for defining packets used in network protocols. This
is especially useful for raw IP--see examples/ping-recv.rb. All
multibyte numeric fields are stored in network order (though this could
be different for user-defined field classes).

BitStruct is most efficient when your data is primarily treated as a
binary string, and only secondarily treated as a data structure. (For
instance, you are routing packets from one socket to another, possibly
looking at one or two fields as it passes through or munging some
headers.) If accessor operations are a bottleneck, a better approach is
to define a class that wraps an array and uses pack/unpack when the
object needs to behave like a binary string.

== Example

An IP packet can be defined and used like this:

require 'bit-struct'

class IP < BitStruct
unsigned :ip_v, 4, "Version"
unsigned :ip_hl, 4, "Header length"
unsigned :ip_tos, 8, "TOS"
unsigned :ip_len, 16, "Length"
unsigned :ip_id, 16, "ID"
unsigned :ip_off, 16, "Frag offset"
unsigned :ip_ttl, 8, "TTL"
unsigned :ip_p, 8, "Protocol"
unsigned :ip_sum, 16, "Checksum"
octets :ip_src, 32, "Source addr"
octets :ip_dst, 32, "Dest addr"
rest :body, "Body of message"

note " rest is application defined message body"

initial_value.ip_v = 4
initial_value.ip_hl = 5
end

ip = IP.new
ip.ip_tos = 0
ip.ip_len = 0
ip.ip_id = 0
ip.ip_off = 0
ip.ip_ttl = 255
ip.ip_p = 255
ip.ip_sum = 0
ip.ip_src = "192.168.1.4"
ip.ip_dst = "192.168.1.255"
ip.body = "This is the payload text."
ip.ip_len = ip.length

puts ip.inspect
puts "-"*50
puts ip.inspect_detailed
puts "-"*50
puts ip.body
puts "-"*50
puts IP.describe

(Note that you can also construct an IP packet by passing a string to
new, or by passing a hash of <tt>field,value</tt> pairs, or by providing
a block that is yielded the new BitStruct.)

The output of this fragment is:

#<IP ip_v=4, ip_hl=5, ip_tos=0, ip_len=45, ip_id=0, ip_off=0,
ip_ttl=255, ip_p=255, ip_sum=0, ip_src="192.168.1.4",
ip_dst="192.168.1.255">
--------------------------------------------------
IP:
Version = 4
Header length = 5
TOS = 0
Length = 45
ID = 0
Frag offset = 0
TTL = 255
Protocol = 255
Checksum = 0
Source addr = "192.168.1.4"
Dest addr = "192.168.1.255"
--------------------------------------------------
This is the payload text.
--------------------------------------------------

Description of IP Packet:
byte: type name [size] description
----------------------------------------------------------------------
@0: unsigned ip_v [ 4b] Version
@0: unsigned ip_hl [ 4b] Header length
@1: unsigned ip_tos [ 8b] TOS
@2: unsigned ip_len [ 16b] Length
@4: unsigned ip_id [ 16b] ID
@6: unsigned ip_off [ 16b] Frag offset
@8: unsigned ip_ttl [ 8b] TTL
@9: unsigned ip_p [ 8b] Protocol
@10: unsigned ip_sum [ 16b] Checksum
@12: octets ip_src [ 32b] Source addr
@16: octets ip_dst [ 32b] Dest addr
rest is application defined message body
 
J

Joel VanderWerf

Someone asked offline about block-based initialization, and BitStruct
supports that, as well as hash-based and string-based. Here are the
variants:

ip1 = IP.new
ip1.ip_tos = 0
ip1.ip_len = 0
ip1.ip_id = 0
ip1.ip_off = 0
ip1.ip_ttl = 255
ip1.ip_p = 255
ip1.ip_sum = 0
ip1.ip_src = "192.168.1.4"
ip1.ip_dst = "192.168.1.255"
ip1.body = "This is the payload text."
ip1.ip_len = ip1.length

ip2 = IP.new do |ip|
ip.ip_tos = 0
ip.ip_len = 0
ip.ip_id = 0
ip.ip_off = 0
ip.ip_ttl = 255
ip.ip_p = 255
ip.ip_sum = 0
ip.ip_src = "192.168.1.4"
ip.ip_dst = "192.168.1.255"
ip.body = "This is the payload text."
ip.ip_len = ip.length
end

ip3 = IP.new(
:ip_tos => 0,
:ip_len => 0,
:ip_id => 0,
:ip_off => 0,
:ip_ttl => 255,
:ip_p => 255,
:ip_sum => 0,
:ip_src => "192.168.1.4",
:ip_dst => "192.168.1.255",
:body => "This is the payload text."
) do |ip|
ip.ip_len = ip.length
end

ip4 = IP.new(ip1) # Construct from a BitStruct (or String)

Btw, if you've ever wanted to write ping in ruby, check out ping-recv.rb
in the example dir.
 
M

Morgan

[I really wish I knew how to make eudora do attribution lines automatically...]

Joel said:
(Exerpt from the docs at the project page,
http://redshift.sourceforge.net/bit-struct/.)

= BitStruct


Looks pretty interesting. I do have one question though.

I've got a project I've been thinking about doing a project that would
benefit from the use of arrays of two-bit unsigned integers, of
unknown-at-time-of-coding but probably large size. Which will also
need to be shoved across sockets occaisionally. Is there a convenient
way to make a structure like this using BitStruct?

-Morgan.

"Ginger ale. And leave the bottle."
 
J

Joel VanderWerf

Morgan said:
[I really wish I knew how to make eudora do attribution lines
automatically...]

Joel said:
(Exerpt from the docs at the project page,
http://redshift.sourceforge.net/bit-struct/.)

= BitStruct



Looks pretty interesting. I do have one question though.

I've got a project I've been thinking about doing a project that would
benefit from the use of arrays of two-bit unsigned integers, of
unknown-at-time-of-coding but probably large size. Which will also
need to be shoved across sockets occaisionally. Is there a convenient
way to make a structure like this using BitStruct?

Not yet. I haven't needed variable sized arrays of anything except
chars. For now, if you want to get something working quickly, you're
probably better off with String#[] (i.e. slice) and some bit arithmetic
(which is just what BitStruct does anyway). Or use a C extension. (You
can take a look at unsigned-field.rb for how to set up masks and so on,
but it's nothing special.)

Maybe someone else can pipe up if there is a way to do this with Ruby/DL
or something else?

It would be great if ruby had a built-in method (or a standard lib
extension) for doing bit-wise slice operations (and other bit-oriented
string operations)...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,228
Members
46,818
Latest member
SapanaCarpetStudio

Latest Threads

Top