hexdump 0.2.4 → 0.3.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.
- 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
|