1 {-# LANGUAGE BangPatterns #-}
    2 -- |
    3 -- Module      : Data.Text.Lazy.Fusion
    4 -- Copyright   : (c) 2009, 2010 Bryan O'Sullivan
    5 --
    6 -- License     : BSD-style
    7 -- Maintainer  : bos@serpentine.com, rtomharper@googlemail.com,
    8 --               duncan@haskell.org
    9 -- Stability   : experimental
   10 -- Portability : GHC
   11 --
   12 -- Core stream fusion functionality for text.
   13 
   14 module Data.Text.Lazy.Fusion
   15     (
   16       stream
   17     , unstream
   18     , unstreamChunks
   19     , length
   20     , unfoldrN
   21     , index
   22     , countChar
   23     ) where
   24 
   25 import Prelude hiding (length)
   26 import qualified Data.Text.Fusion.Common as S
   27 import Data.Text.Fusion.Internal
   28 import Data.Text.Fusion.Size (isEmpty)
   29 import Data.Text.Lazy.Internal
   30 import qualified Data.Text.Internal as I
   31 import qualified Data.Text.Array as A
   32 import Data.Text.UnsafeChar (unsafeWrite)
   33 import Data.Text.UnsafeShift (shiftL)
   34 import Data.Text.Unsafe (Iter(..), iter)
   35 import Data.Int (Int64)
   36 
   37 default(Int64)
   38 
   39 -- | /O(n)/ Convert a 'Text' into a 'Stream Char'.
   40 stream :: Text -> Stream Char
   41 -- entered 103,742 timesstream text = Stream next (text :*: 0) 4 -- random HINT
   42   where
   43     next (Empty :*: _) = Done
   44     next (txt@(Chunk t@(I.Text _ _ len) ts) :*: i)
   45         | i >= len  = next (ts :*: 0)
   46         | otherwise = Yield c (txt :*: i+d)
   47         where Iter c d = iter t i
   48 {-# INLINE [0] stream #-}
   49 
   50 -- | /O(n)/ Convert a 'Stream Char' into a 'Text', using the given
   51 -- chunk size.
   52 unstreamChunks :: Int -> Stream Char -> Text
   53 -- entered 66,953 timesunstreamChunks chunkSize (Stream next s0 len0)
   54   | isEmpty len0 = Empty
   55   | otherwise    = outer s0
   56   where
   57     outer s = case next s of
   58                 Done       -> Empty
   59                 Skip s'    -> outer s'
   60                 Yield x s' -> I.Text arr 0 len `chunk` outer s''
   61                   where (arr,(s'',len)) = A.run2 fill
   62                         fill = do a <- A.unsafeNew unknownLength
   63                                   unsafeWrite a 0 x >>= inner a unknownLength s'
   64                         unknownLength = 4
   65     inner marr len s !i
   66         | i + 1 >= chunkSize = return (marr, (s,i))
   67         | i + 1 >= len       = do
   68             let newLen = min (len `shiftL` 1) chunkSize
   69             marr' <- A.unsafeNew newLen
   70             A.copyM marr' 0 marr 0 len
   71             inner marr' newLen s i
   72         | otherwise =
   73             case next s of
   74               Done        -> return (marr,(s,i))
   75               Skip s'     -> inner marr len s' i
   76               Yield x s'  -> do d <- unsafeWrite marr i x
   77                                 inner marr len s' (i+d)
   78 {-# INLINE [0] unstreamChunks #-}
   79 
   80 -- | /O(n)/ Convert a 'Stream Char' into a 'Text', using
   81 -- 'defaultChunkSize'.
   82 unstream :: Stream Char -> Text
   83 -- entered 37,203 timesunstream = unstreamChunks defaultChunkSize
   84 {-# INLINE [0] unstream #-}
   85 
   86 -- | /O(n)/ Returns the number of characters in a text.
   87 length :: Stream Char -> Int64
   88 -- entered 33 timeslength = S.lengthI
   89 {-# INLINE[0] length #-}
   90 
   91 {-# RULES "LAZY STREAM stream/unstream fusion" forall s.
   92     stream (unstream s) = s #-}
   93 
   94 -- | /O(n)/ Like 'unfoldr', 'unfoldrN' builds a stream from a seed
   95 -- value. However, the length of the result is limited by the
   96 -- first argument to 'unfoldrN'. This function is more efficient than
   97 -- 'unfoldr' when the length of the result is known.
   98 unfoldrN :: Int64 -> (a -> Maybe (Char,a)) -> a -> Stream Char
   99 -- entered 100 timesunfoldrN n = S.unfoldrNI n
  100 {-# INLINE [0] unfoldrN #-}
  101 
  102 -- | /O(n)/ stream index (subscript) operator, starting from 0.
  103 index :: Stream Char -> Int64 -> Char
  104 -- entered 100 timesindex = S.indexI
  105 {-# INLINE [0] index #-}
  106 
  107 -- | /O(n)/ The 'count' function returns the number of times the query
  108 -- element appears in the given stream.
  109 countChar :: Char -> Stream Char -> Int64
  110 -- entered 100 timescountChar = S.countCharI
  111 {-# INLINE [0] countChar #-}