import Data.Char (ord)
import Data.Word (Word8)
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.Base16 as Base16
import qualified Data.ByteString.Base16.Lazy as Base16L
-- Three-line "test framework" for Haskell. Crude, but at least it tells you
-- the line number of the failure without you having to specify it.
shouldBe :: (Eq a, Show a) => a -> a -> IO Bool
shouldBe a b = return (a == b)
infix 0 `shouldBe`
c2w :: Char -> Word8
c2w = fromIntegral . ord
main :: IO ()
main = do
let hexL = map c2w "0123456789abcdef"
hexU = map c2w "0123456789ABCDEF"
hexUL = map c2w "0123456789ABCDEFabcdef"
notHex = [c | c <- [0..255], c `notElem` hexUL]
hexL2 = do a <- hexL; b <- hexL; [a,b]
hexU2 = do a <- hexU; b <- hexU; [a,b]
bytes = B.pack [0..255]
-- Encode every byte
True <- Base16.encode bytes `shouldBe` B.pack hexL2
-- Decode every valid hex pair
True <- Base16.decode (B.pack hexL2) `shouldBe` (bytes, B.empty)
True <- Base16.decode (B.pack hexU2) `shouldBe` (bytes, B.empty)
-- Decode every invalid byte paired with a correct byte
let bads1 = [B.pack [a,b] | a <- notHex, b <- hexUL]
let bads2 = [B.pack [a,b] | a <- hexUL, b <- notHex]
True <- map Base16.decode bads1 `shouldBe` map (\s -> (B.empty, s)) bads1
True <- map Base16.decode bads2 `shouldBe` map (\s -> (B.empty, s)) bads2
-- Like above, but start with a correct byte
let correctHex = B.pack [97,98]
correctBytes = B.pack [171]
True <- map (Base16.decode . (correctHex `B.append`)) bads1
`shouldBe` map (\s -> (correctBytes, s)) bads1
True <- map (Base16.decode . (correctHex `B.append`)) bads2
`shouldBe` map (\s -> (correctBytes, s)) bads2
-- Like above, but end with a correct byte
True <- map (Base16.decode . (`B.append` correctHex)) bads1
`shouldBe` map (\s -> (B.empty, s `B.append` correctHex)) bads1
True <- map (Base16.decode . (`B.append` correctHex)) bads2
`shouldBe` map (\s -> (B.empty, s `B.append` correctHex)) bads2
-- Lazy decoding also works with odd length chunks
let encodedLazy = BL.fromChunks $ map (B.pack . map c2w) ["614","239","6","142","39"]
True <- Base16L.decode encodedLazy `shouldBe` (BL.pack . map c2w $ "aB9aB9",BL.empty)
-- Lazy decoding is lazy on success
let encodedLazy = BL.iterate id 48
True <- (BL.unpack . BL.take 8 . fst . Base16L.decode $ encodedLazy)
`shouldBe` [0,0,0,0,0,0,0,0]
-- Lazy decoding is lazy on failure
let encodedLazy = BL.iterate id 47
True <- (BL.unpack . BL.take 8 . fst . Base16L.decode $ encodedLazy)
`shouldBe` []
return ()