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 #-}