Thursday, January 26, 2012

Hexlify in Clojure

Looking at this gist, I have created functionality similar to EMACS hexlify-buffer. In EMACS, it reads a binary file and presents two views by breaking the file into 16 bytes sections.  Each section is shown on the same line as in example below:


The first column is the offset into the file, in hex.
The 8 columns are the hex representation of the 16 bytes represented in the line.
The last column is a printable representation of the 16 bytes, with unprintable characters represented as a period.

The hexlify clojure function accepts a seq of bytes or chars and returns a vector, each element of the vector represents 16 bytes of the input.

For example:

(hexlify "The quick brown  dog jumped over the lazy fox") 
 
=> ([("54" "68" "65" "20" "71" "75" "69" "63" "6b" "20" "62" "72" "6f" "77" "6e" "20")
  ("T" "h" "e" "." "q" "u" "i" "c" "k" "." "b" "r" "o" "w" "n" ".")
  (84 104 101 32 113 117 105 99 107 32 98 114 111 119 110 32)]

 [("20"  "64"   "6f"   "67"   "20"   "6a"   "75"   "6d"   "70"   "65"   "64"   "20"   "6f"   "76"   "65"   "72")
  ("." "d" "o" "g" "." "j" "u" "m" "p" "e" "d" "." "o" "v" "e" "r")
  (32 100 111 103 32 106 117 109 112 101 100 32 111 118 101 114)]

 [("20" "74" "68" "65" "20" "6c" "61" "7a" "79" "20" "66" "6f" "78")
  ("." "t" "h" "e" "." "l" "a" "z" "y" "." "f" "o" "x")
  (32 116 104 101 32 108 97 122 121 32 102 111 120)])

The first element of each vector is the 16 hex values, one for each byte in the partition.  The second element in the vector is the printatble character representation and the last element in the vector is the numeric representation.

This allows inspection of byte arrays within clojure quickly and easily.


The code is repeated below:
(defprotocol Hexl
(hexl-hex [val])
(hexl-char [char]))
(extend-type Number
Hexl
(hexl-hex [i]
(let [rtnval (Integer/toHexString (if (< i 0) (+ 256 i) i)) ]
(if (< (count rtnval) 2) (str "0" rtnval) rtnval)))
(hexl-char [b]
(let [v (if (< b 0) (+ 256 b) b)
c (char v)]
(if (and (< v 128 )(Character/isLetter c)) (.toString c) "."))))
(extend-type Character
Hexl
(hexl-hex [char]
(hexl-hex (int (.charValue char))))
(hexl-char [char]
(hexl-char (int (.charValue char)))))
(defn hexlify
"Perform similar to hexlify in emacs. Accept a seq of bytes and
convert it into a seq of vectors. The first element of the vector is a
seq of 16 strings for the HEX value of each byte. The second element
of the vector is a seq of the printable representation of the byte and the
third elevment of thee vector is a seq of the integer value for each
byte. Works for chars as well."
([bytes] (hexlify bytes 16))
([bytes size]
(let [parts (partition-all size bytes)]
(for [part parts]
[ (map hexl-hex part) (map hexl-char part) (map int part)]))))
(defn hexlify-chars
"Convert the bytes into a string of printable chars
with . being used for unprintable chars"
[bytes]
(let [chars (mapcat second (hexlify bytes))]
(apply str chars)))
view raw hexlify.clj hosted with ❤ by GitHub