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,2 @@
1
+ *.bck
2
+ pkg/*
@@ -0,0 +1,102 @@
1
+ bit-struct 0.12
2
+
3
+ - Added vectors.
4
+
5
+ - BitStruct#initialize can take an IO argument.
6
+
7
+ bit-struct 0.11
8
+
9
+ - Allow unaligned fields to cross up to two byte boundaries.
10
+ See examples/byte-bdy.rb.
11
+
12
+ bit-struct 0.10
13
+
14
+ - Fixed a bug when calling #to_yaml on a BitStruct with a pad field. Thanks
15
+ to Jay Reitz for reporting it and providing the solution.
16
+
17
+ - Added BitStruct.default_options. Particularly useful for default endian
18
+ settings--see examples/native.rb.
19
+
20
+ - Fixed a bug that prevented warning about field name conflicts with
21
+ existing methods when the name is given as a symbol.
22
+
23
+ - Fixed point fields may now have fractional divisors (like :fixed => 0.001).
24
+
25
+ bit-struct 0.9
26
+
27
+ - Added examples/field-ripper.rb.
28
+
29
+ - Added BitStruct#field_by_name.
30
+
31
+ - Added more warnings about nested field accessors returning a *copy* of the
32
+ nested data.
33
+
34
+ - Added "pad" fields. See documentation in pad-field.rb and examples/pad.rb.
35
+
36
+ - The #initial_value method now yields the value to a block, if given.
37
+
38
+ - A BitStruct class is only closed when an instance is created. (After this
39
+ point no fields can be added.) Formerly, a class was closed when any method
40
+ requested the total length of the structure. This change makes it easier
41
+ to add groups of fields modularly, each with their own initial_value block.
42
+
43
+ - Added examples/modular-def.rb to explain how to factor a BitStruct
44
+ definition into modules.
45
+
46
+ bit-struct 0.8
47
+
48
+ - Signed fields can now (like unsigned fields) be any multiple of 8 bits, and are accessed as fixnums or bignums, as needed.
49
+
50
+ - It's easier to subclass BitStruct::OctetField. BitStruct::HexOctetField is now implemented in this way, just by defining three constants and two trivial class methods.
51
+
52
+ bit-struct 0.7
53
+
54
+ - BitStruct.describe now takes an option hash, and the :expand=>true option causes nested fields to be expanded in the description.
55
+
56
+ - Unsigned integer fields can now be any multiple of 8 bytes: 8, 16, 24, 32, 40, ... (Fields of 1..15 bits in length are of course still supported.)
57
+
58
+ - Added the :endian => :little option to signed integer, unsigned integer, and float fields. The full set of endian options is now [:little, :big, :network, :native]. The default is always :network.
59
+
60
+ - Option names may be strings or symbols. Values that can be symbols can also be strings.
61
+
62
+ - Added examples/bignum.rb.
63
+
64
+ - Added support for the YAML in ruby 1.8.2 (the YAML in 1.8.4 was already supported).
65
+
66
+ bit-struct 0.6
67
+
68
+ - Added the :endian => :native option for numerical fields (signed, unsigned, float).
69
+
70
+ - Fixed error message with 9..15 bit fields aligned on byte boundary.
71
+
72
+ - The #initial_value is now inherited (before applying defaults).
73
+
74
+ - New examples: raw.rb, native.rb.
75
+
76
+ bit-struct 0.5
77
+
78
+ - Integer fields may now cross byte boundaries, as long as the field fits within two whole bytes.
79
+
80
+ bit-struct 0.4
81
+
82
+ - Fixed a bug in reading text and char fields in YAML: if the value was interpreted by YAML as something other than a string, an error would result.
83
+
84
+ - When BitStructs are loaded from yaml, the key is treated as a setter method, rather than a field. This is useful in case there is a field that needs special setters to accept humanly readable input. (For example, a char field with length-prefixed subfields.)
85
+
86
+ bit-struct 0.3
87
+
88
+ - BitStruct classes are now YAML friendly.
89
+
90
+ - The default behavior of BitStruct#inspect and BitStruct#inspect_detailed is changed to print out the "rest" field, if any. This can be disabled by passing inspect an options hash with :include_rest => false. See BitStruct::DEFAULT_INSPECT_OPTS and BitStruct::DETAILED_INSPECT_OPTS. See examples/ip.rb and examples/rest.rb.
91
+
92
+ - The default behavior of BitStruct#to_h is changed to include the "rest" field. As above, this can be disabled by passing to_h an options hash with :include_rest => false.
93
+
94
+ - The default behavior of BitStruct#to_a is changed to include the "rest" field. As above, this can be disabled by passing false as an argument to to_a.
95
+
96
+ bit-struct 0.2
97
+
98
+ - first public release
99
+
100
+ bit-struct 0.1
101
+
102
+ - first release
@@ -0,0 +1,189 @@
1
+ = BitStruct
2
+
3
+ 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.
4
+
5
+ Field types include:
6
+
7
+ * signed and unsigned integer (1..16 bits, or 24, 32, 40, 48... bits)
8
+
9
+ * numeric fields (signed, unsigned, float) can be designated as any of the following endians: little, big, native, network (default)
10
+
11
+ * fixed point, with arbitrary scale factor
12
+
13
+ * fixed length character array
14
+
15
+ * null-terminated character array for printable text
16
+
17
+ * octets (hex and decimal representation options; useful for IP and MAC addrs)
18
+
19
+ * float
20
+
21
+ * nested BitStruct
22
+
23
+ * vectors of embedded BitStructs
24
+
25
+ * free-form "rest" field (e.g., for the variable-size payload of a packet)
26
+
27
+ Field options (specifiable as :foo => val or "foo" => val) include:
28
+
29
+ * *display_name*: used in BitStruct#inspect_detailed and BitStruct#describe outputs.
30
+
31
+ * *default*: default field value
32
+
33
+ * *format*: alternate format string for inspect
34
+
35
+ * *endian*: for byte ordering of numeric fields (unsigned, signed, float): little, big, native, network (default)
36
+
37
+ * *fixed*: float stored as fixed-point integer, with specified scale factor
38
+
39
+
40
+ == Installation
41
+
42
+ For .gem:
43
+
44
+ gem install bit-struct
45
+
46
+ For .tgz, unpack and then:
47
+
48
+ ruby install.rb config
49
+ ruby install.rb setup
50
+ ruby install.rb install
51
+
52
+ == Uses
53
+
54
+ 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 by default in network order.
55
+
56
+ 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.
57
+
58
+ == Features
59
+
60
+ * Extensible with user-defined field classes.
61
+
62
+ * Fields are fully introspectable and can be defined programmatically.
63
+
64
+ * BitStruct.describe prints out documentation of all the fields of a BitStruct subclass, based on declarations. This is useful for communicating with developers who are not using ruby, but need to talk the same protocols. See Example, below.
65
+
66
+ * Fields are inherited by subclasses. (The free-form "rest" field does not inherit, because it usually represents a payload whose structure is defined in subclasses using the fixed-size fields.)
67
+
68
+ * BitStruct#inspect and BitStruct#inspect_detailed can be used for prettified display of contents. (More generally, BitStruct#inspect takes some options that control formatting and detail level.) See Example, below.
69
+
70
+ * BitStruct inherits from String, so all the usual methods are available, and string-sharing (copy-on-write) is in effect.
71
+
72
+ * Easy access to a "prototype" instance of each BitStruct subclass, from which all instances of that subclass are initialized as a copy (in the absence of other initialization parameters, such as a hash, a string, or a block). See BitStruct.initial_value, and BitStruct#initialize. See Example, below.
73
+
74
+ * Easy conversion to and from hashes, using BitStruct#to_h and BitStruct.new.
75
+
76
+ * BitStructs can persist using Marshal (a BitStruct is after all just a string) or using YAML (with human readable representation of the fields).
77
+
78
+ * Includes tests, examples, and rdoc API documentation.
79
+
80
+ == Limitations
81
+
82
+ * Fields that are not aligned on byte boundaries may cross no more than two bytes boundaries. (See examples/byte-bdy.rb.)
83
+
84
+ * No variable length fields (except the #rest field).
85
+
86
+ == Future plans
87
+
88
+ * Currently, the library is written in pure ruby. The implementation uses Array#pack and String#unpack calls, as well as shifting and masking in pure ruby. Future versions will optionally generate a customized C extension for better efficiency.
89
+
90
+ * A debug mode in which a class identifier is prepended to every BitStruct, so that protocol errors can be detected. (This feature has been implemented in an app that uses BitStruct, but needs to be refactored into the BitStruct library itself.)
91
+
92
+ * Remove field size and alignment limitations.
93
+
94
+ == Example
95
+
96
+ An IP packet can be defined and used like this:
97
+
98
+ require 'bit-struct'
99
+
100
+ class IP < BitStruct
101
+ unsigned :ip_v, 4, "Version"
102
+ unsigned :ip_hl, 4, "Header length"
103
+ unsigned :ip_tos, 8, "TOS"
104
+ unsigned :ip_len, 16, "Length"
105
+ unsigned :ip_id, 16, "ID"
106
+ unsigned :ip_off, 16, "Frag offset"
107
+ unsigned :ip_ttl, 8, "TTL"
108
+ unsigned :ip_p, 8, "Protocol"
109
+ unsigned :ip_sum, 16, "Checksum"
110
+ octets :ip_src, 32, "Source addr"
111
+ octets :ip_dst, 32, "Dest addr"
112
+ rest :body, "Body of message"
113
+
114
+ note " rest is application defined message body"
115
+
116
+ initial_value.ip_v = 4
117
+ initial_value.ip_hl = 5
118
+ end
119
+
120
+ ip = IP.new
121
+ ip.ip_tos = 0
122
+ ip.ip_len = 0
123
+ ip.ip_id = 0
124
+ ip.ip_off = 0
125
+ ip.ip_ttl = 255
126
+ ip.ip_p = 255
127
+ ip.ip_sum = 0
128
+ ip.ip_src = "192.168.1.4"
129
+ ip.ip_dst = "192.168.1.255"
130
+ ip.body = "This is the payload text."
131
+ ip.ip_len = ip.length
132
+
133
+ puts ip.inspect
134
+ puts "-"*50
135
+ puts ip.inspect_detailed
136
+ puts "-"*50
137
+ puts IP.describe
138
+
139
+ (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.)
140
+
141
+ The output of this fragment is:
142
+
143
+ #<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", body="This is the payload text.">
144
+ --------------------------------------------------
145
+ IP:
146
+ Version = 4
147
+ Header length = 5
148
+ TOS = 0
149
+ Length = 45
150
+ ID = 0
151
+ Frag offset = 0
152
+ TTL = 255
153
+ Protocol = 255
154
+ Checksum = 0
155
+ Source addr = "192.168.1.4"
156
+ Dest addr = "192.168.1.255"
157
+ Body of message = "This is the payload text."
158
+ --------------------------------------------------
159
+
160
+ Description of IP Packet:
161
+ byte: type name [size] description
162
+ ----------------------------------------------------------------------
163
+ @0: unsigned ip_v [ 4b] Version
164
+ @0: unsigned ip_hl [ 4b] Header length
165
+ @1: unsigned ip_tos [ 8b] TOS
166
+ @2: unsigned ip_len [ 16b] Length
167
+ @4: unsigned ip_id [ 16b] ID
168
+ @6: unsigned ip_off [ 16b] Frag offset
169
+ @8: unsigned ip_ttl [ 8b] TTL
170
+ @9: unsigned ip_p [ 8b] Protocol
171
+ @10: unsigned ip_sum [ 16b] Checksum
172
+ @12: octets ip_src [ 32b] Source addr
173
+ @16: octets ip_dst [ 32b] Dest addr
174
+ rest is application defined message body
175
+
176
+ == Version
177
+
178
+ bit-struct 0.12
179
+
180
+ The current version of this software can be found at http://redshift.sourceforge.net/bit-struct.
181
+
182
+ == License
183
+
184
+ This software is distributed under the Ruby license. See http://www.ruby-lang.org.
185
+
186
+ == Author
187
+
188
+ Joel VanderWerf, mailto:vjoel@users.sourceforge.net
189
+ Copyright (c) 2005-2009, Joel VanderWerf.
@@ -0,0 +1,34 @@
1
+ # Look in the tasks/setup.rb file for the various options that can be
2
+ # configured in this Rakefile. The .rake files in the tasks directory
3
+ # are where the options are used.
4
+
5
+ begin
6
+ require 'bones'
7
+ Bones.setup
8
+ rescue LoadError
9
+ begin
10
+ load 'tasks/setup.rb'
11
+ rescue LoadError
12
+ raise RuntimeError, '### please install the "bones" gem ###'
13
+ end
14
+ end
15
+
16
+ ensure_in_path 'lib'
17
+ require 'bit-struct/bit-struct'
18
+
19
+ task :default => 'spec:run'
20
+
21
+ PROJ.name = 'bit-struct'
22
+ PROJ.authors = 'Joel VanderWerf'
23
+ PROJ.email = 'vjoel@users.sourceforge.net'
24
+ PROJ.url = 'http://rubyforge.org/projects/bit-struct/'
25
+ PROJ.version = BitStruct::VERSION
26
+ PROJ.rubyforge.name = 'bit-struct'
27
+ PROJ.summary = "Library for packed binary data stored in ruby Strings"
28
+ PROJ.description = <<END
29
+ Library for packed binary data stored in ruby Strings. Useful for accessing fields in network packets and binary files.
30
+ END
31
+
32
+ PROJ.spec.opts << '--color'
33
+
34
+ # EOF
data/TODO ADDED
@@ -0,0 +1,23 @@
1
+ website:
2
+
3
+ scp doc/ vjoel@rubyforge.org:/var/www/gforge-projects/bit-struct/
4
+
5
+
6
+ easy way to define wrappers
7
+
8
+
9
+ to do:
10
+
11
+ generate C code from bit-struct spec?
12
+
13
+ variable-length embedded fields, with referenced length field
14
+
15
+ terminated arrays?
16
+
17
+ use with mmap; in general: bit struct that references a position in another string or string-like object (e.g. a Mmap).
18
+
19
+ use require 'ipaddr'?
20
+
21
+ See ideas in
22
+
23
+ http://www.notwork.org/~gotoken/ruby/p/as-is/pack.rb
@@ -0,0 +1,71 @@
1
+ rest :name, :terminator => ...
2
+
3
+ > * Allow block also for "nest" ?
4
+ >
5
+ > It may sound redundant in the first place, but when it's just for
6
+ > providing more structure without requiring an extra class definition, it
7
+ > could be useful:
8
+ >
9
+ > class Foo < BitStruct
10
+ > nest :coord do
11
+ > signed :x, 8
12
+ > signed :y, 8
13
+ > end
14
+ > end
15
+ >
16
+ > Foo.new.coord.x
17
+
18
+ Agree completely.
19
+
20
+ > * Is explicit :length for vector necessary?
21
+ >
22
+ > class Tag < BitStruct
23
+ > signed :flags, 32
24
+ > vector :axis, Vec, :length => 3
25
+ > end
26
+ >
27
+ > compared to
28
+ >
29
+ > class Tag < BitStruct
30
+ > signed :flags, 32
31
+ > vector :axis, Vec, 3
32
+ > end
33
+ >
34
+ > Or am I missing any ambiguities because of the simple cases?
35
+
36
+ Nothing ambiguous, I just get giddy when there are too many positional args. But since length is required, it does make sense to pass it as a positional arg. I'll change that...
37
+
38
+ > * Allow length not only in bits, but also bytes?
39
+ >
40
+ > In my cases I've never encountered a situation where I needed a member
41
+ > of a structure to represent anything which is not on a byte boundary.
42
+
43
+ Sigh, if only life we always like that. There's a reason I call the library _bit_-struct :)
44
+
45
+ > All my signed and unsigned int have to read "32" (32bit, 4bytes) all
46
+ > over the place. For strings I'm writing "64*8" for string with 64
47
+ > characters.
48
+ >
49
+ > How about a new default_option :granularity which is "1" by default.
50
+ > When set to "8", every specified length (except for vector ...) is
51
+ > multiplied by that factor before used?
52
+
53
+ Hm, will think about that. I wish I could define Fixnum#bytes, but only in the context of field declarations in a BitStruct subclass.
54
+
55
+ I don't like granularity because it's really only meaningful if the value is 1 or 8. Perhaps :granularity => :bit, or :granularity => :byte. But I still don't like an option that you have to keep in mind to understand whether
56
+
57
+ char :x, 80
58
+
59
+ is 8 or 80 chars. It's too much context sensitivity.
60
+
61
+ What about
62
+
63
+ signed :x, bytes(4)
64
+
65
+ Is that too clunky? It would be trivial to implement...
66
+
67
+ > * Why "vector" and not just "array"?
68
+
69
+ For consistency with things like NArray, which has Vector, Matrix, and Array classes, where vectors are essentially one-dim arrays.
70
+
71
+ Also, "array" might lead to confusion with ruby arrays.
@@ -0,0 +1,82 @@
1
+ class PlayerData
2
+ class << self
3
+ def create(*a)
4
+ new(a.pack(FORMAT))
5
+ end
6
+ end
7
+
8
+ SPEC = [
9
+ %w( pid i ),
10
+ %w( x_position f ),
11
+ %w( y_position f ),
12
+ %w( z_position f ),
13
+ %w( foobar i ),
14
+ ]
15
+
16
+ ATTRIBUTES = SPEC.map{|s| s.first}
17
+
18
+ FORMAT = SPEC.map{|s| s.last}.join
19
+
20
+ SPEC.each_with_index do |spec, ix|
21
+ at, format = spec
22
+ eval <<-src
23
+ def #{ at }
24
+ @#{ at } ||= @data[#{ ix }]
25
+ end
26
+ def #{ at }= value
27
+ raise TypeError unless self.#{ at }.class == value.class
28
+ uncache
29
+ @#{ at } = @data[#{ ix }] = value
30
+ end
31
+ src
32
+ end
33
+
34
+ def update buffer
35
+ uncache
36
+ @data = buffer.unpack FORMAT
37
+ end
38
+ alias initialize update
39
+ def uncache
40
+ @to_s = @to_bin = nil
41
+ end
42
+ def to_s
43
+ @to_s ||= ATTRIBUTES.inject(''){|s,a| s << "#{ a } : #{ send a }, " }.chop.chop
44
+ end
45
+ def to_bin
46
+ @to_bin ||= @data.pack(FORMAT)
47
+ end
48
+ end
49
+
50
+ id, x_position, y_position, z_position, foobar =
51
+ 400, 1.0, 2.0, 3.0, 0b101010
52
+
53
+ pd = PlayerData::create id, x_position, y_position, z_position, foobar
54
+
55
+ p pd
56
+ p pd.pid
57
+ p pd.x_position
58
+ p pd.foobar
59
+ puts pd
60
+
61
+ begin
62
+ require 'timeout'
63
+ n = 0
64
+ sec = 10
65
+ Timeout::timeout(sec) do
66
+ loop do
67
+ PlayerData::create id, x_position, y_position, z_position, foobar
68
+ n += 1
69
+ end
70
+ end
71
+ rescue Timeout::Error
72
+ puts "creations per second : #{ n / sec }"
73
+ end
74
+
75
+ __END__
76
+
77
+ #<PlayerData:0xb7e29b7c @to_s=nil, @data=[400, 1.0, 2.0, 3.0, 42], @to_bin=nil>
78
+ 400
79
+ 1.0
80
+ 42
81
+ pid : 400, x_position : 1.0, y_position : 2.0, z_position : 3.0, foobar : 42
82
+ creations per second : 131746