hexdump 0.3.0 → 1.0.0

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