hexdump 0.1.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog.md +14 -0
- data/README.md +78 -44
- data/benchmarks/hexdump.rb +11 -1
- data/gemspec.yml +5 -3
- data/lib/hexdump/dumper.rb +441 -0
- data/lib/hexdump/hexdump.rb +22 -70
- data/spec/dumper_spec.rb +260 -0
- data/spec/hexdump_spec.rb +7 -149
- metadata +9 -8
data/ChangeLog.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
### 0.2.1 / 2011-06-11
|
2
|
+
|
3
|
+
* Fixed a major bug in {Hexdump::Dumper#dump}, where the line buffers
|
4
|
+
were not being cleared.
|
5
|
+
|
6
|
+
### 0.2.0 / 2011-06-11
|
7
|
+
|
8
|
+
* Added {Hexdump::Dumper}.
|
9
|
+
* Added support for hexdumping 1, 2, 4, 8 byte words.
|
10
|
+
* Added support for hexdumping Little and Big Endian words.
|
11
|
+
* Optimized the hexdump algorithm to not use Arrays, use a lookup table
|
12
|
+
of printable characters and cache formatted numbers.
|
13
|
+
* Opted into [test.rubygems.org](http://test.rubygems.org/).
|
14
|
+
|
1
15
|
### 0.1.0 / 2011-03-05
|
2
16
|
|
3
17
|
* Initial release:
|
data/README.md
CHANGED
@@ -15,53 +15,11 @@ Simple and Fast hexdumping for Ruby.
|
|
15
15
|
* Can send the hexdump output to any Object supporting the `<<` method.
|
16
16
|
* Can yield each line of hexdump, instead of printing the output.
|
17
17
|
* Supports printing ASCII, hexadecimal, decimal, octal and binary bytes.
|
18
|
+
* Supports hexdumping 1, 2, 4, 8 byte words.
|
19
|
+
* Supports hexdumping Little and Big Endian words.
|
18
20
|
* Makes {String}, {StringIO}, {IO}, {File} objects hexdumpable.
|
19
21
|
* Fast-ish.
|
20
22
|
|
21
|
-
## Benchmarks
|
22
|
-
|
23
|
-
Benchmarks show {Hexdump.dump} processing 2.4M of data.
|
24
|
-
|
25
|
-
### Ruby 1.9.2-p180
|
26
|
-
|
27
|
-
user system total real
|
28
|
-
hexdump (block) 7.740000 0.030000 7.770000 ( 8.138029)
|
29
|
-
hexdump 9.590000 0.050000 9.640000 ( 10.178203)
|
30
|
-
hexdump width=256 (block) 7.280000 0.020000 7.300000 ( 7.534507)
|
31
|
-
hexdump width=256 8.130000 0.030000 8.160000 ( 8.342448)
|
32
|
-
hexdump ascii=true (block) 7.740000 0.030000 7.770000 ( 7.958550)
|
33
|
-
hexdump ascii=true 9.550000 0.050000 9.600000 ( 9.803758)
|
34
|
-
|
35
|
-
### Ruby 1.8.7-p334
|
36
|
-
|
37
|
-
user system total real
|
38
|
-
hexdump (block) 10.520000 0.010000 10.530000 ( 10.692901)
|
39
|
-
hexdump 11.580000 0.010000 11.590000 ( 11.873978)
|
40
|
-
hexdump width=256 (block) 9.960000 0.110000 10.070000 ( 11.592033)
|
41
|
-
hexdump width=256 10.660000 0.010000 10.670000 ( 10.987417)
|
42
|
-
hexdump ascii=true (block) 10.620000 0.010000 10.630000 ( 10.899925)
|
43
|
-
hexdump ascii=true 11.590000 0.030000 11.620000 ( 12.765259)
|
44
|
-
|
45
|
-
### Jruby 1.5.6
|
46
|
-
|
47
|
-
user system total real
|
48
|
-
hexdump (block) 6.690000 0.000000 6.690000 ( 6.517000)
|
49
|
-
hexdump 8.234000 0.000000 8.234000 ( 8.234000)
|
50
|
-
hexdump width=256 (block) 4.488000 0.000000 4.488000 ( 4.488000)
|
51
|
-
hexdump width=256 5.462000 0.000000 5.462000 ( 5.462000)
|
52
|
-
hexdump ascii=true (block) 4.456000 0.000000 4.456000 ( 4.456000)
|
53
|
-
hexdump ascii=true 5.039000 0.000000 5.039000 ( 5.039000)
|
54
|
-
|
55
|
-
### Rubinius 1.2.3
|
56
|
-
|
57
|
-
user system total real
|
58
|
-
hexdump (block) 10.013478 0.018997 10.032475 ( 11.148450)
|
59
|
-
hexdump 13.153001 0.015997 13.168998 ( 13.740888)
|
60
|
-
hexdump width=256 (block) 8.845656 0.008999 8.854655 ( 9.022673)
|
61
|
-
hexdump width=256 9.894496 0.008999 9.903495 ( 10.121070)
|
62
|
-
hexdump ascii=true (block) 9.576544 0.021996 9.598540 ( 9.810846)
|
63
|
-
hexdump ascii=true 13.088011 0.015998 13.104009 ( 13.390532)
|
64
|
-
|
65
23
|
## Examples
|
66
24
|
|
67
25
|
require 'hexdump'
|
@@ -74,6 +32,10 @@ Benchmarks show {Hexdump.dump} processing 2.4M of data.
|
|
74
32
|
data.hexdump
|
75
33
|
# 00000000 68 65 6c 6c 6f 00 |hello.|
|
76
34
|
|
35
|
+
File.open('dump.txt','w') do |file|
|
36
|
+
data.hexdump(:output => file)
|
37
|
+
end
|
38
|
+
|
77
39
|
# iterate over the hexdump lines
|
78
40
|
data.hexdump do |index,hex,printable|
|
79
41
|
index # => 0
|
@@ -102,10 +64,82 @@ Benchmarks show {Hexdump.dump} processing 2.4M of data.
|
|
102
64
|
Hexdump.dump(data, :base => :binary)
|
103
65
|
# 00000000 01101000 01100101 01101100 01101100 01101111 00000000 |hello.|
|
104
66
|
|
67
|
+
("ABC" * 10).hexdump(:word_size => 2)
|
68
|
+
# 00000000 4241 4143 4342 4241 4143 4342 4241 4143 |䉁䅃䍂䉁䅃䍂䉁䅃|
|
69
|
+
# 00000010 4342 4241 4143 4342 4241 4143 4342 |䍂䉁䅃䍂䉁䅃䍂|
|
70
|
+
|
105
71
|
## Install
|
106
72
|
|
107
73
|
$ gem install hexdump
|
108
74
|
|
75
|
+
## Benchmarks
|
76
|
+
|
77
|
+
Benchmarks show {Hexdump.dump} processing 2.4M of data.
|
78
|
+
|
79
|
+
### Ruby 1.9.2-p180
|
80
|
+
|
81
|
+
user system total real
|
82
|
+
hexdump (block) 3.010000 0.010000 3.020000 ( 3.529396)
|
83
|
+
hexdump 5.430000 0.030000 5.460000 ( 6.216174)
|
84
|
+
hexdump width=256 (block) 3.010000 0.020000 3.030000 ( 3.308961)
|
85
|
+
hexdump width=256 4.700000 0.040000 4.740000 ( 5.520189)
|
86
|
+
hexdump ascii=true (block) 3.050000 0.010000 3.060000 ( 3.501436)
|
87
|
+
hexdump ascii=true 5.450000 0.040000 5.490000 ( 6.352144)
|
88
|
+
hexdump word_size=2 (block) 7.420000 0.050000 7.470000 ( 9.174734)
|
89
|
+
hexdump word_size=2 9.500000 0.070000 9.570000 ( 11.228204)
|
90
|
+
hexdump word_size=4 (block) 4.110000 0.030000 4.140000 ( 4.849785)
|
91
|
+
hexdump word_size=4 5.380000 0.060000 5.440000 ( 6.209022)
|
92
|
+
hexdump word_size=8 (block) 3.350000 0.070000 3.420000 ( 4.147304)
|
93
|
+
hexdump word_size=8 4.430000 0.040000 4.470000 ( 5.930758)
|
94
|
+
|
95
|
+
### Ruby 1.8.7-p334
|
96
|
+
|
97
|
+
user system total real
|
98
|
+
hexdump (block) 8.470000 0.020000 8.490000 ( 9.585524)
|
99
|
+
hexdump 11.080000 0.050000 11.130000 ( 12.542401)
|
100
|
+
hexdump width=256 (block) 8.360000 0.030000 8.390000 ( 9.431877)
|
101
|
+
hexdump width=256 10.310000 0.050000 10.360000 ( 12.278973)
|
102
|
+
hexdump ascii=true (block) 8.550000 0.030000 8.580000 ( 10.502437)
|
103
|
+
hexdump ascii=true 11.140000 0.040000 11.180000 ( 12.752712)
|
104
|
+
hexdump word_size=2 (block) 12.680000 0.060000 12.740000 ( 14.657269)
|
105
|
+
hexdump word_size=2 13.560000 0.080000 13.640000 ( 16.368675)
|
106
|
+
hexdump word_size=4 (block) 8.500000 0.040000 8.540000 ( 9.687623)
|
107
|
+
hexdump word_size=4 9.340000 0.040000 9.380000 ( 10.657158)
|
108
|
+
hexdump word_size=8 (block) 7.520000 0.040000 7.560000 ( 8.565246)
|
109
|
+
hexdump word_size=8 8.240000 0.040000 8.280000 ( 9.475693)
|
110
|
+
|
111
|
+
### JRuby 1.6.0
|
112
|
+
|
113
|
+
user system total real
|
114
|
+
hexdump (block) 6.742000 0.000000 6.742000 ( 6.495000)
|
115
|
+
hexdump 7.498000 0.000000 7.498000 ( 7.498000)
|
116
|
+
hexdump width=256 (block) 4.601000 0.000000 4.601000 ( 4.601000)
|
117
|
+
hexdump width=256 5.569000 0.000000 5.569000 ( 5.569000)
|
118
|
+
hexdump ascii=true (block) 5.198000 0.000000 5.198000 ( 5.198000)
|
119
|
+
hexdump ascii=true 5.799000 0.000000 5.799000 ( 5.798000)
|
120
|
+
hexdump word_size=2 (block) 8.440000 0.000000 8.440000 ( 8.440000)
|
121
|
+
hexdump word_size=2 8.698000 0.000000 8.698000 ( 8.698000)
|
122
|
+
hexdump word_size=4 (block) 5.603000 0.000000 5.603000 ( 5.602000)
|
123
|
+
hexdump word_size=4 5.999000 0.000000 5.999000 ( 5.999000)
|
124
|
+
hexdump word_size=8 (block) 7.975000 0.000000 7.975000 ( 7.975000)
|
125
|
+
hexdump word_size=8 5.255000 0.000000 5.255000 ( 5.255000)
|
126
|
+
|
127
|
+
### Rubinius 1.2.4
|
128
|
+
|
129
|
+
user system total real
|
130
|
+
hexdump (block) 5.064230 0.029996 5.094226 ( 6.236865)
|
131
|
+
hexdump 7.401875 0.039993 7.441868 ( 10.154394)
|
132
|
+
hexdump width=256 (block) 4.149369 0.054992 4.204361 ( 6.518306)
|
133
|
+
hexdump width=256 4.960246 0.089986 5.050232 ( 8.647516)
|
134
|
+
hexdump ascii=true (block) 4.458322 0.026996 4.485318 ( 5.570982)
|
135
|
+
hexdump ascii=true 6.961941 0.056992 7.018933 ( 9.895088)
|
136
|
+
hexdump word_size=2 (block) 8.856653 0.078988 8.935641 ( 11.226360)
|
137
|
+
hexdump word_size=2 10.489405 0.083988 10.573393 ( 12.980509)
|
138
|
+
hexdump word_size=4 (block) 4.848263 0.047992 4.896255 ( 6.526478)
|
139
|
+
hexdump word_size=4 6.649989 0.053992 6.703981 ( 8.245247)
|
140
|
+
hexdump word_size=8 (block) 5.638143 0.047993 5.686136 ( 12.530454)
|
141
|
+
hexdump word_size=8 7.598844 0.066990 7.665834 ( 16.881667)
|
142
|
+
|
109
143
|
## Copyright
|
110
144
|
|
111
145
|
Copyright (c) 2011 Hal Brodigan
|
data/benchmarks/hexdump.rb
CHANGED
@@ -8,7 +8,7 @@ require 'benchmark'
|
|
8
8
|
DATA = ((0..255).map { |b| b.chr }.join) * 10000
|
9
9
|
OUTPUT = Class.new { def <<(data); end }.new
|
10
10
|
|
11
|
-
Benchmark.bm(
|
11
|
+
Benchmark.bm(27) do |b|
|
12
12
|
b.report('hexdump (block)') do
|
13
13
|
Hexdump.dump(DATA) { |index,hex,print| }
|
14
14
|
end
|
@@ -32,4 +32,14 @@ Benchmark.bm(26) do |b|
|
|
32
32
|
b.report('hexdump ascii=true') do
|
33
33
|
Hexdump.dump(DATA, :ascci => true, :output => OUTPUT)
|
34
34
|
end
|
35
|
+
|
36
|
+
[2, 4, 8].each do |word_size|
|
37
|
+
b.report("hexdump word_size=#{word_size} (block)") do
|
38
|
+
Hexdump.dump(DATA, :word_size => word_size) { |index,hex,print| }
|
39
|
+
end
|
40
|
+
|
41
|
+
b.report("hexdump word_size=#{word_size}") do
|
42
|
+
Hexdump.dump(DATA, :word_size => word_size, :output => OUTPUT)
|
43
|
+
end
|
44
|
+
end
|
35
45
|
end
|
data/gemspec.yml
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
name: hexdump
|
2
|
-
version: 0.1
|
3
|
-
summary:
|
2
|
+
version: 0.2.1
|
3
|
+
summary: Hexdump Strings and IO objects.
|
4
4
|
description: Simple and Fast hexdumping for Ruby.
|
5
5
|
license: MIT
|
6
|
-
authors:
|
6
|
+
authors: Postmodern
|
7
7
|
email: postmodern.mod3@gmail.com
|
8
8
|
homepage: http://github.com/postmodern/hexdump
|
9
9
|
has_yard: true
|
10
10
|
|
11
|
+
required_ruby_version: ">= 1.8.7"
|
12
|
+
|
11
13
|
development_dependencies:
|
12
14
|
ore-tasks: ~> 0.4
|
13
15
|
rspec: ~> 2.4
|
@@ -0,0 +1,441 @@
|
|
1
|
+
module Hexdump
|
2
|
+
#
|
3
|
+
# Handles the parsing of data and formatting of the hexdump.
|
4
|
+
#
|
5
|
+
# @since 0.2.0
|
6
|
+
#
|
7
|
+
class Dumper
|
8
|
+
|
9
|
+
# Widths for formatted numbers
|
10
|
+
WIDTHS = {
|
11
|
+
:hexadecimal => proc { |word_size| word_size * 2 },
|
12
|
+
:decimal => {
|
13
|
+
1 => 3,
|
14
|
+
2 => 5,
|
15
|
+
4 => 10,
|
16
|
+
8 => 20
|
17
|
+
},
|
18
|
+
:octal => {
|
19
|
+
1 => 3,
|
20
|
+
2 => 6,
|
21
|
+
4 => 11,
|
22
|
+
8 => 22
|
23
|
+
},
|
24
|
+
:binary => proc { |word_size| word_size * 8 }
|
25
|
+
}
|
26
|
+
|
27
|
+
# Format Strings for the various bases
|
28
|
+
FORMATS = {
|
29
|
+
:hexadecimal => proc { |width| "%.#{width}x" },
|
30
|
+
:decimal => proc { |width| "%#{width}.d" },
|
31
|
+
:octal => proc { |width| "%.#{width}o" },
|
32
|
+
:binary => proc { |width| "%.#{width}b" }
|
33
|
+
}
|
34
|
+
|
35
|
+
# Character to represent unprintable characters
|
36
|
+
UNPRINTABLE = '.'
|
37
|
+
|
38
|
+
# ASCII printable bytes and characters
|
39
|
+
PRINTABLE = {
|
40
|
+
0x20 => " ",
|
41
|
+
0x21 => "!",
|
42
|
+
0x22 => "\"",
|
43
|
+
0x23 => "#",
|
44
|
+
0x24 => "$",
|
45
|
+
0x25 => "%",
|
46
|
+
0x26 => "&",
|
47
|
+
0x27 => "'",
|
48
|
+
0x28 => "(",
|
49
|
+
0x29 => ")",
|
50
|
+
0x2a => "*",
|
51
|
+
0x2b => "+",
|
52
|
+
0x2c => ",",
|
53
|
+
0x2d => "-",
|
54
|
+
0x2e => ".",
|
55
|
+
0x2f => "/",
|
56
|
+
0x30 => "0",
|
57
|
+
0x31 => "1",
|
58
|
+
0x32 => "2",
|
59
|
+
0x33 => "3",
|
60
|
+
0x34 => "4",
|
61
|
+
0x35 => "5",
|
62
|
+
0x36 => "6",
|
63
|
+
0x37 => "7",
|
64
|
+
0x38 => "8",
|
65
|
+
0x39 => "9",
|
66
|
+
0x3a => ":",
|
67
|
+
0x3b => ";",
|
68
|
+
0x3c => "<",
|
69
|
+
0x3d => "=",
|
70
|
+
0x3e => ">",
|
71
|
+
0x3f => "?",
|
72
|
+
0x40 => "@",
|
73
|
+
0x41 => "A",
|
74
|
+
0x42 => "B",
|
75
|
+
0x43 => "C",
|
76
|
+
0x44 => "D",
|
77
|
+
0x45 => "E",
|
78
|
+
0x46 => "F",
|
79
|
+
0x47 => "G",
|
80
|
+
0x48 => "H",
|
81
|
+
0x49 => "I",
|
82
|
+
0x4a => "J",
|
83
|
+
0x4b => "K",
|
84
|
+
0x4c => "L",
|
85
|
+
0x4d => "M",
|
86
|
+
0x4e => "N",
|
87
|
+
0x4f => "O",
|
88
|
+
0x50 => "P",
|
89
|
+
0x51 => "Q",
|
90
|
+
0x52 => "R",
|
91
|
+
0x53 => "S",
|
92
|
+
0x54 => "T",
|
93
|
+
0x55 => "U",
|
94
|
+
0x56 => "V",
|
95
|
+
0x57 => "W",
|
96
|
+
0x58 => "X",
|
97
|
+
0x59 => "Y",
|
98
|
+
0x5a => "Z",
|
99
|
+
0x5b => "[",
|
100
|
+
0x5c => "\\",
|
101
|
+
0x5d => "]",
|
102
|
+
0x5e => "^",
|
103
|
+
0x5f => "_",
|
104
|
+
0x60 => "`",
|
105
|
+
0x61 => "a",
|
106
|
+
0x62 => "b",
|
107
|
+
0x63 => "c",
|
108
|
+
0x64 => "d",
|
109
|
+
0x65 => "e",
|
110
|
+
0x66 => "f",
|
111
|
+
0x67 => "g",
|
112
|
+
0x68 => "h",
|
113
|
+
0x69 => "i",
|
114
|
+
0x6a => "j",
|
115
|
+
0x6b => "k",
|
116
|
+
0x6c => "l",
|
117
|
+
0x6d => "m",
|
118
|
+
0x6e => "n",
|
119
|
+
0x6f => "o",
|
120
|
+
0x70 => "p",
|
121
|
+
0x71 => "q",
|
122
|
+
0x72 => "r",
|
123
|
+
0x73 => "s",
|
124
|
+
0x74 => "t",
|
125
|
+
0x75 => "u",
|
126
|
+
0x76 => "v",
|
127
|
+
0x77 => "w",
|
128
|
+
0x78 => "x",
|
129
|
+
0x79 => "y",
|
130
|
+
0x7a => "z",
|
131
|
+
0x7b => "{",
|
132
|
+
0x7c => "|",
|
133
|
+
0x7d => "}",
|
134
|
+
0x7e => "~"
|
135
|
+
}
|
136
|
+
|
137
|
+
PRINTABLE.default = UNPRINTABLE
|
138
|
+
|
139
|
+
# The base to dump words as.
|
140
|
+
attr_reader :base
|
141
|
+
|
142
|
+
# The size of the words parse from the data.
|
143
|
+
attr_reader :word_size
|
144
|
+
|
145
|
+
# The endianness of the words parsed from the data.
|
146
|
+
attr_reader :endian
|
147
|
+
|
148
|
+
# The width in words of each hexdump line.
|
149
|
+
attr_reader :width
|
150
|
+
|
151
|
+
# Whether to display ASCII characters alongside numeric values.
|
152
|
+
attr_reader :ascii
|
153
|
+
|
154
|
+
#
|
155
|
+
# Creates a new Hexdump dumper.
|
156
|
+
#
|
157
|
+
# @param [Hash] options
|
158
|
+
# Additional options.
|
159
|
+
#
|
160
|
+
# @option options [Integer] :width (16)
|
161
|
+
# The number of bytes to dump for each line.
|
162
|
+
#
|
163
|
+
# @option options [Integer] :word_size (1)
|
164
|
+
# The number of bytes within a word.
|
165
|
+
#
|
166
|
+
# @option options [Symbol, Integer] :base (:hexadecimal)
|
167
|
+
# The base to print bytes in. Supported bases include, `:hexadecimal`,
|
168
|
+
# `:hex`, `16, `:decimal`, `:dec`, `10, `:octal`, `:oct`, `8`,
|
169
|
+
# `:binary`, `:bin` and `2`.
|
170
|
+
#
|
171
|
+
# @option options [Boolean] :ascii (false)
|
172
|
+
# Print ascii characters when possible.
|
173
|
+
#
|
174
|
+
# @raise [ArgumentError]
|
175
|
+
# The values for `:base` or `:endian` were unknown.
|
176
|
+
#
|
177
|
+
# @since 0.2.0
|
178
|
+
#
|
179
|
+
def initialize(options={})
|
180
|
+
@base = case options[:base]
|
181
|
+
when :hexadecimal, :hex, 16
|
182
|
+
:hexadecimal
|
183
|
+
when :decimal, :dec, 10
|
184
|
+
:decimal
|
185
|
+
when :octal, :oct, 8
|
186
|
+
:octal
|
187
|
+
when :binary, :bin, 2
|
188
|
+
:binary
|
189
|
+
when nil
|
190
|
+
:hexadecimal
|
191
|
+
else
|
192
|
+
raise(ArgumentError,"unknown base #{options[:base].inspect}")
|
193
|
+
end
|
194
|
+
|
195
|
+
@word_size = options.fetch(:word_size,1)
|
196
|
+
@endian = case options[:endian]
|
197
|
+
when 'little', :little
|
198
|
+
:little
|
199
|
+
when 'big', :big
|
200
|
+
:big
|
201
|
+
when nil
|
202
|
+
:little
|
203
|
+
else
|
204
|
+
raise(ArgumentError,"unknown endian: #{options[:endian].inspect}")
|
205
|
+
end
|
206
|
+
|
207
|
+
@width = (options.fetch(:width,16) / @word_size)
|
208
|
+
@ascii = options.fetch(:ascii,false)
|
209
|
+
|
210
|
+
@format_width = (WIDTHS[@base][@word_size] || 1)
|
211
|
+
@format = FORMATS[@base][@format_width]
|
212
|
+
|
213
|
+
if @word_size == 1
|
214
|
+
@format_cache = Hash.new do |hash,key|
|
215
|
+
hash[key] = sprintf(@format,key)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
#
|
221
|
+
# Iterates over every word within the data.
|
222
|
+
#
|
223
|
+
# @param [#each_byte] data
|
224
|
+
# The data containing bytes.
|
225
|
+
#
|
226
|
+
# @yield [word]
|
227
|
+
# The given block will be passed each word within the data.
|
228
|
+
#
|
229
|
+
# @yieldparam [Integer]
|
230
|
+
# An unpacked word from the data.
|
231
|
+
#
|
232
|
+
# @return [Enumerator]
|
233
|
+
# If no block is given, an Enumerator will be returned.
|
234
|
+
#
|
235
|
+
# @raise [ArgumentError]
|
236
|
+
# The given data does not define the `#each_byte` method.
|
237
|
+
#
|
238
|
+
# @since 0.2.0
|
239
|
+
#
|
240
|
+
def each_word(data,&block)
|
241
|
+
return enum_for(:each_word,data) unless block
|
242
|
+
|
243
|
+
unless data.respond_to?(:each_byte)
|
244
|
+
raise(ArgumentError,"the data to hexdump must define #each_byte")
|
245
|
+
end
|
246
|
+
|
247
|
+
if @word_size > 1
|
248
|
+
word = 0
|
249
|
+
count = 0
|
250
|
+
|
251
|
+
init_shift = if @endian == :big
|
252
|
+
((@word_size - 1) * 8)
|
253
|
+
else
|
254
|
+
0
|
255
|
+
end
|
256
|
+
shift = init_shift
|
257
|
+
|
258
|
+
data.each_byte do |b|
|
259
|
+
word |= (b << shift)
|
260
|
+
|
261
|
+
if @endian == :big
|
262
|
+
shift -= 8
|
263
|
+
else
|
264
|
+
shift += 8
|
265
|
+
end
|
266
|
+
|
267
|
+
count += 1
|
268
|
+
|
269
|
+
if count >= @word_size
|
270
|
+
yield word
|
271
|
+
|
272
|
+
word = 0
|
273
|
+
count = 0
|
274
|
+
shift = init_shift
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
# yield the remaining word
|
279
|
+
yield word if count > 0
|
280
|
+
else
|
281
|
+
data.each_byte(&block)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
#
|
286
|
+
# Iterates over the hexdump.
|
287
|
+
#
|
288
|
+
# @param [#each_byte] data
|
289
|
+
# The data to be hexdumped.
|
290
|
+
#
|
291
|
+
# @yield [index,numeric,printable]
|
292
|
+
# The given block will be passed the hexdump break-down of each
|
293
|
+
# segment.
|
294
|
+
#
|
295
|
+
# @yieldparam [Integer] index
|
296
|
+
# The index of the hexdumped segment.
|
297
|
+
#
|
298
|
+
# @yieldparam [Array<String>] numeric
|
299
|
+
# The numeric representation of the segment.
|
300
|
+
#
|
301
|
+
# @yieldparam [Array<String>] printable
|
302
|
+
# The printable representation of the segment.
|
303
|
+
#
|
304
|
+
# @return [Enumerator]
|
305
|
+
# If no block is given, an Enumerator will be returned.
|
306
|
+
#
|
307
|
+
# @since 0.2.0
|
308
|
+
#
|
309
|
+
def each(data)
|
310
|
+
return enum_for(:each,data) unless block_given?
|
311
|
+
|
312
|
+
index = 0
|
313
|
+
count = 0
|
314
|
+
|
315
|
+
numeric = []
|
316
|
+
printable = []
|
317
|
+
|
318
|
+
each_word(data) do |word|
|
319
|
+
numeric << format_numeric(word)
|
320
|
+
printable << format_printable(word)
|
321
|
+
|
322
|
+
count += 1
|
323
|
+
|
324
|
+
if count >= @width
|
325
|
+
yield(index,numeric,printable)
|
326
|
+
|
327
|
+
numeric.clear
|
328
|
+
printable.clear
|
329
|
+
|
330
|
+
index += (@width * @word_size)
|
331
|
+
count = 0
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
if count > 0
|
336
|
+
# yield the remaining data
|
337
|
+
yield(index,numeric,printable)
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
#
|
342
|
+
# Dumps the hexdump.
|
343
|
+
#
|
344
|
+
# @param [#each_byte] data
|
345
|
+
# The data to be hexdumped.
|
346
|
+
#
|
347
|
+
# @param [#<<] output
|
348
|
+
# The output to dump the hexdump to.
|
349
|
+
#
|
350
|
+
# @return [nil]
|
351
|
+
#
|
352
|
+
# @raise [ArgumentError]
|
353
|
+
# The output value does not support the `#<<` method.
|
354
|
+
#
|
355
|
+
# @since 0.2.0
|
356
|
+
#
|
357
|
+
def dump(data,output=STDOUT)
|
358
|
+
unless output.respond_to?(:<<)
|
359
|
+
raise(ArgumentError,"output must support the #<< method")
|
360
|
+
end
|
361
|
+
|
362
|
+
bytes_segment_width = ((@width * @format_width) + @width)
|
363
|
+
line_format = "%.8x %-#{bytes_segment_width}s |%s|\n"
|
364
|
+
|
365
|
+
index = 0
|
366
|
+
count = 0
|
367
|
+
|
368
|
+
numeric = ''
|
369
|
+
printable = ''
|
370
|
+
|
371
|
+
each_word(data) do |word|
|
372
|
+
numeric << format_numeric(word) << ' '
|
373
|
+
printable << format_printable(word)
|
374
|
+
|
375
|
+
count += 1
|
376
|
+
|
377
|
+
if count >= @width
|
378
|
+
output << sprintf(line_format,index,numeric,printable)
|
379
|
+
|
380
|
+
numeric = ''
|
381
|
+
printable = ''
|
382
|
+
|
383
|
+
index += (@width * @word_size)
|
384
|
+
count = 0
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
if count > 0
|
389
|
+
# output the remaining line
|
390
|
+
output << sprintf(line_format,index,numeric,printable)
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
protected
|
395
|
+
|
396
|
+
#
|
397
|
+
# Converts the word into a numeric String.
|
398
|
+
#
|
399
|
+
# @param [Integer] word
|
400
|
+
# The word to convert.
|
401
|
+
#
|
402
|
+
# @return [String]
|
403
|
+
# The numeric representation of the word.
|
404
|
+
#
|
405
|
+
# @since 0.2.0
|
406
|
+
#
|
407
|
+
def format_numeric(word)
|
408
|
+
if @word_size == 1
|
409
|
+
if (@ascii && (word >= 0x20 && word <= 0x7e))
|
410
|
+
PRINTABLE[word]
|
411
|
+
else
|
412
|
+
@format_cache[word]
|
413
|
+
end
|
414
|
+
else
|
415
|
+
sprintf(@format,word)
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
#
|
420
|
+
# Converts a word into a printable String.
|
421
|
+
#
|
422
|
+
# @param [Integer] word
|
423
|
+
# The word to convert.
|
424
|
+
#
|
425
|
+
# @return [String]
|
426
|
+
# The printable representation of the word.
|
427
|
+
#
|
428
|
+
# @since 0.2.0
|
429
|
+
#
|
430
|
+
def format_printable(word)
|
431
|
+
if @word_size == 1
|
432
|
+
PRINTABLE[word]
|
433
|
+
elsif (RUBY_VERSION > '1.9.' && (word >= -2 && word <= 0x7fffffff))
|
434
|
+
word.chr(Encoding::UTF_8)
|
435
|
+
else
|
436
|
+
UNPRINTABLE
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
end
|
441
|
+
end
|
data/lib/hexdump/hexdump.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'hexdump/dumper'
|
2
|
+
|
1
3
|
#
|
2
4
|
# Provides the {Hexdump.dump} method and can add hexdumping to other classes
|
3
5
|
# by including the {Hexdump} module.
|
@@ -28,6 +30,9 @@ module Hexdump
|
|
28
30
|
# @option options [Integer] :width (16)
|
29
31
|
# The number of bytes to dump for each line.
|
30
32
|
#
|
33
|
+
# @option options [Integer] :word_size (1)
|
34
|
+
# The number of bytes within a word.
|
35
|
+
#
|
31
36
|
# @option options [Symbol, Integer] :base (:hexadecimal)
|
32
37
|
# The base to print bytes in. Supported bases include, `:hexadecimal`,
|
33
38
|
# `:hex`, `16, `:decimal`, `:dec`, `10, `:octal`, `:oct`, `8`,
|
@@ -39,89 +44,36 @@ module Hexdump
|
|
39
44
|
# @option options [#<<] :output (STDOUT)
|
40
45
|
# The output to print the hexdump to.
|
41
46
|
#
|
42
|
-
# @yield [index,
|
43
|
-
# The given block will be passed the hexdump break-down of each
|
47
|
+
# @yield [index,numeric,printable]
|
48
|
+
# The given block will be passed the hexdump break-down of each
|
49
|
+
# segment.
|
44
50
|
#
|
45
51
|
# @yieldparam [Integer] index
|
46
52
|
# The index of the hexdumped segment.
|
47
53
|
#
|
48
|
-
# @yieldparam [Array<String>]
|
49
|
-
# The
|
54
|
+
# @yieldparam [Array<String>] numeric
|
55
|
+
# The numeric representation of the segment.
|
50
56
|
#
|
51
|
-
# @yieldparam [Array<String>]
|
52
|
-
# The
|
57
|
+
# @yieldparam [Array<String>] printable
|
58
|
+
# The printable representation of the segment.
|
53
59
|
#
|
54
60
|
# @return [nil]
|
55
61
|
#
|
56
62
|
# @raise [ArgumentError]
|
57
|
-
# The given data does not define the `#each_byte` method,
|
58
|
-
# the `:output` value does not support the `#<<` method
|
63
|
+
# The given data does not define the `#each_byte` method,
|
64
|
+
# the `:output` value does not support the `#<<` method or
|
65
|
+
# the `:base` value was unknown.
|
59
66
|
#
|
60
|
-
def Hexdump.dump(data,options={})
|
61
|
-
|
62
|
-
|
63
|
-
end
|
64
|
-
|
65
|
-
output = options.fetch(:output,STDOUT)
|
66
|
-
|
67
|
-
unless output.respond_to?(:<<)
|
68
|
-
raise(ArgumentError,":output must support the #<< method")
|
69
|
-
end
|
70
|
-
|
71
|
-
width = options.fetch(:width,16)
|
72
|
-
base = options.fetch(:base,:hexadecimal)
|
73
|
-
ascii = options.fetch(:ascii,false)
|
74
|
-
byte_width, byte_format = case base
|
75
|
-
when :hexadecimal, :hex, 16
|
76
|
-
[2, "%.2x"]
|
77
|
-
when :decimal, :dec, 10
|
78
|
-
[3, "%3.d"]
|
79
|
-
when :octal, :oct, 8
|
80
|
-
[4, "0%.3o"]
|
81
|
-
when :binary, :bin, 2
|
82
|
-
[8, "%.8b"]
|
83
|
-
end
|
84
|
-
|
85
|
-
hex_byte = lambda { |byte|
|
86
|
-
if (ascii && (byte >= 0x20 && byte <= 0x7e))
|
87
|
-
byte.chr
|
88
|
-
else
|
89
|
-
byte_format % byte
|
90
|
-
end
|
91
|
-
}
|
92
|
-
|
93
|
-
print_byte = lambda { |byte|
|
94
|
-
if (byte >= 0x20 && byte <= 0x7e)
|
95
|
-
byte.chr
|
96
|
-
else
|
97
|
-
'.'
|
98
|
-
end
|
99
|
-
}
|
100
|
-
|
101
|
-
index = 0
|
102
|
-
|
103
|
-
hex_segment_width = ((width * byte_width) + (width - 1))
|
104
|
-
line_format = "%.8x %-#{hex_segment_width}s |%s|\n"
|
105
|
-
|
106
|
-
data.each_byte.each_slice(width) do |bytes|
|
107
|
-
hex_segment = bytes.map(&hex_byte)
|
108
|
-
print_segment = bytes.map(&print_byte)
|
109
|
-
|
110
|
-
if block_given?
|
111
|
-
yield(index,hex_segment,print_segment)
|
112
|
-
else
|
113
|
-
output << sprintf(
|
114
|
-
line_format,
|
115
|
-
index,
|
116
|
-
hex_segment.join(' '),
|
117
|
-
print_segment.join
|
118
|
-
)
|
119
|
-
end
|
67
|
+
def Hexdump.dump(data,options={},&block)
|
68
|
+
output = (options.delete(:output) || STDOUT)
|
69
|
+
dumper = Dumper.new(options)
|
120
70
|
|
121
|
-
|
71
|
+
if block
|
72
|
+
dumper.each(data,&block)
|
73
|
+
else
|
74
|
+
dumper.dump(data,output)
|
122
75
|
end
|
123
76
|
|
124
|
-
# flush the hexdump buffer
|
125
77
|
return nil
|
126
78
|
end
|
127
79
|
|
data/spec/dumper_spec.rb
ADDED
@@ -0,0 +1,260 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'hexdump/dumper'
|
4
|
+
|
5
|
+
describe Hexdump::Dumper do
|
6
|
+
let(:bytes) { [104, 101, 108, 108, 111] }
|
7
|
+
let(:hex_chars) { ['68', '65', '6c', '6c', '6f'] }
|
8
|
+
let(:decimal_chars) { ['104', '101', '108', '108', '111'] }
|
9
|
+
let(:octal_chars) { ['150', '145', '154', '154', '157'] }
|
10
|
+
let(:binary_chars) { ['01101000', '01100101', '01101100', '01101100', '01101111'] }
|
11
|
+
let(:print_chars) { ['h', 'e', 'l', 'l', 'o'] }
|
12
|
+
let(:data) { print_chars.join }
|
13
|
+
|
14
|
+
it "should only accept known :base values" do
|
15
|
+
lambda {
|
16
|
+
described_class.new(data, :base => :foo)
|
17
|
+
}.should raise_error(ArgumentError)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should only accept known :endian values" do
|
21
|
+
lambda {
|
22
|
+
described_class.new(data, :endian => :foo)
|
23
|
+
}.should raise_error(ArgumentError)
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "each_word" do
|
27
|
+
let(:data) { 'ABAB' }
|
28
|
+
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
|
+
|
33
|
+
it "should check if the data defines '#each_byte'" do
|
34
|
+
lambda {
|
35
|
+
subject.each_word(Object.new).to_a
|
36
|
+
}.should raise_error(ArgumentError)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should iterate over each byte by default" do
|
40
|
+
subject.each_word(data).to_a.should == bytes
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should allow iterating over custom word-sizes" do
|
44
|
+
dumper = described_class.new(:word_size => 3)
|
45
|
+
|
46
|
+
dumper.each_word(data).to_a.should == custom_words
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should iterate over little-endian words by default" do
|
50
|
+
dumper = described_class.new(:word_size => 2)
|
51
|
+
|
52
|
+
dumper.each_word(data).to_a.should == shorts_le
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should iterate over big-endian words" do
|
56
|
+
dumper = described_class.new(:word_size => 2, :endian => :big)
|
57
|
+
|
58
|
+
dumper.each_word(data).to_a.should == shorts_be
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#each" do
|
63
|
+
it "should yield the parts of each hexdump line to the given block" do
|
64
|
+
lines = []
|
65
|
+
|
66
|
+
subject.each(data) do |index,hex,print|
|
67
|
+
lines << [index, hex, print]
|
68
|
+
end
|
69
|
+
|
70
|
+
lines.length.should == 1
|
71
|
+
lines[0][0].should == 0
|
72
|
+
lines[0][1].should == hex_chars
|
73
|
+
lines[0][2].should == print_chars
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should provide the index within the data for each line" do
|
77
|
+
dumper = described_class.new(:width => 10)
|
78
|
+
indices = []
|
79
|
+
|
80
|
+
dumper.each('A' * 100) do |index,hex,print|
|
81
|
+
indices << index
|
82
|
+
end
|
83
|
+
|
84
|
+
indices.should == [0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should allow configuring the width, in bytes, of each line" do
|
88
|
+
dumper = described_class.new(:width => 10)
|
89
|
+
widths = []
|
90
|
+
|
91
|
+
dumper.each('A' * 100) do |index,hex,print|
|
92
|
+
widths << hex.length
|
93
|
+
end
|
94
|
+
|
95
|
+
widths.should == ([10] * 10)
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should hexdump the remaining bytes" do
|
99
|
+
dumper = described_class.new(:width => 10)
|
100
|
+
chars = (['B'] * 4)
|
101
|
+
string = chars.join
|
102
|
+
leading = ('A' * 100)
|
103
|
+
remainder = nil
|
104
|
+
|
105
|
+
dumper.each(leading + string) do |index,hex,print|
|
106
|
+
remainder = print
|
107
|
+
end
|
108
|
+
|
109
|
+
remainder.should == chars
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should provide the hexadecimal characters for each line" do
|
113
|
+
dumper = described_class.new(:width => 10)
|
114
|
+
chars = []
|
115
|
+
|
116
|
+
dumper.each(data * 100) do |index,hex,print|
|
117
|
+
chars += hex
|
118
|
+
end
|
119
|
+
|
120
|
+
chars.should == (hex_chars * 100)
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should allow printing ASCII characters in place of hex characters" do
|
124
|
+
dumper = described_class.new(:ascii => true)
|
125
|
+
chars = []
|
126
|
+
|
127
|
+
dumper.each(data) do |index,hex,print|
|
128
|
+
chars += hex
|
129
|
+
end
|
130
|
+
|
131
|
+
chars.should == print_chars
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should provide the print characters for each line" do
|
135
|
+
dumper = described_class.new(:width => 10)
|
136
|
+
chars = []
|
137
|
+
|
138
|
+
dumper.each(data * 100) do |index,hex,print|
|
139
|
+
chars += print
|
140
|
+
end
|
141
|
+
|
142
|
+
chars.should == (print_chars * 100)
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should map unprintable characters to '.'" do
|
146
|
+
unprintable = ((0x00..0x1f).map(&:chr) + (0x7f..0xff).map(&:chr)).join
|
147
|
+
chars = []
|
148
|
+
|
149
|
+
subject.each(unprintable) do |index,hex,print|
|
150
|
+
chars += print
|
151
|
+
end
|
152
|
+
|
153
|
+
chars.should == (['.'] * unprintable.length)
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should support dumping bytes in decimal format" do
|
157
|
+
dumper = described_class.new(:base => :decimal)
|
158
|
+
chars = []
|
159
|
+
|
160
|
+
dumper.each(data) do |index,hex,print|
|
161
|
+
chars += hex
|
162
|
+
end
|
163
|
+
|
164
|
+
chars.should == decimal_chars
|
165
|
+
end
|
166
|
+
|
167
|
+
it "should support dumping bytes in octal format" do
|
168
|
+
dumper = described_class.new(:base => :octal)
|
169
|
+
chars = []
|
170
|
+
|
171
|
+
dumper.each(data) do |index,hex,print|
|
172
|
+
chars += hex
|
173
|
+
end
|
174
|
+
|
175
|
+
chars.should == octal_chars
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should support dumping bytes in binary format" do
|
179
|
+
dumper = described_class.new(:base => :binary)
|
180
|
+
chars = []
|
181
|
+
|
182
|
+
dumper.each(data) do |index,hex,print|
|
183
|
+
chars += hex
|
184
|
+
end
|
185
|
+
|
186
|
+
chars.should == binary_chars
|
187
|
+
end
|
188
|
+
|
189
|
+
context ":word_size" do
|
190
|
+
let(:options) { {:word_size => 2, :endian => :little} }
|
191
|
+
|
192
|
+
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
|
+
|
197
|
+
it "should dump words in hexadecimal" do
|
198
|
+
dumper = described_class.new(options)
|
199
|
+
words = []
|
200
|
+
|
201
|
+
dumper.each(data) do |index,hex,print|
|
202
|
+
words += hex
|
203
|
+
end
|
204
|
+
|
205
|
+
words.should == hex_words
|
206
|
+
end
|
207
|
+
|
208
|
+
it "should dump words in decimal" do
|
209
|
+
dumper = described_class.new(options.merge(:base => :decimal))
|
210
|
+
words = []
|
211
|
+
|
212
|
+
dumper.each(data) do |index,dec,print|
|
213
|
+
words += dec
|
214
|
+
end
|
215
|
+
|
216
|
+
words.should == decimal_words
|
217
|
+
end
|
218
|
+
|
219
|
+
it "should dump words in octal" do
|
220
|
+
dumper = described_class.new(options.merge(:base => :octal))
|
221
|
+
words = []
|
222
|
+
|
223
|
+
dumper.each(data) do |index,oct,print|
|
224
|
+
words += oct
|
225
|
+
end
|
226
|
+
|
227
|
+
words.should == octal_words
|
228
|
+
end
|
229
|
+
|
230
|
+
it "should dump words in binary" do
|
231
|
+
dumper = described_class.new(options.merge(:base => :binary))
|
232
|
+
words = []
|
233
|
+
|
234
|
+
dumper.each(data) do |index,bin,print|
|
235
|
+
words += bin
|
236
|
+
end
|
237
|
+
|
238
|
+
words.should == binary_words
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
describe "#dump" do
|
244
|
+
it "should check if the :output supports the '#<<' method" do
|
245
|
+
lambda {
|
246
|
+
subject.dump(data,Object.new)
|
247
|
+
}.should raise_error(ArgumentError)
|
248
|
+
end
|
249
|
+
|
250
|
+
it "should append each line of the hexdump to the output" do
|
251
|
+
lines = []
|
252
|
+
|
253
|
+
subject.dump(data,lines)
|
254
|
+
|
255
|
+
lines.length.should == 1
|
256
|
+
lines[0].should include(hex_chars.join(' '))
|
257
|
+
lines[0].should include(print_chars.join)
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
data/spec/hexdump_spec.rb
CHANGED
@@ -1,159 +1,17 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
|
2
3
|
require 'hexdump'
|
3
4
|
|
4
5
|
describe Hexdump do
|
5
|
-
let(:bytes) { [104, 101, 108, 108, 111] }
|
6
|
-
let(:hex_chars) { ['68', '65', '6c', '6c', '6f'] }
|
7
|
-
let(:decimal_chars) { ['104', '101', '108', '108', '111'] }
|
8
|
-
let(:octal_chars) { ['0150', '0145', '0154', '0154', '0157'] }
|
9
|
-
let(:binary_chars) { ['01101000', '01100101', '01101100', '01101100', '01101111'] }
|
10
|
-
let(:print_chars) { ['h', 'e', 'l', 'l', 'o'] }
|
11
|
-
let(:data) { print_chars.join }
|
12
|
-
|
13
|
-
describe "dump" do
|
14
|
-
it "should check if the data defines '#each_byte'" do
|
15
|
-
lambda {
|
16
|
-
subject.dump(Object.new)
|
17
|
-
}.should raise_error(ArgumentError)
|
18
|
-
end
|
19
|
-
|
20
|
-
it "should check if the :output supports the '#<<' method" do
|
21
|
-
lambda {
|
22
|
-
subject.dump(data, :output => Object.new)
|
23
|
-
}.should raise_error(ArgumentError)
|
24
|
-
end
|
25
|
-
|
26
|
-
it "should append each line of the hexdump to the output" do
|
27
|
-
lines = []
|
28
|
-
subject.dump(data, :output => lines)
|
29
|
-
|
30
|
-
lines.length.should == 1
|
31
|
-
lines[0].should include(hex_chars.join(' '))
|
32
|
-
lines[0].should include(print_chars.join)
|
33
|
-
end
|
34
|
-
|
35
|
-
it "should yield the parts of each hexdump line to the given block" do
|
36
|
-
lines = []
|
37
|
-
|
38
|
-
subject.dump(data) do |index,hex,print|
|
39
|
-
lines << [index, hex, print]
|
40
|
-
end
|
41
|
-
|
42
|
-
lines.length.should == 1
|
43
|
-
lines[0][0].should == 0
|
44
|
-
lines[0][1].should == hex_chars
|
45
|
-
lines[0][2].should == print_chars
|
46
|
-
end
|
47
|
-
|
48
|
-
it "should provide the index within the data for each line" do
|
49
|
-
indices = []
|
50
|
-
|
51
|
-
subject.dump('A' * 100, :width => 10) do |index,hex,print|
|
52
|
-
indices << index
|
53
|
-
end
|
54
|
-
|
55
|
-
indices.should == [0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
|
56
|
-
end
|
57
|
-
|
58
|
-
it "should allow configuring the width, in bytes, of each line" do
|
59
|
-
widths = []
|
60
|
-
|
61
|
-
subject.dump('A' * 100, :width => 10) do |index,hex,print|
|
62
|
-
widths << hex.length
|
63
|
-
end
|
64
|
-
|
65
|
-
widths.should == ([10] * 10)
|
66
|
-
end
|
67
|
-
|
68
|
-
it "should hexdump the remaining bytes" do
|
69
|
-
chars = (['B'] * 4)
|
70
|
-
string = chars.join
|
71
|
-
leading = ('A' * 100)
|
72
|
-
remainder = nil
|
73
|
-
|
74
|
-
subject.dump(leading + string, :width => 10) do |index,hex,print|
|
75
|
-
remainder = print
|
76
|
-
end
|
77
|
-
|
78
|
-
remainder.should == chars
|
79
|
-
end
|
80
|
-
|
81
|
-
it "should provide the hexadecimal characters for each line" do
|
82
|
-
chars = []
|
83
|
-
|
84
|
-
subject.dump(data * 100, :width => 10) do |index,hex,print|
|
85
|
-
chars += hex
|
86
|
-
end
|
87
|
-
|
88
|
-
chars.should == (hex_chars * 100)
|
89
|
-
end
|
90
|
-
|
91
|
-
it "should allow printing ASCII characters in place of hex characters" do
|
92
|
-
chars = []
|
93
|
-
|
94
|
-
subject.dump(data, :ascii => true) do |index,hex,print|
|
95
|
-
chars += hex
|
96
|
-
end
|
97
|
-
|
98
|
-
chars.should == print_chars
|
99
|
-
end
|
100
|
-
|
101
|
-
it "should provide the print characters for each line" do
|
102
|
-
chars = []
|
103
|
-
|
104
|
-
subject.dump(data * 100, :width => 10) do |index,hex,print|
|
105
|
-
chars += print
|
106
|
-
end
|
107
|
-
|
108
|
-
chars.should == (print_chars * 100)
|
109
|
-
end
|
110
|
-
|
111
|
-
it "should map unprintable characters to '.'" do
|
112
|
-
unprintable = ((0x00..0x1f).map(&:chr) + (0x7f..0xff).map(&:chr)).join
|
113
|
-
chars = []
|
114
|
-
|
115
|
-
subject.dump(unprintable) do |index,hex,print|
|
116
|
-
chars += print
|
117
|
-
end
|
118
|
-
|
119
|
-
chars.should == (['.'] * unprintable.length)
|
120
|
-
end
|
121
|
-
|
122
|
-
it "should support dumping bytes in decimal format" do
|
123
|
-
chars = []
|
124
|
-
|
125
|
-
subject.dump(data, :base => :decimal) do |index,hex,print|
|
126
|
-
chars += hex
|
127
|
-
end
|
128
|
-
|
129
|
-
chars.should == decimal_chars
|
130
|
-
end
|
131
|
-
|
132
|
-
it "should support dumping bytes in octal format" do
|
133
|
-
chars = []
|
134
|
-
|
135
|
-
subject.dump(data, :base => :octal) do |index,hex,print|
|
136
|
-
chars += hex
|
137
|
-
end
|
138
|
-
|
139
|
-
chars.should == octal_chars
|
140
|
-
end
|
141
|
-
|
142
|
-
it "should support dumping bytes in binary format" do
|
143
|
-
chars = []
|
144
|
-
|
145
|
-
subject.dump(data, :base => :binary) do |index,hex,print|
|
146
|
-
chars += hex
|
147
|
-
end
|
148
|
-
|
149
|
-
chars.should == binary_chars
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
6
|
describe "#hexdump" do
|
7
|
+
let(:bytes) { [104, 101, 108, 108, 111] }
|
8
|
+
let(:hex_chars) { ['68', '65', '6c', '6c', '6f'] }
|
9
|
+
|
154
10
|
subject do
|
155
11
|
obj = Object.new.extend(Hexdump)
|
156
|
-
|
12
|
+
|
13
|
+
stub = obj.stub!(:each_byte)
|
14
|
+
bytes.each { |b| stub = stub.and_yield(b) }
|
157
15
|
|
158
16
|
obj
|
159
17
|
end
|
metadata
CHANGED
@@ -2,16 +2,15 @@
|
|
2
2
|
name: hexdump
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.1
|
5
|
+
version: 0.2.1
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
|
-
-
|
8
|
+
- Postmodern
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-
|
14
|
-
default_executable:
|
13
|
+
date: 2011-06-11 00:00:00 Z
|
15
14
|
dependencies:
|
16
15
|
- !ruby/object:Gem::Dependency
|
17
16
|
name: ore-tasks
|
@@ -70,16 +69,17 @@ files:
|
|
70
69
|
- gemspec.yml
|
71
70
|
- hexdump.gemspec
|
72
71
|
- lib/hexdump.rb
|
72
|
+
- lib/hexdump/dumper.rb
|
73
73
|
- lib/hexdump/extensions.rb
|
74
74
|
- lib/hexdump/extensions/file.rb
|
75
75
|
- lib/hexdump/extensions/io.rb
|
76
76
|
- lib/hexdump/extensions/string.rb
|
77
77
|
- lib/hexdump/extensions/string_io.rb
|
78
78
|
- lib/hexdump/hexdump.rb
|
79
|
+
- spec/dumper_spec.rb
|
79
80
|
- spec/extensions_spec.rb
|
80
81
|
- spec/hexdump_spec.rb
|
81
82
|
- spec/spec_helper.rb
|
82
|
-
has_rdoc: yard
|
83
83
|
homepage: http://github.com/postmodern/hexdump
|
84
84
|
licenses:
|
85
85
|
- MIT
|
@@ -93,7 +93,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
93
93
|
requirements:
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
96
|
+
version: 1.8.7
|
97
97
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
98
|
none: false
|
99
99
|
requirements:
|
@@ -103,10 +103,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
103
103
|
requirements: []
|
104
104
|
|
105
105
|
rubyforge_project: hexdump
|
106
|
-
rubygems_version: 1.
|
106
|
+
rubygems_version: 1.8.1
|
107
107
|
signing_key:
|
108
108
|
specification_version: 3
|
109
|
-
summary:
|
109
|
+
summary: Hexdump Strings and IO objects.
|
110
110
|
test_files:
|
111
|
+
- spec/dumper_spec.rb
|
111
112
|
- spec/hexdump_spec.rb
|
112
113
|
- spec/extensions_spec.rb
|