bit-struct 0.13.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/.gitignore +2 -0
  2. data/History.txt +102 -0
  3. data/README.txt +189 -0
  4. data/Rakefile +34 -0
  5. data/TODO +23 -0
  6. data/TODO-ALSO +71 -0
  7. data/examples/ara-player-data.rb +82 -0
  8. data/examples/bignum.rb +18 -0
  9. data/examples/bits.rb +19 -0
  10. data/examples/byte-bdy.rb +30 -0
  11. data/examples/field-ripper.rb +22 -0
  12. data/examples/fixed-point.rb +17 -0
  13. data/examples/ip.rb +81 -0
  14. data/examples/longlong.rb +30 -0
  15. data/examples/md.rb +23 -0
  16. data/examples/modular-def.rb +38 -0
  17. data/examples/native.rb +31 -0
  18. data/examples/nested.rb +33 -0
  19. data/examples/pad.rb +14 -0
  20. data/examples/ping-recv.rb +25 -0
  21. data/examples/ping.rb +73 -0
  22. data/examples/player-data.rb +75 -0
  23. data/examples/raw.rb +62 -0
  24. data/examples/rest.rb +30 -0
  25. data/examples/switch-endian.rb +49 -0
  26. data/examples/vector.rb +98 -0
  27. data/lib/bit-struct.rb +6 -0
  28. data/lib/bit-struct/bit-struct.rb +549 -0
  29. data/lib/bit-struct/char-field.rb +48 -0
  30. data/lib/bit-struct/fields.rb +273 -0
  31. data/lib/bit-struct/float-field.rb +61 -0
  32. data/lib/bit-struct/hex-octet-field.rb +20 -0
  33. data/lib/bit-struct/nested-field.rb +76 -0
  34. data/lib/bit-struct/octet-field.rb +45 -0
  35. data/lib/bit-struct/pad-field.rb +15 -0
  36. data/lib/bit-struct/signed-field.rb +258 -0
  37. data/lib/bit-struct/text-field.rb +44 -0
  38. data/lib/bit-struct/unsigned-field.rb +248 -0
  39. data/lib/bit-struct/vector-field.rb +77 -0
  40. data/lib/bit-struct/vector.rb +173 -0
  41. data/lib/bit-struct/yaml.rb +69 -0
  42. data/tasks/ann.rake +80 -0
  43. data/tasks/bones.rake +20 -0
  44. data/tasks/gem.rake +201 -0
  45. data/tasks/git.rake +40 -0
  46. data/tasks/notes.rake +27 -0
  47. data/tasks/post_load.rake +34 -0
  48. data/tasks/rdoc.rake +51 -0
  49. data/tasks/rubyforge.rake +55 -0
  50. data/tasks/setup.rb +292 -0
  51. data/tasks/spec.rake +54 -0
  52. data/tasks/svn.rake +47 -0
  53. data/tasks/test.rake +40 -0
  54. data/tasks/zentest.rake +36 -0
  55. data/test/test-endian.rb +39 -0
  56. data/test/test-vector.rb +38 -0
  57. data/test/test.rb +433 -0
  58. metadata +126 -0
@@ -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*")
@@ -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>
@@ -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
@@ -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>
@@ -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
+
@@ -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
@@ -0,0 +1,14 @@
1
+ require 'bit-struct'
2
+
3
+ class PadTest < BitStruct
4
+ unsigned :x, 3
5
+ pad :p1, 2
6
+ unsigned :y, 3
7
+ end
8
+
9
+ pt = PadTest.new
10
+ pt.x = 1
11
+ pt.y = 2
12
+
13
+ p pt
14
+ y pt
@@ -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"
@@ -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