hexdump 0.3.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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