hexdump 0.3.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +5 -6
  3. data/.gitignore +1 -0
  4. data/.yardopts +1 -1
  5. data/ChangeLog.md +79 -6
  6. data/Gemfile +3 -0
  7. data/LICENSE.txt +1 -1
  8. data/README.md +500 -137
  9. data/benchmark.rb +29 -22
  10. data/gemspec.yml +2 -1
  11. data/hexdump.gemspec +1 -4
  12. data/lib/hexdump/chars.rb +46 -0
  13. data/lib/hexdump/core_ext/file.rb +68 -6
  14. data/lib/hexdump/core_ext/io.rb +2 -2
  15. data/lib/hexdump/core_ext/kernel.rb +5 -0
  16. data/lib/hexdump/core_ext/string.rb +2 -2
  17. data/lib/hexdump/core_ext/string_io.rb +2 -2
  18. data/lib/hexdump/core_ext.rb +5 -4
  19. data/lib/hexdump/format_string.rb +43 -0
  20. data/lib/hexdump/hexdump.rb +766 -75
  21. data/lib/hexdump/mixin.rb +192 -0
  22. data/lib/hexdump/module_methods.rb +132 -0
  23. data/lib/hexdump/numeric/binary.rb +55 -0
  24. data/lib/hexdump/numeric/char_or_int.rb +95 -0
  25. data/lib/hexdump/numeric/decimal.rb +56 -0
  26. data/lib/hexdump/numeric/exceptions.rb +11 -0
  27. data/lib/hexdump/numeric/hexadecimal.rb +59 -0
  28. data/lib/hexdump/numeric/octal.rb +55 -0
  29. data/lib/hexdump/numeric.rb +5 -0
  30. data/lib/hexdump/reader.rb +313 -0
  31. data/lib/hexdump/theme/ansi.rb +82 -0
  32. data/lib/hexdump/theme/rule.rb +159 -0
  33. data/lib/hexdump/theme.rb +61 -0
  34. data/lib/hexdump/type.rb +233 -0
  35. data/lib/hexdump/types.rb +108 -0
  36. data/lib/hexdump/version.rb +1 -1
  37. data/lib/hexdump.rb +14 -3
  38. data/spec/chars_spec.rb +76 -0
  39. data/spec/core_ext_spec.rb +10 -6
  40. data/spec/format_string_spec.rb +22 -0
  41. data/spec/hexdump_class_spec.rb +1708 -0
  42. data/spec/hexdump_module_spec.rb +23 -0
  43. data/spec/mixin_spec.rb +37 -0
  44. data/spec/numeric/binary_spec.rb +239 -0
  45. data/spec/numeric/char_or_int_spec.rb +210 -0
  46. data/spec/numeric/decimal_spec.rb +317 -0
  47. data/spec/numeric/hexadecimal_spec.rb +320 -0
  48. data/spec/numeric/octal_spec.rb +239 -0
  49. data/spec/reader_spec.rb +866 -0
  50. data/spec/spec_helper.rb +2 -0
  51. data/spec/theme/ansi_spec.rb +242 -0
  52. data/spec/theme/rule_spec.rb +199 -0
  53. data/spec/theme_spec.rb +94 -0
  54. data/spec/type_spec.rb +317 -0
  55. data/spec/types_spec.rb +904 -0
  56. metadata +42 -12
  57. data/.gemtest +0 -0
  58. data/lib/hexdump/dumper.rb +0 -419
  59. data/lib/hexdump/extensions.rb +0 -2
  60. data/spec/dumper_spec.rb +0 -329
  61. 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,11 @@
1
+ module Hexdump
2
+ module Numeric
3
+ #
4
+ # @api private
5
+ #
6
+ # @since 1.0.0
7
+ #
8
+ class IncompatibleTypeError < TypeError
9
+ end
10
+ end
11
+ 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
@@ -0,0 +1,5 @@
1
+ require_relative 'numeric/binary'
2
+ require_relative 'numeric/octal'
3
+ require_relative 'numeric/decimal'
4
+ require_relative 'numeric/hexadecimal'
5
+ require_relative 'numeric/char_or_int'