{-# 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 -> Stream Char stream text = Stream next (text :*: 0) unknownSize where next (Empty :*: _) = Done next (txt@(Chunk t@(I.Text _ _ len) ts) :*: i) | i >= len = next (ts :*: 0) | otherwise = Yield c (txt :*: i+d) where Iter c d = iter t i {-# INLINE [0] stream #-} -- | /O(n)/ Convert a 'Stream Char' into a 'Text', using the given -- chunk size. unstreamChunks :: Int -> Stream Char -> Text unstreamChunks !chunkSize (Stream next s0 len0) | isEmpty len0 = Empty | otherwise = outer s0 where outer so = {-# 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 inner marr !len s !i | i + 1 >= chunkSize = finish marr i s | 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) finish marr len s' = do arr <- A.unsafeFreeze marr return (I.Text arr 0 len `Chunk` outer s') {-# INLINE [0] unstreamChunks #-} -- | /O(n)/ Convert a 'Stream Char' into a 'Text', using -- 'defaultChunkSize'. unstream :: Stream Char -> Text unstream = unstreamChunks defaultChunkSize {-# INLINE [0] unstream #-} -- | /O(n)/ Returns the number of characters in a text. length :: Stream Char -> Int64 length = 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 -> Stream Char unfoldrN n = S.unfoldrNI n {-# INLINE [0] unfoldrN #-} -- | /O(n)/ stream index (subscript) operator, starting from 0. index :: Stream Char -> Int64 -> Char index = 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 -> Stream Char -> Int64 countChar = S.countCharI {-# INLINE [0] countChar #-}