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.
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