Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: crystal-lang/crystal
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 68f39a01c51e
Choose a base ref
...
head repository: crystal-lang/crystal
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: a80db64ec117
Choose a head ref
  • 3 commits
  • 4 files changed
  • 2 contributors

Commits on Aug 2, 2016

  1. Slice#hexdump add index and separate bytes

    - initial 32bits hex index
    - separate each byte (instead of each short)
    - extra separator every 8 bytes
    ysbaddaden committed Aug 2, 2016
    Copy the full SHA
    8ae2085 View commit details
  2. Add IO::Hexdump

    Transparent IO that prints an hexadecimal dump of read and written
    bytes. Mostly useful for inspecting and debugging binary protocols.
    ysbaddaden committed Aug 2, 2016
    Copy the full SHA
    ce0ffc3 View commit details
  3. Merge pull request #3043 from ysbaddaden/std-io-hexdump

    IO::Hexdump for binary protocol inspection
    Ary Borenszweig authored Aug 2, 2016
    Copy the full SHA
    a80db64 View commit details
Showing with 156 additions and 26 deletions.
  1. +59 −0 spec/std/io/hexdump_spec.cr
  2. +15 −15 spec/std/slice_spec.cr
  3. +59 −0 src/io/hexdump.cr
  4. +23 −11 src/slice.cr
59 changes: 59 additions & 0 deletions spec/std/io/hexdump_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
require "spec"
require "io/hexdump"

describe IO::Hexdump do
describe "read" do
it "prints hexdump" do
ascii_table = <<-EOF
00000000 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f !"#$%&'()*+,-./
00000010 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 0123456789:;<=>?
00000020 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO
00000030 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f PQRSTUVWXYZ[\\]^_
00000040 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f `abcdefghijklmno
00000050 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f pqrstuvwxyz{|}~.
00000060 80 81 82 83 84 .....
EOF

IO.pipe do |r, w|
io = MemoryIO.new(ascii_table.bytesize)
r = IO::Hexdump.new(r, output: io, read: true)

slice = Slice(UInt8).new(101) { |i| i.to_u8 + 32 }
w.write(slice)

buf = uninitialized UInt8[101]
r.read_fully(buf.to_slice).should eq(101)
buf.to_slice.should eq(slice)

io.to_s.should eq("#{ascii_table}\n")
end
end
end

describe "write" do
it "prints hexdump" do
ascii_table = <<-EOF
00000000 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f !"#$%&'()*+,-./
00000010 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 0123456789:;<=>?
00000020 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO
00000030 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f PQRSTUVWXYZ[\\]^_
00000040 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f `abcdefghijklmno
00000050 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f pqrstuvwxyz{|}~.
EOF

IO.pipe do |r, w|
io = MemoryIO.new(ascii_table.bytesize)
w = IO::Hexdump.new(w, output: io, write: true)

slice = Slice(UInt8).new(96) { |i| i.to_u8 + 32 }
w.write(slice)

buf = uninitialized UInt8[96]
r.read_fully(buf.to_slice).should eq(96)
buf.to_slice.should eq(slice)

io.to_s.should eq("#{ascii_table}\n")
end
end
end
end
30 changes: 15 additions & 15 deletions spec/std/slice_spec.cr
Original file line number Diff line number Diff line change
@@ -231,28 +231,28 @@ describe "Slice" do

it "does hexdump" do
ascii_table = <<-EOF
2021 2223 2425 2627 2829 2a2b 2c2d 2e2f !"#$%&'()*+,-./
3031 3233 3435 3637 3839 3a3b 3c3d 3e3f 0123456789:;<=>?
4041 4243 4445 4647 4849 4a4b 4c4d 4e4f @ABCDEFGHIJKLMNO
5051 5253 5455 5657 5859 5a5b 5c5d 5e5f PQRSTUVWXYZ[\\]^_
6061 6263 6465 6667 6869 6a6b 6c6d 6e6f `abcdefghijklmno
7071 7273 7475 7677 7879 7a7b 7c7d 7e7f pqrstuvwxyz{|}~.
00000000 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f !"#$%&'()*+,-./
00000010 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 0123456789:;<=>?
00000020 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO
00000030 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f PQRSTUVWXYZ[\\]^_
00000040 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f `abcdefghijklmno
00000050 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f pqrstuvwxyz{|}~.
EOF

slice = StaticArray(UInt8, 96).new(&.to_u8.+(32)).to_slice
slice = Slice(UInt8).new(96) { |i| i.to_u8 + 32 }
slice.hexdump.should eq(ascii_table)

ascii_table_plus = <<-EOF
2021 2223 2425 2627 2829 2a2b 2c2d 2e2f !"#$%&'()*+,-./
3031 3233 3435 3637 3839 3a3b 3c3d 3e3f 0123456789:;<=>?
4041 4243 4445 4647 4849 4a4b 4c4d 4e4f @ABCDEFGHIJKLMNO
5051 5253 5455 5657 5859 5a5b 5c5d 5e5f PQRSTUVWXYZ[\\]^_
6061 6263 6465 6667 6869 6a6b 6c6d 6e6f `abcdefghijklmno
7071 7273 7475 7677 7879 7a7b 7c7d 7e7f pqrstuvwxyz{|}~.
8081 8283 84 .....
00000000 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f !"#$%&'()*+,-./
00000010 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 0123456789:;<=>?
00000020 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO
00000030 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f PQRSTUVWXYZ[\\]^_
00000040 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f `abcdefghijklmno
00000050 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f pqrstuvwxyz{|}~.
00000060 80 81 82 83 84 .....
EOF

plus = StaticArray(UInt8, 101).new(&.to_u8.+(32)).to_slice
plus = Slice(UInt8).new(101) { |i| i.to_u8 + 32 }
plus.hexdump.should eq(ascii_table_plus)
end

59 changes: 59 additions & 0 deletions src/io/hexdump.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# IO object that prints an hexadecimal dump of all transfered data.
#
# Especially useful for debugging binary protocols on an IO, to understand
# better when and how data is sent or received.
#
# By default `IO::Hexdump` won't print anything; you must specify which of
# `read`, `write` or both you want to print.
#
# Example:
# ```
# require "io/hexdump"
# io = IO::Hexdump.new(socket, output: STDERR, read: true)
# ```
#
# When data is read from `io` it will print something akin to the following on
# STDERR:
# ```
# 00000000 50 52 49 20 2a 20 48 54 54 50 2f 32 2e 30 0d 0a PRI * HTTP/2.0..
# 00000010 0d 0a 53 4d 0d 0a 0d 0a ..SM....
# 00000000 00 00 00 04 ....
# 00000000 00 .
# 00000000 00 00 00 00 ....
# ```
module IO
class Hexdump
include IO

def initialize(@io : IO, @output : IO = STDERR, @read = false, @write = false)
end

def read(buf : Slice(UInt8))
@io.read(buf).tap do |read_bytes|
@output.puts buf[0, read_bytes].hexdump if @read && read_bytes
end
end

def write(buf : Slice(UInt8))
@io.write(buf).tap do
@output.puts buf.hexdump if @write
end
end

def closed?
@io.closed?
end

def close
@io.close
end

def flush
@io.flush
end

def tty?
@io.tty?
end
end
end
34 changes: 23 additions & 11 deletions src/slice.cr
Original file line number Diff line number Diff line change
@@ -349,44 +349,56 @@ struct Slice(T)
#
# ```
# slice = UInt8.slice(97, 62, 63, 8, 255)
# slice.hexdump # => "6162 6308 ff abc.."
# slice.hexdump # => "00000000 61 3e 3f 08 ff a>?.."
# ```
def hexdump
self.as(Slice(UInt8))

full_lines, leftover = size.divmod(16)
if leftover == 0
str_size = full_lines*58 - 1
str_size = full_lines * 77 - 1
lines = full_lines
else
str_size = (full_lines + 1)*58 - (16 - leftover) - 1
str_size = (full_lines + 1) * 77 - (16 - leftover) - 1
lines = full_lines + 1
end

String.new(str_size) do |buffer|
hex_offset = 0
ascii_offset = 41
index_offset = 0
hex_offset = 10
ascii_offset = 60

each_with_index do |v, i|
if i % 16 == 0
0.upto(7) do |j|
buffer[index_offset + 7 - j] = to_hex((i >> (4 * j)) & 0xf)
end
buffer[index_offset + 8] = ' '.ord.to_u8
buffer[index_offset + 9] = ' '.ord.to_u8
index_offset += 77
end

buffer[hex_offset] = to_hex(v >> 4)
buffer[hex_offset + 1] = to_hex(v & 0x0f)
hex_offset += 2
buffer[hex_offset + 2] = ' '.ord.to_u8
hex_offset += 3

buffer[ascii_offset] = (v > 31 && v < 127) ? v : '.'.ord.to_u8
ascii_offset += 1

if i % 2 == 1
if i % 8 == 7
buffer[hex_offset] = ' '.ord.to_u8
hex_offset += 1
end

if i % 16 == 15
buffer[hex_offset] = ' '.ord.to_u8
buffer[ascii_offset] = '\n'.ord.to_u8
ascii_offset += 42
hex_offset += 18
hex_offset += 27
ascii_offset += 61
end
end

while hex_offset % 58 < 41
while hex_offset % 77 < 60
buffer[hex_offset] = ' '.ord.to_u8
hex_offset += 1
end