Class: Hexdump::Dumper

Inherits:
Object
  • Object
show all
Defined in:
lib/hexdump/dumper.rb

Overview

Handles the parsing of data and formatting of the hexdump.

Since:

  • 0.2.0

Constant Summary collapse

WIDTHS =

Widths for formatted numbers

Since:

  • 0.2.0

{
  :hexadecimal => proc { |word_size| word_size * 2 },
  :decimal => {
    1 => 3,
    2 => 5,
    4 => 10,
    8 => 20
  },
  :octal => {
    1 => 3,
    2 => 6,
    4 => 11,
    8 => 22
  },
  :binary => proc { |word_size| word_size * 8 }
}
FORMATS =

Format Strings for the various bases

Since:

  • 0.2.0

{
  :hexadecimal => proc { |width| "%.#{width}x" },
  :decimal     => proc { |width| "%#{width}.d" },
  :octal       => proc { |width| "%.#{width}o" },
  :binary      => proc { |width| "%.#{width}b" }
}
UNPRINTABLE =

Character to represent unprintable characters

Since:

  • 0.2.0

'.'
PRINTABLE =

ASCII printable bytes and characters

Since:

  • 0.2.0

{
  0x20 => " ",
  0x21 => "!",
  0x22 => "\"",
  0x23 => "#",
  0x24 => "$",
  0x25 => "%",
  0x26 => "&",
  0x27 => "'",
  0x28 => "(",
  0x29 => ")",
  0x2a => "*",
  0x2b => "+",
  0x2c => ",",
  0x2d => "-",
  0x2e => ".",
  0x2f => "/",
  0x30 => "0",
  0x31 => "1",
  0x32 => "2",
  0x33 => "3",
  0x34 => "4",
  0x35 => "5",
  0x36 => "6",
  0x37 => "7",
  0x38 => "8",
  0x39 => "9",
  0x3a => ":",
  0x3b => ";",
  0x3c => "<",
  0x3d => "=",
  0x3e => ">",
  0x3f => "?",
  0x40 => "@",
  0x41 => "A",
  0x42 => "B",
  0x43 => "C",
  0x44 => "D",
  0x45 => "E",
  0x46 => "F",
  0x47 => "G",
  0x48 => "H",
  0x49 => "I",
  0x4a => "J",
  0x4b => "K",
  0x4c => "L",
  0x4d => "M",
  0x4e => "N",
  0x4f => "O",
  0x50 => "P",
  0x51 => "Q",
  0x52 => "R",
  0x53 => "S",
  0x54 => "T",
  0x55 => "U",
  0x56 => "V",
  0x57 => "W",
  0x58 => "X",
  0x59 => "Y",
  0x5a => "Z",
  0x5b => "[",
  0x5c => "\\",
  0x5d => "]",
  0x5e => "^",
  0x5f => "_",
  0x60 => "`",
  0x61 => "a",
  0x62 => "b",
  0x63 => "c",
  0x64 => "d",
  0x65 => "e",
  0x66 => "f",
  0x67 => "g",
  0x68 => "h",
  0x69 => "i",
  0x6a => "j",
  0x6b => "k",
  0x6c => "l",
  0x6d => "m",
  0x6e => "n",
  0x6f => "o",
  0x70 => "p",
  0x71 => "q",
  0x72 => "r",
  0x73 => "s",
  0x74 => "t",
  0x75 => "u",
  0x76 => "v",
  0x77 => "w",
  0x78 => "x",
  0x79 => "y",
  0x7a => "z",
  0x7b => "{",
  0x7c => "|",
  0x7d => "}",
  0x7e => "~"
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Dumper

Creates a new Hexdump dumper.

Parameters:

  • options (Hash) (defaults to: {})

    Additional options.

Options Hash (options):

  • :width (Integer) — default: 16

    The number of bytes to dump for each line.

  • :endian (Integer) — default: :little

    The endianness that the bytes are organized in. Supported endianness include :little and :big.

  • :word_size (Integer) — default: 1

    The number of bytes within a word.

  • :base (Symbol, Integer) — default: :hexadecimal

    The base to print bytes in. Supported bases include, :hexadecimal, :hex, 16,:decimal,:dec,10, :octal, :oct, 8, :binary, :bin and 2.

  • :ascii (Boolean) — default: false

    Print ascii characters when possible.

Raises:

  • (ArgumentError)

    The values for :base or :endian were unknown.

Since:

  • 0.2.0



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/hexdump/dumper.rb', line 183

def initialize(options={})
  @base = case options[:base]
          when :hexadecimal, :hex, 16
            :hexadecimal
          when :decimal, :dec, 10
            :decimal
          when :octal, :oct, 8
            :octal
          when :binary, :bin, 2
            :binary
          when nil
            :hexadecimal
          else
            raise(ArgumentError,"unknown base #{options[:base].inspect}")
          end

  @word_size = options.fetch(:word_size,1)
  @endian = case options[:endian]
            when 'little', :little
              :little
            when 'big', :big
              :big
            when nil
              :little
            else
              raise(ArgumentError,"unknown endian: #{options[:endian].inspect}")
            end

  @width = (options.fetch(:width,16) / @word_size)
  @ascii = options.fetch(:ascii,false)

  @format_width = (WIDTHS[@base][@word_size] || 1)
  @format = FORMATS[@base][@format_width]

  if @word_size == 1
    @format_cache = Hash.new do |hash,key|
      hash[key] = sprintf(@format,key)
    end
  end
end

Instance Attribute Details

#asciiObject (readonly)

Whether to display ASCII characters alongside numeric values.

Since:

  • 0.2.0



152
153
154
# File 'lib/hexdump/dumper.rb', line 152

def ascii
  @ascii
end

#baseObject (readonly)

The base to dump words as.

Since:

  • 0.2.0



140
141
142
# File 'lib/hexdump/dumper.rb', line 140

def base
  @base
end

#endianObject (readonly)

The endianness of the words parsed from the data.

Since:

  • 0.2.0



146
147
148
# File 'lib/hexdump/dumper.rb', line 146

def endian
  @endian
end

#widthObject (readonly)

The width in words of each hexdump line.

Since:

  • 0.2.0



149
150
151
# File 'lib/hexdump/dumper.rb', line 149

def width
  @width
end

#word_sizeObject (readonly)

The size of the words parse from the data.

Since:

  • 0.2.0



143
144
145
# File 'lib/hexdump/dumper.rb', line 143

def word_size
  @word_size
end

Instance Method Details

#dump(data, output = STDOUT) ⇒ nil

Dumps the hexdump.

Parameters:

  • data (#each_byte)

    The data to be hexdumped.

  • output (#<<) (defaults to: STDOUT)

    The output to dump the hexdump to.

Returns:

  • (nil)

Raises:

  • (ArgumentError)

    The output value does not support the #<< method.

Since:

  • 0.2.0



361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
# File 'lib/hexdump/dumper.rb', line 361

def dump(data,output=STDOUT)
  unless output.respond_to?(:<<)
    raise(ArgumentError,"output must support the #<< method")
  end

  bytes_segment_width = ((@width * @format_width) + @width)
  line_format = "%.8x  %-#{bytes_segment_width}s |%s|\n"

  index = 0
  count = 0

  numeric   = ''
  printable = ''

  each_word(data) do |word|
    numeric   << format_numeric(word) << ' '
    printable << format_printable(word)

    count += 1

    if count >= @width
      output << sprintf(line_format,index,numeric,printable)

      numeric   = ''
      printable = ''

      index += (@width * @word_size)
      count  = 0
    end
  end

  if count > 0
    # output the remaining line
    output << sprintf(line_format,index,numeric,printable)
  end
end

#each(data) {|index, numeric, printable| ... } ⇒ Enumerator

Iterates over the hexdump.

Parameters:

  • data (#each_byte)

    The data to be hexdumped.

Yields:

  • (index, numeric, printable)

    The given block will be passed the hexdump break-down of each segment.

Yield Parameters:

  • index (Integer)

    The index of the hexdumped segment.

  • numeric (Array<String>)

    The numeric representation of the segment.

  • printable (Array<String>)

    The printable representation of the segment.

Returns:

  • (Enumerator)

    If no block is given, an Enumerator will be returned.

Since:

  • 0.2.0



313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
# File 'lib/hexdump/dumper.rb', line 313

def each(data)
  return enum_for(:each,data) unless block_given?

  index = 0
  count = 0

  numeric = []
  printable = []

  each_word(data) do |word|
    numeric   << format_numeric(word)
    printable << format_printable(word)

    count += 1

    if count >= @width
      yield index, numeric, printable

      numeric.clear
      printable.clear

      index += (@width * @word_size)
      count = 0
    end
  end

  if count > 0
    # yield the remaining data
    yield index, numeric, printable
  end
end

#each_word(data) {|word| ... } ⇒ Enumerator

Iterates over every word within the data.

Parameters:

  • data (#each_byte)

    The data containing bytes.

Yields:

  • (word)

    The given block will be passed each word within the data.

Yield Parameters:

  • An (Integer)

    unpacked word from the data.

Returns:

  • (Enumerator)

    If no block is given, an Enumerator will be returned.

Raises:

  • (ArgumentError)

    The given data does not define the #each_byte method.

Since:

  • 0.2.0



244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/hexdump/dumper.rb', line 244

def each_word(data,&block)
  return enum_for(:each_word,data) unless block

  unless data.respond_to?(:each_byte)
    raise(ArgumentError,"the data to hexdump must define #each_byte")
  end

  if @word_size > 1
    word  = 0
    count = 0

    init_shift = if @endian == :big
                   ((@word_size - 1) * 8)
                 else
                   0
                 end
    shift = init_shift

    data.each_byte do |b|
      word |= (b << shift)

      if @endian == :big
        shift -= 8
      else
        shift += 8
      end

      count += 1

      if count >= @word_size
        yield word

        word  = 0
        count = 0
        shift = init_shift
      end
    end

    # yield the remaining word
    yield word if count > 0
  else
    data.each_byte(&block)
  end
end

#format_numeric(word) ⇒ String (protected)

Converts the word into a numeric String.

Parameters:

  • word (Integer)

    The word to convert.

Returns:

  • (String)

    The numeric representation of the word.

Since:

  • 0.2.0



411
412
413
414
415
416
417
418
419
420
421
# File 'lib/hexdump/dumper.rb', line 411

def format_numeric(word)
  if @word_size == 1
    if (@ascii && (word >= 0x20 && word <= 0x7e))
      PRINTABLE[word]
    else
      @format_cache[word]
    end
  else
    sprintf(@format,word)
  end
end

#format_printable(word) ⇒ String (protected)

Converts a word into a printable String.

Parameters:

  • word (Integer)

    The word to convert.

Returns:

  • (String)

    The printable representation of the word.

Since:

  • 0.2.0



434
435
436
437
438
439
440
441
442
443
444
445
446
# File 'lib/hexdump/dumper.rb', line 434

def format_printable(word)
  if @word_size == 1
    PRINTABLE[word]
  elsif (RUBY_VERSION > '1.9.' && (word >= -2 && word <= 0x7fffffff))
    begin
      word.chr(Encoding::UTF_8)
    rescue RangeError
      UNPRINTABLE
    end
  else
    UNPRINTABLE
  end
end