-- | Base 16 or hexadecimal encoding of objects. {-# LANGUAGE GeneralizedNewtypeDeriving #-} module Raaz.Core.Encode.Base16 ( Base16 , fromBase16, showBase16 ) where import Data.Char import Data.Bits import Data.String import Data.ByteString as B import Data.ByteString.Char8 as C8 import Data.ByteString.Internal (c2w ) import Data.ByteString.Unsafe(unsafeIndex) import Data.Monoid import Data.Word import Prelude import Raaz.Core.Encode.Internal -- | The type corresponding to base-16 or hexadecimal encoding. The -- `Base16` encoding has a special place in this library: most -- cryptographic types use `Base16` encoding for their `Show` and -- `IsString` instance. The combinators `fromBase16` and `showBase16` -- are exposed mainly to make these definitions easy. -- -- The base16 encoding only produces valid hex characters. However, to -- aid easy presentation of long hexadecimal strings, a user can add -- add arbitrary amount of spaces, newlines and the character ':'. The -- decoding ignores these characters. newtype Base16 = Base16 {unBase16 :: ByteString} deriving (Eq, Monoid) -- Developers note: Internally base16 just stores the bytestring as -- is. The conversion happens when we do an encode and decode of -- actual base16. instance Encodable Base16 where toByteString = hex . unBase16 fromByteString bs | odd (B.length bs) = Nothing | badCharacter bs = Nothing | otherwise = Just $ Base16 $ unsafeFromHex bs where badCharacter = C8.any (not . isHexDigit) unsafeFromByteString = Base16 . unsafeFromHex instance Show Base16 where show = C8.unpack . toByteString instance IsString Base16 where fromString = unsafeFromByteString . fromString instance Format Base16 where encodeByteString = Base16 {-# INLINE encodeByteString #-} decodeFormat = unBase16 {-# INLINE decodeFormat #-} -- TODO: Since the encoding to base16 is usually used for user interaction -- we can afford to be slower here. hex :: ByteString -> ByteString hex bs = fst $ B.unfoldrN (2 * B.length bs) gen 0 where gen i | rm == 0 = Just (hexDigit $ top4 w, i+1) | otherwise = Just (hexDigit $ bot4 w, i+1) where (idx, rm) = quotRem i 2 w = unsafeIndex bs idx hexDigit :: Word8 -> Word8 hexDigit x | x < 10 = c2w '0' + x | otherwise = c2w 'a' + (x - 10) top4 :: Word8 -> Word8; top4 x = x `shiftR` 4 bot4 :: Word8 -> Word8; bot4 x = x .&. 0x0F unsafeFromHex :: ByteString -> ByteString unsafeFromHex = unsafeFromHexP . C8.filter (not . useless) where useless c = isSpace c || c == ':' unsafeFromHexP :: ByteString -> ByteString unsafeFromHexP bs | odd (B.length bs) = error "base16 encoding is always of even size" | otherwise = fst $ B.unfoldrN len gen 0 where len = B.length bs `quot` 2 gen i = Just (shiftL w0 4 .|. w1, i + 1) where w0 = fromHexWord $ unsafeIndex bs (2 * i) w1 = fromHexWord $ unsafeIndex bs (2 * i + 1) fromHexWord x | c2w '0' <= x && x <= c2w '9' = x - c2w '0' | c2w 'a' <= x && x <= c2w 'f' = 10 + (x - c2w 'a') | c2w 'A' <= x && x <= c2w 'F' = 10 + (x - c2w 'A') | otherwise = error "bad base16 character" -- | Base16 variant of `fromString`. Useful in definition of -- `IsString` instances as well as in cases where the default -- `IsString` instance does not parse from a base16 encoding. fromBase16 :: Encodable a => String -> a fromBase16 = unsafeFromByteString . unBase16 . fromString -- | Base16 variant of `show`. showBase16 :: Encodable a => a -> String showBase16 = show . Base16 . toByteString