hexdump 0.2.4 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +3 -5
- data/ChangeLog.md +11 -0
- data/LICENSE.txt +1 -1
- data/README.md +99 -78
- data/benchmark.rb +47 -0
- data/gemspec.yml +8 -2
- data/hexdump.gemspec +1 -0
- data/lib/hexdump.rb +1 -1
- data/lib/hexdump/core_ext.rb +4 -0
- data/lib/hexdump/{extensions → core_ext}/file.rb +0 -0
- data/lib/hexdump/{extensions → core_ext}/io.rb +0 -0
- data/lib/hexdump/{extensions → core_ext}/string.rb +0 -0
- data/lib/hexdump/{extensions → core_ext}/string_io.rb +0 -0
- data/lib/hexdump/dumper.rb +61 -91
- data/lib/hexdump/extensions.rb +2 -4
- data/lib/hexdump/hexdump.rb +12 -21
- data/lib/hexdump/version.rb +1 -1
- data/spec/{extensions_spec.rb → core_ext_spec.rb} +2 -2
- data/spec/dumper_spec.rb +176 -107
- data/spec/hexdump_spec.rb +0 -1
- metadata +17 -12
- data/benchmarks/hexdump.rb +0 -45
data/lib/hexdump/extensions.rb
CHANGED
data/lib/hexdump/hexdump.rb
CHANGED
@@ -24,28 +24,25 @@ module Hexdump
|
|
24
24
|
# @param [#each_byte] data
|
25
25
|
# The data to be hexdumped.
|
26
26
|
#
|
27
|
-
# @param [
|
28
|
-
# Additional options.
|
29
|
-
#
|
30
|
-
# @option options [Integer] :width (16)
|
27
|
+
# @param [Integer] width (16)
|
31
28
|
# The number of bytes to dump for each line.
|
32
29
|
#
|
33
|
-
# @
|
30
|
+
# @param [Integer] endian (:little)
|
34
31
|
# The endianness that the bytes are organized in. Supported endianness
|
35
32
|
# include `:little` and `:big`.
|
36
33
|
#
|
37
|
-
# @
|
34
|
+
# @param [Integer] word_size (1)
|
38
35
|
# The number of bytes within a word.
|
39
36
|
#
|
40
|
-
# @
|
37
|
+
# @param [Symbol, Integer] base (:hexadecimal)
|
41
38
|
# The base to print bytes in. Supported bases include, `:hexadecimal`,
|
42
39
|
# `:hex`, `16, `:decimal`, `:dec`, `10, `:octal`, `:oct`, `8`,
|
43
40
|
# `:binary`, `:bin` and `2`.
|
44
41
|
#
|
45
|
-
# @
|
42
|
+
# @param [Boolean] ascii (false)
|
46
43
|
# Print ascii characters when possible.
|
47
44
|
#
|
48
|
-
# @
|
45
|
+
# @param [#<<] output ($stdout)
|
49
46
|
# The output to print the hexdump to.
|
50
47
|
#
|
51
48
|
# @yield [index,numeric,printable]
|
@@ -68,14 +65,11 @@ module Hexdump
|
|
68
65
|
# the `:output` value does not support the `#<<` method or
|
69
66
|
# the `:base` value was unknown.
|
70
67
|
#
|
71
|
-
def self.dump(data,options
|
72
|
-
|
73
|
-
dumper = Dumper.new(options)
|
68
|
+
def self.dump(data, output: $stdout, **options,&block)
|
69
|
+
dumper = Dumper.new(**options)
|
74
70
|
|
75
|
-
if block
|
76
|
-
|
77
|
-
else
|
78
|
-
dumper.dump(data,output)
|
71
|
+
if block then dumper.each(data,&block)
|
72
|
+
else dumper.dump(data,output)
|
79
73
|
end
|
80
74
|
|
81
75
|
return nil
|
@@ -84,12 +78,9 @@ module Hexdump
|
|
84
78
|
#
|
85
79
|
# Hexdumps the object.
|
86
80
|
#
|
87
|
-
# @param [Hash] options
|
88
|
-
# Additional options.
|
89
|
-
#
|
90
81
|
# @see Hexdump.dump
|
91
82
|
#
|
92
|
-
def hexdump(options
|
93
|
-
Hexdump.dump(self
|
83
|
+
def hexdump(**options,&block)
|
84
|
+
Hexdump.dump(self,**options,&block)
|
94
85
|
end
|
95
86
|
end
|
data/lib/hexdump/version.rb
CHANGED
data/spec/dumper_spec.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
|
3
2
|
require 'hexdump/dumper'
|
4
3
|
|
5
4
|
describe Hexdump::Dumper do
|
@@ -11,24 +10,23 @@ describe Hexdump::Dumper do
|
|
11
10
|
let(:print_chars) { ['h', 'e', 'l', 'l', 'o'] }
|
12
11
|
let(:data) { print_chars.join }
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
13
|
+
describe "#initialize" do
|
14
|
+
it "should only accept known base: values" do
|
15
|
+
expect {
|
16
|
+
described_class.new(data, base: :foo)
|
17
|
+
}.to raise_error(ArgumentError)
|
18
|
+
end
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
it "should only accept known endian: values" do
|
21
|
+
expect {
|
22
|
+
described_class.new(data, endian: :foo)
|
23
|
+
}.to raise_error(ArgumentError)
|
24
|
+
end
|
24
25
|
end
|
25
26
|
|
26
27
|
describe "each_word" do
|
27
28
|
let(:data) { 'ABAB' }
|
28
29
|
let(:bytes) { [0x41, 0x42, 0x41, 0x42] }
|
29
|
-
let(:shorts_le) { [0x4241, 0x4241] }
|
30
|
-
let(:shorts_be) { [0x4142, 0x4142] }
|
31
|
-
let(:custom_words) { [0x414241, 0x42] }
|
32
30
|
|
33
31
|
it "should check if the data defines '#each_byte'" do
|
34
32
|
expect {
|
@@ -40,22 +38,34 @@ describe Hexdump::Dumper do
|
|
40
38
|
expect(subject.each_word(data).to_a).to be == bytes
|
41
39
|
end
|
42
40
|
|
43
|
-
|
44
|
-
|
41
|
+
context "when initialized with a custom word_size:" do
|
42
|
+
subject { described_class.new(word_size: 3) }
|
45
43
|
|
46
|
-
|
44
|
+
let(:custom_words) { [0x414241, 0x42] }
|
45
|
+
|
46
|
+
it "should allow iterating over custom word-sizes" do
|
47
|
+
expect(subject.each_word(data).to_a).to be == custom_words
|
48
|
+
end
|
47
49
|
end
|
48
50
|
|
49
|
-
|
50
|
-
|
51
|
+
context "when initialized with default endian:" do
|
52
|
+
subject { described_class.new(word_size: 2) }
|
51
53
|
|
52
|
-
|
54
|
+
let(:shorts_le) { [0x4241, 0x4241] }
|
55
|
+
|
56
|
+
it "should iterate over little-endian words by default" do
|
57
|
+
expect(subject.each_word(data).to_a).to be == shorts_le
|
58
|
+
end
|
53
59
|
end
|
54
60
|
|
55
|
-
|
56
|
-
|
61
|
+
context "when initialized with endian: :big" do
|
62
|
+
subject { described_class.new(word_size: 2, endian: :big) }
|
63
|
+
|
64
|
+
let(:shorts_be) { [0x4142, 0x4142] }
|
57
65
|
|
58
|
-
|
66
|
+
it "should iterate over big-endian words" do
|
67
|
+
expect(subject.each_word(data).to_a).to be == shorts_be
|
68
|
+
end
|
59
69
|
end
|
60
70
|
end
|
61
71
|
|
@@ -74,72 +84,86 @@ describe Hexdump::Dumper do
|
|
74
84
|
end
|
75
85
|
|
76
86
|
it "should provide the index within the data for each line" do
|
77
|
-
dumper = described_class.new(:width => 10)
|
78
87
|
indices = []
|
79
88
|
|
80
|
-
|
89
|
+
subject.each('A' * (16 * 10)) do |index,hex,print|
|
81
90
|
indices << index
|
82
91
|
end
|
83
92
|
|
84
|
-
expect(indices).to be == [0,
|
93
|
+
expect(indices).to be == [0, 16, 32, 48, 64, 80, 96, 112, 128, 144]
|
85
94
|
end
|
86
95
|
|
87
|
-
|
88
|
-
|
89
|
-
widths = []
|
96
|
+
context "when initialized with a custom wdith:" do
|
97
|
+
let(:width) { 10 }
|
90
98
|
|
91
|
-
|
92
|
-
widths << hex.length
|
93
|
-
end
|
99
|
+
subject { described_class.new(width: width) }
|
94
100
|
|
95
|
-
|
96
|
-
|
101
|
+
it "should change the width, in bytes, of each line" do
|
102
|
+
widths = []
|
103
|
+
count = 10
|
97
104
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
string = chars.join
|
102
|
-
leading = ('A' * 100)
|
103
|
-
remainder = nil
|
105
|
+
subject.each('A' * (width * count)) do |index,hex,print|
|
106
|
+
widths << hex.length
|
107
|
+
end
|
104
108
|
|
105
|
-
|
106
|
-
remainder = print
|
109
|
+
expect(widths).to be == ([width] * count)
|
107
110
|
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context "when there are leftover bytes" do
|
114
|
+
let(:width) { 10 }
|
115
|
+
|
116
|
+
subject { described_class.new(width: width) }
|
108
117
|
|
109
|
-
|
118
|
+
let(:chars) { ['B'] * 4 }
|
119
|
+
let(:string) { chars.join }
|
120
|
+
let(:leading) { 'A' * 100 }
|
121
|
+
|
122
|
+
it "should hexdump the remaining bytes" do
|
123
|
+
remainder = nil
|
124
|
+
|
125
|
+
subject.each(leading + string) do |index,hex,print|
|
126
|
+
remainder = print
|
127
|
+
end
|
128
|
+
|
129
|
+
expect(remainder).to be == chars
|
130
|
+
end
|
110
131
|
end
|
111
132
|
|
112
133
|
it "should provide the hexadecimal characters for each line" do
|
113
|
-
dumper = described_class.new(:width => 10)
|
114
134
|
chars = []
|
135
|
+
length = (16 * 10)
|
115
136
|
|
116
|
-
|
137
|
+
subject.each(data * length) do |index,hex,print|
|
117
138
|
chars += hex
|
118
139
|
end
|
119
140
|
|
120
|
-
expect(chars).to be == (hex_chars *
|
141
|
+
expect(chars).to be == (hex_chars * length)
|
121
142
|
end
|
122
143
|
|
123
|
-
|
124
|
-
|
125
|
-
chars = []
|
144
|
+
context "when initialized with ascii: true" do
|
145
|
+
subject { described_class.new(ascii: true) }
|
126
146
|
|
127
|
-
|
128
|
-
chars
|
129
|
-
|
147
|
+
it "should allow printing ASCII characters in place of hex characters" do
|
148
|
+
chars = []
|
149
|
+
|
150
|
+
subject.each(data) do |index,hex,print|
|
151
|
+
chars += hex
|
152
|
+
end
|
130
153
|
|
131
|
-
|
154
|
+
expect(chars).to be == print_chars
|
155
|
+
end
|
132
156
|
end
|
133
157
|
|
134
158
|
it "should provide the print characters for each line" do
|
135
|
-
dumper = described_class.new(:width => 10)
|
136
159
|
chars = []
|
160
|
+
length = (16 * 10)
|
137
161
|
|
138
|
-
|
162
|
+
subject.each(data * length) do |index,hex,print|
|
139
163
|
chars += print
|
140
164
|
end
|
141
165
|
|
142
|
-
expect(chars).to be == (print_chars *
|
166
|
+
expect(chars).to be == (print_chars * length)
|
143
167
|
end
|
144
168
|
|
145
169
|
it "should map unprintable characters to '.'" do
|
@@ -153,108 +177,153 @@ describe Hexdump::Dumper do
|
|
153
177
|
expect(chars).to be == (['.'] * unprintable.length)
|
154
178
|
end
|
155
179
|
|
156
|
-
|
157
|
-
|
158
|
-
chars = []
|
180
|
+
context "when initialized with base: :decimal" do
|
181
|
+
subject { described_class.new(base: :decimal) }
|
159
182
|
|
160
|
-
|
161
|
-
chars
|
162
|
-
end
|
183
|
+
it "should support dumping bytes in decimal format" do
|
184
|
+
chars = []
|
163
185
|
|
164
|
-
|
186
|
+
subject.each(data) do |index,hex,print|
|
187
|
+
chars += hex
|
188
|
+
end
|
189
|
+
|
190
|
+
expect(chars).to be == decimal_chars
|
191
|
+
end
|
165
192
|
end
|
166
193
|
|
167
|
-
|
168
|
-
|
169
|
-
chars = []
|
194
|
+
context "when initialized with base: :octal" do
|
195
|
+
subject { described_class.new(base: :octal) }
|
170
196
|
|
171
|
-
|
172
|
-
chars
|
173
|
-
|
197
|
+
it "should support dumping bytes in octal format" do
|
198
|
+
chars = []
|
199
|
+
|
200
|
+
subject.each(data) do |index,hex,print|
|
201
|
+
chars += hex
|
202
|
+
end
|
174
203
|
|
175
|
-
|
204
|
+
expect(chars).to be == octal_chars
|
205
|
+
end
|
176
206
|
end
|
177
207
|
|
178
|
-
|
179
|
-
|
180
|
-
chars = []
|
208
|
+
context "when initialized with base: :binary" do
|
209
|
+
subject { described_class.new(base: :binary) }
|
181
210
|
|
182
|
-
|
183
|
-
chars
|
184
|
-
end
|
211
|
+
it "should support dumping bytes in binary format" do
|
212
|
+
chars = []
|
185
213
|
|
186
|
-
|
214
|
+
subject.each(data) do |index,hex,print|
|
215
|
+
chars += hex
|
216
|
+
end
|
217
|
+
|
218
|
+
expect(chars).to be == binary_chars
|
219
|
+
end
|
187
220
|
end
|
188
221
|
|
189
|
-
context ":
|
222
|
+
context "when initialized with word_size: and endian:" do
|
190
223
|
let(:options) { {:word_size => 2, :endian => :little} }
|
191
|
-
|
192
224
|
let(:hex_words) { ['6568', '6c6c', '006f'] }
|
193
|
-
let(:decimal_words) { ['25960', '27756', ' 111'] }
|
194
|
-
let(:octal_words) { ['062550', '066154', '000157'] }
|
195
|
-
let(:binary_words) { ['0110010101101000', '0110110001101100', '0000000001101111'] }
|
196
225
|
|
197
|
-
|
198
|
-
|
226
|
+
subject { described_class.new(**options) }
|
227
|
+
|
228
|
+
it "should dump words in hexadecimal by default" do
|
199
229
|
words = []
|
200
230
|
|
201
|
-
|
231
|
+
subject.each(data) do |index,hex,print|
|
202
232
|
words += hex
|
203
233
|
end
|
204
234
|
|
205
235
|
expect(words).to be == hex_words
|
206
236
|
end
|
207
237
|
|
208
|
-
|
209
|
-
|
210
|
-
words = []
|
238
|
+
context "and base: :decimal" do
|
239
|
+
subject { described_class.new(base: :decimal, **options) }
|
211
240
|
|
212
|
-
|
213
|
-
words += dec
|
214
|
-
end
|
241
|
+
let(:decimal_words) { ['25960', '27756', ' 111'] }
|
215
242
|
|
216
|
-
|
217
|
-
|
243
|
+
it "should dump words in decimal" do
|
244
|
+
words = []
|
218
245
|
|
219
|
-
|
220
|
-
|
221
|
-
|
246
|
+
subject.each(data) do |index,dec,print|
|
247
|
+
words += dec
|
248
|
+
end
|
222
249
|
|
223
|
-
|
224
|
-
words += oct
|
250
|
+
expect(words).to be == decimal_words
|
225
251
|
end
|
252
|
+
end
|
253
|
+
|
254
|
+
context "and base: :octal" do
|
255
|
+
subject { described_class.new(base: :octal, **options) }
|
256
|
+
|
257
|
+
let(:octal_words) { ['062550', '066154', '000157'] }
|
258
|
+
|
259
|
+
it "should dump words in octal" do
|
260
|
+
words = []
|
226
261
|
|
227
|
-
|
262
|
+
subject.each(data) do |index,oct,print|
|
263
|
+
words += oct
|
264
|
+
end
|
265
|
+
|
266
|
+
expect(words).to be == octal_words
|
267
|
+
end
|
228
268
|
end
|
229
269
|
|
230
|
-
|
231
|
-
|
232
|
-
|
270
|
+
context "and base: :binary" do
|
271
|
+
subject { described_class.new(base: :binary, **options) }
|
272
|
+
|
273
|
+
let(:binary_words) { ['0110010101101000', '0110110001101100', '0000000001101111'] }
|
274
|
+
|
275
|
+
it "should dump words in binary" do
|
276
|
+
words = []
|
233
277
|
|
234
|
-
|
235
|
-
|
278
|
+
subject.each(data) do |index,bin,print|
|
279
|
+
words += bin
|
280
|
+
end
|
281
|
+
|
282
|
+
expect(words).to be == binary_words
|
236
283
|
end
|
284
|
+
end
|
285
|
+
end
|
237
286
|
|
238
|
-
|
287
|
+
it "must return the number of bytes read" do
|
288
|
+
length = 100
|
289
|
+
data = 'A' * length
|
290
|
+
|
291
|
+
expect(subject.each(data) { |index,hex,print| }).to be == length
|
292
|
+
end
|
293
|
+
|
294
|
+
context "when no block is given" do
|
295
|
+
it "must return an Enumerator" do
|
296
|
+
expect(subject.each(data)).to be_kind_of(Enumerator)
|
239
297
|
end
|
240
298
|
end
|
241
299
|
end
|
242
300
|
|
243
301
|
describe "#dump" do
|
244
|
-
it "should check if the
|
302
|
+
it "should check if the output supports the '#<<' method" do
|
245
303
|
expect {
|
246
304
|
subject.dump(data,Object.new)
|
247
305
|
}.to raise_error(ArgumentError)
|
248
306
|
end
|
249
307
|
|
308
|
+
let(:index_format) { "%.8x" }
|
309
|
+
|
250
310
|
it "should append each line of the hexdump to the output" do
|
251
311
|
lines = []
|
252
312
|
|
253
313
|
subject.dump(data,lines)
|
254
314
|
|
255
|
-
expect(lines.length).to be(
|
315
|
+
expect(lines.length).to be(2)
|
316
|
+
expect(lines[0]).to start_with(index_format % 0)
|
256
317
|
expect(lines[0]).to include(hex_chars.join(' '))
|
257
|
-
expect(lines[0]).to
|
318
|
+
expect(lines[0]).to end_with("|#{print_chars.join}|#{$/}")
|
319
|
+
end
|
320
|
+
|
321
|
+
it "must always print the total number of bytes read on the last line" do
|
322
|
+
lines = []
|
323
|
+
|
324
|
+
subject.dump(data,lines)
|
325
|
+
|
326
|
+
expect(lines.last).to start_with(index_format % data.length)
|
258
327
|
end
|
259
328
|
end
|
260
329
|
end
|