{-# LANGUAGE BangPatterns #-}
-- |-- Module : Data.Text.Lazy.Fusion-- Copyright : (c) 2009, 2010 Bryan O'Sullivan---- License : BSD-style-- Maintainer : bos@serpentine.com-- Stability : experimental-- Portability : GHC---- /Warning/: this is an internal module, and does not have a stable-- API or name. Functions in this module may not check or enforce-- preconditions expected by public modules. Use at your own risk!---- Core stream fusion functionality for text.
module Data.Text.Internal.Lazy.Fusion
(
stream
, unstream
, unstreamChunks
, length
, unfoldrN
, index
, countChar
) where
import Prelude hiding (length)
import qualified Data.Text.Internal.Fusion.Common as S
import Control.Monad.ST (runST)
import Data.Text.Internal.Fusion.Types
import Data.Text.Internal.Fusion.Size (isEmpty, unknownSize)
import Data.Text.Internal.Lazy
import qualified Data.Text.Internal as I
import qualified Data.Text.Array as A
import Data.Text.Internal.Unsafe.Char (unsafeWrite)
import Data.Text.Internal.Unsafe.Shift (shiftL)
import Data.Text.Unsafe (Iter(..), iter)
import Data.Int (Int64)
default(Int64)-- | /O(n)/ Convert a 'Text' into a 'Stream Char'.stream :: Text -> StreamCharstreamtext = Streamnext (text:*:0) unknownSize
where
next (Empty:*: _) = Donenext (txt@(Chunkt@(I.Text _ _ len) ts) :*:i)
| i>=len = next (ts:*:0)
| otherwise = Yieldc (txt:*:i+d)
where Itercd = iterti{-# INLINE [0] stream #-}-- | /O(n)/ Convert a 'Stream Char' into a 'Text', using the given-- chunk size.unstreamChunks :: Int -> StreamChar -> TextunstreamChunks !chunkSize (Streamnexts0len0)
| isEmptylen0 = Empty
| otherwise = outers0
where
outerso = {-# SCC "unstreamChunks/outer" #-}
case next so of
Done -> Empty
Skip s' -> outer s'
Yield x s' -> runST $ do
a <- A.new unknownLength
unsafeWrite a 0 x >>= inner a unknownLength s'
where unknownLength = 4
where
innermarr !lens !i
| i+1>=chunkSize = finishmarris
| i+1>=len = {-# SCC "unstreamChunks/resize" #-} do
let newLen = min (len `shiftL` 1) chunkSize
marr' <- A.new newLen
A.copyM marr' 0 marr 0 len
inner marr' newLen s i
| otherwise =
{-# SCC "unstreamChunks/inner" #-}
case next s of
Done -> finish marr i s
Skip s' -> inner marr len s' i
Yield x s' -> do d <- unsafeWrite marr i x
inner marr len s' (i+d)finishmarrlens' = do
arr <- A.unsafeFreezemarrreturn (I.Textarr0len`Chunk`outers')
{-# INLINE [0] unstreamChunks #-}-- | /O(n)/ Convert a 'Stream Char' into a 'Text', using-- 'defaultChunkSize'.unstream :: StreamChar -> Textunstream = unstreamChunksdefaultChunkSize{-# INLINE [0] unstream #-}-- | /O(n)/ Returns the number of characters in a text.length :: StreamChar -> Int64length = S.lengthI{-# INLINE[0] length #-}{-# RULES "LAZY STREAM stream/unstream fusion" forall s.
stream (unstream s) = s #-}-- | /O(n)/ Like 'unfoldr', 'unfoldrN' builds a stream from a seed-- value. However, the length of the result is limited by the-- first argument to 'unfoldrN'. This function is more efficient than-- 'unfoldr' when the length of the result is known.unfoldrN :: Int64 -> (a -> Maybe (Char,a)) -> a -> StreamCharunfoldrNn = S.unfoldrNIn{-# INLINE [0] unfoldrN #-}-- | /O(n)/ stream index (subscript) operator, starting from 0.index :: StreamChar -> Int64 -> Charindex = S.indexI{-# INLINE [0] index #-}-- | /O(n)/ The 'count' function returns the number of times the query-- element appears in the given stream.countChar :: Char -> StreamChar -> Int64countChar = S.countCharI{-# INLINE [0] countChar #-}