hexdump 0.3.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog.md +68 -2
  3. data/Gemfile +1 -0
  4. data/README.md +486 -135
  5. data/benchmark.rb +29 -22
  6. data/lib/hexdump/chars.rb +46 -0
  7. data/lib/hexdump/core_ext/file.rb +68 -6
  8. data/lib/hexdump/core_ext/io.rb +2 -2
  9. data/lib/hexdump/core_ext/kernel.rb +7 -0
  10. data/lib/hexdump/core_ext/string.rb +2 -2
  11. data/lib/hexdump/core_ext/string_io.rb +2 -2
  12. data/lib/hexdump/core_ext.rb +1 -0
  13. data/lib/hexdump/format_string.rb +43 -0
  14. data/lib/hexdump/hexdump.rb +768 -75
  15. data/lib/hexdump/mixin.rb +198 -0
  16. data/lib/hexdump/module_methods.rb +132 -0
  17. data/lib/hexdump/numeric/binary.rb +55 -0
  18. data/lib/hexdump/numeric/char_or_int.rb +90 -0
  19. data/lib/hexdump/numeric/decimal.rb +56 -0
  20. data/lib/hexdump/numeric/exceptions.rb +11 -0
  21. data/lib/hexdump/numeric/hexadecimal.rb +59 -0
  22. data/lib/hexdump/numeric/octal.rb +55 -0
  23. data/lib/hexdump/numeric.rb +5 -0
  24. data/lib/hexdump/reader.rb +314 -0
  25. data/lib/hexdump/theme/ansi.rb +81 -0
  26. data/lib/hexdump/theme/rule.rb +159 -0
  27. data/lib/hexdump/theme.rb +61 -0
  28. data/lib/hexdump/type.rb +232 -0
  29. data/lib/hexdump/types.rb +108 -0
  30. data/lib/hexdump/version.rb +1 -1
  31. data/lib/hexdump.rb +12 -1
  32. data/spec/chars_spec.rb +76 -0
  33. data/spec/core_ext_spec.rb +10 -6
  34. data/spec/hexdump_class_spec.rb +1708 -0
  35. data/spec/hexdump_module_spec.rb +23 -0
  36. data/spec/mixin_spec.rb +37 -0
  37. data/spec/numeric/binary_spec.rb +239 -0
  38. data/spec/numeric/char_or_int_spec.rb +210 -0
  39. data/spec/numeric/decimal_spec.rb +317 -0
  40. data/spec/numeric/hexadecimal_spec.rb +320 -0
  41. data/spec/numeric/octal_spec.rb +239 -0
  42. data/spec/reader_spec.rb +863 -0
  43. data/spec/theme/ansi_spec.rb +242 -0
  44. data/spec/theme/rule_spec.rb +199 -0
  45. data/spec/theme_spec.rb +94 -0
  46. data/spec/type_spec.rb +317 -0
  47. data/spec/types_spec.rb +904 -0
  48. metadata +39 -10
  49. data/.gemtest +0 -0
  50. data/lib/hexdump/dumper.rb +0 -419
  51. data/spec/dumper_spec.rb +0 -329
  52. data/spec/hexdump_spec.rb +0 -30
@@ -0,0 +1,239 @@
1
+ require 'spec_helper'
2
+ require 'hexdump/numeric/octal'
3
+ require 'hexdump/type'
4
+
5
+ describe Hexdump::Numeric::Octal do
6
+ describe "#initialize" do
7
+ subject { described_class.new(type) }
8
+
9
+ context "when given a Type::Int type" do
10
+ context "and size is 1" do
11
+ let(:type) { Hexdump::Type::Int8.new }
12
+
13
+ it "must set #width to 3 + 1" do
14
+ expect(subject.width).to eq(3 + 1)
15
+ end
16
+ end
17
+
18
+ context "and size is 2" do
19
+ let(:type) { Hexdump::Type::Int16.new }
20
+
21
+ it "must set #width to 6 + 1" do
22
+ expect(subject.width).to eq(6 + 1)
23
+ end
24
+ end
25
+
26
+ context "and size is 4" do
27
+ let(:type) { Hexdump::Type::Int32.new }
28
+
29
+ it "must set #width to 11 + 1" do
30
+ expect(subject.width).to eq(11 + 1)
31
+ end
32
+ end
33
+
34
+ context "and size is 8" do
35
+ let(:type) { Hexdump::Type::Int64.new }
36
+
37
+ it "must set #width to 22 + 1" do
38
+ expect(subject.width).to eq(22 + 1)
39
+ end
40
+ end
41
+
42
+ context "but the type has an unsupported size" do
43
+ class UnsupportedIntType < Hexdump::Type::Int
44
+
45
+ def initialize
46
+ super(size: 3)
47
+ end
48
+
49
+ end
50
+
51
+ let(:type) { UnsupportedIntType.new }
52
+
53
+ it do
54
+ expect { described_class.new(type) }.to raise_error(NotImplementedError)
55
+ end
56
+ end
57
+ end
58
+
59
+ context "when given a Type::UInt type" do
60
+ context "and size is 1" do
61
+ let(:type) { Hexdump::Type::UInt8.new }
62
+
63
+ it "must set #width to 3" do
64
+ expect(subject.width).to eq(3)
65
+ end
66
+ end
67
+
68
+ context "and size is 2" do
69
+ let(:type) { Hexdump::Type::UInt16.new }
70
+
71
+ it "must set #width to 6" do
72
+ expect(subject.width).to eq(6)
73
+ end
74
+ end
75
+
76
+ context "and size is 4" do
77
+ let(:type) { Hexdump::Type::UInt32.new }
78
+
79
+ it "must set #width to 11" do
80
+ expect(subject.width).to eq(11)
81
+ end
82
+ end
83
+
84
+ context "and size is 8" do
85
+ let(:type) { Hexdump::Type::UInt64.new }
86
+
87
+ it "must set #width to 22" do
88
+ expect(subject.width).to eq(22)
89
+ end
90
+ end
91
+
92
+ context "but the type has an unsupported size" do
93
+ class UnsupportedUIntType < Hexdump::Type::UInt
94
+
95
+ def initialize
96
+ super(size: 3)
97
+ end
98
+
99
+ end
100
+
101
+ let(:type) { UnsupportedUIntType.new }
102
+
103
+ it do
104
+ expect { described_class.new(type) }.to raise_error(NotImplementedError)
105
+ end
106
+ end
107
+ end
108
+
109
+ context "when given a Type::Float type" do
110
+ let(:type) { Hexdump::Type::Float32.new }
111
+
112
+ it do
113
+ expect {
114
+ described_class.new(type)
115
+ }.to raise_error(Hexdump::Numeric::IncompatibleTypeError,"cannot format floating-point numbers in octal")
116
+ end
117
+ end
118
+
119
+ context "when given a non-Type object" do
120
+ let(:type) { Object.new }
121
+
122
+ it do
123
+ expect { described_class.new(type) }.to raise_error(TypeError)
124
+ end
125
+ end
126
+ end
127
+
128
+ describe "#%" do
129
+ subject { described_class.new(type) }
130
+
131
+ context "when the given type has size 1" do
132
+ let(:type) { Hexdump::Type::UInt8.new }
133
+
134
+ let(:value) { 0xf }
135
+ let(:octal) { '017' }
136
+
137
+ it "must return a octal string of length 8" do
138
+ expect(subject % value).to eq(octal)
139
+ end
140
+
141
+ context "and is signed" do
142
+ let(:type) { Hexdump::Type::Int8.new }
143
+
144
+ context "and the value is positive" do
145
+ it "must return a octal string of length 8 prefixed with a ' '" do
146
+ expect(subject % value).to eq(" #{octal}")
147
+ end
148
+ end
149
+
150
+ context "and the value is negative" do
151
+ it "must return a octal string of length 8 prefixed with a '-'" do
152
+ expect(subject % -value).to eq("-#{octal}")
153
+ end
154
+ end
155
+ end
156
+ end
157
+
158
+ context "when the given type has size 2" do
159
+ let(:type) { Hexdump::Type::UInt16.new }
160
+
161
+ let(:value) { 0xff }
162
+ let(:octal) { '000377' }
163
+
164
+ it "must return a octal string of length 16" do
165
+ expect(subject % value).to eq(octal)
166
+ end
167
+
168
+ context "and is signed" do
169
+ let(:type) { Hexdump::Type::Int16.new }
170
+
171
+ context "and the value is positive" do
172
+ it "must return a octal string of length 16 prefixed with a ' '" do
173
+ expect(subject % value).to eq(" #{octal}")
174
+ end
175
+ end
176
+
177
+ context "and the value is negative" do
178
+ it "must return a octal string of length 16 prefixed with a '-'" do
179
+ expect(subject % -value).to eq("-#{octal}")
180
+ end
181
+ end
182
+ end
183
+ end
184
+
185
+ context "when the given type has size 4" do
186
+ let(:type) { Hexdump::Type::UInt32.new }
187
+
188
+ let(:value) { 0xffff }
189
+ let(:octal) { '00000177777' }
190
+
191
+ it "must return a octal string of length 32" do
192
+ expect(subject % value).to eq(octal)
193
+ end
194
+
195
+ context "and is signed" do
196
+ let(:type) { Hexdump::Type::Int32.new }
197
+
198
+ context "and the value is positive" do
199
+ it "must return a octal string of length 32 prefixed with a ' '" do
200
+ expect(subject % value).to eq(" #{octal}")
201
+ end
202
+ end
203
+
204
+ context "and the value is negative" do
205
+ it "must return a octal string of length 32 prefixed with a '-'" do
206
+ expect(subject % -value).to eq("-#{octal}")
207
+ end
208
+ end
209
+ end
210
+ end
211
+
212
+ context "when the given type has size 8" do
213
+ let(:type) { Hexdump::Type::UInt64.new }
214
+
215
+ let(:value) { 0xffffffff }
216
+ let(:octal) { '0000000000037777777777' }
217
+
218
+ it "must return a octal string of length 64" do
219
+ expect(subject % value).to eq(octal)
220
+ end
221
+
222
+ context "and is signed" do
223
+ let(:type) { Hexdump::Type::Int64.new }
224
+
225
+ context "and the value is positive" do
226
+ it "must return a octal string of length 64 prefixed with a ' '" do
227
+ expect(subject % value).to eq(" #{octal}")
228
+ end
229
+ end
230
+
231
+ context "and the value is negative" do
232
+ it "must return a octal string of length 64 prefixed with a '-'" do
233
+ expect(subject % -value).to eq("-#{octal}")
234
+ end
235
+ end
236
+ end
237
+ end
238
+ end
239
+ end
@@ -0,0 +1,863 @@
1
+ require 'spec_helper'
2
+ require 'hexdump/reader'
3
+ require 'hexdump/types'
4
+
5
+ describe Hexdump::Reader do
6
+ describe "#initialize" do
7
+ let(:type) { Hexdump::TYPES[:uint8] }
8
+
9
+ subject { described_class.new(type) }
10
+
11
+ it "must set #type" do
12
+ expect(subject.type).to eq(type)
13
+ end
14
+
15
+ it "must default #offset to nil" do
16
+ expect(subject.offset).to be(nil)
17
+ end
18
+
19
+ it "must default #length to nil" do
20
+ expect(subject.length).to be(nil)
21
+ end
22
+
23
+ it "must default #zero_pad? to false" do
24
+ expect(subject.zero_pad?).to be(false)
25
+ end
26
+
27
+ context "when offset: is given" do
28
+ let(:offset) { 2 }
29
+
30
+ subject { described_class.new(type, offset: offset) }
31
+
32
+ it "must set #offset" do
33
+ expect(subject.offset).to eq(offset)
34
+ end
35
+ end
36
+
37
+ context "when length: is given" do
38
+ let(:length) { 256 }
39
+
40
+ subject { described_class.new(type, length: length) }
41
+
42
+ it "must set #length" do
43
+ expect(subject.length).to eq(length)
44
+ end
45
+ end
46
+
47
+ context "when zero_pad: is true" do
48
+ subject { described_class.new(type, zero_pad: true) }
49
+
50
+ it "must enable #zero_pad?" do
51
+ expect(subject.zero_pad?).to be(true)
52
+ end
53
+ end
54
+ end
55
+
56
+ describe "#each_byte" do
57
+ let(:type) { Hexdump::TYPES[:uint8] }
58
+
59
+ subject { described_class.new(type) }
60
+
61
+ let(:chars) { %w[A B C D] }
62
+ let(:data) { chars.join }
63
+ let(:bytes) { data.bytes }
64
+
65
+ it "must yield each byte from the given data" do
66
+ expect { |b|
67
+ subject.each_byte(data,&b)
68
+ }.to yield_successive_args(*bytes)
69
+ end
70
+
71
+ context "when #offset is > 0" do
72
+ let(:offset) { 2 }
73
+ let(:bytes) { data.bytes[offset..-1] }
74
+
75
+ subject { described_class.new(type, offset: offset) }
76
+
77
+ it "must offset the first N bytes before yielding any bytes" do
78
+ expect { |b|
79
+ subject.each_byte(data,&b)
80
+ }.to yield_successive_args(*bytes)
81
+ end
82
+ end
83
+
84
+ context "when #length is set" do
85
+ let(:length) { 3 }
86
+ let(:bytes) { data.bytes[0,length] }
87
+
88
+ subject { described_class.new(type, length: length) }
89
+
90
+ it "must read at most N bytes" do
91
+ expect { |b|
92
+ subject.each_byte(data,&b)
93
+ }.to yield_successive_args(*bytes)
94
+ end
95
+ end
96
+
97
+ context "when no block is given" do
98
+ it "must return an Enumerator" do
99
+ expect(subject.each_byte(data)).to be_kind_of(Enumerator)
100
+ end
101
+ end
102
+ end
103
+
104
+ describe "#each_slice" do
105
+ context "when type has size of 1" do
106
+ let(:type) { Hexdump::TYPES[:int8] }
107
+ let(:chars) { %w[A B C D] }
108
+ let(:data) { "ABCD" }
109
+
110
+ subject { described_class.new(type) }
111
+
112
+ it "must yield each consequetize character" do
113
+ expect { |b|
114
+ subject.each_slice(data,&b)
115
+ }.to yield_successive_args(*chars)
116
+ end
117
+
118
+ context "and when #offset is > 0" do
119
+ let(:offset) { 2 }
120
+ let(:chars) { %w[C D] }
121
+
122
+ subject { described_class.new(type, offset: offset) }
123
+
124
+ it "must offset the first N bytes before reading each character" do
125
+ expect { |b|
126
+ subject.each_slice(data,&b)
127
+ }.to yield_successive_args(*chars)
128
+ end
129
+ end
130
+
131
+ context "and when #length is set" do
132
+ let(:length) { 3 }
133
+ let(:chars) { %w[A B C] }
134
+
135
+ subject { described_class.new(type, length: length) }
136
+
137
+ it "must read at most N bytes" do
138
+ expect { |b|
139
+ subject.each_slice(data,&b)
140
+ }.to yield_successive_args(*chars)
141
+ end
142
+ end
143
+ end
144
+
145
+ context "when type has size > 1" do
146
+ let(:type) { Hexdump::TYPES[:int16] }
147
+ let(:slices) { %w[AA BB CC DD EE FF] }
148
+ let(:data) { "AABBCCDDEEFF" }
149
+
150
+ subject { described_class.new(type) }
151
+
152
+ it "must yield each slice of the String" do
153
+ expect { |b|
154
+ subject.each_slice(data,&b)
155
+ }.to yield_successive_args(*slices)
156
+ end
157
+
158
+ it "must yield a new String instance for each slice" do
159
+ yielded_object_ids = []
160
+
161
+ subject.each_slice(data) do |slice|
162
+ yielded_object_ids << slice.object_id
163
+ end
164
+
165
+ expect(yielded_object_ids.uniq).to eq(yielded_object_ids)
166
+ end
167
+
168
+ context "and when #offset is > 0" do
169
+ let(:offset) { 3 }
170
+ let(:slices) { %w[BC CD DE EF F] }
171
+
172
+ subject { described_class.new(type, offset: offset) }
173
+
174
+ it "must offset the first N bytes before reading each slice" do
175
+ expect { |b|
176
+ subject.each_slice(data,&b)
177
+ }.to yield_successive_args(*slices)
178
+ end
179
+ end
180
+
181
+ context "and when #length is set" do
182
+ let(:length) { 7 }
183
+ let(:slices) { %w[AA BB CC D] }
184
+
185
+ subject { described_class.new(type, length: length) }
186
+
187
+ it "must read at most N bytes" do
188
+ expect { |b|
189
+ subject.each_slice(data,&b)
190
+ }.to yield_successive_args(*slices)
191
+ end
192
+ end
193
+
194
+ context "when the given data is not evenly divisible by the type's size" do
195
+ let(:type) { Hexdump::TYPES[:int32] }
196
+ let(:slices) { %w[AABB CCDD E] }
197
+ let(:data) { "AABBCCDDE" }
198
+
199
+ it "must yield the reamining data" do
200
+ expect { |b|
201
+ subject.each_slice(data,&b)
202
+ }.to yield_successive_args('AABB', 'CCDD', "E")
203
+ end
204
+
205
+ context "but #zero_pad? is true" do
206
+ subject { described_class.new(type, zero_pad: true) }
207
+
208
+ it "must zero out the rest of the buffer" do
209
+ expect { |b|
210
+ subject.each_slice(data,&b)
211
+ }.to yield_successive_args('AABB', 'CCDD', "E\0\0\0")
212
+ end
213
+ end
214
+ end
215
+ end
216
+ end
217
+
218
+ describe "#each_uint" do
219
+ context "when the type has size of 1" do
220
+ let(:bytes) { [0x41, 0x42, 0x43, 0x44] }
221
+ let(:raw) { bytes.map(&:chr) }
222
+ let(:data) { raw.join }
223
+ let(:type) { Hexdump::TYPES[:int8] }
224
+
225
+ subject { described_class.new(type) }
226
+
227
+ it "must yield each byte" do
228
+ expect { |b|
229
+ subject.each_uint(data,&b)
230
+ }.to yield_successive_args(*raw.zip(bytes))
231
+ end
232
+ end
233
+
234
+ context "when the type has size of 2" do
235
+ let(:uints) { [0xfa01, 0xfb02, 0xfc03, 0xfd04] }
236
+
237
+ context "when the type is little-endian" do
238
+ let(:type) { Hexdump::TYPES[:uint16_le] }
239
+ let(:raw) { uints.map { |uint| [uint].pack('S<') } }
240
+ let(:data) { raw.join }
241
+
242
+ subject { described_class.new(type) }
243
+
244
+ it "must decode the bytes in little-endian order" do
245
+ expect { |b|
246
+ subject.each_uint(data,&b)
247
+ }.to yield_successive_args(*raw.zip(uints))
248
+ end
249
+
250
+ context "but there is not enough bytes to decode a value" do
251
+ let(:data) { "\x11".encode(Encoding::BINARY) }
252
+
253
+ it "must yield remaining bytes and nil" do
254
+ expect { |b|
255
+ subject.each_uint(data,&b)
256
+ }.to yield_with_args(data,nil)
257
+ end
258
+
259
+ context "but #zero_pad? is true" do
260
+ subject { described_class.new(type, zero_pad: true) }
261
+
262
+ it "must yield the zero-padded data and partially decoded uint" do
263
+ expect { |b|
264
+ subject.each_uint(data,&b)
265
+ }.to yield_with_args("#{data}\x00",0x0011)
266
+ end
267
+ end
268
+ end
269
+ end
270
+
271
+ context "when the type is big-endian" do
272
+ let(:type) { Hexdump::TYPES[:uint16_be] }
273
+ let(:raw) { uints.map { |uint| [uint].pack('S>') } }
274
+ let(:data) { raw.join }
275
+
276
+ subject { described_class.new(type) }
277
+
278
+ it "must decode the bytes in big-endian order" do
279
+ expect { |b|
280
+ subject.each_uint(data,&b)
281
+ }.to yield_successive_args(*raw.zip(uints))
282
+ end
283
+
284
+ context "but there is not enough bytes to decode a value" do
285
+ let(:data) { "\x11".encode(Encoding::BINARY) }
286
+
287
+ it "must yield remaining bytes and nil" do
288
+ expect { |b|
289
+ subject.each_uint(data,&b)
290
+ }.to yield_with_args(data,nil)
291
+ end
292
+
293
+ context "but #zero_pad? is true" do
294
+ subject { described_class.new(type, zero_pad: true) }
295
+
296
+ it "must yield the zero-padded data and partially decoded uint" do
297
+ expect { |b|
298
+ subject.each_uint(data,&b)
299
+ }.to yield_with_args("#{data}\x00",0x1100)
300
+ end
301
+ end
302
+ end
303
+ end
304
+ end
305
+
306
+ context "when the type has size of 4" do
307
+ let(:uints) { [0xffeedd01, 0xffccbb02, 0xffaa9903, 0xff887704] }
308
+
309
+ context "when the type is little-endian" do
310
+ let(:type) { Hexdump::TYPES[:uint32_le] }
311
+ let(:raw) { uints.map { |uint| [uint].pack('L<') } }
312
+ let(:data) { raw.join }
313
+
314
+ subject { described_class.new(type) }
315
+
316
+ it "must decode the bytes in little-endian order" do
317
+ expect { |b|
318
+ subject.each_uint(data,&b)
319
+ }.to yield_successive_args(*raw.zip(uints))
320
+ end
321
+
322
+ context "but there is not enough bytes to decode a value" do
323
+ let(:data) { "\x11\x22\x33".encode(Encoding::BINARY) }
324
+
325
+ it "must yield nil and the remaining bytes" do
326
+ expect { |b|
327
+ subject.each_uint(data,&b)
328
+ }.to yield_with_args(data,nil)
329
+ end
330
+
331
+ context "but #zero_pad? is true" do
332
+ subject { described_class.new(type, zero_pad: true) }
333
+
334
+ it "must yield the zero-padded data and partially decoded uint" do
335
+ expect { |b|
336
+ subject.each_uint(data,&b)
337
+ }.to yield_with_args("#{data}\x00",0x00332211)
338
+ end
339
+ end
340
+ end
341
+ end
342
+
343
+ context "when the type is big-endian" do
344
+ let(:type) { Hexdump::TYPES[:uint32_be] }
345
+ let(:raw) { uints.map { |uint| [uint].pack('L>') } }
346
+ let(:data) { raw.join }
347
+
348
+ subject { described_class.new(type) }
349
+
350
+ it "must decode the bytes in big-endian order" do
351
+ expect { |b|
352
+ subject.each_uint(data,&b)
353
+ }.to yield_successive_args(*raw.zip(uints))
354
+ end
355
+
356
+ context "but there is not enough bytes to decode a value" do
357
+ let(:data) { "\x11\x22\x33".encode(Encoding::BINARY) }
358
+
359
+ it "must yield the remaining bytes and nil" do
360
+ expect { |b|
361
+ subject.each_uint(data,&b)
362
+ }.to yield_with_args(data,nil)
363
+ end
364
+
365
+ context "but #zero_pad? is true" do
366
+ subject { described_class.new(type, zero_pad: true) }
367
+
368
+ it "must yield the zero-padded data and partially decoded uint" do
369
+ expect { |b|
370
+ subject.each_uint(data,&b)
371
+ }.to yield_with_args("#{data}\x00",0x11223300)
372
+ end
373
+ end
374
+ end
375
+ end
376
+ end
377
+
378
+ context "when the type has size of 8" do
379
+ let(:uints) { [0xffffffff11223344, 0xffffffff55667788, 0xffffffff99aabbcc, 0xffffffffddeeff00] }
380
+
381
+ context "when the type is little-endian" do
382
+ let(:type) { Hexdump::TYPES[:uint64_le] }
383
+ let(:raw) { uints.map { |uint| [uint].pack('Q<') } }
384
+ let(:data) { raw.join }
385
+
386
+ subject { described_class.new(type) }
387
+
388
+ it "must decode the bytes in little-endian order" do
389
+ expect { |b|
390
+ subject.each_uint(data,&b)
391
+ }.to yield_successive_args(*raw.zip(uints))
392
+ end
393
+
394
+ context "but there is not enough bytes to decode a value" do
395
+ let(:data) { "\x11\x22\x33\x44\x55\x66\x77".encode(Encoding::BINARY) }
396
+
397
+ it "must yield the remaining bytes and nil" do
398
+ expect { |b|
399
+ subject.each_uint(data,&b)
400
+ }.to yield_with_args(data,nil)
401
+ end
402
+
403
+ context "but #zero_pad? is true" do
404
+ subject { described_class.new(type, zero_pad: true) }
405
+
406
+ it "must yield the zero-padded data and partially decoded uint" do
407
+ expect { |b|
408
+ subject.each_uint(data,&b)
409
+ }.to yield_with_args("#{data}\x00",0x0077665544332211)
410
+ end
411
+ end
412
+ end
413
+ end
414
+
415
+ context "when the type is big-endian" do
416
+ let(:type) { Hexdump::TYPES[:uint64_be] }
417
+ let(:raw) { uints.map { |uint| [uint].pack('Q>') } }
418
+ let(:data) { raw.join }
419
+
420
+ subject { described_class.new(type) }
421
+
422
+ it "must decode the bytes in big-endian order" do
423
+ expect { |b|
424
+ subject.each_uint(data,&b)
425
+ }.to yield_successive_args(*raw.zip(uints))
426
+ end
427
+
428
+ context "but there is not enough bytes to decode a value" do
429
+ let(:data) { "\x11\x22\x33\x44\x55\x66\x77".encode(Encoding::BINARY) }
430
+
431
+ it "must yield the remaining bytes and nil" do
432
+ expect { |b|
433
+ subject.each_uint(data,&b)
434
+ }.to yield_with_args(data,nil)
435
+ end
436
+
437
+ context "but #zero_pad? is true" do
438
+ subject { described_class.new(type, zero_pad: true) }
439
+
440
+ it "must yield the zero-padded data and partially decoded uint" do
441
+ expect { |b|
442
+ subject.each_uint(data,&b)
443
+ }.to yield_with_args("#{data}\x00",0x1122334455667700)
444
+ end
445
+ end
446
+ end
447
+ end
448
+ end
449
+
450
+ context "when the given data does not define #each_byte" do
451
+ it do
452
+ expect {
453
+ subject.each_uint(Object.new).to_a
454
+ }.to raise_error(ArgumentError)
455
+ end
456
+ end
457
+ end
458
+
459
+ describe "#each_int" do
460
+ context "when the type has size of 1" do
461
+ let(:ints) { [0x01, -0x02, 0x03, -0x04] }
462
+ let(:type) { Hexdump::TYPES[:int8] }
463
+ let(:raw) { ints.map { |int| [int].pack('c') } }
464
+ let(:data) { raw.join }
465
+
466
+ subject { described_class.new(type) }
467
+
468
+ it "must decode the bytes" do
469
+ expect { |b|
470
+ subject.each_int(data,&b)
471
+ }.to yield_successive_args(*raw.zip(ints))
472
+ end
473
+ end
474
+
475
+ context "when the type has size of 2" do
476
+ let(:ints) { [0x0001, -0x0002, 0x0003, -0x0004] }
477
+
478
+ context "when the type is little-endian" do
479
+ let(:type) { Hexdump::TYPES[:int16_le] }
480
+ let(:raw) { ints.map { |int| [int].pack('s<') } }
481
+ let(:data) { raw.join }
482
+
483
+ subject { described_class.new(type) }
484
+
485
+ it "must decode the bytes in little-endian order" do
486
+ expect { |b|
487
+ subject.each_int(data,&b)
488
+ }.to yield_successive_args(*raw.zip(ints))
489
+ end
490
+
491
+ context "but there is not enough bytes to decode a value" do
492
+ let(:data) { "\x01".encode(Encoding::BINARY) }
493
+
494
+ it "must yield the remaining bytes and nil" do
495
+ expect { |b|
496
+ subject.each_int(data,&b)
497
+ }.to yield_with_args(data, nil)
498
+ end
499
+
500
+ context "but #zero_pad? is true" do
501
+ subject { described_class.new(type, zero_pad: true) }
502
+
503
+ it "must yield the zero-padded data and partially decoded int" do
504
+ expect { |b|
505
+ subject.each_int(data,&b)
506
+ }.to yield_with_args("#{data}\x00",0x01)
507
+ end
508
+ end
509
+ end
510
+ end
511
+
512
+ context "when the type is big-endian" do
513
+ let(:type) { Hexdump::TYPES[:int16_be] }
514
+ let(:raw) { ints.map { |int| [int].pack('s>') } }
515
+ let(:data) { raw.join }
516
+
517
+ subject { described_class.new(type) }
518
+
519
+ it "must decode the bytes in big-endian order" do
520
+ expect { |b|
521
+ subject.each_int(data,&b)
522
+ }.to yield_successive_args(*raw.zip(ints))
523
+ end
524
+
525
+ context "but there is not enough bytes to decode a value" do
526
+ let(:data) { "\x01".encode(Encoding::BINARY) }
527
+
528
+ it "must yield the remaining bytes and nil" do
529
+ expect { |b|
530
+ subject.each_int(data,&b)
531
+ }.to yield_with_args(data, nil)
532
+ end
533
+
534
+ context "but #zero_pad? is true" do
535
+ subject { described_class.new(type, zero_pad: true) }
536
+
537
+ it "must yield the zero-padded data and partially decoded int" do
538
+ expect { |b|
539
+ subject.each_int(data,&b)
540
+ }.to yield_with_args("#{data}\x00",0x0100)
541
+ end
542
+ end
543
+ end
544
+ end
545
+ end
546
+
547
+ context "when the type has size of 4" do
548
+ let(:ints) { [0x00aa0001, -0x00bb0002, 0x00cc0003, -0x00dd0004] }
549
+
550
+ context "when the type is little-endian" do
551
+ let(:type) { Hexdump::TYPES[:int32_le] }
552
+ let(:raw) { ints.map { |int| [int].pack('l<') } }
553
+ let(:data) { raw.join }
554
+
555
+ subject { described_class.new(type) }
556
+
557
+ it "must decode the bytes in little-endian order" do
558
+ expect { |b|
559
+ subject.each_int(data,&b)
560
+ }.to yield_successive_args(*raw.zip(ints))
561
+ end
562
+
563
+ context "but there is not enough bytes to decode a value" do
564
+ let(:data) { "\x01\x02\x03".encode(Encoding::BINARY) }
565
+
566
+ it "must yield the remaining bytes and nil" do
567
+ expect { |b|
568
+ subject.each_int(data,&b)
569
+ }.to yield_with_args(data, nil)
570
+ end
571
+
572
+ context "but #zero_pad? is true" do
573
+ subject { described_class.new(type, zero_pad: true) }
574
+
575
+ it "must yield the zero-padded data and partially decoded int" do
576
+ expect { |b|
577
+ subject.each_int(data,&b)
578
+ }.to yield_with_args("#{data}\x00",0x00030201)
579
+ end
580
+ end
581
+ end
582
+ end
583
+
584
+ context "when the type is big-endian" do
585
+ let(:type) { Hexdump::TYPES[:int32_be] }
586
+ let(:raw) { ints.map { |int| [int].pack('l>') } }
587
+ let(:data) { raw.join }
588
+
589
+ subject { described_class.new(type) }
590
+
591
+ it "must decode the bytes in big-endian order" do
592
+ expect { |b|
593
+ subject.each_int(data,&b)
594
+ }.to yield_successive_args(*raw.zip(ints))
595
+ end
596
+
597
+ context "but there is not enough bytes to decode a value" do
598
+ let(:data) { "\x01\x02\x03".encode(Encoding::BINARY) }
599
+
600
+ it "must yield the remaining bytes and nil" do
601
+ expect { |b|
602
+ subject.each_int(data,&b)
603
+ }.to yield_with_args(data, nil)
604
+ end
605
+
606
+ context "but #zero_pad? is true" do
607
+ subject { described_class.new(type, zero_pad: true) }
608
+
609
+ it "must yield the zero-padded data and partially decoded int" do
610
+ expect { |b|
611
+ subject.each_int(data,&b)
612
+ }.to yield_with_args("#{data}\x00",0x01020300)
613
+ end
614
+ end
615
+ end
616
+ end
617
+ end
618
+
619
+ context "when the type has size of 8" do
620
+ let(:ints) { [0x1122334400aa0001, -0x1122334400bb0002, 0x1122334400cc0003, -0x1122334400dd0004] }
621
+
622
+ context "when the type is little-endian" do
623
+ let(:type) { Hexdump::TYPES[:int64_le] }
624
+ let(:raw) { ints.map { |int| [int].pack('q<') } }
625
+ let(:data) { raw.join }
626
+
627
+ subject { described_class.new(type) }
628
+
629
+ it "must decode the bytes in little-endian order" do
630
+ expect { |b|
631
+ subject.each_int(data,&b)
632
+ }.to yield_successive_args(*raw.zip(ints))
633
+ end
634
+
635
+ context "but there is not enough bytes to decode a value" do
636
+ let(:data) { "\x01\x02\x03\x04\x05\x06\x07".encode(Encoding::BINARY) }
637
+
638
+ it "must yield the remaining bytes and nil" do
639
+ expect { |b|
640
+ subject.each_int(data,&b)
641
+ }.to yield_with_args(data, nil)
642
+ end
643
+
644
+ context "but #zero_pad? is true" do
645
+ subject { described_class.new(type, zero_pad: true) }
646
+
647
+ it "must yield the zero-padded data and partially decoded int" do
648
+ expect { |b|
649
+ subject.each_int(data,&b)
650
+ }.to yield_with_args("#{data}\x00",0x0007060504030201)
651
+ end
652
+ end
653
+ end
654
+ end
655
+
656
+ context "when the type is big-endian" do
657
+ let(:type) { Hexdump::TYPES[:int64_be] }
658
+ let(:raw) { ints.map { |int| [int].pack('q>') } }
659
+ let(:data) { raw.join }
660
+
661
+ subject { described_class.new(type) }
662
+
663
+ it "must decode the bytes in big-endian order" do
664
+ expect { |b|
665
+ subject.each_int(data,&b)
666
+ }.to yield_successive_args(*raw.zip(ints))
667
+ end
668
+
669
+ context "but there is not enough bytes to decode a value" do
670
+ let(:data) { "\x01\x02\x03\x04\x05\x06\x07".encode(Encoding::BINARY) }
671
+
672
+ it "must yield nil and the remaining bytes" do
673
+ expect { |b|
674
+ subject.each_int(data,&b)
675
+ }.to yield_with_args(data, nil)
676
+ end
677
+
678
+ context "but #zero_pad? is true" do
679
+ subject { described_class.new(type, zero_pad: true) }
680
+
681
+ it "must yield the zero-padded data and partially decoded int" do
682
+ expect { |b|
683
+ subject.each_int(data,&b)
684
+ }.to yield_with_args("#{data}\x00",0x0102030405060700)
685
+ end
686
+ end
687
+ end
688
+ end
689
+ end
690
+
691
+ context "when the given data does not define #each_byte" do
692
+ it do
693
+ expect {
694
+ subject.each_int(Object.new).to_a
695
+ }.to raise_error(ArgumentError)
696
+ end
697
+ end
698
+ end
699
+
700
+ describe "#each_float" do
701
+ context "when the type has size of 4" do
702
+ let(:floats) { [1.0, -3.0, 5.0, -7.0, 9.0] }
703
+
704
+ context "when the type is little-endian" do
705
+ let(:type) { Hexdump::TYPES[:float_le] }
706
+ let(:raw) { floats.map { |float| [float].pack('e') } }
707
+ let(:data) { raw.join }
708
+
709
+ subject { described_class.new(type) }
710
+
711
+ it "must decode the bytes in little-endian order" do
712
+ expect { |b|
713
+ subject.each_float(data,&b)
714
+ }.to yield_successive_args(*raw.zip(floats))
715
+ end
716
+
717
+ context "but there is not enough bytes to decode a value" do
718
+ let(:data) { "\x01\x02\x03".encode(Encoding::BINARY) }
719
+
720
+ it "must yield the remaining bytes and nil" do
721
+ expect { |b|
722
+ subject.each_float(data,&b)
723
+ }.to yield_with_args(data, nil)
724
+ end
725
+
726
+ context "but #zero_pad? is true" do
727
+ subject { described_class.new(type, zero_pad: true) }
728
+
729
+ it "must yield the zero-padded data and partially decoded int" do
730
+ expect { |b|
731
+ subject.each_float(data,&b)
732
+ }.to yield_with_args("#{data}\x00",2.7622535458617227e-40)
733
+ end
734
+ end
735
+ end
736
+ end
737
+
738
+ context "when the type is big-endian" do
739
+ let(:type) { Hexdump::TYPES[:float_be] }
740
+ let(:raw) { floats.map { |float| [float].pack('g') } }
741
+ let(:data) { raw.join }
742
+
743
+ subject { described_class.new(type) }
744
+
745
+ it "must decode the bytes in big-endian order" do
746
+ expect { |b|
747
+ subject.each_float(data,&b)
748
+ }.to yield_successive_args(*raw.zip(floats))
749
+ end
750
+
751
+ context "but there is not enough bytes to decode a value" do
752
+ let(:data) { "\x01\x02\x03".encode(Encoding::BINARY) }
753
+
754
+ it "must yield the remaining bytes and nil" do
755
+ expect { |b|
756
+ subject.each_float(data,&b)
757
+ }.to yield_with_args(data, nil)
758
+ end
759
+
760
+ context "but #zero_pad? is true" do
761
+ subject { described_class.new(type, zero_pad: true) }
762
+
763
+ it "must yield the zero-padded data and partially decoded int" do
764
+ expect { |b|
765
+ subject.each_float(data,&b)
766
+ }.to yield_with_args("#{data}\x00",2.387938139551892e-38)
767
+ end
768
+ end
769
+ end
770
+ end
771
+ end
772
+
773
+ context "when the type has size of 8" do
774
+ let(:floats) { [1.2, -3.4, 5.6, -7.8, 9.0] }
775
+
776
+ context "when the type is little-endian" do
777
+ let(:type) { Hexdump::TYPES[:double_le] }
778
+ let(:raw) { floats.map { |float| [float].pack('E') } }
779
+ let(:data) { raw.join }
780
+
781
+ subject { described_class.new(type) }
782
+
783
+ it "must decode the bytes in little-endian order" do
784
+ expect { |b|
785
+ subject.each_float(data,&b)
786
+ }.to yield_successive_args(*raw.zip(floats))
787
+ end
788
+
789
+ context "but there is not enough bytes to decode a value" do
790
+ let(:data) { "\x01\x02\x03\x04\x05\x06\x07".encode(Encoding::BINARY) }
791
+
792
+ it "must yield the remaining bytes and nil" do
793
+ expect { |b|
794
+ subject.each_float(data,&b)
795
+ }.to yield_with_args(data, nil)
796
+ end
797
+
798
+ context "but #zero_pad? is true" do
799
+ subject { described_class.new(type, zero_pad: true) }
800
+
801
+ it "must yield the zero-padded data and partially decoded int" do
802
+ expect { |b|
803
+ subject.each_float(data,&b)
804
+ }.to yield_with_args("#{data}\x00",9.76739841864353e-309)
805
+ end
806
+ end
807
+ end
808
+ end
809
+
810
+ context "when the type is big-endian" do
811
+ let(:type) { Hexdump::TYPES[:double_be] }
812
+ let(:raw) { floats.map { |float| [float].pack('G') } }
813
+ let(:data) { raw.join }
814
+
815
+ subject { described_class.new(type) }
816
+
817
+ it "must decode the bytes in big-endian order" do
818
+ expect { |b|
819
+ subject.each_float(data,&b)
820
+ }.to yield_successive_args(*raw.zip(floats))
821
+ end
822
+
823
+ context "but there is not enough bytes to decode a value" do
824
+ let(:data) { "\x01\x02\x03\x04\x05\x06\x07".encode(Encoding::BINARY) }
825
+
826
+ it "must yield the remaining bytes and nil" do
827
+ expect { |b|
828
+ subject.each_float(data,&b)
829
+ }.to yield_with_args(data, nil)
830
+ end
831
+
832
+ context "but #zero_pad? is true" do
833
+ subject { described_class.new(type, zero_pad: true) }
834
+
835
+ it "must yield the zero-padded data and partially decoded int" do
836
+ expect { |b|
837
+ subject.each_float(data,&b)
838
+ }.to yield_with_args("#{data}\x00",8.207880399131826e-304)
839
+ end
840
+ end
841
+ end
842
+ end
843
+ end
844
+
845
+ context "when the given data does not define #each_byte" do
846
+ it do
847
+ expect {
848
+ subject.each_float(Object.new).to_a
849
+ }.to raise_error(ArgumentError)
850
+ end
851
+ end
852
+ end
853
+
854
+ describe "#each" do
855
+ context "when the given data does not define #each_byte" do
856
+ it do
857
+ expect {
858
+ subject.each(Object.new).to_a
859
+ }.to raise_error(ArgumentError)
860
+ end
861
+ end
862
+ end
863
+ end