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.
- checksums.yaml +4 -4
- data/ChangeLog.md +68 -2
- data/Gemfile +1 -0
- data/README.md +486 -135
- data/benchmark.rb +29 -22
- 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 +7 -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 +1 -0
- data/lib/hexdump/format_string.rb +43 -0
- data/lib/hexdump/hexdump.rb +768 -75
- data/lib/hexdump/mixin.rb +198 -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 +90 -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 +314 -0
- data/lib/hexdump/theme/ansi.rb +81 -0
- data/lib/hexdump/theme/rule.rb +159 -0
- data/lib/hexdump/theme.rb +61 -0
- data/lib/hexdump/type.rb +232 -0
- data/lib/hexdump/types.rb +108 -0
- data/lib/hexdump/version.rb +1 -1
- data/lib/hexdump.rb +12 -1
- data/spec/chars_spec.rb +76 -0
- data/spec/core_ext_spec.rb +10 -6
- 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 +863 -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 +39 -10
- data/.gemtest +0 -0
- data/lib/hexdump/dumper.rb +0 -419
- data/spec/dumper_spec.rb +0 -329
- data/spec/hexdump_spec.rb +0 -30
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'hexdump/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[:int32]
|
80
|
+
TYPES[:long_le] = TYPES[:int32_le]
|
81
|
+
TYPES[:long_be] = TYPES[:int32_be]
|
82
|
+
TYPES[:long_ne] = TYPES[:int32_ne]
|
83
|
+
|
84
|
+
TYPES[:ulong] = TYPES[:uint32]
|
85
|
+
TYPES[:ulong_le] = TYPES[:uint32_le]
|
86
|
+
TYPES[:ulong_be] = TYPES[:uint32_be]
|
87
|
+
TYPES[:ulong_ne] = TYPES[:uint32_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
|
-
require 'hexdump/
|
1
|
+
require 'hexdump/module_methods'
|
2
2
|
require 'hexdump/core_ext'
|
3
3
|
require '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,1708 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'hexdump/hexdump'
|
3
|
+
|
4
|
+
describe Hexdump::Hexdump do
|
5
|
+
describe "#initialize" do
|
6
|
+
it "must default type to :byte" do
|
7
|
+
expect(subject.type).to be(Hexdump::TYPES[:byte])
|
8
|
+
end
|
9
|
+
|
10
|
+
it "must default #columns to 16" do
|
11
|
+
expect(subject.columns).to eq(16)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "must default #group_columns to nil" do
|
15
|
+
expect(subject.group_columns).to be(nil)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "must default #base to 16" do
|
19
|
+
expect(subject.base).to eq(16)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "must initialize #index to Hexdump::Numeric::Hexdecimal by default" do
|
23
|
+
expect(subject.index).to be_kind_of(Hexdump::Numeric::Hexadecimal)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "must default #index_offset to nil" do
|
27
|
+
expect(subject.index_offset).to be(nil)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "must initialize #numeric to a Hexdump::Numeric::Hexadecimal" do
|
31
|
+
expect(subject.numeric).to be_kind_of(Hexdump::Numeric::Hexadecimal)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "must initialize the #reader with zero-padding disabled by default" do
|
35
|
+
expect(subject.reader.zero_pad?).to be(false)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "must initialize #chars by default" do
|
39
|
+
expect(subject.chars).to be_kind_of(Hexdump::Chars)
|
40
|
+
end
|
41
|
+
|
42
|
+
context "when given the chars_column: true" do
|
43
|
+
subject { described_class.new(chars_column: true) }
|
44
|
+
|
45
|
+
it "must set #chars_column to true" do
|
46
|
+
expect(subject.chars_column).to be(true)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "must initialize #chars with #encoding" do
|
50
|
+
expect(subject.chars).to be_kind_of(Hexdump::Chars)
|
51
|
+
end
|
52
|
+
|
53
|
+
context "and encoding: keyword" do
|
54
|
+
let(:encoding) { :utf8 }
|
55
|
+
|
56
|
+
subject { described_class.new(chars_column: true, encoding: encoding) }
|
57
|
+
|
58
|
+
it "must set #chars.encoding to the given encoding: value" do
|
59
|
+
expect(subject.chars.encoding).to be(subject.encoding)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "when given chars_column: false" do
|
65
|
+
subject { described_class.new(chars_column: false) }
|
66
|
+
|
67
|
+
it "must set #chars_column to false" do
|
68
|
+
expect(subject.chars_column).to be(false)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "must set #chars to nil" do
|
72
|
+
expect(subject.chars).to be(nil)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "when given the encoding: keyword" do
|
77
|
+
subject { described_class.new(encoding: :utf8) }
|
78
|
+
|
79
|
+
it "must set #encoding" do
|
80
|
+
expect(subject.encoding).to eq(Encoding::UTF_8)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context "when given the offset: keyword" do
|
85
|
+
let(:offset) { 4 }
|
86
|
+
|
87
|
+
subject { described_class.new(offset: offset) }
|
88
|
+
|
89
|
+
it "must initialize the #reader with the offset: keyword" do
|
90
|
+
expect(subject.reader.offset).to eq(offset)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "must also set #index_offset" do
|
94
|
+
expect(subject.index_offset).to eq(offset)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context "when given the length: keyword" do
|
99
|
+
let(:length) { 1024 }
|
100
|
+
|
101
|
+
subject { described_class.new(length: length) }
|
102
|
+
|
103
|
+
it "must initialize the #reader with the length: keyword" do
|
104
|
+
expect(subject.reader.length).to eq(length)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context "when given zero_pad: true" do
|
109
|
+
subject { described_class.new(zero_pad: true) }
|
110
|
+
|
111
|
+
it "must initialize the #reader with zero-padding enabled" do
|
112
|
+
expect(subject.reader.zero_pad?).to be(true)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
context "when given a type: keyword" do
|
117
|
+
let(:type) { :uint16_le }
|
118
|
+
|
119
|
+
subject { described_class.new(type: type) }
|
120
|
+
|
121
|
+
it "must look up the given type in Hexdump::TYPES" do
|
122
|
+
expect(subject.type).to be(Hexdump::TYPES[type])
|
123
|
+
end
|
124
|
+
|
125
|
+
it "must divide the number of columns by the size of the type" do
|
126
|
+
expect(subject.columns).to eq(16 / Hexdump::TYPES[type].size)
|
127
|
+
end
|
128
|
+
|
129
|
+
context "when initialized with a type: :char" do
|
130
|
+
subject { described_class.new(type: :char) }
|
131
|
+
|
132
|
+
it "must set #type to Hexdump::Type::Char" do
|
133
|
+
expect(subject.type).to be_kind_of(Hexdump::Type::Char)
|
134
|
+
end
|
135
|
+
|
136
|
+
it "must initialize #numeric to Hexdump::Numeric::CharOrInt" do
|
137
|
+
expect(subject.numeric).to be_kind_of(Hexdump::Numeric::CharOrInt)
|
138
|
+
end
|
139
|
+
|
140
|
+
it "must disable #chars" do
|
141
|
+
expect(subject.chars).to be(nil)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context "when with type: is :uchar" do
|
146
|
+
let(:type) { :uchar }
|
147
|
+
|
148
|
+
it "must initialize #numeric to Hexdump::Numeric::CharOrInt" do
|
149
|
+
expect(subject.numeric).to be_kind_of(Hexdump::Numeric::CharOrInt)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
context "when the type: is :byte" do
|
154
|
+
let(:type) { :byte }
|
155
|
+
|
156
|
+
it "must set #type to Hexdump::Type::UInt8" do
|
157
|
+
expect(subject.type).to be_kind_of(Hexdump::Type::UInt8)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
context "when the type: is :uint8" do
|
162
|
+
let(:type) { :uint8 }
|
163
|
+
|
164
|
+
it "must set #type to Hexdump::Type::UInt8" do
|
165
|
+
expect(subject.type).to be_kind_of(Hexdump::Type::UInt8)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
context "when the type: is :int8" do
|
170
|
+
let(:type) { :int8 }
|
171
|
+
|
172
|
+
it "must set #type to Hexdump::Type::Int8" do
|
173
|
+
expect(subject.type).to be_kind_of(Hexdump::Type::Int8)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
context "when the type: is :uint16" do
|
178
|
+
let(:type) { :uint16 }
|
179
|
+
|
180
|
+
it "must set #type to Hexdump::Type::UInt16" do
|
181
|
+
expect(subject.type).to be_kind_of(Hexdump::Type::UInt16)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
context "when the type: is :int16" do
|
186
|
+
let(:type) { :int16 }
|
187
|
+
|
188
|
+
it "must set #type to Hexdump::Type::Int16" do
|
189
|
+
expect(subject.type).to be_kind_of(Hexdump::Type::Int16)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
context "when the type: is :uint32" do
|
194
|
+
let(:type) { :uint32 }
|
195
|
+
|
196
|
+
it "must set #type to Hexdump::Type::UInt32" do
|
197
|
+
expect(subject.type).to be_kind_of(Hexdump::Type::UInt32)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
context "when the type: is :int32" do
|
202
|
+
let(:type) { :int32 }
|
203
|
+
|
204
|
+
it "must set #type to Hexdump::Type::Int32" do
|
205
|
+
expect(subject.type).to be_kind_of(Hexdump::Type::Int32)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
context "when the type: is :uint64" do
|
210
|
+
let(:type) { :uint64 }
|
211
|
+
|
212
|
+
it "must set #type to Hexdump::Type::UInt64" do
|
213
|
+
expect(subject.type).to be_kind_of(Hexdump::Type::UInt64)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
context "when the type: is :int64" do
|
218
|
+
let(:type) { :int64 }
|
219
|
+
|
220
|
+
it "must set #type to Hexdump::Type::Int64" do
|
221
|
+
expect(subject.type).to be_kind_of(Hexdump::Type::Int64)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
context "and the type is :float, :float32, :float64, or :double" do
|
226
|
+
let(:type) { :float }
|
227
|
+
|
228
|
+
it "must set #type to Hexdump::Type::Float" do
|
229
|
+
expect(subject.type).to be_kind_of(Hexdump::Type::Float)
|
230
|
+
end
|
231
|
+
|
232
|
+
it "must default the #base to 10" do
|
233
|
+
expect(subject.base).to eq(10)
|
234
|
+
end
|
235
|
+
|
236
|
+
context "but the base: value isn't 10 or 16" do
|
237
|
+
it do
|
238
|
+
expect {
|
239
|
+
described_class.new(type: type, base: 8)
|
240
|
+
}.to raise_error(Hexdump::Numeric::IncompatibleTypeError)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
context "when given an unsupported type" do
|
246
|
+
it do
|
247
|
+
expect {
|
248
|
+
described_class.new(type: :foo)
|
249
|
+
}.to raise_error(ArgumentError,"unsupported type: :foo")
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
context "when given base: keyword" do
|
255
|
+
subject { described_class.new(base: base) }
|
256
|
+
|
257
|
+
context "when the base: is 16" do
|
258
|
+
let(:base) { 16 }
|
259
|
+
|
260
|
+
it "must initialize #numeric to Hexdump::Numeric::Hexdecimal" do
|
261
|
+
expect(subject.numeric).to be_kind_of(Hexdump::Numeric::Hexadecimal)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
context "when the base: is 10" do
|
266
|
+
let(:base) { 10 }
|
267
|
+
|
268
|
+
it "must initialize #numeric to Hexdump::Numeric::Decimal" do
|
269
|
+
expect(subject.numeric).to be_kind_of(Hexdump::Numeric::Decimal)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
context "when given base: 8" do
|
274
|
+
let(:base) { 8 }
|
275
|
+
|
276
|
+
it "must initialize #numeric to Hexdump::Numeric::Octal" do
|
277
|
+
expect(subject.numeric).to be_kind_of(Hexdump::Numeric::Octal)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
context "when given base: 2" do
|
282
|
+
let(:base) { 2 }
|
283
|
+
|
284
|
+
it "must initialize #numeric to Hexdump::Numeric::Binary" do
|
285
|
+
expect(subject.numeric).to be_kind_of(Hexdump::Numeric::Binary)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
context "when given index_base: keyword" do
|
291
|
+
subject { described_class.new(index_base: index_base) }
|
292
|
+
|
293
|
+
context "when the index: is 16" do
|
294
|
+
let(:index_base) { 16 }
|
295
|
+
|
296
|
+
it "must initialize #index to Hexdump::Numeric::Hexdecimal" do
|
297
|
+
expect(subject.index).to be_kind_of(Hexdump::Numeric::Hexadecimal)
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
context "when the index: is 10" do
|
302
|
+
let(:index_base) { 10 }
|
303
|
+
|
304
|
+
it "must initialize #index to Hexdump::Numeric::Decimal" do
|
305
|
+
expect(subject.index).to be_kind_of(Hexdump::Numeric::Decimal)
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
context "when given index: 8" do
|
310
|
+
let(:index_base) { 8 }
|
311
|
+
|
312
|
+
it "must initialize #index to Hexdump::Numeric::Octal" do
|
313
|
+
expect(subject.index).to be_kind_of(Hexdump::Numeric::Octal)
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
context "when given index: 2" do
|
318
|
+
let(:index_base) { 2 }
|
319
|
+
|
320
|
+
it "must initialize #index to Hexdump::Numeric::Binary" do
|
321
|
+
expect(subject.index).to be_kind_of(Hexdump::Numeric::Binary)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
context "when given the index_offset: keyword" do
|
327
|
+
let(:index_offset) { 1024 }
|
328
|
+
|
329
|
+
subject { described_class.new(index_offset: index_offset) }
|
330
|
+
|
331
|
+
it "must set #index_offset" do
|
332
|
+
expect(subject.index_offset).to eq(index_offset)
|
333
|
+
end
|
334
|
+
|
335
|
+
context "but the offset: keyword is also given" do
|
336
|
+
let(:offset) { 4 }
|
337
|
+
|
338
|
+
subject do
|
339
|
+
described_class.new(
|
340
|
+
offset: offset,
|
341
|
+
index_offset: index_offset
|
342
|
+
)
|
343
|
+
end
|
344
|
+
|
345
|
+
it "must set #index_offset to the index_offset: value" do
|
346
|
+
expect(subject.index_offset).to eq(index_offset)
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
context "when given the columns: keyword" do
|
352
|
+
let(:columns) { 7 }
|
353
|
+
|
354
|
+
subject { described_class.new(columns: columns) }
|
355
|
+
|
356
|
+
it "must set #columns" do
|
357
|
+
expect(subject.columns).to eq(columns)
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
context "when given the group_columns: keyword" do
|
362
|
+
let(:group_columns) { 4 }
|
363
|
+
|
364
|
+
subject { described_class.new(group_columns: group_columns) }
|
365
|
+
|
366
|
+
it "must set #group_columns" do
|
367
|
+
expect(subject.group_columns).to eq(group_columns)
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
context "when given the group_chars: keyword" do
|
372
|
+
let(:group_chars) { 4 }
|
373
|
+
|
374
|
+
subject { described_class.new(group_chars: group_chars) }
|
375
|
+
|
376
|
+
it "must set #group_chars" do
|
377
|
+
expect(subject.group_chars).to eq(group_chars)
|
378
|
+
end
|
379
|
+
|
380
|
+
context "when group_chars: :type is given" do
|
381
|
+
let(:type) { :uint16 }
|
382
|
+
|
383
|
+
subject { described_class.new(type: type, group_chars: :type) }
|
384
|
+
|
385
|
+
it "must set #group_chars to #type.size" do
|
386
|
+
expect(subject.group_chars).to eq(subject.type.size)
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
context "when given an unsupported base: value" do
|
392
|
+
it do
|
393
|
+
expect {
|
394
|
+
described_class.new(base: 3)
|
395
|
+
}.to raise_error(ArgumentError,"unsupported base: 3")
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
it "must default #theme to nil" do
|
400
|
+
expect(subject.theme).to be(nil)
|
401
|
+
end
|
402
|
+
|
403
|
+
context "when given the style: keyword" do
|
404
|
+
context "and is a Hash" do
|
405
|
+
let(:index_style) { :white }
|
406
|
+
let(:numeric_style) { :cyan }
|
407
|
+
let(:chars_style) { :blue }
|
408
|
+
|
409
|
+
subject do
|
410
|
+
described_class.new(
|
411
|
+
style: {
|
412
|
+
index: index_style,
|
413
|
+
numeric: numeric_style,
|
414
|
+
chars: chars_style
|
415
|
+
}
|
416
|
+
)
|
417
|
+
end
|
418
|
+
|
419
|
+
it "must initialize #style" do
|
420
|
+
expect(subject.theme).to be_kind_of(Hexdump::Theme)
|
421
|
+
end
|
422
|
+
|
423
|
+
it "must initialize #style.index" do
|
424
|
+
expect(subject.theme.index).to be_kind_of(Hexdump::Theme::Rule)
|
425
|
+
end
|
426
|
+
|
427
|
+
it "must populate #style.index.theme" do
|
428
|
+
expect(subject.theme.index.style.parameters).to eq(index_style)
|
429
|
+
end
|
430
|
+
|
431
|
+
it "must initialize #style.numeric" do
|
432
|
+
expect(subject.theme.numeric).to be_kind_of(Hexdump::Theme::Rule)
|
433
|
+
end
|
434
|
+
|
435
|
+
it "must populate #style.numeric.theme" do
|
436
|
+
expect(subject.theme.numeric.style.parameters).to eq(numeric_style)
|
437
|
+
end
|
438
|
+
|
439
|
+
it "must initialize #style.chars" do
|
440
|
+
expect(subject.theme.chars).to be_kind_of(Hexdump::Theme::Rule)
|
441
|
+
end
|
442
|
+
|
443
|
+
it "must populate #style.chars.theme" do
|
444
|
+
expect(subject.theme.chars.style.parameters).to eq(chars_style)
|
445
|
+
end
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
context "when given the highlights: keyword" do
|
450
|
+
context "and is a Hash" do
|
451
|
+
let(:index_pattern) { /00$/ }
|
452
|
+
let(:index_highlight) { :white }
|
453
|
+
let(:index_highlights) { {index_pattern => index_highlight} }
|
454
|
+
|
455
|
+
let(:numeric_pattern) { '00' }
|
456
|
+
let(:numeric_highlight) { :faint }
|
457
|
+
let(:numeric_highlights) { {numeric_pattern => numeric_highlight} }
|
458
|
+
|
459
|
+
let(:chars_pattern) { /A-Z/ }
|
460
|
+
let(:chars_highlight) { :bold }
|
461
|
+
let(:chars_highlights) { {chars_pattern => chars_highlight} }
|
462
|
+
|
463
|
+
subject do
|
464
|
+
described_class.new(
|
465
|
+
highlights: {
|
466
|
+
index: index_highlights,
|
467
|
+
numeric: numeric_highlights,
|
468
|
+
chars: chars_highlights
|
469
|
+
}
|
470
|
+
)
|
471
|
+
end
|
472
|
+
|
473
|
+
it "must initialize #style" do
|
474
|
+
expect(subject.theme).to be_kind_of(Hexdump::Theme)
|
475
|
+
end
|
476
|
+
|
477
|
+
it "must initialize #style.index" do
|
478
|
+
expect(subject.theme.index).to be_kind_of(Hexdump::Theme::Rule)
|
479
|
+
end
|
480
|
+
|
481
|
+
it "must populate #style.index.highlights" do
|
482
|
+
expect(subject.theme.index.highlights[index_pattern].parameters).to eq(index_highlight)
|
483
|
+
end
|
484
|
+
|
485
|
+
it "must initialize #style.numeric" do
|
486
|
+
expect(subject.theme.numeric).to be_kind_of(Hexdump::Theme::Rule)
|
487
|
+
end
|
488
|
+
|
489
|
+
it "must populate #style.numeric.highlights" do
|
490
|
+
expect(subject.theme.numeric.highlights[numeric_pattern].parameters).to eq(numeric_highlight)
|
491
|
+
end
|
492
|
+
|
493
|
+
it "must initialize #style.chars" do
|
494
|
+
expect(subject.theme.chars).to be_kind_of(Hexdump::Theme::Rule)
|
495
|
+
end
|
496
|
+
|
497
|
+
it "must populate #style.chars.highlights" do
|
498
|
+
expect(subject.theme.chars.highlights[chars_pattern].parameters).to eq(chars_highlight)
|
499
|
+
end
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
context "when a block is given" do
|
504
|
+
it "must yield the newly initialized hexdump object" do
|
505
|
+
yielded_hexdump = nil
|
506
|
+
|
507
|
+
described_class.new do |hexdump|
|
508
|
+
yielded_hexdump = hexdump
|
509
|
+
end
|
510
|
+
|
511
|
+
expect(yielded_hexdump).to be_kind_of(described_class)
|
512
|
+
end
|
513
|
+
end
|
514
|
+
end
|
515
|
+
|
516
|
+
describe "#type=" do
|
517
|
+
context "when given a type name" do
|
518
|
+
let(:type_name) { :uint32_le }
|
519
|
+
|
520
|
+
before { subject.type = type_name }
|
521
|
+
|
522
|
+
it "must set the #type to a Type object" do
|
523
|
+
expect(subject.type).to eq(Hexdump::TYPES.fetch(type_name))
|
524
|
+
end
|
525
|
+
end
|
526
|
+
end
|
527
|
+
|
528
|
+
describe "#base=" do
|
529
|
+
context "when given 16" do
|
530
|
+
let(:base) { 16 }
|
531
|
+
|
532
|
+
before { subject.base = base }
|
533
|
+
|
534
|
+
it "must set #base" do
|
535
|
+
expect(subject.base).to eq(base)
|
536
|
+
end
|
537
|
+
end
|
538
|
+
|
539
|
+
context "when given 10" do
|
540
|
+
let(:base) { 10 }
|
541
|
+
|
542
|
+
before { subject.base = base }
|
543
|
+
|
544
|
+
it "must set #base" do
|
545
|
+
expect(subject.base).to eq(base)
|
546
|
+
end
|
547
|
+
end
|
548
|
+
|
549
|
+
context "when given 8" do
|
550
|
+
let(:base) { 8 }
|
551
|
+
|
552
|
+
before { subject.base = base }
|
553
|
+
|
554
|
+
it "must set #base" do
|
555
|
+
expect(subject.base).to eq(base)
|
556
|
+
end
|
557
|
+
end
|
558
|
+
|
559
|
+
context "when given 2" do
|
560
|
+
let(:base) { 2 }
|
561
|
+
|
562
|
+
before { subject.base = base }
|
563
|
+
|
564
|
+
it "must set #base" do
|
565
|
+
expect(subject.base).to eq(base)
|
566
|
+
end
|
567
|
+
end
|
568
|
+
end
|
569
|
+
|
570
|
+
describe "#index_base=" do
|
571
|
+
context "when given 16" do
|
572
|
+
let(:value) { 16 }
|
573
|
+
|
574
|
+
before { subject.index_base = value }
|
575
|
+
|
576
|
+
it "must set #index_base" do
|
577
|
+
expect(subject.index_base).to eq(value)
|
578
|
+
end
|
579
|
+
end
|
580
|
+
|
581
|
+
context "when given 10" do
|
582
|
+
let(:value) { 10 }
|
583
|
+
|
584
|
+
before { subject.index_base = value }
|
585
|
+
|
586
|
+
it "must set #index_base" do
|
587
|
+
expect(subject.index_base).to eq(value)
|
588
|
+
end
|
589
|
+
end
|
590
|
+
|
591
|
+
context "when given 8" do
|
592
|
+
let(:value) { 8 }
|
593
|
+
|
594
|
+
before { subject.index_base = value }
|
595
|
+
|
596
|
+
it "must set #index_base" do
|
597
|
+
expect(subject.index_base).to eq(value)
|
598
|
+
end
|
599
|
+
end
|
600
|
+
|
601
|
+
context "when given 2" do
|
602
|
+
let(:value) { 2 }
|
603
|
+
|
604
|
+
before { subject.index_base = value }
|
605
|
+
|
606
|
+
it "must set #index_base" do
|
607
|
+
expect(subject.index_base).to eq(value)
|
608
|
+
end
|
609
|
+
end
|
610
|
+
end
|
611
|
+
|
612
|
+
describe "#group_chars=" do
|
613
|
+
context "when given an Integer" do
|
614
|
+
let(:value) { 4 }
|
615
|
+
|
616
|
+
before { subject.group_chars = value }
|
617
|
+
|
618
|
+
it "must set #group_chars" do
|
619
|
+
expect(subject.group_chars).to eq(value)
|
620
|
+
end
|
621
|
+
end
|
622
|
+
|
623
|
+
context "when given :type" do
|
624
|
+
subject { described_class.new(type: :uint16) }
|
625
|
+
|
626
|
+
before { subject.group_chars = :type }
|
627
|
+
|
628
|
+
it "must set #group_chars to #type.size" do
|
629
|
+
expect(subject.group_chars).to eq(subject.type.size)
|
630
|
+
end
|
631
|
+
end
|
632
|
+
end
|
633
|
+
|
634
|
+
describe "#encoding=" do
|
635
|
+
context "when given nil" do
|
636
|
+
before { subject.encoding = nil }
|
637
|
+
|
638
|
+
it "must set #encoding to nil" do
|
639
|
+
expect(subject.encoding).to be(nil)
|
640
|
+
end
|
641
|
+
end
|
642
|
+
|
643
|
+
context "when given :ascii" do
|
644
|
+
before { subject.encoding = :ascii }
|
645
|
+
|
646
|
+
it "must set #encoding to nil" do
|
647
|
+
expect(subject.encoding).to be(nil)
|
648
|
+
end
|
649
|
+
end
|
650
|
+
|
651
|
+
context "when given :utf8" do
|
652
|
+
before { subject.encoding = :utf8 }
|
653
|
+
|
654
|
+
it "must set #encoding to Encoding::UTF_8" do
|
655
|
+
expect(subject.encoding).to eq(Encoding::UTF_8)
|
656
|
+
end
|
657
|
+
end
|
658
|
+
|
659
|
+
context "when given an Encoding object" do
|
660
|
+
let(:encoding) { Encoding::UTF_7 }
|
661
|
+
|
662
|
+
before { subject.encoding = encoding }
|
663
|
+
|
664
|
+
it "must set #encoding" do
|
665
|
+
expect(subject.encoding).to eq(encoding)
|
666
|
+
end
|
667
|
+
end
|
668
|
+
end
|
669
|
+
|
670
|
+
describe "#theme" do
|
671
|
+
context "when a block is given" do
|
672
|
+
context "when not initialized with style: or highlights: keywords" do
|
673
|
+
it "must yield a new Theme object" do
|
674
|
+
yielded_theme = nil
|
675
|
+
|
676
|
+
subject.theme { |theme| yielded_theme = theme }
|
677
|
+
|
678
|
+
initialized_theme = subject.instance_variable_get("@theme")
|
679
|
+
|
680
|
+
expect(yielded_theme).to be(initialized_theme)
|
681
|
+
end
|
682
|
+
|
683
|
+
context "when called multiple times" do
|
684
|
+
it "must yield the same Theme object" do
|
685
|
+
theme_id1 = nil
|
686
|
+
theme_id2 = nil
|
687
|
+
|
688
|
+
subject.theme { |theme| theme_id1 = theme.object_id }
|
689
|
+
subject.theme { |theme| theme_id2 = theme.object_id }
|
690
|
+
|
691
|
+
expect(theme_id1).to eq(theme_id2)
|
692
|
+
end
|
693
|
+
end
|
694
|
+
end
|
695
|
+
|
696
|
+
context "when initialized with style: or highlights: keywords" do
|
697
|
+
subject do
|
698
|
+
described_class.new(
|
699
|
+
style: {},
|
700
|
+
highlights: {}
|
701
|
+
)
|
702
|
+
end
|
703
|
+
|
704
|
+
it "must yield the initialized theme object" do
|
705
|
+
initialized_theme = subject.instance_variable_get("@theme")
|
706
|
+
|
707
|
+
yielded_theme = nil
|
708
|
+
|
709
|
+
subject.theme { |theme| yielded_theme = theme }
|
710
|
+
|
711
|
+
expect(yielded_theme).to be(initialized_theme)
|
712
|
+
end
|
713
|
+
end
|
714
|
+
end
|
715
|
+
|
716
|
+
context "when no block is given" do
|
717
|
+
context "when not initialized with style: or highlights: keywords" do
|
718
|
+
it "must return nil" do
|
719
|
+
expect(subject.theme).to be(nil)
|
720
|
+
end
|
721
|
+
end
|
722
|
+
|
723
|
+
context "when initialized with style: or highlights: keywords" do
|
724
|
+
subject do
|
725
|
+
described_class.new(
|
726
|
+
style: {},
|
727
|
+
highlights: {}
|
728
|
+
)
|
729
|
+
end
|
730
|
+
|
731
|
+
it "must return the initialized theme object" do
|
732
|
+
initialized_theme = subject.instance_variable_get("@theme")
|
733
|
+
|
734
|
+
expect(subject.theme).to be(initialized_theme)
|
735
|
+
end
|
736
|
+
end
|
737
|
+
end
|
738
|
+
end
|
739
|
+
|
740
|
+
let(:data) do
|
741
|
+
'A' * subject.columns + \
|
742
|
+
'B' * subject.columns + \
|
743
|
+
'C' * subject.columns
|
744
|
+
end
|
745
|
+
|
746
|
+
let(:columns) { subject.columns }
|
747
|
+
|
748
|
+
describe "#each_row" do
|
749
|
+
let(:rows) do
|
750
|
+
[
|
751
|
+
[columns * 0, [0x41] * columns, ['A'] * columns],
|
752
|
+
[columns * 1, [0x42] * columns, ['B'] * columns],
|
753
|
+
[columns * 2, [0x43] * columns, ['C'] * columns],
|
754
|
+
]
|
755
|
+
end
|
756
|
+
|
757
|
+
it "must yield each row of index, numeric values, and raw characters" do
|
758
|
+
yielded_rows = []
|
759
|
+
|
760
|
+
subject.each_row(data) do |*args|
|
761
|
+
yielded_rows << args
|
762
|
+
end
|
763
|
+
|
764
|
+
expect(yielded_rows).to eq(rows)
|
765
|
+
end
|
766
|
+
|
767
|
+
it "must return the total number of bytes read" do
|
768
|
+
index = subject.each_row(data) do |*args|
|
769
|
+
end
|
770
|
+
|
771
|
+
expect(index).to eq(data.length)
|
772
|
+
end
|
773
|
+
|
774
|
+
context "when #reader.offset is > 0" do
|
775
|
+
let(:offset) { 8 }
|
776
|
+
|
777
|
+
subject { described_class.new(offset: offset) }
|
778
|
+
|
779
|
+
let(:indexes) do
|
780
|
+
[
|
781
|
+
offset + (columns * 0),
|
782
|
+
offset + (columns * 1),
|
783
|
+
offset + (columns * 2),
|
784
|
+
]
|
785
|
+
end
|
786
|
+
|
787
|
+
it "must start the index at #offset" do
|
788
|
+
yielded_indexes = []
|
789
|
+
|
790
|
+
subject.each_row(data) do |index,*args|
|
791
|
+
yielded_indexes << index
|
792
|
+
end
|
793
|
+
|
794
|
+
expect(yielded_indexes).to eq(indexes)
|
795
|
+
end
|
796
|
+
|
797
|
+
context "and #index_offset is > 0" do
|
798
|
+
let(:index_offset) { 0 }
|
799
|
+
|
800
|
+
subject do
|
801
|
+
described_class.new(
|
802
|
+
offset: offset,
|
803
|
+
index_offset: index_offset
|
804
|
+
)
|
805
|
+
end
|
806
|
+
|
807
|
+
let(:indexes) do
|
808
|
+
[
|
809
|
+
index_offset + (columns * 0),
|
810
|
+
index_offset + (columns * 1),
|
811
|
+
index_offset + (columns * 2),
|
812
|
+
]
|
813
|
+
end
|
814
|
+
|
815
|
+
it "must override :index and start the index at #index_offset" do
|
816
|
+
yielded_indexes = []
|
817
|
+
|
818
|
+
subject.each_row(data) do |index,*args|
|
819
|
+
yielded_indexes << index
|
820
|
+
end
|
821
|
+
|
822
|
+
expect(yielded_indexes).to eq(indexes)
|
823
|
+
end
|
824
|
+
end
|
825
|
+
end
|
826
|
+
|
827
|
+
context "when the data's length is not evenly divisble by the columns" do
|
828
|
+
let(:rows) do
|
829
|
+
[
|
830
|
+
[columns * 0, [0x41] * columns, ['A'] * columns ],
|
831
|
+
[columns * 1, [0x42] * columns, ['B'] * columns ],
|
832
|
+
[columns * 2, [0x43] * (columns / 2), ['C'] * (columns / 2)]
|
833
|
+
]
|
834
|
+
end
|
835
|
+
|
836
|
+
let(:data) do
|
837
|
+
'A' * columns + \
|
838
|
+
'B' * columns + \
|
839
|
+
'C' * (columns / 2)
|
840
|
+
end
|
841
|
+
|
842
|
+
it "must yield a partial row of read values last" do
|
843
|
+
yielded_rows = []
|
844
|
+
|
845
|
+
subject.each_row(data) do |*args|
|
846
|
+
yielded_rows << args
|
847
|
+
end
|
848
|
+
|
849
|
+
expect(yielded_rows).to eq(rows)
|
850
|
+
end
|
851
|
+
end
|
852
|
+
|
853
|
+
context "when #columns is not 16" do
|
854
|
+
let(:columns) { 7 }
|
855
|
+
|
856
|
+
subject { described_class.new(columns: columns) }
|
857
|
+
|
858
|
+
it "must yield rows with the same number of columns" do
|
859
|
+
yielded_row_lengths = []
|
860
|
+
|
861
|
+
subject.each_row(data) do |index,numeric,chars|
|
862
|
+
yielded_row_lengths << numeric.length
|
863
|
+
end
|
864
|
+
|
865
|
+
expect(yielded_row_lengths).to all(eq(columns))
|
866
|
+
end
|
867
|
+
end
|
868
|
+
|
869
|
+
context "when there are not enough bytes to decode the last value" do
|
870
|
+
subject { described_class.new(type: :uint32) }
|
871
|
+
|
872
|
+
let(:size) { subject.type.size }
|
873
|
+
|
874
|
+
let(:data) do
|
875
|
+
"\x41\x41\x41\x41" * columns + \
|
876
|
+
"ABC"
|
877
|
+
end
|
878
|
+
|
879
|
+
let(:rows) do
|
880
|
+
[
|
881
|
+
[size * columns * 0, [0x41414141] * columns, ["AAAA"] * columns],
|
882
|
+
[size * columns * 1, [nil], ["ABC"]]
|
883
|
+
]
|
884
|
+
end
|
885
|
+
|
886
|
+
it "must yield the read characters, but not the partially decoded value" do
|
887
|
+
yielded_rows = []
|
888
|
+
|
889
|
+
subject.each_row(data) do |*row|
|
890
|
+
yielded_rows << row
|
891
|
+
end
|
892
|
+
|
893
|
+
expect(yielded_rows).to eq(rows)
|
894
|
+
end
|
895
|
+
end
|
896
|
+
|
897
|
+
context "when no block is given" do
|
898
|
+
it "must return an Enumerator" do
|
899
|
+
expect(subject.each_row(data)).to be_kind_of(Enumerator)
|
900
|
+
end
|
901
|
+
end
|
902
|
+
end
|
903
|
+
|
904
|
+
describe "#each_non_repeating_row" do
|
905
|
+
let(:rows) do
|
906
|
+
[
|
907
|
+
[columns * 0, [0x41] * columns, ['A'] * columns],
|
908
|
+
[columns * 1, [0x42] * columns, ['B'] * columns],
|
909
|
+
['*'],
|
910
|
+
[columns * 4, [0x43] * columns, ['C'] * columns]
|
911
|
+
]
|
912
|
+
end
|
913
|
+
|
914
|
+
let(:data) do
|
915
|
+
'A' * subject.columns + \
|
916
|
+
'B' * subject.columns + \
|
917
|
+
'B' * subject.columns + \
|
918
|
+
'B' * subject.columns + \
|
919
|
+
'C' * subject.columns
|
920
|
+
end
|
921
|
+
|
922
|
+
it "must omit repeating rows, instead yielding an index of '*'" do
|
923
|
+
yielded_rows = []
|
924
|
+
|
925
|
+
subject.each_non_repeating_row(data) do |*args|
|
926
|
+
yielded_rows << args
|
927
|
+
end
|
928
|
+
|
929
|
+
expect(yielded_rows).to eq(rows)
|
930
|
+
end
|
931
|
+
|
932
|
+
it "must return the total number of bytes read" do
|
933
|
+
index = subject.each_non_repeating_row(data) do |*args|
|
934
|
+
end
|
935
|
+
|
936
|
+
expect(index).to eq(data.length)
|
937
|
+
end
|
938
|
+
|
939
|
+
context "when the repeating rows is at the end of the data" do
|
940
|
+
let(:rows) do
|
941
|
+
[
|
942
|
+
[columns * 0, [0x41] * columns, ['A'] * columns],
|
943
|
+
[columns * 1, [0x42] * columns, ['B'] * columns],
|
944
|
+
[columns * 2, [0x43] * columns, ['C'] * columns],
|
945
|
+
['*']
|
946
|
+
]
|
947
|
+
end
|
948
|
+
|
949
|
+
let(:data) do
|
950
|
+
'A' * columns + \
|
951
|
+
'B' * columns + \
|
952
|
+
'C' * columns + \
|
953
|
+
'C' * columns + \
|
954
|
+
'C' * columns
|
955
|
+
end
|
956
|
+
|
957
|
+
it "must yield an index of '*' at the end" do
|
958
|
+
yielded_rows = []
|
959
|
+
|
960
|
+
subject.each_non_repeating_row(data) do |*args|
|
961
|
+
yielded_rows << args
|
962
|
+
end
|
963
|
+
|
964
|
+
expect(yielded_rows).to eq(rows)
|
965
|
+
end
|
966
|
+
end
|
967
|
+
|
968
|
+
context "when no block is given" do
|
969
|
+
it "must return an Enumerator" do
|
970
|
+
expect(subject.each_non_repeating_row(data)).to be_kind_of(Enumerator)
|
971
|
+
end
|
972
|
+
end
|
973
|
+
end
|
974
|
+
|
975
|
+
describe "#each_formatted_row" do
|
976
|
+
let(:formatted_rows) do
|
977
|
+
[
|
978
|
+
["%.8x" % (columns * 0), ["41"] * columns, "A" * columns],
|
979
|
+
["%.8x" % (columns * 1), ["42"] * columns, "B" * columns],
|
980
|
+
["%.8x" % (columns * 2), ["43"] * columns, "C" * columns],
|
981
|
+
]
|
982
|
+
end
|
983
|
+
|
984
|
+
it "should yield the formatted rows to the given block" do
|
985
|
+
yielded_rows = []
|
986
|
+
|
987
|
+
subject.each_formatted_row(data) do |*row|
|
988
|
+
yielded_rows << row
|
989
|
+
end
|
990
|
+
|
991
|
+
expect(yielded_rows).to eq(formatted_rows)
|
992
|
+
end
|
993
|
+
|
994
|
+
it "must return the formatted total number of bytes read" do
|
995
|
+
index = subject.each_formatted_row(data) do |*row|
|
996
|
+
end
|
997
|
+
|
998
|
+
expect(index).to eq(subject.index % data.length)
|
999
|
+
end
|
1000
|
+
|
1001
|
+
[*(0x00..0x1f), *(0x7f..0xff)].each do |byte|
|
1002
|
+
context "when the row contains an ASCII #{byte.chr.dump} characters" do
|
1003
|
+
let(:data) { "ABC#{byte.chr}" }
|
1004
|
+
|
1005
|
+
let(:formatted_rows) do
|
1006
|
+
[
|
1007
|
+
["00000000", ["41", "42", "43", "%.2x" % byte], "ABC."]
|
1008
|
+
]
|
1009
|
+
end
|
1010
|
+
|
1011
|
+
it "must replace unprintable characters with a '.'" do
|
1012
|
+
yielded_rows = []
|
1013
|
+
|
1014
|
+
subject.each_formatted_row(data) do |*row|
|
1015
|
+
yielded_rows << row
|
1016
|
+
end
|
1017
|
+
|
1018
|
+
expect(yielded_rows).to eq(formatted_rows)
|
1019
|
+
end
|
1020
|
+
end
|
1021
|
+
end
|
1022
|
+
|
1023
|
+
context "when the data's length is not evenly divisble by the columns" do
|
1024
|
+
let(:data) do
|
1025
|
+
'A' * columns + \
|
1026
|
+
'B' * columns + \
|
1027
|
+
'C' * (columns / 2)
|
1028
|
+
end
|
1029
|
+
|
1030
|
+
let(:formatted_rows) do
|
1031
|
+
[
|
1032
|
+
["%.8x" % (columns * 0), ["41"] * columns, "A" * columns],
|
1033
|
+
["%.8x" % (columns * 1), ["42"] * columns, "B" * columns],
|
1034
|
+
[
|
1035
|
+
"%.8x" % (columns * 2), ["43"] * (columns / 2), "C" * (columns / 2)
|
1036
|
+
]
|
1037
|
+
]
|
1038
|
+
end
|
1039
|
+
|
1040
|
+
it "must yield the last formatted partial row" do
|
1041
|
+
yielded_rows = []
|
1042
|
+
|
1043
|
+
subject.each_formatted_row(data) do |*row|
|
1044
|
+
yielded_rows << row
|
1045
|
+
end
|
1046
|
+
|
1047
|
+
expect(yielded_rows).to eq(formatted_rows)
|
1048
|
+
end
|
1049
|
+
end
|
1050
|
+
|
1051
|
+
context "when there are not enough bytes to decode the last value" do
|
1052
|
+
subject { described_class.new(type: :uint32) }
|
1053
|
+
|
1054
|
+
let(:size) { subject.type.size }
|
1055
|
+
|
1056
|
+
let(:data) do
|
1057
|
+
"\x41\x41\x41\x41" * columns + \
|
1058
|
+
"ABC"
|
1059
|
+
end
|
1060
|
+
|
1061
|
+
let(:formatted_rows) do
|
1062
|
+
[
|
1063
|
+
[
|
1064
|
+
"%.8x" % (size * columns * 0),
|
1065
|
+
["41414141"] * columns,
|
1066
|
+
"AAAA" * columns
|
1067
|
+
],
|
1068
|
+
|
1069
|
+
[
|
1070
|
+
"%.8x" % (size * columns * 1),
|
1071
|
+
[" "],
|
1072
|
+
"ABC"
|
1073
|
+
]
|
1074
|
+
]
|
1075
|
+
end
|
1076
|
+
|
1077
|
+
it "must yield the read characters and a place-holder blank String" do
|
1078
|
+
yielded_rows = []
|
1079
|
+
|
1080
|
+
subject.each_formatted_row(data) do |*row|
|
1081
|
+
yielded_rows << row
|
1082
|
+
end
|
1083
|
+
|
1084
|
+
expect(yielded_rows).to eq(formatted_rows)
|
1085
|
+
end
|
1086
|
+
end
|
1087
|
+
|
1088
|
+
context "when there is repeating rows of data" do
|
1089
|
+
let(:data) do
|
1090
|
+
'A' * columns + \
|
1091
|
+
'B' * columns + \
|
1092
|
+
'B' * columns + \
|
1093
|
+
'B' * columns + \
|
1094
|
+
'C' * columns
|
1095
|
+
end
|
1096
|
+
|
1097
|
+
let(:formatted_rows) do
|
1098
|
+
[
|
1099
|
+
["%.8x" % (columns * 0), ["41"] * columns, "A" * columns],
|
1100
|
+
["%.8x" % (columns * 1), ["42"] * columns, "B" * columns],
|
1101
|
+
["*"],
|
1102
|
+
["%.8x" % (columns * 4), ["43"] * columns, "C" * columns]
|
1103
|
+
]
|
1104
|
+
end
|
1105
|
+
|
1106
|
+
it "must yield the formatted rows and a '*' row to denote the beginning of repeating rows" do
|
1107
|
+
yielded_rows = []
|
1108
|
+
|
1109
|
+
subject.each_formatted_row(data) do |*row|
|
1110
|
+
yielded_rows << row
|
1111
|
+
end
|
1112
|
+
|
1113
|
+
expect(yielded_rows).to eq(formatted_rows)
|
1114
|
+
end
|
1115
|
+
|
1116
|
+
context "and #repeating is true" do
|
1117
|
+
subject { described_class.new(repeating: true) }
|
1118
|
+
|
1119
|
+
let(:formatted_rows) do
|
1120
|
+
[
|
1121
|
+
["%.8x" % (columns * 0), ["41"] * columns, 'A' * columns],
|
1122
|
+
["%.8x" % (columns * 1), ["42"] * columns, 'B' * columns],
|
1123
|
+
["%.8x" % (columns * 2), ["42"] * columns, 'B' * columns],
|
1124
|
+
["%.8x" % (columns * 3), ["42"] * columns, 'B' * columns],
|
1125
|
+
["%.8x" % (columns * 4), ["43"] * columns, 'C' * columns]
|
1126
|
+
]
|
1127
|
+
end
|
1128
|
+
|
1129
|
+
it "must yield the formatted repeating rows" do
|
1130
|
+
yielded_rows = []
|
1131
|
+
|
1132
|
+
subject.each_formatted_row(data) do |*row|
|
1133
|
+
yielded_rows << row
|
1134
|
+
end
|
1135
|
+
|
1136
|
+
expect(yielded_rows).to eq(formatted_rows)
|
1137
|
+
end
|
1138
|
+
end
|
1139
|
+
end
|
1140
|
+
|
1141
|
+
context "when #columns is not 16" do
|
1142
|
+
let(:columns) { 7 }
|
1143
|
+
|
1144
|
+
subject { described_class.new(columns: columns) }
|
1145
|
+
|
1146
|
+
it "must yield rows with the same number of formatted columns" do
|
1147
|
+
yielded_numeric_lengths = []
|
1148
|
+
yielded_chars_lengths = []
|
1149
|
+
|
1150
|
+
subject.each_formatted_row(data) do |index,numeric,chars|
|
1151
|
+
yielded_numeric_lengths << numeric.length
|
1152
|
+
yielded_chars_lengths << chars.length
|
1153
|
+
end
|
1154
|
+
|
1155
|
+
expect(yielded_numeric_lengths).to all(eq(columns))
|
1156
|
+
expect(yielded_chars_lengths).to all(eq(columns))
|
1157
|
+
end
|
1158
|
+
end
|
1159
|
+
|
1160
|
+
context "when #chars.encoding is set" do
|
1161
|
+
let(:encoding) { Encoding::UTF_8 }
|
1162
|
+
|
1163
|
+
subject { described_class.new(encoding: encoding) }
|
1164
|
+
|
1165
|
+
it "must encode the characters to the given Encoding" do
|
1166
|
+
yielded_char_encodings = []
|
1167
|
+
|
1168
|
+
subject.each_formatted_row(data) do |index,numeric,chars|
|
1169
|
+
yielded_char_encodings << chars.encoding
|
1170
|
+
end
|
1171
|
+
|
1172
|
+
expect(yielded_char_encodings).to all(eq(encoding))
|
1173
|
+
end
|
1174
|
+
|
1175
|
+
context "and the data contains unprintable characters" do
|
1176
|
+
let(:codepoint) { 888 }
|
1177
|
+
let(:char) { codepoint.chr(encoding) }
|
1178
|
+
let(:data) { "A#{char}B" }
|
1179
|
+
let(:bytes) { data.bytes }
|
1180
|
+
|
1181
|
+
let(:formatted_rows) do
|
1182
|
+
[
|
1183
|
+
[subject.index % 0, bytes.map { |b| subject.numeric % b }, "A.B"]
|
1184
|
+
]
|
1185
|
+
end
|
1186
|
+
|
1187
|
+
it "must replace any unprintable characters with a '.'" do
|
1188
|
+
yielded_rows = []
|
1189
|
+
|
1190
|
+
subject.each_formatted_row(data) do |*row|
|
1191
|
+
yielded_rows << row
|
1192
|
+
end
|
1193
|
+
|
1194
|
+
expect(yielded_rows).to eq(formatted_rows)
|
1195
|
+
end
|
1196
|
+
end
|
1197
|
+
|
1198
|
+
context "and the data contains invalid byte sequences" do
|
1199
|
+
let(:invalid_bytes) { "\x80\x81" }
|
1200
|
+
let(:data) { "A#{invalid_bytes}B" }
|
1201
|
+
let(:bytes) { data.bytes }
|
1202
|
+
|
1203
|
+
let(:formatted_rows) do
|
1204
|
+
[
|
1205
|
+
[subject.index % 0, bytes.map { |b| subject.numeric % b }, "A..B"]
|
1206
|
+
]
|
1207
|
+
end
|
1208
|
+
|
1209
|
+
it "must replace any invalid byte sequences with a '.'" do
|
1210
|
+
yielded_rows = []
|
1211
|
+
|
1212
|
+
subject.each_formatted_row(data) do |*row|
|
1213
|
+
yielded_rows << row
|
1214
|
+
end
|
1215
|
+
|
1216
|
+
expect(yielded_rows).to eq(formatted_rows)
|
1217
|
+
end
|
1218
|
+
end
|
1219
|
+
end
|
1220
|
+
|
1221
|
+
context "when #theme is initialized" do
|
1222
|
+
let(:ansi_reset) { Hexdump::Theme::ANSI::RESET }
|
1223
|
+
let(:ansi_green) { Hexdump::Theme::ANSI::PARAMETERS[:green] }
|
1224
|
+
let(:ansi_blue) { Hexdump::Theme::ANSI::PARAMETERS[:blue] }
|
1225
|
+
|
1226
|
+
let(:ansi_reset) { Hexdump::Theme::ANSI::RESET }
|
1227
|
+
|
1228
|
+
context "and #style.index.theme is set" do
|
1229
|
+
subject { described_class.new(style: {index: :cyan}) }
|
1230
|
+
|
1231
|
+
let(:ansi_style) { Hexdump::Theme::ANSI::PARAMETERS[:cyan] }
|
1232
|
+
|
1233
|
+
let(:index_format) { "#{ansi_style}%.8x#{ansi_reset}" }
|
1234
|
+
let(:formatted_rows) do
|
1235
|
+
[
|
1236
|
+
[index_format % (columns * 0), ["41"] * columns, "A" * columns],
|
1237
|
+
[index_format % (columns * 1), ["42"] * columns, "B" * columns],
|
1238
|
+
[index_format % (columns * 2), ["43"] * columns, "C" * columns],
|
1239
|
+
]
|
1240
|
+
end
|
1241
|
+
|
1242
|
+
it "must apply ANSI control sequences around the index column" do
|
1243
|
+
yielded_rows = []
|
1244
|
+
|
1245
|
+
subject.each_formatted_row(data) do |*row|
|
1246
|
+
yielded_rows << row
|
1247
|
+
end
|
1248
|
+
|
1249
|
+
expect(yielded_rows).to eq(formatted_rows)
|
1250
|
+
end
|
1251
|
+
|
1252
|
+
let(:formatted_index) do
|
1253
|
+
"#{ansi_style}%.8x#{ansi_reset}" % data.length
|
1254
|
+
end
|
1255
|
+
|
1256
|
+
it "must apply ANSI control sequences around the returned index" do
|
1257
|
+
index = subject.each_formatted_row(data) do |*row|
|
1258
|
+
end
|
1259
|
+
|
1260
|
+
expect(index).to eq(formatted_index)
|
1261
|
+
end
|
1262
|
+
end
|
1263
|
+
|
1264
|
+
context "and #style.index.highlights is populated" do
|
1265
|
+
subject do
|
1266
|
+
described_class.new(
|
1267
|
+
highlights: {
|
1268
|
+
index: {/00$/ => :bold}
|
1269
|
+
}
|
1270
|
+
)
|
1271
|
+
end
|
1272
|
+
|
1273
|
+
let(:ansi_style) { Hexdump::Theme::ANSI::PARAMETERS[:bold] }
|
1274
|
+
|
1275
|
+
let(:highlighted_index) { "000000#{ansi_style}00#{ansi_reset}" }
|
1276
|
+
let(:formatted_rows) do
|
1277
|
+
[
|
1278
|
+
[highlighted_index, ["41"] * columns, "A" * columns],
|
1279
|
+
["%.8x" % (columns * 1), ["42"] * columns, "B" * columns],
|
1280
|
+
["%.8x" % (columns * 2), ["43"] * columns, "C" * columns],
|
1281
|
+
]
|
1282
|
+
end
|
1283
|
+
|
1284
|
+
it "must selectively insert ANSI control sequences to the index column" do
|
1285
|
+
yielded_rows = []
|
1286
|
+
|
1287
|
+
subject.each_formatted_row(data) do |*row|
|
1288
|
+
yielded_rows << row
|
1289
|
+
end
|
1290
|
+
|
1291
|
+
expect(yielded_rows).to eq(formatted_rows)
|
1292
|
+
end
|
1293
|
+
end
|
1294
|
+
|
1295
|
+
context "and #style.numeric.theme is set" do
|
1296
|
+
subject { described_class.new(style: {numeric: :blue}) }
|
1297
|
+
|
1298
|
+
let(:ansi_style) { Hexdump::Theme::ANSI::PARAMETERS[:blue] }
|
1299
|
+
|
1300
|
+
let(:formatted_rows) do
|
1301
|
+
[
|
1302
|
+
["%.8x" % (columns * 0), ["#{ansi_style}41#{ansi_reset}"] * columns, "A" * columns],
|
1303
|
+
["%.8x" % (columns * 1), ["#{ansi_style}42#{ansi_reset}"] * columns, "B" * columns],
|
1304
|
+
["%.8x" % (columns * 2), ["#{ansi_style}43#{ansi_reset}"] * columns, "C" * columns],
|
1305
|
+
]
|
1306
|
+
end
|
1307
|
+
|
1308
|
+
it "must apply ANSI control sequences around each numeric column" do
|
1309
|
+
yielded_rows = []
|
1310
|
+
|
1311
|
+
subject.each_formatted_row(data) do |*row|
|
1312
|
+
yielded_rows << row
|
1313
|
+
end
|
1314
|
+
|
1315
|
+
expect(yielded_rows).to eq(formatted_rows)
|
1316
|
+
end
|
1317
|
+
end
|
1318
|
+
|
1319
|
+
context "and #style.numeric.highlights is populated" do
|
1320
|
+
subject do
|
1321
|
+
described_class.new(
|
1322
|
+
highlights: {
|
1323
|
+
numeric: {/^4/ => :green}
|
1324
|
+
}
|
1325
|
+
)
|
1326
|
+
end
|
1327
|
+
|
1328
|
+
let(:ansi_style) { Hexdump::Theme::ANSI::PARAMETERS[:green] }
|
1329
|
+
|
1330
|
+
let(:formatted_rows) do
|
1331
|
+
[
|
1332
|
+
["%.8x" % (columns * 0), ["#{ansi_style}4#{ansi_reset}1"] * columns, "A" * columns],
|
1333
|
+
["%.8x" % (columns * 1), ["#{ansi_style}4#{ansi_reset}2"] * columns, "B" * columns],
|
1334
|
+
["%.8x" % (columns * 2), ["#{ansi_style}4#{ansi_reset}3"] * columns, "C" * columns],
|
1335
|
+
]
|
1336
|
+
end
|
1337
|
+
|
1338
|
+
it "must selectively insert ANSI control sequences to the numeric columns" do
|
1339
|
+
yielded_rows = []
|
1340
|
+
|
1341
|
+
subject.each_formatted_row(data) do |*row|
|
1342
|
+
yielded_rows << row
|
1343
|
+
end
|
1344
|
+
|
1345
|
+
expect(yielded_rows).to eq(formatted_rows)
|
1346
|
+
end
|
1347
|
+
end
|
1348
|
+
|
1349
|
+
context "and #style.chars.theme is set" do
|
1350
|
+
subject { described_class.new(style: {chars: :green}) }
|
1351
|
+
|
1352
|
+
let(:ansi_style) { Hexdump::Theme::ANSI::PARAMETERS[:green] }
|
1353
|
+
|
1354
|
+
let(:formatted_rows) do
|
1355
|
+
[
|
1356
|
+
["%.8x" % (columns * 0), ["41"] * columns, "#{ansi_style}#{"A" * columns}#{ansi_reset}"],
|
1357
|
+
["%.8x" % (columns * 1), ["42"] * columns, "#{ansi_style}#{"B" * columns}#{ansi_reset}"],
|
1358
|
+
["%.8x" % (columns * 2), ["43"] * columns, "#{ansi_style}#{"C" * columns}#{ansi_reset}"],
|
1359
|
+
]
|
1360
|
+
end
|
1361
|
+
|
1362
|
+
it "must apply ANSI control sequences around the chars column" do
|
1363
|
+
yielded_rows = []
|
1364
|
+
|
1365
|
+
subject.each_formatted_row(data) do |*row|
|
1366
|
+
yielded_rows << row
|
1367
|
+
end
|
1368
|
+
|
1369
|
+
expect(yielded_rows).to eq(formatted_rows)
|
1370
|
+
end
|
1371
|
+
end
|
1372
|
+
|
1373
|
+
context "and #style.chars.highlights is populated" do
|
1374
|
+
subject do
|
1375
|
+
described_class.new(
|
1376
|
+
highlights: {
|
1377
|
+
chars: {/[AC]/ => :red}
|
1378
|
+
}
|
1379
|
+
)
|
1380
|
+
end
|
1381
|
+
|
1382
|
+
let(:ansi_style) { Hexdump::Theme::ANSI::PARAMETERS[:red] }
|
1383
|
+
|
1384
|
+
let(:formatted_rows) do
|
1385
|
+
[
|
1386
|
+
["%.8x" % (columns * 0), ["41"] * columns, "#{ansi_style}A#{ansi_reset}" * columns],
|
1387
|
+
["%.8x" % (columns * 1), ["42"] * columns, "B" * columns],
|
1388
|
+
["%.8x" % (columns * 2), ["43"] * columns, "#{ansi_style}C#{ansi_reset}" * columns],
|
1389
|
+
]
|
1390
|
+
end
|
1391
|
+
|
1392
|
+
it "must selectively insert ANSI control sequences to the chars column" do
|
1393
|
+
yielded_rows = []
|
1394
|
+
|
1395
|
+
subject.each_formatted_row(data) do |*row|
|
1396
|
+
yielded_rows << row
|
1397
|
+
end
|
1398
|
+
|
1399
|
+
expect(yielded_rows).to eq(formatted_rows)
|
1400
|
+
end
|
1401
|
+
end
|
1402
|
+
|
1403
|
+
context "but the ansi: keyword is false" do
|
1404
|
+
subject do
|
1405
|
+
described_class.new(
|
1406
|
+
style: {
|
1407
|
+
index: :cyan,
|
1408
|
+
numeric: :blue,
|
1409
|
+
chars: :green
|
1410
|
+
},
|
1411
|
+
highlights: {
|
1412
|
+
index: {/00$/ => :bold},
|
1413
|
+
numeric: {/^4/ => :green},
|
1414
|
+
chars: {/[AC]/ => :red}
|
1415
|
+
}
|
1416
|
+
)
|
1417
|
+
end
|
1418
|
+
|
1419
|
+
it "must not apply any ANSI control sequences" do
|
1420
|
+
yielded_rows = []
|
1421
|
+
|
1422
|
+
subject.each_formatted_row(data, ansi: false) do |*row|
|
1423
|
+
yielded_rows << row
|
1424
|
+
end
|
1425
|
+
|
1426
|
+
expect(yielded_rows).to eq(formatted_rows)
|
1427
|
+
end
|
1428
|
+
end
|
1429
|
+
|
1430
|
+
context "when #group_chars is set" do
|
1431
|
+
let(:group_chars) { 4 }
|
1432
|
+
|
1433
|
+
subject { described_class.new(group_chars: group_chars) }
|
1434
|
+
|
1435
|
+
let(:formatted_chars) do
|
1436
|
+
[
|
1437
|
+
["A" * group_chars] * (columns / group_chars),
|
1438
|
+
["B" * group_chars] * (columns / group_chars),
|
1439
|
+
["C" * group_chars] * (columns / group_chars)
|
1440
|
+
]
|
1441
|
+
end
|
1442
|
+
|
1443
|
+
it "must group the characters into fixed length strings" do
|
1444
|
+
yielded_chars= []
|
1445
|
+
|
1446
|
+
subject.each_formatted_row(data) do |*row,chars|
|
1447
|
+
yielded_chars << chars
|
1448
|
+
end
|
1449
|
+
|
1450
|
+
expect(yielded_chars).to eq(formatted_chars)
|
1451
|
+
end
|
1452
|
+
|
1453
|
+
context "but #chars.encoding is Encoding::UTF_8" do
|
1454
|
+
let(:encoding) { Encoding::UTF_8 }
|
1455
|
+
|
1456
|
+
subject do
|
1457
|
+
described_class.new(group_chars: group_chars, encoding: encoding)
|
1458
|
+
end
|
1459
|
+
|
1460
|
+
let(:char) { "\u8000" }
|
1461
|
+
let(:length) { (columns * 2) / char.bytes.length }
|
1462
|
+
let(:data) { char * length }
|
1463
|
+
|
1464
|
+
let(:formatted_chars) do
|
1465
|
+
[
|
1466
|
+
["耀.", "...", ".耀", "耀."],
|
1467
|
+
["...", ".耀", "耀.", ".."]
|
1468
|
+
]
|
1469
|
+
end
|
1470
|
+
|
1471
|
+
it "must split the characters along byte boundaries" do
|
1472
|
+
yielded_chars= []
|
1473
|
+
|
1474
|
+
subject.each_formatted_row(data) do |*row,chars|
|
1475
|
+
yielded_chars << chars
|
1476
|
+
end
|
1477
|
+
|
1478
|
+
expect(yielded_chars).to eq(formatted_chars)
|
1479
|
+
end
|
1480
|
+
end
|
1481
|
+
end
|
1482
|
+
end
|
1483
|
+
|
1484
|
+
context "when no block is given" do
|
1485
|
+
it "must return an Enumerator" do
|
1486
|
+
expect(subject.each_formatted_row(data)).to be_kind_of(Enumerator)
|
1487
|
+
end
|
1488
|
+
end
|
1489
|
+
end
|
1490
|
+
|
1491
|
+
describe "#each_line" do
|
1492
|
+
let(:lines) do
|
1493
|
+
[
|
1494
|
+
"00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 |AAAAAAAAAAAAAAAA|#{$/}",
|
1495
|
+
"00000010 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 |BBBBBBBBBBBBBBBB|#{$/}",
|
1496
|
+
"00000020 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 |CCCCCCCCCCCCCCCC|#{$/}",
|
1497
|
+
"00000030#{$/}"
|
1498
|
+
]
|
1499
|
+
end
|
1500
|
+
|
1501
|
+
it "must yield each line" do
|
1502
|
+
yielded_lines = []
|
1503
|
+
|
1504
|
+
subject.each_line(data) do |line|
|
1505
|
+
yielded_lines << line
|
1506
|
+
end
|
1507
|
+
|
1508
|
+
expect(yielded_lines).to eq(lines)
|
1509
|
+
end
|
1510
|
+
|
1511
|
+
context "when the data's length is not evenly divisble by the columns" do
|
1512
|
+
let(:data) do
|
1513
|
+
'A' * columns + \
|
1514
|
+
'B' * columns + \
|
1515
|
+
'C' * (columns / 2)
|
1516
|
+
end
|
1517
|
+
|
1518
|
+
let(:lines) do
|
1519
|
+
[
|
1520
|
+
"00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 |AAAAAAAAAAAAAAAA|#{$/}",
|
1521
|
+
"00000010 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 |BBBBBBBBBBBBBBBB|#{$/}",
|
1522
|
+
"00000020 43 43 43 43 43 43 43 43 |CCCCCCCC|#{$/}",
|
1523
|
+
"00000028#{$/}"
|
1524
|
+
]
|
1525
|
+
end
|
1526
|
+
|
1527
|
+
it "must yield the last left-adjusted partial row" do
|
1528
|
+
yielded_lines = []
|
1529
|
+
|
1530
|
+
subject.each_line(data) do |line|
|
1531
|
+
yielded_lines << line
|
1532
|
+
end
|
1533
|
+
|
1534
|
+
expect(yielded_lines).to eq(lines)
|
1535
|
+
end
|
1536
|
+
end
|
1537
|
+
|
1538
|
+
context "when the data contains repeating rows" do
|
1539
|
+
let(:data) do
|
1540
|
+
'A' * columns + \
|
1541
|
+
'B' * columns + \
|
1542
|
+
'B' * columns + \
|
1543
|
+
'B' * columns + \
|
1544
|
+
'C' * columns
|
1545
|
+
end
|
1546
|
+
|
1547
|
+
let(:lines) do
|
1548
|
+
[
|
1549
|
+
"00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 |AAAAAAAAAAAAAAAA|#{$/}",
|
1550
|
+
"00000010 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 |BBBBBBBBBBBBBBBB|#{$/}",
|
1551
|
+
"*#{$/}",
|
1552
|
+
"00000040 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 |CCCCCCCCCCCCCCCC|#{$/}",
|
1553
|
+
"00000050#{$/}"
|
1554
|
+
]
|
1555
|
+
end
|
1556
|
+
|
1557
|
+
it "must yield the formatted lines and a '*' row to denote the beginning of repeating lines" do
|
1558
|
+
yielded_lines = []
|
1559
|
+
|
1560
|
+
subject.each_line(data) do |line|
|
1561
|
+
yielded_lines << line
|
1562
|
+
end
|
1563
|
+
|
1564
|
+
expect(yielded_lines).to eq(lines)
|
1565
|
+
end
|
1566
|
+
end
|
1567
|
+
|
1568
|
+
context "when #repeating is true" do
|
1569
|
+
subject { described_class.new(repeating: true) }
|
1570
|
+
|
1571
|
+
let(:data) do
|
1572
|
+
'A' * columns + \
|
1573
|
+
'B' * columns + \
|
1574
|
+
'B' * columns + \
|
1575
|
+
'B' * columns + \
|
1576
|
+
'C' * columns
|
1577
|
+
end
|
1578
|
+
|
1579
|
+
let(:lines) do
|
1580
|
+
[
|
1581
|
+
"00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 |AAAAAAAAAAAAAAAA|#{$/}",
|
1582
|
+
"00000010 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 |BBBBBBBBBBBBBBBB|#{$/}",
|
1583
|
+
"00000020 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 |BBBBBBBBBBBBBBBB|#{$/}",
|
1584
|
+
"00000030 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 |BBBBBBBBBBBBBBBB|#{$/}",
|
1585
|
+
"00000040 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 |CCCCCCCCCCCCCCCC|#{$/}",
|
1586
|
+
"00000050#{$/}"
|
1587
|
+
]
|
1588
|
+
end
|
1589
|
+
|
1590
|
+
it "must yield repeating lines" do
|
1591
|
+
yielded_lines = []
|
1592
|
+
|
1593
|
+
subject.each_line(data) do |line|
|
1594
|
+
yielded_lines << line
|
1595
|
+
end
|
1596
|
+
|
1597
|
+
expect(yielded_lines).to eq(lines)
|
1598
|
+
end
|
1599
|
+
end
|
1600
|
+
|
1601
|
+
context "when #chars_column is false" do
|
1602
|
+
subject { described_class.new(chars_column: false) }
|
1603
|
+
|
1604
|
+
let(:lines) do
|
1605
|
+
[
|
1606
|
+
"00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41#{$/}",
|
1607
|
+
"00000010 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42#{$/}",
|
1608
|
+
"00000020 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43#{$/}",
|
1609
|
+
"00000030#{$/}"
|
1610
|
+
]
|
1611
|
+
end
|
1612
|
+
|
1613
|
+
it "must omit the characters column" do
|
1614
|
+
yielded_lines = []
|
1615
|
+
|
1616
|
+
subject.each_line(data) do |line|
|
1617
|
+
yielded_lines << line
|
1618
|
+
end
|
1619
|
+
|
1620
|
+
expect(yielded_lines).to eq(lines)
|
1621
|
+
end
|
1622
|
+
end
|
1623
|
+
|
1624
|
+
context "when #group_columns is set" do
|
1625
|
+
subject { described_class.new(group_columns: 4) }
|
1626
|
+
|
1627
|
+
let(:lines) do
|
1628
|
+
[
|
1629
|
+
"00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 |AAAAAAAAAAAAAAAA|#{$/}",
|
1630
|
+
"00000010 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 |BBBBBBBBBBBBBBBB|#{$/}",
|
1631
|
+
"00000020 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 |CCCCCCCCCCCCCCCC|#{$/}",
|
1632
|
+
"00000030#{$/}"
|
1633
|
+
]
|
1634
|
+
end
|
1635
|
+
|
1636
|
+
it "must group columns together with an extra space" do
|
1637
|
+
yielded_lines = []
|
1638
|
+
|
1639
|
+
subject.each_line(data) do |line|
|
1640
|
+
yielded_lines << line
|
1641
|
+
end
|
1642
|
+
|
1643
|
+
expect(yielded_lines).to eq(lines)
|
1644
|
+
end
|
1645
|
+
end
|
1646
|
+
|
1647
|
+
context "when #group_chars is set" do
|
1648
|
+
let(:group_chars) { 4 }
|
1649
|
+
|
1650
|
+
subject { described_class.new(group_chars: group_chars) }
|
1651
|
+
|
1652
|
+
let(:lines) do
|
1653
|
+
[
|
1654
|
+
"00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 |AAAA|AAAA|AAAA|AAAA|#{$/}",
|
1655
|
+
"00000010 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 |BBBB|BBBB|BBBB|BBBB|#{$/}",
|
1656
|
+
"00000020 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 |CCCC|CCCC|CCCC|CCCC|#{$/}",
|
1657
|
+
"00000030#{$/}"
|
1658
|
+
]
|
1659
|
+
end
|
1660
|
+
|
1661
|
+
it "must group the characters into fixed length strings" do
|
1662
|
+
yielded_lines = []
|
1663
|
+
|
1664
|
+
subject.each_line(data) do |line|
|
1665
|
+
yielded_lines << line
|
1666
|
+
end
|
1667
|
+
|
1668
|
+
expect(yielded_lines).to eq(lines)
|
1669
|
+
end
|
1670
|
+
end
|
1671
|
+
end
|
1672
|
+
|
1673
|
+
let(:lines) { subject.each_line(data).to_a }
|
1674
|
+
|
1675
|
+
describe "#hexdump" do
|
1676
|
+
it "must print each line of the hexdump to stdout" do
|
1677
|
+
expect {
|
1678
|
+
subject.hexdump(data)
|
1679
|
+
}.to output(lines.join).to_stdout
|
1680
|
+
end
|
1681
|
+
|
1682
|
+
context "when the output: keyword argument is given" do
|
1683
|
+
let(:output) { StringIO.new }
|
1684
|
+
|
1685
|
+
it "must print each line of hte hexdump to the given output" do
|
1686
|
+
subject.hexdump(data, output: output)
|
1687
|
+
|
1688
|
+
expect(output.string).to eq(lines.join)
|
1689
|
+
end
|
1690
|
+
|
1691
|
+
context "but the output does not support #<<" do
|
1692
|
+
let(:output) { Object.new }
|
1693
|
+
|
1694
|
+
it do
|
1695
|
+
expect {
|
1696
|
+
subject.hexdump(data, output: output)
|
1697
|
+
}.to raise_error(ArgumentError,"output must support the #<< method")
|
1698
|
+
end
|
1699
|
+
end
|
1700
|
+
end
|
1701
|
+
end
|
1702
|
+
|
1703
|
+
describe "#dump" do
|
1704
|
+
it "must append each line of the hexdump to the given String" do
|
1705
|
+
expect(subject.dump(data)).to eq(lines.join)
|
1706
|
+
end
|
1707
|
+
end
|
1708
|
+
end
|