bit-struct 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|