{-# LANGUAGE BangPatterns #-} {-# LANGUAGE CPP #-} {-# LANGUAGE MagicHash #-} {-# LANGUAGE TypeApplications #-} module Data.ByteString.Base32.Internal.Loop ( innerLoop , innerLoopNoPad , decodeLoop ) where import Data.Bits import Data.ByteString (ByteString) import Data.ByteString.Base32.Internal.Utils import Data.Text (Text) import qualified Data.Text as T import Foreign.Ptr import Foreign.Storable import GHC.Exts import GHC.Word -- ------------------------------------------------------------------------ -- -- Encoding loops innerLoop :: Addr# -> Ptr Word64 -> Ptr Word8 -> Ptr Word8 -> (Ptr Word8 -> Ptr Word8 -> IO ()) -> IO () innerLoop !lut !dptr !sptr !end finish = go dptr sptr where lix a = return $ w64 (aix (fromIntegral a .&. 0x1f) lut) {-# INLINE lix #-} go !dst !src | plusPtr src 4 >= end = finish (castPtr dst) src | otherwise = do #ifdef WORDS_BIGENDIAN !t <- peek @Word32 (castPtr src) #else !t <- byteSwap32 <$> peek @Word32 (castPtr src) #endif !u <- w32 <$> peek (plusPtr src 4) !a <- lix (unsafeShiftR t 27) !b <- lix (unsafeShiftR t 22) !c <- lix (unsafeShiftR t 17) !d <- lix (unsafeShiftR t 12) !e <- lix (unsafeShiftR t 7) !f <- lix (unsafeShiftR t 2) !g <- lix ((unsafeShiftL t 3) .|. (unsafeShiftR u 5)) !h <- lix u w <- return $ a .|. (unsafeShiftL b 8) .|. (unsafeShiftL c 16) .|. (unsafeShiftL d 24) .|. (unsafeShiftL e 32) .|. (unsafeShiftL f 40) .|. (unsafeShiftL g 48) .|. (unsafeShiftL h 56) poke dst w go (plusPtr dst 8) (plusPtr src 5) {-# INLINE innerLoop #-} innerLoopNoPad :: Addr# -> Ptr Word64 -> Ptr Word8 -> Ptr Word8 -> (Ptr Word8 -> Ptr Word8 -> Int -> IO ByteString) -> IO ByteString innerLoopNoPad !lut !dptr !sptr !end finish = go dptr sptr 0 where lix a = return $ w64 (aix (fromIntegral a .&. 0x1f) lut) go !dst !src !n | plusPtr src 4 >= end = finish (castPtr dptr) src n | otherwise = do #ifdef WORDS_BIGENDIAN !t <- peek @Word32 (castPtr src) #else !t <- byteSwap32 <$> peek @Word32 (castPtr src) #endif !u <- w32 <$> peek (plusPtr src 4) !a <- lix (unsafeShiftR t 27) !b <- lix (unsafeShiftR t 22) !c <- lix (unsafeShiftR t 17) !d <- lix (unsafeShiftR t 12) !e <- lix (unsafeShiftR t 7) !f <- lix (unsafeShiftR t 2) !g <- lix ((unsafeShiftL t 3) .|. (unsafeShiftR u 5)) !h <- lix u !w <- return $ a .|. (unsafeShiftL b 8) .|. (unsafeShiftL c 16) .|. (unsafeShiftL d 24) .|. (unsafeShiftL e 32) .|. (unsafeShiftL f 40) .|. (unsafeShiftL g 48) .|. (unsafeShiftL h 56) poke dst w go (plusPtr dst 8) (plusPtr src 5) (n + 8) {-# INLINE innerLoopNoPad #-} -- ------------------------------------------------------------------------ -- -- Decoding loops decodeLoop :: Addr# -> Ptr Word8 -> Ptr Word64 -> Ptr Word8 -> (Ptr Word8 -> Ptr Word8 -> Int -> IO (Either Text ByteString)) -> IO (Either Text ByteString) decodeLoop !lut !dptr !sptr !end finish = go dptr sptr 0 where lix a = w64 (aix (fromIntegral a) lut) roll !w !acc = (acc `unsafeShiftL` 5) .|. lix w err = return . Left . T.pack go !dst !src !n | plusPtr src 8 == end = finish dst (castPtr src) n | otherwise = do #ifdef WORDS_BIGENDIAN !t <- peek src #else !t <- byteSwap64 <$> peek src #endif !w <- return $ roll (unsafeShiftR t 0) $ roll (unsafeShiftR t 8) $ roll (unsafeShiftR t 16) $ roll (unsafeShiftR t 24) $ roll (unsafeShiftR t 32) $ roll (unsafeShiftR t 40) $ roll (unsafeShiftR t 48) $ roll (unsafeShiftR t 56) 0 if w /= 0xff then do poke @Word8 dst (fromIntegral (w `unsafeShiftR` 32)) poke @Word32 (castPtr (plusPtr dst 1)) (byteSwap32 (fromIntegral w)) go (plusPtr dst 5) (plusPtr src 8) (n + 5) else err $ "invalid character at offset: " ++ show (src `minusPtr` sptr)