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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog.md +68 -2
  3. data/Gemfile +1 -0
  4. data/README.md +486 -135
  5. data/benchmark.rb +29 -22
  6. data/lib/hexdump/chars.rb +46 -0
  7. data/lib/hexdump/core_ext/file.rb +68 -6
  8. data/lib/hexdump/core_ext/io.rb +2 -2
  9. data/lib/hexdump/core_ext/kernel.rb +7 -0
  10. data/lib/hexdump/core_ext/string.rb +2 -2
  11. data/lib/hexdump/core_ext/string_io.rb +2 -2
  12. data/lib/hexdump/core_ext.rb +1 -0
  13. data/lib/hexdump/format_string.rb +43 -0
  14. data/lib/hexdump/hexdump.rb +768 -75
  15. data/lib/hexdump/mixin.rb +198 -0
  16. data/lib/hexdump/module_methods.rb +132 -0
  17. data/lib/hexdump/numeric/binary.rb +55 -0
  18. data/lib/hexdump/numeric/char_or_int.rb +90 -0
  19. data/lib/hexdump/numeric/decimal.rb +56 -0
  20. data/lib/hexdump/numeric/exceptions.rb +11 -0
  21. data/lib/hexdump/numeric/hexadecimal.rb +59 -0
  22. data/lib/hexdump/numeric/octal.rb +55 -0
  23. data/lib/hexdump/numeric.rb +5 -0
  24. data/lib/hexdump/reader.rb +314 -0
  25. data/lib/hexdump/theme/ansi.rb +81 -0
  26. data/lib/hexdump/theme/rule.rb +159 -0
  27. data/lib/hexdump/theme.rb +61 -0
  28. data/lib/hexdump/type.rb +232 -0
  29. data/lib/hexdump/types.rb +108 -0
  30. data/lib/hexdump/version.rb +1 -1
  31. data/lib/hexdump.rb +12 -1
  32. data/spec/chars_spec.rb +76 -0
  33. data/spec/core_ext_spec.rb +10 -6
  34. data/spec/hexdump_class_spec.rb +1708 -0
  35. data/spec/hexdump_module_spec.rb +23 -0
  36. data/spec/mixin_spec.rb +37 -0
  37. data/spec/numeric/binary_spec.rb +239 -0
  38. data/spec/numeric/char_or_int_spec.rb +210 -0
  39. data/spec/numeric/decimal_spec.rb +317 -0
  40. data/spec/numeric/hexadecimal_spec.rb +320 -0
  41. data/spec/numeric/octal_spec.rb +239 -0
  42. data/spec/reader_spec.rb +863 -0
  43. data/spec/theme/ansi_spec.rb +242 -0
  44. data/spec/theme/rule_spec.rb +199 -0
  45. data/spec/theme_spec.rb +94 -0
  46. data/spec/type_spec.rb +317 -0
  47. data/spec/types_spec.rb +904 -0
  48. metadata +39 -10
  49. data/.gemtest +0 -0
  50. data/lib/hexdump/dumper.rb +0 -419
  51. data/spec/dumper_spec.rb +0 -329
  52. data/spec/hexdump_spec.rb +0 -30
@@ -0,0 +1,198 @@
1
+ require 'hexdump/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 [Integer] offset
31
+ # The offset to start the index at.
32
+ #
33
+ # @param [Hash{Symbol => Object}] kwargs
34
+ # Additional keyword arguments for {Hexdump#initialize}.
35
+ #
36
+ # @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)
37
+ # The type to decode the data as.
38
+ #
39
+ # @option kwargs [Integer, nil] :offset
40
+ # Controls whether to skip N number of bytes before starting to read data.
41
+ #
42
+ # @option kwargs [Integer, nil] :length
43
+ # Controls control many bytes to read.
44
+ #
45
+ # @option kwargs [Boolean] :zero_pad (false)
46
+ # Enables or disables zero padding of data, so that the remaining bytes
47
+ # can be decoded as a uint, int, or float.
48
+ #
49
+ # @option kwargs [Integer] :columns (16)
50
+ # The number of bytes to dump for each line.
51
+ #
52
+ # @option kwargs [Integer, nil] :group_columns
53
+ # Separate groups of columns with an additional space.
54
+ #
55
+ # @option kwargs [Integer, :type, nil] :group_chars
56
+ # Group chars into columns.
57
+ # If `:type`, then the chars will be grouped by the `type`'s size.
58
+ #
59
+ # @option kwargs [Boolean] :repeating
60
+ # Controls whether to omit repeating duplicate rows data with a `*`.
61
+ #
62
+ # @option kwargs [16, 10, 8, 2] :base (16)
63
+ # The base to print bytes in.
64
+ #
65
+ # @option kwargs [16, 10, 8, 2] :index_base (16)
66
+ # Control the base that the index is displayed in.
67
+ #
68
+ # @option kwargs [Boolean] :chars_column (true)
69
+ # Controls whether to display the characters column.
70
+ #
71
+ # @option kwargs [:ascii, :utf8, Encoding, nil] :encoding
72
+ # The encoding to display the characters in.
73
+ #
74
+ # @option kwargs [Integer, nil] :index_offset
75
+ # The offset to start the index at.
76
+ #
77
+ # @option kwargs [Boolean, Hash{:index,:numeric,:chars => Symbol,Array<Symbol>}] :style
78
+ # Enables theming of index, numeric, or chars columns.
79
+ #
80
+ # @option kwargs [Boolean, Hash{:index,:numeric,:chars => Hash{String,Regexp => Symbol,Array<Symbol>}}] :highlights
81
+ # Enables selective highlighting of index, numeric, or chars columns.
82
+ #
83
+ # @yield [hexdump]
84
+ # If a block is given, it will be passed the newly initialized hexdump
85
+ # instance.
86
+ #
87
+ # @yieldparam [Hexdump::Hexdump] hexdump
88
+ # The newly initialized hexdump instance.
89
+ #
90
+ # @raise [ArgumentError]
91
+ # The given data does not define the `#each_byte` method,
92
+ # the `:output` value does not support the `#<<` method or
93
+ # the `:base` value was unknown.
94
+ #
95
+ # @example
96
+ # obj.hexdump
97
+ # # ...
98
+ #
99
+ # @example Hexdumping to a custom output:
100
+ # File.open('hexdump.txt') do |output|
101
+ # obj.hexdump(output: output)
102
+ # end
103
+ #
104
+ # @example Hexdumping to an Array:
105
+ # lines = []
106
+ # obj.hexdump(output: lines)
107
+ #
108
+ def hexdump(output: $stdout, **kwargs,&block)
109
+ hexdump = ::Hexdump::Hexdump.new(**kwargs,&block)
110
+
111
+ hexdump.hexdump(self, output: output)
112
+ end
113
+
114
+ #
115
+ # Outputs the hexdump to a String.
116
+ #
117
+ # @param [Integer] offset
118
+ # The offset to start the index at.
119
+ #
120
+ # @param [Hash{Symbol => Object}] kwargs
121
+ # Additional keyword arguments for {Hexdump#initialize}.
122
+ #
123
+ # @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)
124
+ # The type to decode the data as.
125
+ #
126
+ # @option kwargs [Integer, nil] :offset
127
+ # Controls whether to skip N number of bytes before starting to read data.
128
+ #
129
+ # @option kwargs [Integer, nil] :length
130
+ # Controls control many bytes to read.
131
+ #
132
+ # @option kwargs [Boolean] :zero_pad (false)
133
+ # Enables or disables zero padding of data, so that the remaining bytes
134
+ # can be decoded as a uint, int, or float.
135
+ #
136
+ # @option kwargs [Integer] :columns (16)
137
+ # The number of bytes to dump for each line.
138
+ #
139
+ # @option kwargs [Integer, nil] :group_columns
140
+ # Separate groups of columns with an additional space.
141
+ #
142
+ # @option kwargs [Integer, :type, nil] :group_chars
143
+ # Group chars into columns.
144
+ # If `:type`, then the chars will be grouped by the `type`'s size.
145
+ #
146
+ # @option kwargs [Boolean] :repeating
147
+ # Controls whether to omit repeating duplicate rows data with a `*`.
148
+ #
149
+ # @option kwargs [16, 10, 8, 2] :base (16)
150
+ # The base to print bytes in.
151
+ #
152
+ # @option kwargs [16, 10, 8, 2] :index_base (16)
153
+ # Control the base that the index is displayed in.
154
+ #
155
+ # @option kwargs [Integer, nil] :index_offset
156
+ # The offset to start the index at.
157
+ #
158
+ # @option kwargs [Boolean] :chars_column (true)
159
+ # Controls whether to display the characters column.
160
+ #
161
+ # @option kwargs [:ascii, :utf8, Encoding, nil] :encoding
162
+ # The encoding to display the characters in.
163
+ #
164
+ # @option kwargs [Boolean, Hash{:index,:numeric,:chars => Symbol,Array<Symbol>}] :style
165
+ # Enables theming of index, numeric, or chars columns.
166
+ #
167
+ # @option kwargs [Boolean, Hash{:index,:numeric,:chars => Hash{String,Regexp => Symbol,Array<Symbol>}}] :highlights
168
+ # Enables selective highlighting of index, numeric, or chars columns.
169
+ #
170
+ # @yield [hexdump]
171
+ # If a block is given, it will be passed the newly initialized hexdump
172
+ # instance.
173
+ #
174
+ # @yieldparam [Hexdump::Hexdump] hexdump
175
+ # The newly initialized hexdump instance.
176
+ #
177
+ # @return [String]
178
+ # The output of the hexdump.
179
+ #
180
+ # @raise [ArgumentError]
181
+ # The given data does not define the `#each_byte` method,
182
+ # the `:base` value was unknown.
183
+ #
184
+ # @note
185
+ # **Caution:** this method appends each line of the hexdump to a String,
186
+ # and that String can grow quite large and consume a lot of memory.
187
+ #
188
+ # @example
189
+ # obj.hexdump
190
+ # # => "..."
191
+ #
192
+ def to_hexdump(**kwargs,&block)
193
+ hexdump = ::Hexdump::Hexdump.new(**kwargs,&block)
194
+
195
+ hexdump.dump(self)
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,132 @@
1
+ require 'hexdump/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 'hexdump/numeric/exceptions'
2
+ require 'hexdump/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,90 @@
1
+ require 'hexdump/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 [Base::Hexadecimal, Base::Decimal, Base::Octal, Base::Binary]
13
+ attr_reader :base
14
+
15
+ # @return [Encoding, nil]
16
+ attr_reader :encoding
17
+
18
+ #
19
+ # Initializes the character format.
20
+ #
21
+ # @param [Base::Hexadecimal, Base::Decimal, Base::Octal, Base::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
+ def width
36
+ @base.width
37
+ end
38
+
39
+ #
40
+ # Formats a given ASCII byte value to a character or numeric format.
41
+ #
42
+ # @param [Integer] value
43
+ # The ASCII byte value.
44
+ #
45
+ # @return [String]
46
+ # The character or numeric formatted value.
47
+ #
48
+ def %(value)
49
+ if value == 0x00
50
+ super("\\0")
51
+ elsif value == 0x07
52
+ super("\\a")
53
+ elsif value == 0x08
54
+ super("\\b")
55
+ elsif value == 0x09
56
+ super("\\t")
57
+ elsif value == 0x0a
58
+ super("\\n")
59
+ elsif value == 0x0b
60
+ super("\\v")
61
+ elsif value == 0x0c
62
+ super("\\f")
63
+ elsif value == 0x0d
64
+ super("\\r")
65
+ else
66
+ if @encoding
67
+ if value >= 0x00
68
+ char = value.chr(@encoding) rescue nil
69
+
70
+ if char && char =~ /[[:print:]]/
71
+ super(char)
72
+ else
73
+ @base % value
74
+ end
75
+ else
76
+ @base % value
77
+ end
78
+ else
79
+ if (value >= 0x20 && value <= 0x7e)
80
+ super(value.chr)
81
+ else
82
+ @base % value
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ end
89
+ end
90
+ end