-- | -- Module : Data.CompactString.Fusion -- License : BSD-style -- Maintainer : twanvl@gmail.com -- Stability : experimental -- Portability : untested -- -- Fusable loop functions, mirrors "Data.ByteString.Fusion". -- module Data.CompactString.Fusion ( loopAcc, loopArr, NoAcc(..) , foldEFL, mapEFL, filterEFL, scanEFL, mapAccumEFL, mapIndexEFL , loopUp, loopUpC, loopDown, loopUpFold, loopDownFold ) where import Data.CompactString.Internal import qualified Data.ByteString.Internal as B -- ----------------------------------------------------------------------------- -- -- Same as older bytestrings -- -- |Data type for accumulators which can be ignored. The rewrite rules rely on -- the fact that no bottoms of this type are ever constructed; hence, we can -- assume @(_ :: NoAcc) `seq` x = x@. -- data NoAcc = NoAcc -- | Projection functions that are fusion friendly (as in, we determine when -- they are inlined) loopArr :: (PairS acc arr) -> arr loopArr (_ :*: arr) = arr #if defined(__GLASGOW_HASKELL__) {-# INLINE [1] loopArr #-} #endif loopAcc :: (PairS acc arr) -> acc loopAcc (acc :*: _) = acc #if defined(__GLASGOW_HASKELL__) {-# INLINE [1] loopAcc #-} #endif -- ----------------------------------------------------------------------------- -- -- EFLs -- mapEFL :: (Char -> Char) -> AccEFL NoAcc mapEFL f = \_ e -> (NoAcc :*: (JustS $ f e)) foldEFL :: (acc -> Char -> acc) -> AccEFL acc foldEFL f = \a e -> (f a e :*: NothingS) filterEFL :: (Char -> Bool) -> AccEFL NoAcc filterEFL p = \_ e -> if p e then (NoAcc :*: JustS e) else (NoAcc :*: NothingS) scanEFL :: (Char -> Char -> Char) -> AccEFL Char scanEFL f = \a e -> (f a e :*: JustS a) -- | Element function implementing a map and fold mapAccumEFL :: (acc -> Char -> (acc, Char)) -> AccEFL acc mapAccumEFL f = \a e -> case f a e of (a', e') -> (a' :*: JustS e') -- | Element function implementing a map with index mapIndexEFL :: (Int -> Char -> Char) -> AccEFL Int mapIndexEFL f = \i e -> let i' = i+1 in i' `seq` (i' :*: JustS (f i e)) #if defined(__GLASGOW_HASKELL__) {-# INLINE [1] foldEFL #-} {-# INLINE [1] mapEFL #-} {-# INLINE [1] filterEFL #-} {-# INLINE [1] scanEFL #-} {-# INLINE [1] mapAccumEFL #-} {-# INLINE [1] mapIndexEFL #-} #endif -- ----------------------------------------------------------------------------- -- -- Looping -- loopUp :: Encoding a => AccEFL acc -> acc -> CompactString a -> PairS acc (CompactString a) loopUp f a arr = loopWrapper (newSize (encoding arr)) (doUpLoop (encoding arr) f a) arr -- | like loopUp, but the size of the buffer can only become smaller loopUpC :: Encoding a => AccEFL acc -> acc -> CompactString a -> PairS acc (CompactString a) loopUpC f a arr = loopWrapper id (doUpLoop (encoding arr) f a) arr loopDown :: Encoding a => AccEFL acc -> acc -> CompactString a -> PairS acc (CompactString a) loopDown f a arr = loopWrapper (newSize (encoding arr)) (doDownLoop (encoding arr) f a) arr loopUpFold :: Encoding a => FoldEFL acc -> acc -> CompactString a -> acc loopUpFold f a arr = loopWrapperFold (doUpLoopFold (encoding arr) f a) arr loopDownFold :: Encoding a => FoldEFL acc -> acc -> CompactString a -> acc loopDownFold f a arr = loopWrapperFold (doDownLoopFold (encoding arr) f a) arr ------------------------------------------------------------------------------- -- Wrappers -- first argument is the factor by which the string can grow -- maxCharSize for map (all 1 byte chars become maxCharSize byte chars) loopWrapper :: (Int -> Int) -> ImperativeLoop acc -> CompactString a -> PairS acc (CompactString a) loopWrapper factor body cs@(CS (B.PS _ _ srcLen)) = unsafeWithBuffer cs $ \src -> do (ps, acc) <- B.createAndTrim' (factor srcLen) $ \dest -> do (acc :*: destOffset :*: destLen) <- body src dest srcLen return (destOffset, destLen, acc) return (acc :*: CS ps) loopWrapperFold :: ImperativeLoop_ acc -> CompactString a -> acc loopWrapperFold body cs@(CS (B.PS _ _ srcLen)) = unsafeWithBuffer cs $ \src -> body src srcLen