-- This code is in the public domain.
module Text.Hexdump (hexdump) where
import Control.Monad (guard)
import Data.Char (ord)
import Data.List (unfoldr)
import Text.Printf (printf)

-- | Unfold using f until the predicate becomes true.
unfoldUntil :: (b -> Bool) -> (b -> (a, b)) -> b -> [a]
unfoldUntil p f = unfoldr (\x -> guard (not (p x)) >> return (f x))

-- | Return hex characters for the byte value.
bytehex :: Int -> String
bytehex n = printf "%02x" n

-- | Return a printable character, or a dot.
prChar ch | ord ch >= 32 && ord ch < 128 = ch
          | otherwise = '.'

-- | Return a string containing a pretty hexdump of xs using addresses
-- starting at n.
hexdump :: Int -> String -> String
hexdump n xs = unlines $ zipWith hexLine addrs dlines
    where addrs = [n, n+16..]
          dlines = unfoldUntil null (splitAt 16) xs
          hexLine :: Int -> String -> String
          hexLine addr xs = printf "%08x: %-24s %-24s| %s" addr h1 h2 s
              where h1 = unwords $ map (bytehex . ord) $ take 8 xs
                    h2 = unwords $ map (bytehex . ord) $ drop 8 xs
                    s = map prChar xs