{-# LANGUAGE BangPatterns #-} {-# LANGUAGE CPP #-} {-# LANGUAGE MagicHash #-} module Data.ByteString.Base16.Internal ( -- * worker loops encodeLoop , decodeLoop , lenientLoop -- * utils , c2w , aix , reChunk , withBS , mkBS ) where import Data.Bits ((.&.), (.|.), unsafeShiftR) import qualified Data.ByteString as B import Data.ByteString.Internal (ByteString(..)) import Data.Char (ord) import Foreign.ForeignPtr (ForeignPtr, withForeignPtr) import Foreign.Ptr (Ptr, minusPtr, plusPtr) import Foreign.Storable (Storable(poke, peek)) import GHC.Word (Word8(..)) import GHC.Exts (Int(I#), Addr#, indexWord8OffAddr#) #if __GLASGOW_HASKELL__ >= 702 import System.IO.Unsafe (unsafeDupablePerformIO) #else import GHC.IO (unsafeDupablePerformIO) #endif -- ------------------------------------------------------------------ -- -- Loops encodeLoop :: Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> IO () encodeLoop !dptr !sptr !end = go dptr sptr where !hex = "0123456789abcdef"# go !dst !src | src == end = return () | otherwise = do !t <- peek src poke dst (aix (unsafeShiftR t 4) hex) poke (plusPtr dst 1) (aix (t .&. 0x0f) hex) go (plusPtr dst 2) (plusPtr src 1) {-# INLINE encodeLoop #-} decodeLoop :: ForeignPtr Word8 -> Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> IO (Either String ByteString) decodeLoop !dfp !dptr !sptr !end = go dptr sptr where err !src = return . Left $ "invalid character at offset: " ++ show (src `minusPtr` sptr) !lo = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\xff\xff\xff\xff\xff\xff\xff\x0a\x0b\x0c\x0d\x0e\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x0a\x0b\x0c\x0d\x0e\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"# !hi = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x10\x20\x30\x40\x50\x60\x70\x80\x90\xff\xff\xff\xff\xff\xff\xff\xa0\xb0\xc0\xd0\xe0\xf0\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xa0\xb0\xc0\xd0\xe0\xf0\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"# go !dst !src | src == end = return (Right (mkBS dfp (dst `minusPtr` dptr))) | otherwise = do !x <- peek src !y <- peek (plusPtr src 1) let !a = aix x hi !b = aix y lo if a == 0xff then err src else if b == 0xff then err (plusPtr src 1) else do poke dst (a .|. b) go (plusPtr dst 1) (plusPtr src 2) {-# INLINE decodeLoop #-} lenientLoop :: ForeignPtr Word8 -> Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> IO ByteString lenientLoop !dfp !dptr !sptr !end = goHi dptr sptr 0 where !lo = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\xff\xff\xff\xff\xff\xff\xff\x0a\x0b\x0c\x0d\x0e\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x0a\x0b\x0c\x0d\x0e\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"# !hi = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x10\x20\x30\x40\x50\x60\x70\x80\x90\xff\xff\xff\xff\xff\xff\xff\xa0\xb0\xc0\xd0\xe0\xf0\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xa0\xb0\xc0\xd0\xe0\xf0\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"# goHi !dst !src !n | src == end = return (mkBS dfp n) | otherwise = do !x <- peek src let !a = aix x hi if a == 0xff then goHi dst (plusPtr src 1) n else goLo dst (plusPtr src 1) a n goLo !dst !src !a !n | src == end = return (mkBS dfp n) | otherwise = do !y <- peek src let !b = aix y lo if b == 0xff then goLo dst (plusPtr src 1) a n else do poke dst (a .|. b) goHi (plusPtr dst 1) (plusPtr src 1) (n + 1) {-# INLINE lenientLoop #-} -- ------------------------------------------------------------------ -- -- Utils aix :: Word8 -> Addr# -> Word8 aix w table = W8# (indexWord8OffAddr# table i) where !(I# i) = fromIntegral w {-# INLINE aix #-} -- | Form a list of chunks, and rechunk the list of bytestrings -- into length multiples of 2 -- reChunk :: [ByteString] -> [ByteString] reChunk [] = [] reChunk (c:cs) = case B.length c `divMod` 2 of (_, 0) -> c : reChunk cs (n, _) -> case B.splitAt (n * 2) c of ~(m, q) -> m : cont_ q cs where cont_ q [] = [q] cont_ q (a:as) = case B.splitAt 1 a of ~(x, y) -> let q' = B.append q x in if B.length q' == 2 then let as' = if B.null y then as else y:as in q' : reChunk as' else cont_ q' as c2w :: Char -> Word8 c2w = fromIntegral . ord {-# INLINE c2w #-} mkBS :: ForeignPtr Word8 -> Int -> ByteString #if MIN_VERSION_bytestring(0,11,0) mkBS dfp n = BS dfp n #else mkBS dfp n = PS dfp 0 n #endif {-# INLINE mkBS #-} withBS :: ByteString -> (Ptr Word8 -> Int -> IO a) -> a #if MIN_VERSION_bytestring(0,11,0) withBS (BS !sfp !slen) f = unsafeDupablePerformIO $ withForeignPtr sfp $ \p -> f p slen #else withBS (PS !sfp !soff !slen) f = unsafeDupablePerformIO $ withForeignPtr sfp $ \p -> f (plusPtr p soff) slen #endif {-# INLINE withBS #-}