binary_finery 0.0.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/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2011 Michelangelo Altamore
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7
+ the Software, and to permit persons to whom the Software is furnished to do so,
8
+ subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
+
data/README ADDED
@@ -0,0 +1,61 @@
1
+ Binary Finary
2
+ =============
3
+
4
+ Mixes in a _fluent interface_ to any IO entity for reading and
5
+ writing binary data. It handles (de)serialization of:
6
+
7
+ - Integer numbers
8
+ - Null terminated strings
9
+ - Fixed size strings
10
+
11
+
12
+ Requirements
13
+ ------------
14
+
15
+ Binary Finary assumes that the stream where
16
+ is mixed in, provides the following methods:
17
+
18
+ - read or read_nonblock
19
+ - write or write_nonblock
20
+
21
+
22
+ It will run under Ruby version 1.8.7 or newer.
23
+
24
+
25
+ Examples
26
+ --------
27
+
28
+ File.open(my_file.bin) do |f|
29
+ f.extend(BinaryFinary)
30
+ version = f.read_uint16_big
31
+ length = f.read_uint32_little
32
+ end
33
+
34
+
35
+ Install
36
+ -------
37
+
38
+ $ gem install binary_finary
39
+
40
+
41
+ Contributing
42
+ ------------
43
+
44
+ If you'd like to hack on, please follow these instructions.
45
+ To get all of the dependencies, install the gem first.
46
+
47
+ 1. Fork the project and clone down your fork
48
+ 2. Create a branch with a descriptive name to contain your change
49
+ 4. Hack away
50
+ 5. Add tests and make sure everything still passes by running rake
51
+ 6. Do not change the version number, I will do that on my end
52
+ 7. If necessary, rebase your commits into logical chunks, without errors
53
+ 8. Push the branch up to GitHub
54
+ 9. Send me (altamic) a pull request for your branch
55
+
56
+
57
+ Copyright
58
+ =========
59
+
60
+ © Copyright 2011 Michelangelo Altamore. See LICENSE for details.
61
+
data/README.md ADDED
@@ -0,0 +1,61 @@
1
+ Binary Finary
2
+ =============
3
+
4
+ Mixes in a _fluent interface_ to any IO entity for reading and
5
+ writing binary data. It handles (de)serialization of:
6
+
7
+ - Integer numbers
8
+ - Null terminated strings
9
+ - Fixed size strings
10
+
11
+
12
+ Requirements
13
+ ------------
14
+
15
+ Binary Finary assumes that the stream where
16
+ is mixed in, provides the following methods:
17
+
18
+ - read or read_nonblock
19
+ - write or write_nonblock
20
+
21
+
22
+ It will run under Ruby version 1.8.7 or newer.
23
+
24
+
25
+ Examples
26
+ --------
27
+
28
+ File.open(my_file.bin) do |f|
29
+ f.extend(BinaryFinary)
30
+ version = f.read_uint16_big
31
+ length = f.read_uint32_little
32
+ end
33
+
34
+
35
+ Install
36
+ -------
37
+
38
+ $ gem install binary_finary
39
+
40
+
41
+ Contributing
42
+ ------------
43
+
44
+ If you'd like to hack on, please follow these instructions.
45
+ To get all of the dependencies, install the gem first.
46
+
47
+ 1. Fork the project and clone down your fork
48
+ 2. Create a branch with a descriptive name to contain your change
49
+ 4. Hack away
50
+ 5. Add tests and make sure everything still passes by running rake
51
+ 6. Do not change the version number, I will do that on my end
52
+ 7. If necessary, rebase your commits into logical chunks, without errors
53
+ 8. Push the branch up to GitHub
54
+ 9. Send me (altamic) a pull request for your branch
55
+
56
+
57
+ Copyright
58
+ =========
59
+
60
+ © Copyright 2011 Michelangelo Altamore. See LICENSE for details.
61
+
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ # -*- ruby -*-
2
+
3
+ $LOAD_PATH.unshift('lib')
4
+
5
+ desc "Open an irb session preloaded with this library"
6
+ task :console do
7
+ sh "irb -rubygems -r ./lib/binary_finery.rb"
8
+ end
9
+
10
+ task :default => :test
11
+
12
+ require 'rake/testtask'
13
+ Rake::TestTask.new(:test) do |test|
14
+ test.pattern = 'test/**/{helper,test_*}.rb'
15
+ test.warning = true
16
+ test.verbose = true
17
+ end
18
+
19
+
@@ -0,0 +1,348 @@
1
+ ##
2
+ # BinaryFinery mixin is meant to be used as a
3
+ # fluent interface to any IO entity for reading
4
+ # or writing binary data.
5
+ # This module assumes that read(n) and write(str)
6
+ # methods are available where this module is mixed in.
7
+ # It handles (de)serialization of:
8
+ #
9
+ # - Integer numbers ✓
10
+ # - Null terminated strings ✓
11
+ # - Fixed size strings ✓
12
+ #
13
+
14
+ # In order to be plaftorm agnostic, Binary inspects
15
+ # at load time machine's capabilities and adjust its
16
+ # own operations accordingly.
17
+ #
18
+ # BinaryFinary performs operations accepted by the
19
+ # following grammar:
20
+ #
21
+ # operation ::= 'read' | 'write'
22
+ # integer_type ::= 'uint' | 'int'
23
+ # bits ::= '8' | '16' | '32' | '64' | '128' | '256'
24
+ # endianness ::= 'native' | 'little' | 'big' | 'network'
25
+ # OP_INT ::= operation '_' integer_type bits ('_' endianness)?
26
+ #
27
+ # str_padding ::= 'null_padded' | 'c_'
28
+ # string_flavor ::= 'string' | 'fixed_string' | 'binary_string'
29
+ # str_preposition ::= '_of_'
30
+ # integer ::= [0-9]+
31
+ # str_size ::= 'bytes'
32
+ # OP_STR ::= operation '_' (str_padding '_')?
33
+ # string_flavor '_' str_preposition integer '_' str_size
34
+ #
35
+ module BinaryFinery
36
+ OP_RE = /(read|write)_/
37
+ INT_RE = /(uint|int)(8|16|32|64)_?(native|little|big|network)?/
38
+ STR_RE = /(null_padded|c_)?((binary_|fixed_)?string)(_of_)[0-9]+(bytes)/
39
+ OP_INT_RE = Regexp::compile(OP_RE.source + INT_RE.source)
40
+ OP_STR_RE = Regexp::compile(OP_RE.source + STR_RE.source)
41
+
42
+ KNOWN_RE = Regexp::compile(OP_INT_RE.source + OP_STR_RE.source)
43
+
44
+ NUL = 0.chr
45
+
46
+ # Returns the number of bytes used to encode an integer number
47
+ INTEGER_SIZE_IN_BYTES = module_eval { 1.size }
48
+ def integer_size_in_bytes() INTEGER_SIZE_IN_BYTES end
49
+ alias :word_size_in_bytes :integer_size_in_bytes
50
+
51
+ def little_endian_byte_order() :little end
52
+ def big_endian_byte_order() :big end
53
+ alias :network_byte_order :big_endian_byte_order
54
+
55
+ # A machine architecture is said to be little endian if puts first the
56
+ # LSB. We evaluate the first byte of the number 1 packed as an integer.
57
+ # While first_byte is a Fixnum in Ruby 1.8.x, it is a string in 1.9.x;
58
+ # in latter case we employ the ord method to obtain the ordinal number
59
+ # associated,if is the case. Underlying machine is l.e. if the LSB is 1.
60
+
61
+ NATIVE_BYTE_ORDER = module_eval do
62
+ first_byte = [1].pack('i')[0]
63
+ first_byte = first_byte.ord if RUBY_VERSION =~ /^1\.9/
64
+ first_byte == 1 ? :little : :big
65
+ end
66
+
67
+ def native_byte_order() NATIVE_BYTE_ORDER end
68
+
69
+ def little_endian_platform?() native_byte_order.equal? :little end
70
+ def big_endian_platform?() native_byte_order.equal? :big end
71
+ alias :network_endian_platform? :big_endian_platform?
72
+
73
+ @@pack_mappings = {
74
+ 1 => { :uint => { :native => 'C' },
75
+ :int => { :native => 'c' } },
76
+ 2 => { :uint => { :native => 'S', :little => 'v', :big => 'n' },
77
+ :int => { :native => 's', :little => 'v', :big => 'n' } },
78
+ 4 => { :uint => { :native => 'L', :little => 'V', :big => 'N' },
79
+ :int => { :native => 'l', :little => 'V', :big => 'N' } },
80
+ 8 => { :uint => { :native => 'Q' },
81
+ :int => { :native => 'q' } } }
82
+
83
+ BIT_MASK = { 4 => 0xF, 8 => 0xFF, 16 => 0xFFFFF,
84
+ 32 => 0xFFFFFFFF, 64 => 0xFFFFFFFFFFFFFFFF,
85
+ 128 => 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF }
86
+
87
+ DEFAULT_BIT_MASK = module_eval { (1.size >> 1) * 8 }
88
+
89
+ def msb(num, bits=DEFAULT_BIT_MASK) (num & BIT_MASK[bits]) end
90
+ def lsb(num, bits=DEFAULT_BIT_MASK) ((num >> bits) & BIT_MASK[bits]) end
91
+
92
+ def split_msb_lsb(num, bits)
93
+ [msb(num, bits), lsb(num,bits) ]
94
+ end
95
+
96
+ def concat(msb, lsb, bits)
97
+ lsb + (msb << bits)
98
+ end
99
+
100
+ def read_uint256_little
101
+ lsb = read_uint128_little
102
+ msb = read_uint128_little
103
+ concat(msb, lsb, 128)
104
+ end
105
+
106
+ def read_int256_little
107
+ val = read_uint256_little
108
+ val > (2**256 - 1) ? val : -val
109
+ end
110
+
111
+ def read_uint256_big
112
+ msb = read_uint64_big
113
+ lsb = read_uint64_big
114
+ concat(msb, lsb, 128)
115
+ end
116
+ alias :read_uint256_network :read_uint256_big
117
+
118
+ def read_uint256_native
119
+ little_endian_platform? ? read_uint256_little : read_uint256_big
120
+ end
121
+ alias :read_uint256 :read_uint256_native
122
+
123
+ def write_uint256_little(n)
124
+ msb, lsb = split_msb_lsb(n, 128)
125
+ write_uint128_little(msb)
126
+ write_uint128_little(lsb)
127
+ end
128
+
129
+ def write_int256_little(n)
130
+ n = 2**256 - n
131
+ write_uint256_little(n)
132
+ end
133
+
134
+ def write_uint256_big(n)
135
+ lsb, msb = split_msb_lsb(n, 128)
136
+ write_uint128_big(msb)
137
+ write_uint128_big(lsb)
138
+ end
139
+
140
+ def write_uint256_native(n)
141
+ little_endian_platform? ? write_uint256_little(n) : write_uint256_big(n)
142
+ end
143
+ alias :write_uint256 :write_uint256_native
144
+
145
+ def read_uint128_little
146
+ lsb = read_uint64_little
147
+ msb = read_uint64_little
148
+ concat(msb, lsb, 64)
149
+ end
150
+
151
+ def read_int128_little
152
+ val = read_uint128_little
153
+ val > (2**128 - 1) ? val : -val
154
+ end
155
+
156
+ def read_uint128_big
157
+ msb = read_uint64_big
158
+ lsb = read_uint64_big
159
+ concat(msb, lsb, 64)
160
+ end
161
+ alias :read_uint128_network :read_uint128_big
162
+
163
+ def read_uint128_native
164
+ little_endian_platform? ? read_uint128_little : read_uint128_big
165
+ end
166
+ alias :read_uint128 :read_uint128_native
167
+
168
+ def write_uint128_little(n)
169
+ lsb, msb = split_msb_lsb(n, 64)
170
+ write_uint64_little(lsb)
171
+ write_uint64_little(msb)
172
+ end
173
+
174
+ def write_int128_little(n)
175
+ n = 2**128 - n
176
+ write_uint128_little(n)
177
+ end
178
+
179
+ def write_uint128_big(n)
180
+ lsb, msb = split_msb_lsb(n, 64)
181
+ write_uint64_big(msb)
182
+ write_uint64_big(lsb)
183
+ end
184
+
185
+ def write_uint128_native(n)
186
+ little_endian_platform? ? write_uint128_little(n) : write_uint128_big(n)
187
+ end
188
+ alias :write_uint128 :write_uint128_native
189
+
190
+ def read_uint64_little
191
+ lsb = readn_unpack(4, 'L', :little)
192
+ msb = readn_unpack(4, 'L', :little)
193
+ concat(msb, lsb, 32)
194
+ end
195
+
196
+ def read_uint64_big
197
+ msb = readn_unpack(4, 'L', :big)
198
+ lsb = readn_unpack(4, 'L', :big)
199
+ concat(msb, lsb, 32)
200
+ end
201
+ alias :read_uint64_network :read_uint64_big
202
+
203
+ def read_uint64_native
204
+ little_endian_platform? ? read_uint64_little : read_uint64_big
205
+ end
206
+ alias :read_uint64 :read_uint64_native
207
+
208
+ def write_uint64_little(n)
209
+ lsb, msb = split_msb_lsb(n,32)
210
+ write_pack(lsb, 'L', :little)
211
+ write_pack(msb, 'L', :little)
212
+ end
213
+
214
+ def write_uint64_big(n)
215
+ lsb, msb = split_msb_lsb(n,32)
216
+ write_pack(msb, 'L', :big)
217
+ write_pack(lsb, 'L', :big)
218
+ end
219
+
220
+ def write_uint64_native(n)
221
+ little_endian_platform? ? write_uint64_little(n) : write_uint64_big(n)
222
+ end
223
+ alias :write_uint64 :write_uint64_native
224
+
225
+ # obtains the correct pack format for the arguments
226
+ def format(byte_size, type, byte_order)
227
+ byte_order = :native if byte_order.nil?
228
+ byte_order = :big if byte_order.equal?(:network)
229
+ @@pack_mappings[byte_size][type][byte_order]
230
+ end
231
+
232
+ # read n bytes and unpack, swapping bytes as per endianness
233
+ def readn_unpack(size, template, byte_order=NATIVE_BYTE_ORDER)
234
+ str = readn(size)
235
+ str.reverse! if not native_byte_order.equal? byte_order # spotted problem in pack
236
+ str.unpack(template).first
237
+ end
238
+
239
+ # read exactly n characters from the buffer, otherwise raise an exception.
240
+ def readn(n)
241
+ str = read(n)
242
+ raise "couldn't read #{n} characters." if str.nil? or str.size != n
243
+ str
244
+ end
245
+
246
+ def read_null_padded_string(size)
247
+ str = readn(size)
248
+ str.split(/NUL/).first or str
249
+ end
250
+
251
+ def read_c_string
252
+ readline(sep_string = NUL)
253
+ end
254
+
255
+ def read_string(opt={:size => nil, :padding => NUL})
256
+ if opt[:size]
257
+ read_fixed_size_string(opt[:size], opt[:padding])
258
+ else
259
+ read_c_string
260
+ end
261
+ end
262
+
263
+ # writes a number and pack it, swapping bytes as per endianness
264
+ def write_pack(number, template, byte_order=NATIVE_BYTE_ORDER)
265
+ str = [number].pack(template)
266
+ str.reverse! if not native_byte_order.equal? byte_order # blame Array#pack
267
+ write(str)
268
+ end
269
+
270
+ # writes the string and appends NUL
271
+ def write_c_string(str)
272
+ #TODO: improve input validation
273
+ raise ArgumentError, "Invalid Ruby string" if str.include?(NUL)
274
+ write(str)
275
+ write(NUL)
276
+ end
277
+
278
+ def write_string(content, opt = {:padding => nil, :size => content.size})
279
+ return if not (opt[:size].kind_of? Integer)
280
+ output_string = content[0..opt[:size]]
281
+ output_string = output_string.ljust(opt[:size], opt[:padding]) if opt[:padding]
282
+ write(output_string)
283
+ end
284
+
285
+ def write_fixed_size_string(content="")
286
+ write_string(content, :size => content.size)
287
+ end
288
+
289
+ def write_null_padded_string(str, opt = {:size => str.size})
290
+ write_string(str, padding => "\000", :size => str.size)
291
+ end
292
+
293
+ def method_missing(method_name, *args, &block)
294
+ if method_name.to_s =~ OP_INT_RE
295
+ op, type, bits, byte_order = Regexp.last_match[1..4]
296
+ # string → sym
297
+ op, type, byte_order = [op, type].map!(&:to_sym)
298
+ # adjust bits to bytes
299
+ byte_size = (bits.to_i / 8)
300
+ # normalize endianness
301
+ byte_order = byte_order.to_sym unless byte_order.nil?
302
+ byte_order = :big if byte_order == 'network'
303
+
304
+ fmt = format(byte_size,type,byte_order)
305
+
306
+ case op
307
+ when :read
308
+ self.class.send :define_method, method_name do
309
+ readn_unpack(byte_size, fmt, byte_order)
310
+ end
311
+ self.send method_name
312
+ when :write
313
+ if (args.first.kind_of? Integer) && (args.size == 1)
314
+ self.class.send :define_method, method_name do |value|
315
+ write_pack(value, fmt, byte_order)
316
+ end
317
+ self.send method_name, args.first
318
+ end
319
+ end
320
+ elsif method_name.to_s =~ OP_STR_RE
321
+ op, string_flavor = Regexp.last_match[1..2]
322
+ case op
323
+ when :read
324
+ options = args.first.indexes(:size, :padding)
325
+ self.send :read_string, options
326
+ when :write
327
+ str, options = args.shift, args
328
+ self.send :write_string, str, options
329
+ end
330
+ else
331
+ super
332
+ end
333
+ end
334
+
335
+ # Tells the byte size required for the method passed as argument.
336
+ # When recognized.
337
+ def size_of(type, object=nil)
338
+ case
339
+ when type.to_s =~ INT_RE then Regexp.last_match[2].to_i / 8
340
+ when type.to_s =~ STR_RE then Regexp.last_match[-2].to_i
341
+ end
342
+ end
343
+
344
+ def recognize?(type)
345
+ type.to_s =~ Regexp.union(INT_RE,STR_RE)
346
+ end
347
+ end
348
+
data/test/helper.rb ADDED
@@ -0,0 +1,75 @@
1
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__))
2
+
3
+ # TODO: resort to test_unit where minitest
4
+ # is not available and don't require
5
+ # anything
6
+ begin; gem 'minitest' if RUBY_VERSION =~ /^1\.9/; rescue Gem::LoadError; end
7
+
8
+ begin
9
+ require 'minitest/autorun'
10
+ rescue LoadError
11
+ require 'rubygems'
12
+ require 'minitest/autorun'
13
+ end
14
+
15
+ require 'binary_finery'
16
+
17
+ module BinaryFinery
18
+ class TestCase < MiniTest::Unit::TestCase
19
+ #
20
+ # Test::Unit backwards compatibility
21
+ #
22
+ begin
23
+ alias :assert_no_match :refute_match
24
+ alias :assert_not_nil :refute_nil
25
+ alias :assert_raise :assert_raises
26
+ alias :assert_not_equal :refute_equal
27
+ end if RUBY_VERSION =~ /^1\.9/
28
+
29
+ def fixture_path
30
+ File.join(File.dirname(__FILE__), 'fixtures')
31
+ end
32
+
33
+ def lib_path
34
+ File.join(File.dirname(__FILE__), '../lib')
35
+ end
36
+
37
+ def random_string(length)
38
+ (0...length).inject("") { |m,n| m << (?A + rand(25)).chr }
39
+ end
40
+
41
+ def random_integer(bits, type=:uint)
42
+ (type.equal? :int) ? -1*rand(2**(bits-1)) : rand(2**bits)
43
+ end
44
+
45
+ def pack_integer(integer, opt={:bytes => 4, :type => :uint, :endian => nil})
46
+ bytes, type, endian = opt[:bytes], opt[:type], opt[:endian]
47
+ endian = :big if endian == :network
48
+ endian = :native if endian == nil
49
+
50
+ pack_mapping = {
51
+ 1 => { :uint => { :native => 'C' },
52
+ :int => { :native => 'c' } },
53
+ 2 => { :uint => { :native => 'S', :little => 'v', :big => 'n' },
54
+ :int => { :native => 's', :little => 'v', :big => 'n' } },
55
+ 4 => { :uint => { :native => 'L', :little => 'V', :big => 'N' },
56
+ :int => { :native => 'l', :little => 'V', :big => 'N' } },
57
+ 8 => { :uint => { :native => 'Q' },
58
+ :int => { :native => 'q' } } }
59
+
60
+ if bytes == 8 && (endian == :little || endian == :big)
61
+ bytes = 4
62
+ format = pack_mapping[bytes][type][endian]
63
+ msb, lsb = (integer & 0xFFFFFFFF), (integer >> 16 & 0xFFFFFFFF)
64
+ array = [msb, lsb]
65
+ array.reverse! if [1,"\x01"].include?([1].pack('i')[0]) # little endian
66
+ array.pack(format*2)
67
+ else
68
+ format = pack_mapping[bytes][type][endian]
69
+ [integer].pack(format)
70
+ end
71
+
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,303 @@
1
+ require 'helper'
2
+ require 'stringio'
3
+
4
+ def StringIO.of_size(size, &block)
5
+ if (not size.kind_of?(Integer)) || (size < 0)
6
+ raise ArgumentError, 'positive integer required'
7
+ end
8
+ new(0.chr * size, &block)
9
+ end
10
+
11
+ class TestBinaryFinery < BinaryFinery::TestCase
12
+ def setup
13
+ @mockup = StringIO.new.extend(BinaryFinery)
14
+ end
15
+
16
+ def host_endianness
17
+ first_byte = [1].pack('i')[0]
18
+ [1,"\x01"].include?(first_byte) ? :little : :big
19
+ end
20
+
21
+ def test_detect_platform_endianness
22
+ assert_equal BinaryFinery::NATIVE_BYTE_ORDER, host_endianness
23
+ end
24
+
25
+ def test_detect_platform_integer_size
26
+ assert_equal BinaryFinery::INTEGER_SIZE_IN_BYTES, 1.size
27
+ end
28
+
29
+ def test_buffer_includes_binary
30
+ assert(@mockup.class.included_modules & [BinaryFinery])
31
+ end
32
+
33
+ def test_read_correctly_1_byte
34
+ buf = StringIO.of_size(2).extend(BinaryFinery)
35
+ buf.rewind
36
+ buf.write_uint8(0x55)
37
+ buf.rewind
38
+
39
+ assert_equal 0x55, buf.read_uint8
40
+ end
41
+
42
+ def test_does_not_care_about_overflow
43
+ overflow = 0xFF + 1
44
+ buf = StringIO.of_size(1).extend(BinaryFinery)
45
+ buf.rewind
46
+ buf.write_uint8(overflow)
47
+ buf.rewind
48
+
49
+ assert_equal 0, buf.read_uint8
50
+ end
51
+
52
+ def test_read_correctly_2_bytes
53
+ buf = StringIO.of_size(2).extend(BinaryFinery)
54
+ buf.rewind
55
+ buf.write_uint16_little(0x55)
56
+ buf.rewind
57
+
58
+ assert_equal 0x55, buf.read_uint16_little
59
+ end
60
+
61
+ def test_does_not_care_about_overflow_for_uint32
62
+ overflow = 0xFFFFFFFF + 1
63
+ buf = StringIO.of_size(4).extend(BinaryFinery)
64
+ buf.rewind
65
+ buf.write_uint32(overflow)
66
+ buf.rewind
67
+
68
+ assert_equal 0, buf.read_uint32
69
+ end
70
+
71
+ def test_read_correctly_4_bytes
72
+ v2 = [0x32, 0x24, 0x00, 0x00]
73
+ v2.reverse! if host_endianness.equal? :little
74
+ packed = v2.pack("C" * v2.size)
75
+ buf = StringIO.new(packed).extend(BinaryFinery)
76
+
77
+ buf.size.times do |i|
78
+ assert_equal v2[i], buf.read_uint8
79
+ end
80
+ end
81
+
82
+ def test_write_fixed_size_string
83
+ content = 'new blips are arbitrarily created in the electronic transaction system of the Federal Reserve (known as FedWire), no outside detection is possible whatsoever because there is no outside system that verifies (or even can verify) the total quantity of FedWire deposits'
84
+ buf = StringIO.of_size(content.size).extend(BinaryFinery)
85
+ buf.rewind
86
+ buf.write_fixed_size_string(content)
87
+ buf.rewind
88
+
89
+ assert_equal content, buf.gets.chomp
90
+ end
91
+
92
+ def test_find_bytesize_for_int16_little
93
+ buf = StringIO.new('').extend(BinaryFinery)
94
+ assert_equal 2, buf.size_of('read_int16_little')
95
+ end
96
+
97
+ def test_write_uint64_native
98
+ n = 0xB70A4625F1A224CF # Big endian
99
+ bytes = [0xB7, 0x0A, 0x46, 0x25, 0xF1, 0xA2, 0x24, 0xCF]
100
+ bytes.reverse! if BinaryFinery::NATIVE_BYTE_ORDER.equal? :little
101
+
102
+ buf = StringIO.of_size(8).extend(BinaryFinery)
103
+
104
+ buf.rewind
105
+ buf.write_uint64(n)
106
+ buf.rewind
107
+
108
+ bytes.each_with_index do |byte, i|
109
+ buf.pos = i
110
+ assert_equal byte, buf.read_uint8
111
+ end
112
+ buf.rewind
113
+ expected = bytes.pack("C"*bytes.size)
114
+ assert_equal n, buf.read_uint64
115
+ end
116
+
117
+ def test_write_uint64_little
118
+ # write_uint64_little(1) # services
119
+ n = 1
120
+ buf = StringIO.of_size(8).extend(BinaryFinery)
121
+ buf.write_uint64_little(n)
122
+ buf.rewind
123
+
124
+ assert_equal n, buf.read_uint64_little
125
+ end
126
+
127
+ def test_write_uint128_little
128
+ # write_uint128_network(0xFFFF00000000) # ip_address
129
+ n = 0xFFFF00000000
130
+ buf = StringIO.of_size(16).extend(BinaryFinery)
131
+ buf.write_uint128_little(n)
132
+ buf.rewind
133
+
134
+ assert_equal n, buf.read_uint128_little
135
+ end
136
+
137
+ def test_write_uint128_big
138
+ n = 0xFFFF00000000
139
+ buf = StringIO.of_size(16).extend(BinaryFinery)
140
+ buf.write_uint128_big(n)
141
+ buf.rewind
142
+
143
+ assert_equal n, buf.read_uint128_big
144
+ end
145
+
146
+ def test_read_uint128_little
147
+ n = rand(2**128)
148
+ assert_equal 16, n.size
149
+ buf = StringIO.of_size(16).extend(BinaryFinery)
150
+ buf.write_uint128_little(n)
151
+ buf.rewind
152
+
153
+ assert_equal n, buf.read_uint128_little
154
+ end
155
+
156
+ def test_read_uint256_little
157
+ n = rand(2**256)
158
+ assert_equal 32, n.size
159
+ buf = StringIO.of_size(32).extend(BinaryFinery)
160
+ buf.write_uint256_little(n)
161
+ buf.rewind
162
+
163
+ assert_equal n, buf.read_uint256_little
164
+ end
165
+
166
+ def test_read_int128_little
167
+ n = rand(2**64)
168
+ buf = StringIO.of_size(16).extend(BinaryFinery)
169
+ buf.write_int128_little(n)
170
+ buf.rewind
171
+
172
+ assert_equal n, buf.read_int128_little
173
+ end
174
+
175
+ def test_read_int128_little
176
+ n = -rand(2**64)
177
+ buf = StringIO.of_size(16).extend(BinaryFinery)
178
+ buf.write_int128_little(n)
179
+ buf.rewind
180
+
181
+ assert_equal n, buf.read_int128_little
182
+ end
183
+
184
+ def test_read_int256_little
185
+ n = rand(2**128)
186
+ buf = StringIO.of_size(32).extend(BinaryFinery)
187
+ buf.write_int256_little(n)
188
+ buf.rewind
189
+
190
+ assert_equal n, buf.read_int256_little
191
+ end
192
+
193
+ def test_read_int256_little
194
+ n = -rand(2**128)
195
+ buf = StringIO.of_size(32).extend(BinaryFinery)
196
+ buf.write_int256_little(n)
197
+ buf.rewind
198
+
199
+ assert_equal n, buf.read_int256_little
200
+ end
201
+
202
+ def test_write_null_padded_string
203
+ str = "ciao\000\000\000\000"
204
+ buf = StringIO.of_size(32).extend(BinaryFinery)
205
+
206
+ buf.write_string("ciao", :padding => "\000", :size => str.size)
207
+ buf.rewind
208
+
209
+ assert_equal str, buf.readn(str.size)
210
+ end
211
+
212
+ def test_write_c_string
213
+ str = "ciao\000"
214
+ buf = StringIO.of_size(32).extend(BinaryFinery)
215
+
216
+ buf.write_c_string("ciao")
217
+ buf.rewind
218
+
219
+ assert_equal str, buf.read_c_string
220
+ end
221
+
222
+ def test_read_null_padded_string
223
+ str = "ciao\000\000\000\000"
224
+ buf = StringIO.of_size(32).extend(BinaryFinery)
225
+
226
+ buf.write_string("ciao", :padding => "\000", :size => str.size)
227
+ buf.rewind
228
+
229
+ assert_equal str, buf.read_null_padded_string(8)
230
+ end
231
+
232
+ end
233
+
234
+ # test read
235
+ bytes_ary = [1,2,4,8]
236
+
237
+ bytes_ary.each do |bytes|
238
+ [:int, :uint].each do |type|
239
+ [:native, :little, :big, :network].each do |mapped_endian|
240
+ next if bytes.equal? 1
241
+ TestBinaryFinery.class_eval do
242
+ bits = bytes * 8
243
+ packing = { :bytes => bytes, :type => type, :endian => mapped_endian }
244
+
245
+ # read
246
+ read_method = "read_#{type}#{bits}_#{mapped_endian}"
247
+ define_method "test_#{read_method}" do
248
+ number = random_integer(bits, type)
249
+ packed_number = pack_integer(number, packing)
250
+ buf = StringIO.new(packed_number).extend(BinaryFinery)
251
+
252
+ assert number, buf.send(read_method)
253
+ end
254
+
255
+ # write
256
+ write_method = "write_#{type}#{bits}_#{mapped_endian}"
257
+ define_method "test_#{write_method}" do
258
+ number = random_integer(bits, type)
259
+ buf = StringIO.of_size(bytes).extend(BinaryFinery)
260
+ buf.send(write_method, number)
261
+ buf.rewind
262
+
263
+ assert number, buf.send(read_method)
264
+ end
265
+
266
+ #size
267
+ define_method "test_#{type}#{bits}_length_is_#{bytes}_bytes" do
268
+ assert bytes, StringIO.new('').extend(BinaryFinery).size_of(read_method)
269
+ assert bytes, StringIO.new('').extend(BinaryFinery).size_of(write_method)
270
+ end
271
+ end
272
+ end
273
+ [nil].each do |no_endian_specified|
274
+ bits = bytes * 8
275
+ packing = { :bytes => bytes,
276
+ :type => type,
277
+ :endian => no_endian_specified }
278
+
279
+ TestBinaryFinery.class_eval do
280
+ read_method = "read_#{type}#{bits}"
281
+ define_method "test_#{read_method}" do
282
+ number = random_integer(bits, type)
283
+ packed = pack_integer(number, packing)
284
+ buf = StringIO.new(packed).extend(BinaryFinery)
285
+
286
+ assert number, buf.send(read_method)
287
+ end
288
+
289
+ write_method = "write_#{type}#{bits}"
290
+ define_method "test_#{write_method}" do
291
+ number = random_integer(bits, type)
292
+ buf = StringIO.of_size(bytes).extend(BinaryFinery)
293
+ buf.rewind
294
+ buf.send(write_method, number)
295
+ buf.rewind
296
+
297
+ assert number, buf.send(read_method)
298
+ end
299
+ end
300
+ end
301
+ end
302
+ end
303
+
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: binary_finery
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Michelangelo Altamore
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-10-07 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: minitest
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 11
29
+ segments:
30
+ - 2
31
+ - 1
32
+ - 0
33
+ version: 2.1.0
34
+ type: :development
35
+ version_requirements: *id001
36
+ description: |-
37
+ BinaryFinery mixes in a fluent interface to any
38
+ IO entity for reading or writing binary data.
39
+ email: michelangelo@altamore.org
40
+ executables: []
41
+
42
+ extensions: []
43
+
44
+ extra_rdoc_files: []
45
+
46
+ files:
47
+ - README
48
+ - README.md
49
+ - LICENSE
50
+ - Rakefile
51
+ - lib/binary_finery.rb
52
+ - test/helper.rb
53
+ - test/test_binary_finery.rb
54
+ homepage: https://github.com/altamic/binary_finary
55
+ licenses: []
56
+
57
+ post_install_message:
58
+ rdoc_options: []
59
+
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ hash: 3
68
+ segments:
69
+ - 0
70
+ version: "0"
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ hash: 3
77
+ segments:
78
+ - 0
79
+ version: "0"
80
+ requirements: []
81
+
82
+ rubyforge_project:
83
+ rubygems_version: 1.8.10
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: A fluent interface for reading or writing binary data
87
+ test_files: []
88
+