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