hexdump 0.3.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +5 -6
- data/.gitignore +1 -0
- data/.yardopts +1 -1
- data/ChangeLog.md +79 -6
- data/Gemfile +3 -0
- data/LICENSE.txt +1 -1
- data/README.md +500 -137
- data/benchmark.rb +29 -22
- data/gemspec.yml +2 -1
- data/hexdump.gemspec +1 -4
- data/lib/hexdump/chars.rb +46 -0
- data/lib/hexdump/core_ext/file.rb +68 -6
- data/lib/hexdump/core_ext/io.rb +2 -2
- data/lib/hexdump/core_ext/kernel.rb +5 -0
- data/lib/hexdump/core_ext/string.rb +2 -2
- data/lib/hexdump/core_ext/string_io.rb +2 -2
- data/lib/hexdump/core_ext.rb +5 -4
- data/lib/hexdump/format_string.rb +43 -0
- data/lib/hexdump/hexdump.rb +766 -75
- data/lib/hexdump/mixin.rb +192 -0
- data/lib/hexdump/module_methods.rb +132 -0
- data/lib/hexdump/numeric/binary.rb +55 -0
- data/lib/hexdump/numeric/char_or_int.rb +95 -0
- data/lib/hexdump/numeric/decimal.rb +56 -0
- data/lib/hexdump/numeric/exceptions.rb +11 -0
- data/lib/hexdump/numeric/hexadecimal.rb +59 -0
- data/lib/hexdump/numeric/octal.rb +55 -0
- data/lib/hexdump/numeric.rb +5 -0
- data/lib/hexdump/reader.rb +313 -0
- data/lib/hexdump/theme/ansi.rb +82 -0
- data/lib/hexdump/theme/rule.rb +159 -0
- data/lib/hexdump/theme.rb +61 -0
- data/lib/hexdump/type.rb +233 -0
- data/lib/hexdump/types.rb +108 -0
- data/lib/hexdump/version.rb +1 -1
- data/lib/hexdump.rb +14 -3
- data/spec/chars_spec.rb +76 -0
- data/spec/core_ext_spec.rb +10 -6
- data/spec/format_string_spec.rb +22 -0
- data/spec/hexdump_class_spec.rb +1708 -0
- data/spec/hexdump_module_spec.rb +23 -0
- data/spec/mixin_spec.rb +37 -0
- data/spec/numeric/binary_spec.rb +239 -0
- data/spec/numeric/char_or_int_spec.rb +210 -0
- data/spec/numeric/decimal_spec.rb +317 -0
- data/spec/numeric/hexadecimal_spec.rb +320 -0
- data/spec/numeric/octal_spec.rb +239 -0
- data/spec/reader_spec.rb +866 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/theme/ansi_spec.rb +242 -0
- data/spec/theme/rule_spec.rb +199 -0
- data/spec/theme_spec.rb +94 -0
- data/spec/type_spec.rb +317 -0
- data/spec/types_spec.rb +904 -0
- metadata +42 -12
- data/.gemtest +0 -0
- data/lib/hexdump/dumper.rb +0 -419
- data/lib/hexdump/extensions.rb +0 -2
- data/spec/dumper_spec.rb +0 -329
- data/spec/hexdump_spec.rb +0 -30
data/lib/hexdump/type.rb
ADDED
@@ -0,0 +1,233 @@
|
|
1
|
+
module Hexdump
|
2
|
+
#
|
3
|
+
# @api private
|
4
|
+
#
|
5
|
+
# @since 1.0.0
|
6
|
+
#
|
7
|
+
class Type
|
8
|
+
|
9
|
+
# The endian-ness of the type.
|
10
|
+
#
|
11
|
+
# @return [:little, :big, nil]
|
12
|
+
attr_reader :endian
|
13
|
+
|
14
|
+
# The size in bytes of the type.
|
15
|
+
#
|
16
|
+
# @return [1, 2, 4, 8]
|
17
|
+
attr_reader :size
|
18
|
+
|
19
|
+
#
|
20
|
+
# Initializes the type.
|
21
|
+
#
|
22
|
+
# @param [1, 2, 4, 8] size
|
23
|
+
# The type's size in bytes.
|
24
|
+
#
|
25
|
+
# @param [:little, :big, nil] endian
|
26
|
+
# The endianness of the type. `nil` indicates the type has no endianness.
|
27
|
+
#
|
28
|
+
# @param [Boolean] signed
|
29
|
+
# Indicates whether the type is signed or unsigned.
|
30
|
+
#
|
31
|
+
# @raise [ArgumentError]
|
32
|
+
# Invalid `endian:` or `size:` values.
|
33
|
+
#
|
34
|
+
def initialize(size: , endian: nil, signed: )
|
35
|
+
@endian = endian
|
36
|
+
@size = size
|
37
|
+
@signed = signed
|
38
|
+
end
|
39
|
+
|
40
|
+
#
|
41
|
+
# Whether the type is signed.
|
42
|
+
#
|
43
|
+
# @return [Boolean]
|
44
|
+
#
|
45
|
+
def signed?
|
46
|
+
@signed
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# Whether the type is unsigned.
|
51
|
+
#
|
52
|
+
# @return [Boolean]
|
53
|
+
#
|
54
|
+
def unsigned?
|
55
|
+
!@signed
|
56
|
+
end
|
57
|
+
|
58
|
+
# The native endian-ness.
|
59
|
+
NATIVE_ENDIAN = if [0x1].pack('I') == [0x1].pack('N')
|
60
|
+
:big
|
61
|
+
else
|
62
|
+
:little
|
63
|
+
end
|
64
|
+
|
65
|
+
#
|
66
|
+
# Represents a signed integer type.
|
67
|
+
#
|
68
|
+
class Int < self
|
69
|
+
|
70
|
+
#
|
71
|
+
# Initializes the int type.
|
72
|
+
#
|
73
|
+
# @param [:little, :big] endian (NATIVE_ENDIAN)
|
74
|
+
# The endian-ness of the int type.
|
75
|
+
#
|
76
|
+
def initialize(endian: NATIVE_ENDIAN, **kwargs)
|
77
|
+
super(signed: true, endian: endian, **kwargs)
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
class Int8 < Int
|
83
|
+
|
84
|
+
#
|
85
|
+
# @see Int#initialize
|
86
|
+
#
|
87
|
+
def initialize(**kwargs)
|
88
|
+
super(size: 1, endian: nil, **kwargs)
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
class Int16 < Int
|
94
|
+
|
95
|
+
#
|
96
|
+
# @see Int#initialize
|
97
|
+
#
|
98
|
+
def initialize(**kwargs)
|
99
|
+
super(size: 2, **kwargs)
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
class Int32 < Int
|
105
|
+
|
106
|
+
#
|
107
|
+
# @see Int#initialize
|
108
|
+
#
|
109
|
+
def initialize(**kwargs)
|
110
|
+
super(size: 4, **kwargs)
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
class Int64 < Int
|
116
|
+
|
117
|
+
#
|
118
|
+
# @see Int#initialize
|
119
|
+
#
|
120
|
+
def initialize(**kwargs)
|
121
|
+
super(size: 8, **kwargs)
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
#
|
127
|
+
# Represents a unsigned integer type.
|
128
|
+
#
|
129
|
+
class UInt < self
|
130
|
+
|
131
|
+
#
|
132
|
+
# Initializes the uint type.
|
133
|
+
#
|
134
|
+
# @param [:little, :big] endian (NATIVE_ENDIAN)
|
135
|
+
# The endian-ness of the uint type.
|
136
|
+
#
|
137
|
+
def initialize(endian: NATIVE_ENDIAN, **kwargs)
|
138
|
+
super(signed: false, endian: endian, **kwargs)
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
class UInt8 < UInt
|
144
|
+
|
145
|
+
#
|
146
|
+
# @see UInt#initialize
|
147
|
+
#
|
148
|
+
def initialize(**kwargs)
|
149
|
+
super(size: 1, endian: nil, **kwargs)
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
class UInt16 < UInt
|
155
|
+
|
156
|
+
#
|
157
|
+
# @see UInt#initialize
|
158
|
+
#
|
159
|
+
def initialize(**kwargs)
|
160
|
+
super(size: 2, **kwargs)
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
class UInt32 < UInt
|
166
|
+
|
167
|
+
#
|
168
|
+
# @see UInt#initialize
|
169
|
+
#
|
170
|
+
def initialize(**kwargs)
|
171
|
+
super(size: 4, **kwargs)
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
class UInt64 < UInt
|
177
|
+
|
178
|
+
#
|
179
|
+
# @see UInt#initialize
|
180
|
+
#
|
181
|
+
def initialize(**kwargs)
|
182
|
+
super(size: 8, **kwargs)
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
186
|
+
|
187
|
+
#
|
188
|
+
# Represents a single-byte character.
|
189
|
+
#
|
190
|
+
class Char < Int8
|
191
|
+
end
|
192
|
+
|
193
|
+
#
|
194
|
+
# Represents a single-byte unsigned character.
|
195
|
+
#
|
196
|
+
class UChar < UInt8
|
197
|
+
end
|
198
|
+
|
199
|
+
#
|
200
|
+
# Represents a floating point type.
|
201
|
+
#
|
202
|
+
class Float < self
|
203
|
+
|
204
|
+
#
|
205
|
+
# Initializes the float type.
|
206
|
+
#
|
207
|
+
# @param [:little, :big] endian (NATIVE_ENDIAN)
|
208
|
+
# The endian-ness of the float type.
|
209
|
+
#
|
210
|
+
def initialize(endian: NATIVE_ENDIAN, **kwargs)
|
211
|
+
super(signed: true, endian: endian, **kwargs)
|
212
|
+
end
|
213
|
+
|
214
|
+
end
|
215
|
+
|
216
|
+
class Float32 < Float
|
217
|
+
|
218
|
+
def initialize(**kwargs)
|
219
|
+
super(size: 4, **kwargs)
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
223
|
+
|
224
|
+
class Float64 < Float
|
225
|
+
|
226
|
+
def initialize(**kwargs)
|
227
|
+
super(size: 8, **kwargs)
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
231
|
+
|
232
|
+
end
|
233
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require_relative 'type'
|
2
|
+
|
3
|
+
module Hexdump
|
4
|
+
#
|
5
|
+
# @api private
|
6
|
+
#
|
7
|
+
# @since 1.0.0
|
8
|
+
#
|
9
|
+
TYPES = {
|
10
|
+
char: Type::Char.new,
|
11
|
+
uchar: Type::UChar.new,
|
12
|
+
|
13
|
+
int8: Type::Int8.new,
|
14
|
+
uint8: Type::UInt8.new,
|
15
|
+
|
16
|
+
int16: Type::Int16.new,
|
17
|
+
int16_le: Type::Int16.new(endian: :little),
|
18
|
+
int16_be: Type::Int16.new(endian: :big),
|
19
|
+
int16_ne: Type::Int16.new(endian: :big),
|
20
|
+
|
21
|
+
uint16: Type::UInt16.new,
|
22
|
+
uint16_le: Type::UInt16.new(endian: :little),
|
23
|
+
uint16_be: Type::UInt16.new(endian: :big),
|
24
|
+
uint16_ne: Type::UInt16.new(endian: :big),
|
25
|
+
|
26
|
+
int32: Type::Int32.new,
|
27
|
+
int32_le: Type::Int32.new(endian: :little),
|
28
|
+
int32_be: Type::Int32.new(endian: :big),
|
29
|
+
int32_ne: Type::Int32.new(endian: :big),
|
30
|
+
|
31
|
+
uint32: Type::UInt32.new,
|
32
|
+
uint32_le: Type::UInt32.new(endian: :little),
|
33
|
+
uint32_be: Type::UInt32.new(endian: :big),
|
34
|
+
uint32_ne: Type::UInt32.new(endian: :big),
|
35
|
+
|
36
|
+
int64: Type::Int64.new,
|
37
|
+
int64_le: Type::Int64.new(endian: :little),
|
38
|
+
int64_be: Type::Int64.new(endian: :big),
|
39
|
+
int64_ne: Type::Int64.new(endian: :big),
|
40
|
+
|
41
|
+
uint64: Type::UInt64.new,
|
42
|
+
uint64_le: Type::UInt64.new(endian: :little),
|
43
|
+
uint64_be: Type::UInt64.new(endian: :big),
|
44
|
+
uint64_ne: Type::UInt64.new(endian: :big),
|
45
|
+
|
46
|
+
float32: Type::Float32.new,
|
47
|
+
float32_le: Type::Float32.new(endian: :little),
|
48
|
+
float32_be: Type::Float32.new(endian: :big),
|
49
|
+
float32_ne: Type::Float32.new(endian: :big),
|
50
|
+
|
51
|
+
float64: Type::Float64.new,
|
52
|
+
float64_le: Type::Float64.new(endian: :little),
|
53
|
+
float64_be: Type::Float64.new(endian: :big),
|
54
|
+
float64_ne: Type::Float64.new(endian: :big),
|
55
|
+
}
|
56
|
+
|
57
|
+
TYPES[:byte] = TYPES[:uint8]
|
58
|
+
|
59
|
+
TYPES[:short] = TYPES[:int16]
|
60
|
+
TYPES[:short_le] = TYPES[:int16_le]
|
61
|
+
TYPES[:short_be] = TYPES[:int16_be]
|
62
|
+
TYPES[:short_ne] = TYPES[:int16_ne]
|
63
|
+
|
64
|
+
TYPES[:ushort] = TYPES[:uint16]
|
65
|
+
TYPES[:ushort_le] = TYPES[:uint16_le]
|
66
|
+
TYPES[:ushort_be] = TYPES[:uint16_be]
|
67
|
+
TYPES[:ushort_ne] = TYPES[:uint16_ne]
|
68
|
+
|
69
|
+
TYPES[:int] = TYPES[:int32]
|
70
|
+
TYPES[:int_le] = TYPES[:int32_le]
|
71
|
+
TYPES[:int_be] = TYPES[:int32_be]
|
72
|
+
TYPES[:int_ne] = TYPES[:int32_ne]
|
73
|
+
|
74
|
+
TYPES[:uint] = TYPES[:uint32]
|
75
|
+
TYPES[:uint_le] = TYPES[:uint32_le]
|
76
|
+
TYPES[:uint_be] = TYPES[:uint32_be]
|
77
|
+
TYPES[:uint_ne] = TYPES[:uint32_ne]
|
78
|
+
|
79
|
+
TYPES[:long] = TYPES[:int64]
|
80
|
+
TYPES[:long_le] = TYPES[:int64_le]
|
81
|
+
TYPES[:long_be] = TYPES[:int64_be]
|
82
|
+
TYPES[:long_ne] = TYPES[:int64_ne]
|
83
|
+
|
84
|
+
TYPES[:ulong] = TYPES[:uint64]
|
85
|
+
TYPES[:ulong_le] = TYPES[:uint64_le]
|
86
|
+
TYPES[:ulong_be] = TYPES[:uint64_be]
|
87
|
+
TYPES[:ulong_ne] = TYPES[:uint64_ne]
|
88
|
+
|
89
|
+
TYPES[:long_long] = TYPES[:int64]
|
90
|
+
TYPES[:long_long_le] = TYPES[:int64_le]
|
91
|
+
TYPES[:long_long_be] = TYPES[:int64_be]
|
92
|
+
TYPES[:long_long_ne] = TYPES[:int64_ne]
|
93
|
+
|
94
|
+
TYPES[:ulong_long] = TYPES[:uint64]
|
95
|
+
TYPES[:ulong_long_le] = TYPES[:uint64_le]
|
96
|
+
TYPES[:ulong_long_be] = TYPES[:uint64_be]
|
97
|
+
TYPES[:ulong_long_ne] = TYPES[:uint64_ne]
|
98
|
+
|
99
|
+
TYPES[:float] = TYPES[:float32]
|
100
|
+
TYPES[:float_le] = TYPES[:float32_le]
|
101
|
+
TYPES[:float_be] = TYPES[:float32_be]
|
102
|
+
TYPES[:float_ne] = TYPES[:float32_ne]
|
103
|
+
|
104
|
+
TYPES[:double] = TYPES[:float64]
|
105
|
+
TYPES[:double_le] = TYPES[:float64_le]
|
106
|
+
TYPES[:double_be] = TYPES[:float64_be]
|
107
|
+
TYPES[:double_ne] = TYPES[:float64_ne]
|
108
|
+
end
|
data/lib/hexdump/version.rb
CHANGED
data/lib/hexdump.rb
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
require_relative 'hexdump/module_methods'
|
2
|
+
require_relative 'hexdump/core_ext'
|
3
|
+
require_relative 'hexdump/version'
|
4
|
+
|
5
|
+
module Hexdump
|
6
|
+
extend ModuleMethods
|
7
|
+
|
8
|
+
#
|
9
|
+
# @see hexdump
|
10
|
+
#
|
11
|
+
def self.dump(data,**kwargs)
|
12
|
+
hexdump(data,**kwargs)
|
13
|
+
end
|
14
|
+
end
|
data/spec/chars_spec.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'hexdump/chars'
|
3
|
+
|
4
|
+
describe Hexdump::Chars do
|
5
|
+
describe "#initialize" do
|
6
|
+
it "must default #encoding to nil" do
|
7
|
+
expect(subject.encoding).to be(nil)
|
8
|
+
end
|
9
|
+
|
10
|
+
context "when given nil" do
|
11
|
+
subject { described_class.new(nil) }
|
12
|
+
|
13
|
+
it "must set #encoding to nil" do
|
14
|
+
expect(subject.encoding).to be(nil)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context "when given an Encoding object" do
|
19
|
+
let(:encoding) { Encoding::UTF_8 }
|
20
|
+
|
21
|
+
subject { described_class.new(encoding) }
|
22
|
+
|
23
|
+
it "must set #encoding to the Encoding object" do
|
24
|
+
expect(subject.encoding).to be(encoding)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#scrub" do
|
30
|
+
context "when the string only contains printable ASCII characters" do
|
31
|
+
let(:string) { "hello" }
|
32
|
+
|
33
|
+
it "must return the string unchanged" do
|
34
|
+
expect(subject.scrub(string)).to eq(string)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "when the string contains unprintable ASCII characters" do
|
39
|
+
let(:ascii) { (0..255).map(&:chr).join }
|
40
|
+
|
41
|
+
it "must replace non-printable ASCII characters with a '.'" do
|
42
|
+
expect(subject.scrub(ascii)).to eq(ascii.gsub(/[^\x20-\x7e]/,'.'))
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context "#encoding is set" do
|
47
|
+
let(:encoding) { Encoding::UTF_8 }
|
48
|
+
let(:string) { "hello" }
|
49
|
+
|
50
|
+
subject { described_class.new(encoding) }
|
51
|
+
|
52
|
+
it "must convert the string to the #encoding" do
|
53
|
+
expect((subject.scrub(string)).encoding).to eq(encoding)
|
54
|
+
end
|
55
|
+
|
56
|
+
context "when the string contains an invalid byte-sequence" do
|
57
|
+
let(:invalid_bytes) { "\x80\x81" }
|
58
|
+
let(:string) { "A#{invalid_bytes}B" }
|
59
|
+
|
60
|
+
it "must replace any invalid byte sequencnes with a '.'" do
|
61
|
+
expect(subject.scrub(string)).to eq("A..B")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "when the string contains unprintable characters" do
|
66
|
+
let(:codepoint) { 888 }
|
67
|
+
let(:char) { codepoint.chr(encoding) }
|
68
|
+
let(:string) { "A#{char}B" }
|
69
|
+
|
70
|
+
it "must replace unprintable characters with a '.'" do
|
71
|
+
expect(subject.scrub(string)).to eq("A.B")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/spec/core_ext_spec.rb
CHANGED
@@ -2,19 +2,23 @@ require 'spec_helper'
|
|
2
2
|
require 'hexdump/core_ext'
|
3
3
|
|
4
4
|
describe "Hexdump core_ext" do
|
5
|
-
it "should include Hexdump into String" do
|
6
|
-
expect(String).to include(Hexdump)
|
5
|
+
it "should include Hexdump::Mixin into String" do
|
6
|
+
expect(String).to include(Hexdump::Mixin)
|
7
7
|
end
|
8
8
|
|
9
|
-
it "should include Hexdump into StringIO" do
|
10
|
-
expect(StringIO).to include(Hexdump)
|
9
|
+
it "should include Hexdump::Mixin into StringIO" do
|
10
|
+
expect(StringIO).to include(Hexdump::Mixin)
|
11
11
|
end
|
12
12
|
|
13
|
-
it "should include Hexdump into IO" do
|
14
|
-
expect(IO).to include(Hexdump)
|
13
|
+
it "should include Hexdump::Mixin into IO" do
|
14
|
+
expect(IO).to include(Hexdump::Mixin)
|
15
15
|
end
|
16
16
|
|
17
17
|
it "should define File.hexdump" do
|
18
18
|
expect(File).to respond_to(:hexdump)
|
19
19
|
end
|
20
|
+
|
21
|
+
it "should include Hexdump::ModuleMethods into ::Kernel" do
|
22
|
+
expect(Kernel).to include(Hexdump::ModuleMethods)
|
23
|
+
end
|
20
24
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'hexdump/format_string'
|
3
|
+
|
4
|
+
describe Hexdump::FormatString do
|
5
|
+
let(:fmt) { "%x" }
|
6
|
+
|
7
|
+
subject { described_class.new(fmt) }
|
8
|
+
|
9
|
+
describe "#%" do
|
10
|
+
let(:value) { 255 }
|
11
|
+
|
12
|
+
it "must format the given value" do
|
13
|
+
expect(subject % value).to eq(fmt % value)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#to_s" do
|
18
|
+
it "must return the format string" do
|
19
|
+
expect(subject.to_s).to eq(fmt)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|