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