File: Bytedump.hs

package info (click to toggle)
haskell-bytedump 1.0-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 72 kB
  • sloc: haskell: 150; makefile: 2
file content (210 lines) | stat: -rw-r--r-- 7,380 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210

-- |
-- Module      : Text.Bytedump
-- License     : BSD-style
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
-- Stability   : experimental
-- Portability : unknown
--
-- A module containing some routines to debug data dump
--

module Text.Bytedump
    ( hexString

    -- * Formatted string configuration
    , BytedumpConfig(..)
    , defaultConfig

    -- * Dump bytes into not formatted strings
    , dumpRaw
    , dumpRawS
    , dumpRawBS
    , dumpRawLBS

    -- * Dump bytes into formatted strings using a specific config
    , dumpWith
    , dumpWithS
    , dumpWithBS
    , dumpWithLBS

    -- * Dump bytes into formatted strings using default config
    , dump
    , dumpS
    , dumpBS
    , dumpLBS

    -- * Dump 2 set of bytes into formatted side-by-side strings using default config
    , dumpDiff
    , dumpDiffS
    , dumpDiffBS
    , dumpDiffLBS
    ) where

import Data.List
import Data.Word
import qualified Data.ByteString.Lazy as L
import qualified Data.ByteString as B

-- | Configuration structure used for formatting functions
data BytedumpConfig = BytedumpConfig
    { configRowSize      :: Int    -- ^ number of bytes per row.
    , configRowGroupSize :: Int    -- ^ number of bytes per group per row.
    , configRowGroupSep  :: String -- ^ string separating groups.
    , configRowLeft      :: String -- ^ string on the left of the row.
    , configRowRight     :: String -- ^ string on the right of the row.
    , configCellSep      :: String -- ^ string separating cells in row.
    , configPrintChar    :: Bool   -- ^ if the printable ascii table is displayed.
    } deriving (Show,Eq)

-- | Default Config using 16 bytes by row with a separation at the 8th byte, and
-- dumping printable ascii character on the right.
defaultConfig :: BytedumpConfig
defaultConfig = BytedumpConfig
    { configRowSize      = 16
    , configRowGroupSize = 8
    , configRowGroupSep  = " : "
    , configRowLeft      = " | "
    , configRowRight     = " | "
    , configCellSep      = " "
    , configPrintChar    = True
    }

hex :: Int -> Char
hex 0  = '0'
hex 1  = '1'
hex 2  = '2'
hex 3  = '3'
hex 4  = '4'
hex 5  = '5'
hex 6  = '6'
hex 7  = '7'
hex 8  = '8'
hex 9  = '9'
hex 10 = 'a'
hex 11 = 'b'
hex 12 = 'c'
hex 13 = 'd'
hex 14 = 'e'
hex 15 = 'f'
hex _  = ' '

{-# INLINE hexBytes #-}
hexBytes :: Word8 -> (Char, Char)
hexBytes w = (hex h, hex l) where (h,l) = (fromIntegral w) `divMod` 16

-- | Dump one byte into a 2 hexadecimal characters.
hexString :: Word8 -> String
hexString i = [h,l] where (h,l) = hexBytes i

-- | Dump a list of word8 into a raw string of hex value
dumpRaw :: [Word8] -> String
dumpRaw = concatMap hexString

-- | Dump a string into a raw string of hex value
dumpRawS :: String -> String
dumpRawS = dumpRaw . map (toEnum.fromEnum)

-- | Dump a bytestring into a raw string of hex value
dumpRawBS :: B.ByteString -> String
dumpRawBS = dumpRaw . B.unpack

-- | Dump a lazy bytestring into a raw string of hex value
dumpRawLBS :: L.ByteString -> String
dumpRawLBS = dumpRaw . L.unpack

disptable :: BytedumpConfig -> [Word8] -> [String]
disptable _   [] = []
disptable cfg x  =
    let (pre, post) = splitAt (configRowSize cfg) x in
    tableRow pre : disptable cfg post
    where
        tableRow row =
            let l  = splitMultiple (configRowGroupSize cfg) $ map hexString row in
            let lb = intercalate (configRowGroupSep cfg) $ map (intercalate (configCellSep cfg)) l in
            let rb = map printChar row in
            let rowLen = 2 * configRowSize cfg
                       + (configRowSize cfg - 1) * length (configCellSep cfg)
                       + ((configRowSize cfg `div` configRowGroupSize cfg) - 1) * length (configRowGroupSep cfg) in
            configRowLeft cfg ++ lb ++ replicate (rowLen - length lb) ' ' ++ configRowRight cfg ++ (if configPrintChar cfg then rb else "")

        splitMultiple _ [] = []
        splitMultiple n l  = let (pre, post) = splitAt n l in pre : splitMultiple n post

        printChar :: Word8 -> Char
        printChar w
            | w >= 0x20 && w < 0x7f = toEnum $ fromIntegral w
            | otherwise             = '.'

dispDiffTable :: BytedumpConfig -> [Word8] -> [Word8] -> [String]
dispDiffTable _   [] [] = []
dispDiffTable cfg x1 x2 =
    let (pre1, post1) = splitAt (configRowSize cfg) x1 in
    let (pre2, post2) = splitAt (configRowSize cfg) x2 in
    tableRow pre1 pre2 : dispDiffTable cfg post1 post2

    where
        tableRow row1 row2 =
            let l1 = splitMultiple (configRowGroupSize cfg) $ map hexString row1 in
            let l2 = splitMultiple (configRowGroupSize cfg) $ map hexString row2 in
            let lb1 = intercalate (configRowGroupSep cfg) $ map (intercalate (configCellSep cfg)) l1 in
            let lb2 = intercalate (configRowGroupSep cfg) $ map (intercalate (configCellSep cfg)) l2 in
            let rowLen = 2 * configRowSize cfg
                       + (configRowSize cfg - 1) * length (configCellSep cfg)
                       + ((configRowSize cfg `div` configRowGroupSize cfg) - 1) * length (configRowGroupSep cfg) in
            configRowLeft cfg ++ lb1 ++ replicate (rowLen - length lb1) ' ' ++ configRowRight cfg
                              ++ lb2 ++ replicate (rowLen - length lb2) ' ' ++ configRowRight cfg

        splitMultiple _ [] = []
        splitMultiple n l  = let (pre, post) = splitAt n l in pre : splitMultiple n post

-- | Dump a list of bytes into formatted strings using a specific config
dumpWith :: BytedumpConfig -> [Word8] -> String
dumpWith cfg l = intercalate "\n" rows
    where rows = disptable cfg l

-- | Dump a string into formatted strings using a specific config
dumpWithS :: BytedumpConfig -> String -> String
dumpWithS cfg = dumpWith cfg . map (toEnum.fromEnum)

-- | Dump a bytestring into formatted strings using a specific config
dumpWithBS :: BytedumpConfig -> B.ByteString -> String
dumpWithBS cfg = dumpWith cfg . B.unpack

-- | Dump a lazy bytestring into formatted strings using a specific config
dumpWithLBS :: BytedumpConfig -> L.ByteString -> String
dumpWithLBS cfg = dumpWith cfg . L.unpack

-- | Dump a list of word8 into a formatted string of hex value
dump :: [Word8] -> String
dump l = intercalate "\n" rows
    where rows = disptable defaultConfig l

-- | Dump a string into a formatted string of hex value
dumpS :: String -> String
dumpS = dump . map (toEnum.fromEnum)

-- | Dump a bytestring into a formatted string of hex value
dumpBS :: B.ByteString -> String
dumpBS = dump . B.unpack

-- | Dump a lazy bytestring into a formatted string of hex value
dumpLBS :: L.ByteString -> String
dumpLBS = dump . L.unpack

-- | Dump two list of word8 into a formatted string of hex value side by side
dumpDiff :: [Word8] -> [Word8] -> String
dumpDiff l1 l2 = intercalate "\n" rows
    where rows = dispDiffTable defaultConfig l1 l2

-- | Dump a string into a formatted string of hex value
dumpDiffS :: String -> String -> String
dumpDiffS s1 s2 = dumpDiff (map (toEnum.fromEnum) s1) (map (toEnum.fromEnum) s2)

-- | Dump a bytestring into a formatted string of hex value
dumpDiffBS :: B.ByteString -> B.ByteString -> String
dumpDiffBS b1 b2 = dumpDiff (B.unpack b1) (B.unpack b2)

-- | Dump a lazy bytestring into a formatted string of hex value
dumpDiffLBS :: L.ByteString -> L.ByteString -> String
dumpDiffLBS l1 l2 = dumpDiff (L.unpack l1) (L.unpack l2)