module EVM.Hexdump (prettyHex, simpleHex) where
import Data.ByteString (ByteString)
import qualified Data.ByteString as B (length, unpack)
import qualified Data.ByteString.Char8 as B8 (unpack)
import Data.Char (isAscii, isControl)
import Data.List (intercalate, transpose, unfoldr)
import Numeric (showHex)
byteWidth :: Num a => a
byteWidth :: a
byteWidth = 2
numWordBytes :: Num a => a
numWordBytes :: a
numWordBytes = 4
prettyHex :: Int -> ByteString -> String
prettyHex :: Int -> ByteString -> String
prettyHex hexDisplayWidth :: Int
hexDisplayWidth bs :: ByteString
bs = [String] -> String
unlines (String
header String -> [String] -> [String]
forall a. a -> [a] -> [a]
: [String]
body)
where
numLineWords :: Int
numLineWords = 4
addressWidth :: Int
addressWidth = 4
numLineBytes :: Int
numLineBytes = Int
numLineWords Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
forall a. Num a => a
numWordBytes
replacementChar :: Char
replacementChar = '.'
header :: String
header = "Length: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show (ByteString -> Int
B.length ByteString
bs)
String -> String -> String
forall a. [a] -> [a] -> [a]
++ " (0x" String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String -> String
forall a. (Integral a, Show a) => a -> String -> String
showHex (ByteString -> Int
B.length ByteString
bs) ") bytes"
body :: [String]
body = ([String] -> String) -> [[String]] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate " ")
([[String]] -> [String]) -> [[String]] -> [String]
forall a b. (a -> b) -> a -> b
$ [[String]] -> [[String]]
forall a. [[a]] -> [[a]]
transpose [[String]
mkLineNumbers, ByteString -> [String]
mkHexDisplay ByteString
bs, ByteString -> [String]
mkAsciiDump ByteString
bs]
mkHexDisplay :: ByteString -> [String]
mkHexDisplay
= Int -> [String] -> [String]
padLast Int
hexDisplayWidth
([String] -> [String])
-> (ByteString -> [String]) -> ByteString -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([String] -> String) -> [[String]] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate " ") ([[String]] -> [String])
-> (ByteString -> [[String]]) -> ByteString -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> [String] -> [[String]]
forall a. Int -> [a] -> [[a]]
group Int
numLineWords
([String] -> [[String]])
-> (ByteString -> [String]) -> ByteString -> [[String]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([String] -> String) -> [[String]] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate " ") ([[String]] -> [String])
-> (ByteString -> [[String]]) -> ByteString -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> [String] -> [[String]]
forall a. Int -> [a] -> [[a]]
group Int
forall a. Num a => a
numWordBytes
([String] -> [[String]])
-> (ByteString -> [String]) -> ByteString -> [[String]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Word8 -> String) -> [Word8] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> Word8 -> String
forall a. (Show a, Integral a) => Int -> a -> String
paddedShowHex Int
forall a. Num a => a
byteWidth)
([Word8] -> [String])
-> (ByteString -> [Word8]) -> ByteString -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> [Word8]
B.unpack
mkAsciiDump :: ByteString -> [String]
mkAsciiDump = Int -> String -> [String]
forall a. Int -> [a] -> [[a]]
group Int
numLineBytes (String -> [String])
-> (ByteString -> String) -> ByteString -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String
cleanString (String -> String)
-> (ByteString -> String) -> ByteString -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> String
B8.unpack
cleanString :: String -> String
cleanString = (Char -> Char) -> String -> String
forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
go
where
go :: Char -> Char
go x :: Char
x | Char -> Bool
isWorthPrinting Char
x = Char
x
| Bool
otherwise = Char
replacementChar
mkLineNumbers :: [String]
mkLineNumbers = [Int -> Int -> String
forall a. (Show a, Integral a) => Int -> a -> String
paddedShowHex Int
addressWidth (Int
x Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
numLineBytes) String -> String -> String
forall a. [a] -> [a] -> [a]
++ ":"
| Int
x <- [0 .. (ByteString -> Int
B.length ByteString
bs Int -> Int -> Int
forall a. Num a => a -> a -> a
- 1) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
numLineBytes] ]
padLast :: Int -> [String] -> [String]
padLast w :: Int
w [x :: String
x] = [String
x String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> Char -> String
forall a. Int -> a -> [a]
replicate (Int
w Int -> Int -> Int
forall a. Num a => a -> a -> a
- String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
x) ' ']
padLast w :: Int
w (x :: String
x:xs :: [String]
xs) = String
x String -> [String] -> [String]
forall a. a -> [a] -> [a]
: Int -> [String] -> [String]
padLast Int
w [String]
xs
padLast _ [] = []
paddedShowHex :: (Show a, Integral a) => Int -> a -> String
paddedShowHex :: Int -> a -> String
paddedShowHex w :: Int
w n :: a
n = String
pad String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
str
where
str :: String
str = a -> String -> String
forall a. (Integral a, Show a) => a -> String -> String
showHex a
n ""
pad :: String
pad = Int -> Char -> String
forall a. Int -> a -> [a]
replicate (Int
w Int -> Int -> Int
forall a. Num a => a -> a -> a
- String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
str) '0'
simpleHex :: ByteString -> String
simpleHex :: ByteString -> String
simpleHex = String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate " "
([String] -> String)
-> (ByteString -> [String]) -> ByteString -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([String] -> String) -> [[String]] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate " ") ([[String]] -> [String])
-> (ByteString -> [[String]]) -> ByteString -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> [String] -> [[String]]
forall a. Int -> [a] -> [[a]]
group Int
forall a. Num a => a
numWordBytes
([String] -> [[String]])
-> (ByteString -> [String]) -> ByteString -> [[String]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Word8 -> String) -> [Word8] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> Word8 -> String
forall a. (Show a, Integral a) => Int -> a -> String
paddedShowHex Int
forall a. Num a => a
byteWidth)
([Word8] -> [String])
-> (ByteString -> [Word8]) -> ByteString -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> [Word8]
B.unpack
isWorthPrinting :: Char -> Bool
isWorthPrinting :: Char -> Bool
isWorthPrinting x :: Char
x = Char -> Bool
isAscii Char
x Bool -> Bool -> Bool
&& Bool -> Bool
not (Char -> Bool
isControl Char
x)
group :: Int -> [a] -> [[a]]
group :: Int -> [a] -> [[a]]
group n :: Int
n
| Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= 0 = ([a] -> [[a]] -> [[a]]
forall a. a -> [a] -> [a]
:[])
| Bool
otherwise = ([a] -> Maybe ([a], [a])) -> [a] -> [[a]]
forall b a. (b -> Maybe (a, b)) -> b -> [a]
unfoldr [a] -> Maybe ([a], [a])
go
where
go :: [a] -> Maybe ([a], [a])
go [] = Maybe ([a], [a])
forall a. Maybe a
Nothing
go xs :: [a]
xs = ([a], [a]) -> Maybe ([a], [a])
forall a. a -> Maybe a
Just (Int -> [a] -> ([a], [a])
forall a. Int -> [a] -> ([a], [a])
splitAt Int
n [a]
xs)