hexdump 0.3.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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'