hexdump 0.3.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +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
@@ -0,0 +1,192 @@
|
|
1
|
+
require_relative 'hexdump'
|
2
|
+
|
3
|
+
module Hexdump
|
4
|
+
#
|
5
|
+
# Provides a {hexdump Mixin#hexdump} method that can be included into objects.
|
6
|
+
#
|
7
|
+
# class DataObject
|
8
|
+
#
|
9
|
+
# include Hexdump::Mixin
|
10
|
+
#
|
11
|
+
# def each_byte
|
12
|
+
# # ...
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# data.hexdump
|
18
|
+
#
|
19
|
+
# @api public
|
20
|
+
#
|
21
|
+
# @since 1.0.0
|
22
|
+
#
|
23
|
+
module Mixin
|
24
|
+
#
|
25
|
+
# Prints a hexdumps of the object.
|
26
|
+
#
|
27
|
+
# @param [#print] output ($stdout)
|
28
|
+
# The output to print the hexdump to.
|
29
|
+
#
|
30
|
+
# @param [Hash{Symbol => Object}] kwargs
|
31
|
+
# Additional keyword arguments for {Hexdump#initialize}.
|
32
|
+
#
|
33
|
+
# @option kwargs [:int8, :uint8, :char, :uchar, :byte, :int16, :int16_le, :int16_be, :int16_ne, :uint16, :uint16_le, :uint16_be, :uint16_ne, :short, :short_le, :short_be, :short_ne, :ushort, :ushort_le, :ushort_be, :ushort_ne, :int32, :int32_le, :int32_be, :int32_ne, :uint32, :uint32_le, :uint32_be, :uint32_ne, :int, :long, :long_le, :long_be, :long_ne, :uint, :ulong, :ulong_le, :ulong_be, :ulong_ne, :int64, :int64_le, :int64_be, :int64_ne, :uint64, :uint64_le, :uint64_be, :uint64_ne, :long_long, :long_long_le, :long_long_be, :long_long_ne, :ulong_long, :ulong_long_le, :ulong_long_be, :ulong_long_ne, :float, :float_le, :float_be, :float_ne, :double, :double_le, :double_be, :double_ne] :type (:byte)
|
34
|
+
# The type to decode the data as.
|
35
|
+
#
|
36
|
+
# @option kwargs [Integer, nil] :offset
|
37
|
+
# Controls whether to skip N number of bytes before starting to read data.
|
38
|
+
#
|
39
|
+
# @option kwargs [Integer, nil] :length
|
40
|
+
# Controls control many bytes to read.
|
41
|
+
#
|
42
|
+
# @option kwargs [Boolean] :zero_pad (false)
|
43
|
+
# Enables or disables zero padding of data, so that the remaining bytes
|
44
|
+
# can be decoded as a uint, int, or float.
|
45
|
+
#
|
46
|
+
# @option kwargs [Integer] :columns (16)
|
47
|
+
# The number of bytes to dump for each line.
|
48
|
+
#
|
49
|
+
# @option kwargs [Integer, nil] :group_columns
|
50
|
+
# Separate groups of columns with an additional space.
|
51
|
+
#
|
52
|
+
# @option kwargs [Integer, :type, nil] :group_chars
|
53
|
+
# Group chars into columns.
|
54
|
+
# If `:type`, then the chars will be grouped by the `type`'s size.
|
55
|
+
#
|
56
|
+
# @option kwargs [Boolean] :repeating
|
57
|
+
# Controls whether to omit repeating duplicate rows data with a `*`.
|
58
|
+
#
|
59
|
+
# @option kwargs [16, 10, 8, 2] :base (16)
|
60
|
+
# The base to print bytes in.
|
61
|
+
#
|
62
|
+
# @option kwargs [16, 10, 8, 2] :index_base (16)
|
63
|
+
# Control the base that the index is displayed in.
|
64
|
+
#
|
65
|
+
# @option kwargs [Boolean] :chars_column (true)
|
66
|
+
# Controls whether to display the characters column.
|
67
|
+
#
|
68
|
+
# @option kwargs [:ascii, :utf8, Encoding, nil] :encoding
|
69
|
+
# The encoding to display the characters in.
|
70
|
+
#
|
71
|
+
# @option kwargs [Integer, nil] :index_offset
|
72
|
+
# The offset to start the index at.
|
73
|
+
#
|
74
|
+
# @option kwargs [Boolean, Hash{:index,:numeric,:chars => Symbol,Array<Symbol>}] :style
|
75
|
+
# Enables theming of index, numeric, or chars columns.
|
76
|
+
#
|
77
|
+
# @option kwargs [Boolean, Hash{:index,:numeric,:chars => Hash{String,Regexp => Symbol,Array<Symbol>}}] :highlights
|
78
|
+
# Enables selective highlighting of index, numeric, or chars columns.
|
79
|
+
#
|
80
|
+
# @yield [hexdump]
|
81
|
+
# If a block is given, it will be passed the newly initialized hexdump
|
82
|
+
# instance.
|
83
|
+
#
|
84
|
+
# @yieldparam [Hexdump::Hexdump] hexdump
|
85
|
+
# The newly initialized hexdump instance.
|
86
|
+
#
|
87
|
+
# @raise [ArgumentError]
|
88
|
+
# The given data does not define the `#each_byte` method,
|
89
|
+
# the `:output` value does not support the `#<<` method or
|
90
|
+
# the `:base` value was unknown.
|
91
|
+
#
|
92
|
+
# @example
|
93
|
+
# obj.hexdump
|
94
|
+
# # ...
|
95
|
+
#
|
96
|
+
# @example Hexdumping to a custom output:
|
97
|
+
# File.open('hexdump.txt') do |output|
|
98
|
+
# obj.hexdump(output: output)
|
99
|
+
# end
|
100
|
+
#
|
101
|
+
# @example Hexdumping to an Array:
|
102
|
+
# lines = []
|
103
|
+
# obj.hexdump(output: lines)
|
104
|
+
#
|
105
|
+
def hexdump(output: $stdout, **kwargs,&block)
|
106
|
+
hexdump = ::Hexdump::Hexdump.new(**kwargs,&block)
|
107
|
+
|
108
|
+
hexdump.hexdump(self, output: output)
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
# Outputs the hexdump to a String.
|
113
|
+
#
|
114
|
+
# @param [Hash{Symbol => Object}] kwargs
|
115
|
+
# Additional keyword arguments for {Hexdump#initialize}.
|
116
|
+
#
|
117
|
+
# @option kwargs [:int8, :uint8, :char, :uchar, :byte, :int16, :int16_le, :int16_be, :int16_ne, :uint16, :uint16_le, :uint16_be, :uint16_ne, :short, :short_le, :short_be, :short_ne, :ushort, :ushort_le, :ushort_be, :ushort_ne, :int32, :int32_le, :int32_be, :int32_ne, :uint32, :uint32_le, :uint32_be, :uint32_ne, :int, :long, :long_le, :long_be, :long_ne, :uint, :ulong, :ulong_le, :ulong_be, :ulong_ne, :int64, :int64_le, :int64_be, :int64_ne, :uint64, :uint64_le, :uint64_be, :uint64_ne, :long_long, :long_long_le, :long_long_be, :long_long_ne, :ulong_long, :ulong_long_le, :ulong_long_be, :ulong_long_ne, :float, :float_le, :float_be, :float_ne, :double, :double_le, :double_be, :double_ne] :type (:byte)
|
118
|
+
# The type to decode the data as.
|
119
|
+
#
|
120
|
+
# @option kwargs [Integer, nil] :offset
|
121
|
+
# Controls whether to skip N number of bytes before starting to read data.
|
122
|
+
#
|
123
|
+
# @option kwargs [Integer, nil] :length
|
124
|
+
# Controls control many bytes to read.
|
125
|
+
#
|
126
|
+
# @option kwargs [Boolean] :zero_pad (false)
|
127
|
+
# Enables or disables zero padding of data, so that the remaining bytes
|
128
|
+
# can be decoded as a uint, int, or float.
|
129
|
+
#
|
130
|
+
# @option kwargs [Integer] :columns (16)
|
131
|
+
# The number of bytes to dump for each line.
|
132
|
+
#
|
133
|
+
# @option kwargs [Integer, nil] :group_columns
|
134
|
+
# Separate groups of columns with an additional space.
|
135
|
+
#
|
136
|
+
# @option kwargs [Integer, :type, nil] :group_chars
|
137
|
+
# Group chars into columns.
|
138
|
+
# If `:type`, then the chars will be grouped by the `type`'s size.
|
139
|
+
#
|
140
|
+
# @option kwargs [Boolean] :repeating
|
141
|
+
# Controls whether to omit repeating duplicate rows data with a `*`.
|
142
|
+
#
|
143
|
+
# @option kwargs [16, 10, 8, 2] :base (16)
|
144
|
+
# The base to print bytes in.
|
145
|
+
#
|
146
|
+
# @option kwargs [16, 10, 8, 2] :index_base (16)
|
147
|
+
# Control the base that the index is displayed in.
|
148
|
+
#
|
149
|
+
# @option kwargs [Integer, nil] :index_offset
|
150
|
+
# The offset to start the index at.
|
151
|
+
#
|
152
|
+
# @option kwargs [Boolean] :chars_column (true)
|
153
|
+
# Controls whether to display the characters column.
|
154
|
+
#
|
155
|
+
# @option kwargs [:ascii, :utf8, Encoding, nil] :encoding
|
156
|
+
# The encoding to display the characters in.
|
157
|
+
#
|
158
|
+
# @option kwargs [Boolean, Hash{:index,:numeric,:chars => Symbol,Array<Symbol>}] :style
|
159
|
+
# Enables theming of index, numeric, or chars columns.
|
160
|
+
#
|
161
|
+
# @option kwargs [Boolean, Hash{:index,:numeric,:chars => Hash{String,Regexp => Symbol,Array<Symbol>}}] :highlights
|
162
|
+
# Enables selective highlighting of index, numeric, or chars columns.
|
163
|
+
#
|
164
|
+
# @yield [hexdump]
|
165
|
+
# If a block is given, it will be passed the newly initialized hexdump
|
166
|
+
# instance.
|
167
|
+
#
|
168
|
+
# @yieldparam [Hexdump::Hexdump] hexdump
|
169
|
+
# The newly initialized hexdump instance.
|
170
|
+
#
|
171
|
+
# @return [String]
|
172
|
+
# The output of the hexdump.
|
173
|
+
#
|
174
|
+
# @raise [ArgumentError]
|
175
|
+
# The given data does not define the `#each_byte` method,
|
176
|
+
# the `:base` value was unknown.
|
177
|
+
#
|
178
|
+
# @note
|
179
|
+
# **Caution:** this method appends each line of the hexdump to a String,
|
180
|
+
# and that String can grow quite large and consume a lot of memory.
|
181
|
+
#
|
182
|
+
# @example
|
183
|
+
# obj.hexdump
|
184
|
+
# # => "..."
|
185
|
+
#
|
186
|
+
def to_hexdump(**kwargs,&block)
|
187
|
+
hexdump = ::Hexdump::Hexdump.new(**kwargs,&block)
|
188
|
+
|
189
|
+
hexdump.dump(self)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require_relative 'hexdump'
|
2
|
+
|
3
|
+
module Hexdump
|
4
|
+
#
|
5
|
+
# Provides the {hexdump ModuleMethods#hexdump} top-level method that can be
|
6
|
+
# extended into modules.
|
7
|
+
#
|
8
|
+
# module Context
|
9
|
+
#
|
10
|
+
# extend Hexdump::ModuleMethods
|
11
|
+
#
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
module ModuleMethods
|
15
|
+
#
|
16
|
+
# Hexdumps the given data.
|
17
|
+
#
|
18
|
+
# @param [#each_byte] data
|
19
|
+
# The data to be hexdumped.
|
20
|
+
#
|
21
|
+
# @param [#print] output ($stdout)
|
22
|
+
# The output to print the hexdump to.
|
23
|
+
#
|
24
|
+
# @param [Hash{Symbol => Object}] kwargs
|
25
|
+
# Additional keyword arguments for {Hexdump#initialize}.
|
26
|
+
#
|
27
|
+
# @option kwargs [:int8, :uint8, :char, :uchar, :byte, :int16, :int16_le, :int16_be, :int16_ne, :uint16, :uint16_le, :uint16_be, :uint16_ne, :short, :short_le, :short_be, :short_ne, :ushort, :ushort_le, :ushort_be, :ushort_ne, :int32, :int32_le, :int32_be, :int32_ne, :uint32, :uint32_le, :uint32_be, :uint32_ne, :int, :long, :long_le, :long_be, :long_ne, :uint, :ulong, :ulong_le, :ulong_be, :ulong_ne, :int64, :int64_le, :int64_be, :int64_ne, :uint64, :uint64_le, :uint64_be, :uint64_ne, :long_long, :long_long_le, :long_long_be, :long_long_ne, :ulong_long, :ulong_long_le, :ulong_long_be, :ulong_long_ne, :float, :float_le, :float_be, :float_ne, :double, :double_le, :double_be, :double_ne] :type (:byte)
|
28
|
+
# The type to decode the data as.
|
29
|
+
#
|
30
|
+
# @option kwargs [Integer, nil] :offset
|
31
|
+
# Controls whether to skip N number of bytes before starting to read data.
|
32
|
+
#
|
33
|
+
# @option kwargs [Integer, nil] :length
|
34
|
+
# Controls control many bytes to read.
|
35
|
+
#
|
36
|
+
# @option kwargs [Boolean] :zero_pad (false)
|
37
|
+
# Enables or disables zero padding of data, so that the remaining bytes
|
38
|
+
# can be decoded as a uint, int, or float.
|
39
|
+
#
|
40
|
+
# @option kwargs [Integer] :columns (16)
|
41
|
+
# The number of bytes to dump for each line.
|
42
|
+
#
|
43
|
+
# @option kwargs [Integer, nil] :group_columns
|
44
|
+
# Separate groups of columns with an additional space.
|
45
|
+
#
|
46
|
+
# @option kwargs [Integer, :type, nil] :group_chars
|
47
|
+
# Group chars into columns.
|
48
|
+
# If `:type`, then the chars will be grouped by the `type`'s size.
|
49
|
+
#
|
50
|
+
# @option kwargs [Boolean] :repeating
|
51
|
+
# Controls whether to omit repeating duplicate rows data with a `*`.
|
52
|
+
#
|
53
|
+
# @option kwargs [16, 10, 8, 2] :base (16)
|
54
|
+
# The base to print bytes in.
|
55
|
+
#
|
56
|
+
# @option kwargs [16, 10, 8, 2] :index_base (16)
|
57
|
+
# Control the base that the index is displayed in.
|
58
|
+
#
|
59
|
+
# @option kwargs [Integer, nil] :index_offset
|
60
|
+
# The offset to start the index at.
|
61
|
+
#
|
62
|
+
# @option kwargs [Boolean] :chars_column (true)
|
63
|
+
# Controls whether to display the characters column.
|
64
|
+
#
|
65
|
+
# @option kwargs [:ascii, :utf8, Encoding, nil] :encoding
|
66
|
+
# The encoding to display the characters in.
|
67
|
+
#
|
68
|
+
# @option kwargs [Boolean, Hash{:index,:numeric,:chars => Symbol,Array<Symbol>}] :style
|
69
|
+
# Enables theming of index, numeric, or chars columns.
|
70
|
+
#
|
71
|
+
# @option kwargs [Boolean, Hash{:index,:numeric,:chars => Hash{String,Regexp => Symbol,Array<Symbol>}}] :highlights
|
72
|
+
# Enables selective highlighting of index, numeric, or chars columns.
|
73
|
+
#
|
74
|
+
# @yield [hexdump]
|
75
|
+
# If a block is given, it will be passed the newly initialized hexdump
|
76
|
+
# instance.
|
77
|
+
#
|
78
|
+
# @yieldparam [Hexdump::Hexdump] hexdump
|
79
|
+
# The newly initialized hexdump instance.
|
80
|
+
#
|
81
|
+
# @raise [ArgumentError]
|
82
|
+
# The given data does not define the `#each_byte` method,
|
83
|
+
# the `:output` value does not support the `#<<` method or
|
84
|
+
# the `:base` value was unknown.
|
85
|
+
#
|
86
|
+
# @example
|
87
|
+
# hexdump("hello\0")
|
88
|
+
# # 00000000 68 65 6c 6c 6f 00 |hello.|
|
89
|
+
# # 00000006
|
90
|
+
#
|
91
|
+
# @example Hexdumping to a custom output:
|
92
|
+
# File.open('hexdump.txt') do |output|
|
93
|
+
# hexdump("hello\0", output: output)
|
94
|
+
# end
|
95
|
+
#
|
96
|
+
# @example Hexdumping to an Array:
|
97
|
+
# lines = []
|
98
|
+
# hexdump("hello\0", output: lines)
|
99
|
+
#
|
100
|
+
# @example Hexdumping with ANSI styling:
|
101
|
+
# Hexdump.hexdump(style: {index: :white, numeric: :green, chars: :cyan})
|
102
|
+
#
|
103
|
+
# @example Hexdumping with ANSI highlighting:
|
104
|
+
# Hexdump.hexdump("hello\0", highlights: {
|
105
|
+
# index: {/00$/ => [:white, :bold]},
|
106
|
+
# numeric: {
|
107
|
+
# /^[8-f][0-9a-f]$/ => :faint,
|
108
|
+
# /f/ => :cyan,
|
109
|
+
# '00' => [:black, :on_red]
|
110
|
+
# },
|
111
|
+
# chars: {/[^\.]+/ => :green}
|
112
|
+
# })
|
113
|
+
#
|
114
|
+
# @example Configuring the hexdump with a block:
|
115
|
+
# Hexdump.hexdump("hello\0") do |hexdump|
|
116
|
+
# hexdump.type = :uint16
|
117
|
+
# # ...
|
118
|
+
#
|
119
|
+
# hexdump.theme do |theme|
|
120
|
+
# theme.index.highlight(/00$/, [:white, :bold])
|
121
|
+
# theme.numeric.highlight(/^[8-f][0-9a-f]$/, :faint)
|
122
|
+
# # ...
|
123
|
+
# end
|
124
|
+
# end
|
125
|
+
#
|
126
|
+
def hexdump(data, output: $stdout, **kwargs,&block)
|
127
|
+
hexdump = ::Hexdump::Hexdump.new(**kwargs,&block)
|
128
|
+
|
129
|
+
hexdump.hexdump(data, output: output)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require_relative 'exceptions'
|
2
|
+
require_relative '../format_string'
|
3
|
+
|
4
|
+
module Hexdump
|
5
|
+
module Numeric
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
#
|
9
|
+
# @since 1.0.0
|
10
|
+
#
|
11
|
+
class Binary < FormatString
|
12
|
+
|
13
|
+
SIZE_TO_WIDTH = {
|
14
|
+
1 => 8, # 0xff.to_s(2).length
|
15
|
+
2 => 16, # 0xffff.to_s(2).length
|
16
|
+
4 => 32, # 0xffffffff.to_s(2).length
|
17
|
+
8 => 64 # 0xffffffffffffffff.to_s(2).length
|
18
|
+
}
|
19
|
+
|
20
|
+
# @return [Integer]
|
21
|
+
attr_reader :width
|
22
|
+
|
23
|
+
#
|
24
|
+
# Initializes the binary format.
|
25
|
+
#
|
26
|
+
# @param [Type::Int, Type::UInt] type
|
27
|
+
#
|
28
|
+
# @raise [NotImplementedError]
|
29
|
+
#
|
30
|
+
# @raise [IncompatibleTypeError]
|
31
|
+
#
|
32
|
+
# @raise [TypeError]
|
33
|
+
#
|
34
|
+
def initialize(type)
|
35
|
+
case type
|
36
|
+
when Type::Int, Type::UInt
|
37
|
+
@width = SIZE_TO_WIDTH.fetch(type.size) do
|
38
|
+
raise(NotImplementedError,"type #{type} with unsupported size #{type.size}")
|
39
|
+
end
|
40
|
+
|
41
|
+
if type.signed?
|
42
|
+
super("% .#{@width}b"); @width += 1
|
43
|
+
else
|
44
|
+
super("%.#{@width}b")
|
45
|
+
end
|
46
|
+
when Type::Float
|
47
|
+
raise(IncompatibleTypeError,"cannot format floating-point numbers in binary")
|
48
|
+
else
|
49
|
+
raise(TypeError,"unsupported type: #{type.inspect}")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require_relative '../format_string'
|
2
|
+
|
3
|
+
module Hexdump
|
4
|
+
module Numeric
|
5
|
+
#
|
6
|
+
# @api private
|
7
|
+
#
|
8
|
+
# @since 1.0.0
|
9
|
+
#
|
10
|
+
class CharOrInt < FormatString
|
11
|
+
|
12
|
+
# @return [Hexadecimal, Decimal, Octal, Binary]
|
13
|
+
attr_reader :base
|
14
|
+
|
15
|
+
# @return [Encoding, nil]
|
16
|
+
attr_reader :encoding
|
17
|
+
|
18
|
+
#
|
19
|
+
# Initializes the character format.
|
20
|
+
#
|
21
|
+
# @param [Hexadecimal, Decimal, Octal, Binary] base
|
22
|
+
# The numeric base format to fallback to if a value does not map to a
|
23
|
+
# character.
|
24
|
+
#
|
25
|
+
# @param [Encoding, nil] encoding
|
26
|
+
# The optional encoding to convert bytes to.
|
27
|
+
#
|
28
|
+
def initialize(base,encoding=nil)
|
29
|
+
@base = base
|
30
|
+
@encoding = encoding
|
31
|
+
|
32
|
+
super("%#{@base.width}s")
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# The string width associated with the numeric base.
|
37
|
+
#
|
38
|
+
# @return [Integer]
|
39
|
+
#
|
40
|
+
def width
|
41
|
+
@base.width
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
# Formats a given ASCII byte value to a character or numeric format.
|
46
|
+
#
|
47
|
+
# @param [Integer] value
|
48
|
+
# The ASCII byte value.
|
49
|
+
#
|
50
|
+
# @return [String]
|
51
|
+
# The character or numeric formatted value.
|
52
|
+
#
|
53
|
+
def %(value)
|
54
|
+
if value == 0x00
|
55
|
+
super("\\0")
|
56
|
+
elsif value == 0x07
|
57
|
+
super("\\a")
|
58
|
+
elsif value == 0x08
|
59
|
+
super("\\b")
|
60
|
+
elsif value == 0x09
|
61
|
+
super("\\t")
|
62
|
+
elsif value == 0x0a
|
63
|
+
super("\\n")
|
64
|
+
elsif value == 0x0b
|
65
|
+
super("\\v")
|
66
|
+
elsif value == 0x0c
|
67
|
+
super("\\f")
|
68
|
+
elsif value == 0x0d
|
69
|
+
super("\\r")
|
70
|
+
else
|
71
|
+
if @encoding
|
72
|
+
if value >= 0x00
|
73
|
+
char = value.chr(@encoding) rescue nil
|
74
|
+
|
75
|
+
if char && char =~ /[[:print:]]/
|
76
|
+
super(char)
|
77
|
+
else
|
78
|
+
@base % value
|
79
|
+
end
|
80
|
+
else
|
81
|
+
@base % value
|
82
|
+
end
|
83
|
+
else
|
84
|
+
if (value >= 0x20 && value <= 0x7e)
|
85
|
+
super(value.chr)
|
86
|
+
else
|
87
|
+
@base % value
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require_relative '../format_string'
|
2
|
+
|
3
|
+
module Hexdump
|
4
|
+
module Numeric
|
5
|
+
#
|
6
|
+
# @api private
|
7
|
+
#
|
8
|
+
# @since 1.0.0
|
9
|
+
#
|
10
|
+
class Decimal < FormatString
|
11
|
+
|
12
|
+
INT_SIZE_TO_WIDTH = {
|
13
|
+
1 => 3, # 0xff.to_s(10).length
|
14
|
+
2 => 5, # 0xffff.to_s(10).length
|
15
|
+
4 => 10, # 0xffffffff.to_s(10).length
|
16
|
+
8 => 20 # 0xffffffffffffffff.to_s(10).length
|
17
|
+
}
|
18
|
+
|
19
|
+
FLOAT_SIZE_TO_WIDTH = {
|
20
|
+
4 => 15,
|
21
|
+
8 => 24
|
22
|
+
}
|
23
|
+
|
24
|
+
# @return [Integer]
|
25
|
+
attr_reader :width
|
26
|
+
|
27
|
+
#
|
28
|
+
# Initializes the decimal format.
|
29
|
+
#
|
30
|
+
# @param [Type:Int, Type::UInt, Type::Float] type
|
31
|
+
#
|
32
|
+
def initialize(type)
|
33
|
+
widths = case type
|
34
|
+
when Type::Float then FLOAT_SIZE_TO_WIDTH
|
35
|
+
else INT_SIZE_TO_WIDTH
|
36
|
+
end
|
37
|
+
|
38
|
+
@width = widths.fetch(type.size) do
|
39
|
+
raise(NotImplementedError,"type #{type} with unsupported size #{type.size}")
|
40
|
+
end
|
41
|
+
|
42
|
+
case type
|
43
|
+
when Type::Float
|
44
|
+
super("% #{@width}g"); @width += 1
|
45
|
+
else
|
46
|
+
if type.signed?
|
47
|
+
super("% #{@width}d"); @width += 1
|
48
|
+
else
|
49
|
+
super("%#{@width}d")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require_relative '../format_string'
|
2
|
+
|
3
|
+
module Hexdump
|
4
|
+
module Numeric
|
5
|
+
#
|
6
|
+
# @api private
|
7
|
+
#
|
8
|
+
# @since 1.0.0
|
9
|
+
#
|
10
|
+
class Hexadecimal < FormatString
|
11
|
+
|
12
|
+
INT_SIZE_TO_WIDTH = {
|
13
|
+
1 => 2, # 0xff.to_s(16).length
|
14
|
+
2 => 4, # 0xffff.to_s(16).length
|
15
|
+
4 => 8, # 0xffffffff.to_s(16).length
|
16
|
+
8 => 16 # 0xffffffffffffffff.to_s(16).length
|
17
|
+
}
|
18
|
+
|
19
|
+
FLOAT_WIDTH = 20
|
20
|
+
|
21
|
+
# @return [Integer]
|
22
|
+
attr_reader :width
|
23
|
+
|
24
|
+
#
|
25
|
+
# Initializes the hexadecimal format.
|
26
|
+
#
|
27
|
+
# @param [Type::Int, Type::UInt, Type::Float] type
|
28
|
+
#
|
29
|
+
def initialize(type)
|
30
|
+
case type
|
31
|
+
when Type::Float
|
32
|
+
if RUBY_ENGINE == 'jruby'
|
33
|
+
# XXX: https://github.com/jruby/jruby/issues/5122
|
34
|
+
begin
|
35
|
+
"%a" % 1.0
|
36
|
+
rescue ArgumentError
|
37
|
+
raise(NotImplementedError,"jruby #{RUBY_ENGINE_VERSION} does not support the \"%a\" format string")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# NOTE: jruby does not currently support the %a format string
|
42
|
+
@width = FLOAT_WIDTH
|
43
|
+
super("% #{@width}a"); @width += 1
|
44
|
+
else
|
45
|
+
@width = INT_SIZE_TO_WIDTH.fetch(type.size) do
|
46
|
+
raise(NotImplementedError,"type #{type} with unsupported size #{type.size}")
|
47
|
+
end
|
48
|
+
|
49
|
+
if type.signed?
|
50
|
+
super("% .#{@width}x"); @width += 1
|
51
|
+
else
|
52
|
+
super("%.#{@width}x")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require_relative 'exceptions'
|
2
|
+
require_relative '../format_string'
|
3
|
+
|
4
|
+
module Hexdump
|
5
|
+
module Numeric
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
#
|
9
|
+
# @since 1.0.0
|
10
|
+
#
|
11
|
+
class Octal < FormatString
|
12
|
+
|
13
|
+
SIZE_TO_WIDTH = {
|
14
|
+
1 => 3, # 0xff.to_s(7).length
|
15
|
+
2 => 6, # 0xffff.to_s(7).length
|
16
|
+
4 => 11, # 0xffffffff.to_s(7).length
|
17
|
+
8 => 22 # 0xffffffffffffffff.to_s(7).length
|
18
|
+
}
|
19
|
+
|
20
|
+
# @return [Integer]
|
21
|
+
attr_reader :width
|
22
|
+
|
23
|
+
#
|
24
|
+
# Initializes the octal format.
|
25
|
+
#
|
26
|
+
# @param [Type::Int, Type::UInt] type
|
27
|
+
#
|
28
|
+
# @raise [NotImplementedError]
|
29
|
+
#
|
30
|
+
# @raise [IncompatibleTypeError]
|
31
|
+
#
|
32
|
+
# @raise [TypeError]
|
33
|
+
#
|
34
|
+
def initialize(type)
|
35
|
+
case type
|
36
|
+
when Type::Int, Type::UInt
|
37
|
+
@width = SIZE_TO_WIDTH.fetch(type.size) do
|
38
|
+
raise(NotImplementedError,"type #{type} with unsupported size #{type.size}")
|
39
|
+
end
|
40
|
+
|
41
|
+
if type.signed?
|
42
|
+
super("% .#{@width}o"); @width += 1
|
43
|
+
else
|
44
|
+
super("%.#{@width}o")
|
45
|
+
end
|
46
|
+
when Type::Float
|
47
|
+
raise(IncompatibleTypeError,"cannot format floating-point numbers in octal")
|
48
|
+
else
|
49
|
+
raise(TypeError,"unsupported type: #{type.inspect}")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|