bit-struct 0.13.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/History.txt +102 -0
- data/README.txt +189 -0
- data/Rakefile +34 -0
- data/TODO +23 -0
- data/TODO-ALSO +71 -0
- data/examples/ara-player-data.rb +82 -0
- data/examples/bignum.rb +18 -0
- data/examples/bits.rb +19 -0
- data/examples/byte-bdy.rb +30 -0
- data/examples/field-ripper.rb +22 -0
- data/examples/fixed-point.rb +17 -0
- data/examples/ip.rb +81 -0
- data/examples/longlong.rb +30 -0
- data/examples/md.rb +23 -0
- data/examples/modular-def.rb +38 -0
- data/examples/native.rb +31 -0
- data/examples/nested.rb +33 -0
- data/examples/pad.rb +14 -0
- data/examples/ping-recv.rb +25 -0
- data/examples/ping.rb +73 -0
- data/examples/player-data.rb +75 -0
- data/examples/raw.rb +62 -0
- data/examples/rest.rb +30 -0
- data/examples/switch-endian.rb +49 -0
- data/examples/vector.rb +98 -0
- data/lib/bit-struct.rb +6 -0
- data/lib/bit-struct/bit-struct.rb +549 -0
- data/lib/bit-struct/char-field.rb +48 -0
- data/lib/bit-struct/fields.rb +273 -0
- data/lib/bit-struct/float-field.rb +61 -0
- data/lib/bit-struct/hex-octet-field.rb +20 -0
- data/lib/bit-struct/nested-field.rb +76 -0
- data/lib/bit-struct/octet-field.rb +45 -0
- data/lib/bit-struct/pad-field.rb +15 -0
- data/lib/bit-struct/signed-field.rb +258 -0
- data/lib/bit-struct/text-field.rb +44 -0
- data/lib/bit-struct/unsigned-field.rb +248 -0
- data/lib/bit-struct/vector-field.rb +77 -0
- data/lib/bit-struct/vector.rb +173 -0
- data/lib/bit-struct/yaml.rb +69 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +201 -0
- data/tasks/git.rake +40 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +34 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +292 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- data/tasks/zentest.rake +36 -0
- data/test/test-endian.rb +39 -0
- data/test/test-vector.rb +38 -0
- data/test/test.rb +433 -0
- metadata +126 -0
data/examples/bignum.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'bit-struct'
|
2
|
+
|
3
|
+
class C < BitStruct
|
4
|
+
unsigned :x, 80, :endian => :big, :fixed => 1_000_000_000
|
5
|
+
unsigned :y, 24, :endian => :native, :format => "0x%x"
|
6
|
+
unsigned :z, 64, :format => "0x%x" # big-endian by default
|
7
|
+
signed :w, 48
|
8
|
+
end
|
9
|
+
|
10
|
+
c = C.new
|
11
|
+
|
12
|
+
c.x = 1.000_000_001
|
13
|
+
c.y = 0xa00002
|
14
|
+
c.z = 0x1234abcd5678efef
|
15
|
+
c.w = -1
|
16
|
+
|
17
|
+
p c
|
18
|
+
#puts c.unpack("H*")
|
data/examples/bits.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'bit-struct' # http://redshift.sourceforge.net/bit-struct
|
2
|
+
|
3
|
+
class Bits < BitStruct
|
4
|
+
unsigned :bit0, 1, "Foo bit"
|
5
|
+
unsigned :bit1, 1, "Bar bit"
|
6
|
+
unsigned :bit2, 1
|
7
|
+
unsigned :bit3, 1
|
8
|
+
unsigned :bit4, 1
|
9
|
+
unsigned :bit5, 1
|
10
|
+
unsigned :bit6, 1
|
11
|
+
unsigned :bit7, 1
|
12
|
+
end
|
13
|
+
|
14
|
+
b = Bits.new
|
15
|
+
|
16
|
+
b.bit3 = 1
|
17
|
+
p b.bit3 # ==> 1
|
18
|
+
p b.bit4 # ==> 0
|
19
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# an example where a field may crosse one or two byte boundaries
|
2
|
+
#
|
3
|
+
# try with arguments like 4, 12, and 13 to see the difference
|
4
|
+
#
|
5
|
+
# based on test case from Jon Hart
|
6
|
+
|
7
|
+
require 'bit-struct'
|
8
|
+
class Foo < BitStruct
|
9
|
+
unsigned :a, 4
|
10
|
+
unsigned :b, 8
|
11
|
+
unsigned :c, (ARGV[0] || (raise "USAGE: #{$0} bits")).to_i
|
12
|
+
end
|
13
|
+
|
14
|
+
puts Foo.describe
|
15
|
+
|
16
|
+
foo = Foo.new
|
17
|
+
p foo
|
18
|
+
p foo.unpack("B*").first.scan(/\d{8,8}/)
|
19
|
+
puts
|
20
|
+
|
21
|
+
foo.c = 3123
|
22
|
+
p foo
|
23
|
+
p foo.unpack("B*").first.scan(/\d{8,8}/)
|
24
|
+
puts
|
25
|
+
|
26
|
+
foo.c = (2**(ARGV[0].to_i)-1)
|
27
|
+
p foo
|
28
|
+
p foo.unpack("B*").first.scan(/\d{8,8}/)
|
29
|
+
puts
|
30
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Example to show how to programmatically "cut and paste" some of the fields
|
2
|
+
# from one BitStruct class into another.
|
3
|
+
|
4
|
+
require 'bit-struct'
|
5
|
+
|
6
|
+
class BS1 < BitStruct
|
7
|
+
unsigned :f1, 8
|
8
|
+
unsigned :f2, 8
|
9
|
+
unsigned :f3, 8
|
10
|
+
unsigned :f4, 8
|
11
|
+
unsigned :f5, 8
|
12
|
+
end
|
13
|
+
|
14
|
+
class BS2 < BitStruct
|
15
|
+
fields_to_add = BS1.fields.select {|f| f.name.to_s =~ /[234]/}
|
16
|
+
|
17
|
+
fields_to_add.each do |field|
|
18
|
+
add_field(field.name, field.length, field.options)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
puts BS2.describe
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'bit-struct'
|
2
|
+
|
3
|
+
class C < BitStruct
|
4
|
+
unsigned :f1, 3, :fixed => 100
|
5
|
+
unsigned :f2, 5, :fixed => 1000
|
6
|
+
unsigned :f3, 8, :fixed => 10000
|
7
|
+
unsigned :f4, 32, :fixed => 100000
|
8
|
+
end
|
9
|
+
|
10
|
+
c = C.new
|
11
|
+
|
12
|
+
c.f1 = 0.03
|
13
|
+
c.f2 = 0.005
|
14
|
+
c.f3 = 0.0144
|
15
|
+
c.f4 = 456.78912
|
16
|
+
|
17
|
+
p c # ==> #<C f1=0.03, f2=0.005, f3=0.0144, f4=456.78912>
|
data/examples/ip.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'bit-struct'
|
2
|
+
|
3
|
+
class IP < BitStruct
|
4
|
+
unsigned :ip_v, 4, "Version"
|
5
|
+
unsigned :ip_hl, 4, "Header length"
|
6
|
+
unsigned :ip_tos, 8, "TOS"
|
7
|
+
unsigned :ip_len, 16, "Length"
|
8
|
+
unsigned :ip_id, 16, "ID"
|
9
|
+
unsigned :ip_off, 16, "Frag offset"
|
10
|
+
unsigned :ip_ttl, 8, "TTL"
|
11
|
+
unsigned :ip_p, 8, "Protocol"
|
12
|
+
unsigned :ip_sum, 16, "Checksum"
|
13
|
+
octets :ip_src, 32, "Source addr"
|
14
|
+
octets :ip_dst, 32, "Dest addr"
|
15
|
+
rest :body, "Body of message"
|
16
|
+
|
17
|
+
note " rest is application defined message body"
|
18
|
+
|
19
|
+
initial_value.ip_v = 4
|
20
|
+
initial_value.ip_hl = 5
|
21
|
+
end
|
22
|
+
|
23
|
+
if __FILE__ == $0
|
24
|
+
ip1 = IP.new
|
25
|
+
ip1.ip_tos = 0
|
26
|
+
ip1.ip_len = 0
|
27
|
+
ip1.ip_id = 0
|
28
|
+
ip1.ip_off = 0
|
29
|
+
ip1.ip_ttl = 255
|
30
|
+
ip1.ip_p = 255
|
31
|
+
ip1.ip_sum = 0
|
32
|
+
ip1.ip_src = "192.168.1.4"
|
33
|
+
ip1.ip_dst = "192.168.1.255"
|
34
|
+
ip1.body = "This is the payload text."
|
35
|
+
ip1.ip_len = ip1.length
|
36
|
+
|
37
|
+
ip2 = IP.new do |ip|
|
38
|
+
ip.ip_tos = 0
|
39
|
+
ip.ip_len = 0
|
40
|
+
ip.ip_id = 0
|
41
|
+
ip.ip_off = 0
|
42
|
+
ip.ip_ttl = 255
|
43
|
+
ip.ip_p = 255
|
44
|
+
ip.ip_sum = 0
|
45
|
+
ip.ip_src = "192.168.1.4"
|
46
|
+
ip.ip_dst = "192.168.1.255"
|
47
|
+
ip.body = "This is the payload text."
|
48
|
+
ip.ip_len = ip.length
|
49
|
+
end
|
50
|
+
|
51
|
+
ip3 = IP.new(
|
52
|
+
:ip_tos => 0,
|
53
|
+
:ip_len => 0,
|
54
|
+
:ip_id => 0,
|
55
|
+
:ip_off => 0,
|
56
|
+
:ip_ttl => 255,
|
57
|
+
:ip_p => 255,
|
58
|
+
:ip_sum => 0,
|
59
|
+
:ip_src => "192.168.1.4",
|
60
|
+
:ip_dst => "192.168.1.255",
|
61
|
+
:body => "This is the payload text."
|
62
|
+
) do |ip|
|
63
|
+
ip.ip_len = ip.length
|
64
|
+
end
|
65
|
+
|
66
|
+
ip4 = IP.new(ip1) # Construct from a BitStruct (or String)
|
67
|
+
|
68
|
+
raise unless ip1 == ip2
|
69
|
+
raise unless ip1 == ip3
|
70
|
+
raise unless ip1 == ip4
|
71
|
+
|
72
|
+
ip = ip1
|
73
|
+
|
74
|
+
puts ip.inspect
|
75
|
+
puts "-"*50
|
76
|
+
puts ip.inspect_detailed
|
77
|
+
puts "-"*50
|
78
|
+
puts
|
79
|
+
puts "Description of IP Packet:"
|
80
|
+
puts IP.describe
|
81
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'bit-struct'
|
2
|
+
|
3
|
+
class MyPacket < BitStruct
|
4
|
+
unsigned :x, 8*8, "The x field", :endian => :network
|
5
|
+
# :network is the default, and it's the same as :big
|
6
|
+
unsigned :y, 8*8, "The y field", :endian => :little
|
7
|
+
end
|
8
|
+
|
9
|
+
pkt = MyPacket.new
|
10
|
+
pkt.x = 59843759843759843
|
11
|
+
pkt.y = 59843759843759843
|
12
|
+
|
13
|
+
p pkt.x # 59843759843759843
|
14
|
+
p pkt.y # 59843759843759843
|
15
|
+
|
16
|
+
p pkt
|
17
|
+
# #<MyPacket x=59843759843759843, y=59843759843759843>
|
18
|
+
p pkt.to_s
|
19
|
+
# "\000\324\233\225\037\202\326\343\343\326\202\037\225\233\324\000"
|
20
|
+
|
21
|
+
puts pkt.inspect_detailed
|
22
|
+
# MyPacket:
|
23
|
+
# The x field = 59843759843759843
|
24
|
+
# The y field = 59843759843759843
|
25
|
+
|
26
|
+
puts MyPacket.describe
|
27
|
+
# byte: type name [size] description
|
28
|
+
# ----------------------------------------------------------------------
|
29
|
+
# @0: unsigned x [ 8B] The x field
|
30
|
+
# @8: unsigned y [ 8B] The y field
|
data/examples/md.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'bit-struct'
|
2
|
+
|
3
|
+
class MD < BitStruct
|
4
|
+
vector :row, :length => 3 do
|
5
|
+
vector :col, :length => 10 do
|
6
|
+
float :x, 32
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
md = MD.new
|
12
|
+
rows = md.row
|
13
|
+
row = rows[2]
|
14
|
+
cols = row.col
|
15
|
+
col = cols[7]
|
16
|
+
col.x = 1.23
|
17
|
+
cols[7] = col
|
18
|
+
row.col = cols
|
19
|
+
rows[2] = row
|
20
|
+
md.row = rows
|
21
|
+
|
22
|
+
p md
|
23
|
+
p md.row[2].col[7].x
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# This example shows how to refactor a BitStruct class defininition
|
2
|
+
# using modules.
|
3
|
+
|
4
|
+
module ModuleMethodSaver
|
5
|
+
def method_missing(meth, *args, &block)
|
6
|
+
@saved ||= []
|
7
|
+
@saved << [meth, args, block]
|
8
|
+
end
|
9
|
+
|
10
|
+
def included(m)
|
11
|
+
if @saved
|
12
|
+
@saved.each do |meth, args, block|
|
13
|
+
m.send(meth, *args, &block)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
require 'bit-struct'
|
21
|
+
|
22
|
+
module M
|
23
|
+
extend ModuleMethodSaver
|
24
|
+
|
25
|
+
unsigned :x, 13
|
26
|
+
signed :y, 7
|
27
|
+
end
|
28
|
+
|
29
|
+
class BS < BitStruct
|
30
|
+
include M
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
bs = BS.new
|
35
|
+
bs.x = 123
|
36
|
+
bs.y = -63
|
37
|
+
|
38
|
+
p bs # ==> #<BS x=123, y=-63>
|
data/examples/native.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'bit-struct'
|
2
|
+
|
3
|
+
class NativeNumbers < BitStruct
|
4
|
+
|
5
|
+
unsigned :x0, 1, :endian => :native
|
6
|
+
unsigned :x1, 13, :endian => :native
|
7
|
+
unsigned :x2, 2, :endian => :native
|
8
|
+
unsigned :x3, 32, :endian => :native
|
9
|
+
|
10
|
+
float :f1, 32, :endian => :native
|
11
|
+
float :f2, 64, :endian => :native
|
12
|
+
float :f3, 32
|
13
|
+
float :f4, 64
|
14
|
+
|
15
|
+
default_options :endian => :native
|
16
|
+
# affects fields defined after this and in subclasses
|
17
|
+
|
18
|
+
unsigned :y1, 32
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
n = NativeNumbers.new
|
23
|
+
p n
|
24
|
+
n.x1 = 5
|
25
|
+
n.f1 = n.f3 = 1234.567
|
26
|
+
n.f2 = n.f4 = 6543.321
|
27
|
+
n.y1 = 1
|
28
|
+
|
29
|
+
p n
|
30
|
+
p n.unpack("C*")
|
31
|
+
|
data/examples/nested.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'bit-struct'
|
2
|
+
|
3
|
+
class NestedPart < BitStruct
|
4
|
+
unsigned :x, 5
|
5
|
+
unsigned :y, 3
|
6
|
+
char :s, 5*8
|
7
|
+
end
|
8
|
+
|
9
|
+
class Container < BitStruct
|
10
|
+
nest :n1, NestedPart, "Nest 1"
|
11
|
+
nest :n2, NestedPart, "Nest 2"
|
12
|
+
end
|
13
|
+
|
14
|
+
cont = Container.new
|
15
|
+
|
16
|
+
n = NestedPart.new(:x=>1, :y=>2, :s=>"abc")
|
17
|
+
|
18
|
+
p n
|
19
|
+
|
20
|
+
cont.n1 = n
|
21
|
+
|
22
|
+
n.x = 5
|
23
|
+
n.y = 0
|
24
|
+
n.s = " xyz "
|
25
|
+
|
26
|
+
cont.n2 = n # note copy semantics here!
|
27
|
+
|
28
|
+
puts
|
29
|
+
p cont
|
30
|
+
puts
|
31
|
+
puts cont.inspect_detailed
|
32
|
+
|
33
|
+
puts "-"*80
|
data/examples/pad.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require "socket"
|
2
|
+
require "./ip"
|
3
|
+
|
4
|
+
# Example of using the IP class to receive ping (ICMP) messages.
|
5
|
+
|
6
|
+
begin
|
7
|
+
rsock = Socket.open(Socket::PF_INET, Socket::SOCK_RAW, Socket::IPPROTO_ICMP)
|
8
|
+
rescue Errno::EPERM
|
9
|
+
$stderr.puts "Must run #{$0} as root."
|
10
|
+
exit!
|
11
|
+
end
|
12
|
+
|
13
|
+
Thread.new do
|
14
|
+
loop do
|
15
|
+
data, sender = rsock.recvfrom(8192)
|
16
|
+
port, host = Socket.unpack_sockaddr_in(sender)
|
17
|
+
puts "-"*80,
|
18
|
+
"packet received from #{host}:#{port}:",
|
19
|
+
IP.new(data).inspect_detailed,
|
20
|
+
"-"*80
|
21
|
+
$stdout.flush
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
system "ping 127.0.0.1"
|
data/examples/ping.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
require "socket"
|
2
|
+
require "./ip"
|
3
|
+
|
4
|
+
### example is broken
|
5
|
+
|
6
|
+
# A more substantial example of sending and receiving ICMP packets.
|
7
|
+
|
8
|
+
class ICMP < IP
|
9
|
+
unsigned :icmp_type, 8, "Message type"
|
10
|
+
unsigned :icmp_code, 8, "Message code"
|
11
|
+
unsigned :icmp_cksum, 16, "ICMP checksum"
|
12
|
+
unsigned :icmp_id, 16
|
13
|
+
unsigned :icmp_seq, 16
|
14
|
+
rest :body, "Body of ICMP message"
|
15
|
+
end
|
16
|
+
|
17
|
+
# Example of using the IP class to receive ping (ICMP) messages.
|
18
|
+
|
19
|
+
begin
|
20
|
+
rsock = Socket.open(Socket::PF_INET, Socket::SOCK_RAW, Socket::IPPROTO_ICMP)
|
21
|
+
rescue Errno::EPERM
|
22
|
+
$stderr.puts "Must run #{$0} as root."
|
23
|
+
exit!
|
24
|
+
end
|
25
|
+
|
26
|
+
begin
|
27
|
+
ssock = Socket.open(Socket::PF_INET, Socket::SOCK_RAW, Socket::IPPROTO_ICMP)
|
28
|
+
ssock.setsockopt(Socket::SOL_IP, Socket::IP_HDRINCL, true)
|
29
|
+
# IP_HDRINCL isn't necessary to send ICMP, but we are inheriting
|
30
|
+
# ICMP from IP, so the header is included in this example.
|
31
|
+
rescue Errno::EPERM
|
32
|
+
$stderr.puts "Must run #{$0} as root."
|
33
|
+
exit!
|
34
|
+
end
|
35
|
+
|
36
|
+
Thread.new do
|
37
|
+
loop do
|
38
|
+
data, sender = rsock.recvfrom(8192)
|
39
|
+
port, host = Socket.unpack_sockaddr_in(sender)
|
40
|
+
out = "-"*80,
|
41
|
+
"packet received from #{host}:#{port}:",
|
42
|
+
ICMP.new(data).inspect_detailed,
|
43
|
+
"-"*80
|
44
|
+
puts out
|
45
|
+
$stdout.flush
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
addr = Socket.pack_sockaddr_in(1024, "localhost")
|
50
|
+
5.times do |i|
|
51
|
+
icmp = ICMP.new do |b|
|
52
|
+
# ip_v and ip_hl are set for us by IP class
|
53
|
+
b.ip_tos = 0
|
54
|
+
b.ip_id = i
|
55
|
+
b.ip_off = 0 ## ?
|
56
|
+
b.ip_ttl = 64
|
57
|
+
b.ip_p = Socket::IPPROTO_ICMP
|
58
|
+
b.ip_src = "127.0.0.1"
|
59
|
+
b.ip_dst = "127.0.0.1"
|
60
|
+
b.body = "" ## ?
|
61
|
+
b.ip_len = b.length
|
62
|
+
b.ip_sum = 0 ## ?
|
63
|
+
end
|
64
|
+
|
65
|
+
out = "-"*80,
|
66
|
+
"packet sent:",
|
67
|
+
icmp.inspect_detailed,
|
68
|
+
"-"*80
|
69
|
+
puts out
|
70
|
+
$stdout.flush
|
71
|
+
ssock.send(icmp, 0, addr)
|
72
|
+
sleep 1
|
73
|
+
end
|