packages feed

text 0.3 → 0.4

raw patch · 12 files changed

+709/−243 lines, 12 files

Files

Data/Text.hs view
@@ -54,13 +54,19 @@     , intersperse     , transpose     , reverse+    , replace -    -- * Case conversion+    -- ** Case conversion     -- $case     , toCaseFold     , toLower     , toUpper +    -- ** Justification+    , justifyLeft+    , justifyRight+    , center+     -- * Folds     , foldl     , foldl'@@ -91,6 +97,7 @@      -- ** Generation and unfolding     , replicate+    , replicateChar     , unfoldr     , unfoldrN @@ -101,6 +108,11 @@     , drop     , takeWhile     , dropWhile+    , dropWhileEnd+    , dropAround+    , strip+    , stripStart+    , stripEnd     , splitAt     , span     , break@@ -110,9 +122,12 @@     , tails      -- ** Breaking into many substrings+    -- $split     , split+    , splitTimes+    , splitTimesEnd     , splitWith-    , breakSubstring+    , chunksOf      -- ** Breaking into lines and words     , lines@@ -135,6 +150,7 @@     -- , findSubstring          -- * Indexing+    -- $index     , index     , findIndex     , findIndices@@ -154,7 +170,7 @@                 Eq(..), Ord(..), (++),                 Read(..), Show(..),                 (&&), (||), (+), (-), (.), ($),-                not, return, otherwise)+                fromIntegral, div, not, return, otherwise) import Control.Exception (assert) import Data.Char (isSpace) import Control.Monad.ST (ST)@@ -170,15 +186,15 @@  import Data.Text.Internal (Text(..), empty, text, textP) import qualified Prelude as P-import Data.Text.Unsafe (iter, iter_, unsafeHead, unsafeTail)+import Data.Text.Unsafe (iter, iter_, reverseIter, unsafeHead, unsafeTail) import Data.Text.UnsafeChar (unsafeChr) import qualified Data.Text.Encoding.Utf16 as U16  -- $fusion ----- Most of the functions in this module are subject to /array fusion/,--- meaning that a pipeline of functions will usually allocate at most--- one 'Text' value.+-- Most of the functions in this module are subject to /fusion/,+-- meaning that a pipeline of such functions will usually allocate at+-- most one 'Text' value.  instance Eq Text where     t1 == t2 = stream t1 == stream t2@@ -205,21 +221,18 @@ -- ----------------------------------------------------------------------------- -- * Conversion to/from 'Text' --- | /O(n)/ Convert a 'String' into a 'Text'.------ This function is subject to array fusion.+-- | /O(n)/ Convert a 'String' into a 'Text'.  Subject to fusion. pack :: String -> Text pack = unstream . S.streamList {-# INLINE [1] pack #-} --- | /O(n)/ Convert a Text into a String.--- Subject to array fusion.+-- | /O(n)/ Convert a Text into a String.  Subject to fusion. unpack :: Text -> String unpack = S.unstreamList . stream {-# INLINE [1] unpack #-}  -- | /O(1)/ Convert a character into a Text.--- Subject to array fusion.+-- Subject to fusion. singleton :: Char -> Text singleton = unstream . S.singleton {-# INLINE [1] singleton #-}@@ -229,19 +242,19 @@  -- | /O(n)/ Adds a character to the front of a 'Text'.  This function -- is more costly than its 'List' counterpart because it requires--- copying a new array.  Subject to array fusion.+-- copying a new array.  Subject to fusion. cons :: Char -> Text -> Text cons c t = unstream (S.cons c (stream t)) {-# INLINE cons #-}  -- | /O(n)/ Adds a character to the end of a 'Text'.  This copies the--- entire array in the process.  Subject to array fusion.+-- entire array in the process, unless fused.  Subject to fusion. snoc :: Text -> Char -> Text snoc t c = unstream (S.snoc (stream t) c) {-# INLINE snoc #-}  -- | /O(n)/ Appends one 'Text' to the other by copying both of them--- into a new 'Text'.  Subject to array fusion.+-- into a new 'Text'.  Subject to fusion. append :: Text -> Text -> Text append (Text arr1 off1 len1) (Text arr2 off2 len2) = Text (A.run x) 0 len     where@@ -266,19 +279,19 @@  #-}  -- | /O(1)/ Returns the first character of a 'Text', which must be--- non-empty.  Subject to array fusion.+-- non-empty.  Subject to fusion. head :: Text -> Char head t = S.head (stream t) {-# INLINE head #-}  -- | /O(1)/ Returns the first character and rest of a 'Text', or--- 'Nothing' if empty. Subject to array fusion.+-- 'Nothing' if empty. Subject to fusion. uncons :: Text -> Maybe (Char, Text) uncons t@(Text arr off len)     | len <= 0  = Nothing     | otherwise = Just (c, textP arr (off+d) (len-d))     where (c,d) = iter t 0-{-# INLINE uncons #-}+{-# INLINE [1] uncons #-}  -- | Lifted from Control.Arrow and specialized. second :: (b -> c) -> (a,b) -> (a,c)@@ -292,7 +305,7 @@   #-}  -- | /O(1)/ Returns the last character of a 'Text', which must be--- non-empty.  Subject to array fusion.+-- non-empty.  Subject to fusion. last :: Text -> Char last (Text arr off len)     | len <= 0                 = emptyError "last"@@ -310,7 +323,7 @@   #-}  -- | /O(1)/ Returns all characters after the head of a 'Text', which--- must be non-empty.  Subject to array fusion.+-- must be non-empty.  Subject to fusion. tail :: Text -> Text tail t@(Text arr off len)     | len <= 0  = emptyError "tail"@@ -326,7 +339,7 @@  #-}  -- | /O(1)/ Returns all but the last character of a 'Text', which must--- be non-empty.  Subject to array fusion.+-- be non-empty.  Subject to fusion. init :: Text -> Text init (Text arr off len) | len <= 0                   = emptyError "init"                         | n >= 0xDC00 && n <= 0xDFFF = textP arr off (len-2)@@ -342,7 +355,7 @@     unstream (S.init (stream t)) = init t  #-} --- | /O(1)/ Tests whether a 'Text' is empty or not.  Subject to array+-- | /O(1)/ Tests whether a 'Text' is empty or not.  Subject to -- fusion. null :: Text -> Bool null (Text _arr _off len) = assert (len >= 0) $ len <= 0@@ -356,15 +369,15 @@  #-}  -- | /O(n)/ Returns the number of characters in a 'Text'.--- Subject to array fusion.+-- Subject to fusion. length :: Text -> Int length t = S.length (stream t) {-# INLINE length #-}  -- ----------------------------------------------------------------------------- -- * Transformations--- | /O(n)/ 'map' @f @xs is the 'Text' obtained by applying @f@ to--- each element of @xs@.  Subject to array fusion.+-- | /O(n)/ 'map' @f@ @t@ is the 'Text' obtained by applying @f@ to+-- each element of @t@.  Subject to fusion. map :: (Char -> Char) -> Text -> Text map f t = unstream (S.map f (stream t)) {-# INLINE [1] map #-}@@ -377,31 +390,41 @@ {-# INLINE intercalate #-}  -- | /O(n)/ The 'intersperse' function takes a character and places it--- between the characters of a 'Text'.  Subject to array fusion.+-- between the characters of a 'Text'.  Subject to fusion. intersperse     :: Char -> Text -> Text intersperse c t = unstream (S.intersperse c (stream t)) {-# INLINE intersperse #-} --- | /O(n)/ Reverse the characters of a string. Subject to array fusion.+-- | /O(n)/ Reverse the characters of a string. Subject to fusion. reverse :: Text -> Text reverse t = S.reverse (stream t) {-# INLINE reverse #-} +-- | /O(m)*O(n)/ Replace every occurrence of one substring with another.+replace :: Text                 -- ^ Text to search for+        -> Text                 -- ^ Replacement text+        -> Text                 -- ^ Input text+        -> Text+replace s d = intercalate d . split s+{-# INLINE replace #-}+ -- ---------------------------------------------------------------------------- -- ** Case conversions (folds)  -- $case ----- With Unicode text, it is incorrect to use combinators like @map--- toUpper@ to case convert each character of a string individually.--- Instead, use the whole-string case conversion functions from this--- module.  For correctness in different writing systems, these--- functions may map one input character to two or three output--- characters.+-- When case converting 'Text' values, do not use combinators like+-- @map toUpper@ to case convert each character of a string+-- individually, as this gives incorrect results according to the+-- rules of some writing systems.  The whole-string case conversion+-- functions from this module, such as @toUpper@, obey the correct+-- case conversion rules.  As a result, these functions may map one+-- input character to two or three output characters. For examples,+-- see the documentation of each function.  -- | /O(n)/ Convert a string to folded case.  This function is mainly--- useful for performing caseless (or case insensitive) string--- comparisons.+-- useful for performing caseless (also known as case insensitive)+-- string comparisons. -- -- A string @x@ is a caseless match for a string @y@ if and only if: --@@ -409,31 +432,77 @@ -- -- The result string may be longer than the input string, and may -- differ from applying 'toLower' to the input string.  For instance,--- the Armenian small ligature men now (U+FB13) is case folded to the--- bigram men now (U+0574 U+0576), while the micro sign (U+00B5) is--- case folded to the Greek small letter letter mu (U+03BC) instead of--- itself.+-- the Armenian small ligature \"&#xfb13;\" (men now, U+FB13) is case+-- folded to the sequence \"&#x574;\" (men, U+0574) followed by+-- \"&#x576;\" (now, U+0576), while the Greek \"&#xb5;\" (micro sign,+-- U+00B5) is case folded to \"&#x3bc;\" (small letter mu, U+03BC)+-- instead of itself. toCaseFold :: Text -> Text toCaseFold t = unstream (S.toCaseFold (stream t)) {-# INLINE [0] toCaseFold #-}  -- | /O(n)/ Convert a string to lower case, using simple case -- conversion.  The result string may be longer than the input string.--- For instance, the Latin capital letter I with dot above (U+0130)--- maps to the sequence Latin small letter i (U+0069) followed by--- combining dot above (U+0307).+-- For instance, \"&#x130;\" (Latin capital letter I with dot above,+-- U+0130) maps to the sequence \"i\" (Latin small letter i, U+0069) followed+-- by \" &#x307;\" (combining dot above, U+0307). toLower :: Text -> Text toLower t = unstream (S.toLower (stream t)) {-# INLINE toLower #-}  -- | /O(n)/ Convert a string to upper case, using simple case -- conversion.  The result string may be longer than the input string.--- For instance, the German eszett (U+00DF) maps to the two-letter--- sequence SS.+-- For instance, the German \"&#xdf;\" (eszett, U+00DF) maps to the+-- two-letter sequence \"SS\". toUpper :: Text -> Text toUpper t = unstream (S.toUpper (stream t)) {-# INLINE toUpper #-} +-- | /O(n)/ Left-justify a string to the given length, using the+-- specified fill character on the right. Subject to fusion. Examples:+--+-- > justifyLeft 7 'x' "foo"    == "fooxxxx"+-- > justifyLeft 3 'x' "foobar" == "foobar"+justifyLeft :: Int -> Char -> Text -> Text+justifyLeft k c t+    | len >= k  = t+    | otherwise = t `append` replicateChar (k-len) c+  where len = length t+{-# INLINE [1] justifyLeft #-}++{-# RULES+"TEXT justifyLeft -> fused" [~1] forall k c t.+    justifyLeft k c t = unstream (S.justifyLeftI k c (stream t))+"TEXT justifyLeft -> unfused" [1] forall k c t.+    unstream (S.justifyLeftI k c (stream t)) = justifyLeft k c t+  #-}++-- | /O(n)/ Right-justify a string to the given length, using the+-- specified fill character on the left. Examples:+--+-- > justifyRight 7 'x' "bar"    == "xxxxbar"+-- > justifyRight 3 'x' "foobar" == "foobar"+justifyRight :: Int -> Char -> Text -> Text+justifyRight k c t+    | len >= k  = t+    | otherwise = replicateChar (k-len) c `append` t+  where len = length t+{-# INLINE justifyRight #-}++-- | /O(n)/ Center a string to the given length, using the+-- specified fill character on either side. Examples:+--+-- > center 8 'x' "HS" = "xxxHSxxx"+center :: Int -> Char -> Text -> Text+center k c t+    | len >= k  = t+    | otherwise = replicateChar l c `append` t `append` replicateChar r c+  where len = length t+        d   = k - len+        r   = d `div` 2+        l   = d - r+{-# INLINE center #-}+ -- | /O(n)/ The 'transpose' function transposes the rows and columns -- of its 'Text' argument.  Note that this function uses 'pack', -- 'unpack', and the list version of transpose, and is thus not very@@ -447,26 +516,23 @@ -- | /O(n)/ 'foldl', applied to a binary operator, a starting value -- (typically the left-identity of the operator), and a 'Text', -- reduces the 'Text' using the binary operator, from left to right.--- Subject to array fusion.+-- Subject to fusion. foldl :: (b -> Char -> b) -> b -> Text -> b foldl f z t = S.foldl f z (stream t) {-# INLINE foldl #-} --- | /O(n)/ A strict version of 'foldl'.--- Subject to array fusion.+-- | /O(n)/ A strict version of 'foldl'.  Subject to fusion. foldl' :: (b -> Char -> b) -> b -> Text -> b foldl' f z t = S.foldl' f z (stream t) {-# INLINE foldl' #-}  -- | /O(n)/ A variant of 'foldl' that has no starting value argument,--- and thus must be applied to a non-empty 'Text'.  Subject to array--- fusion.+-- and thus must be applied to a non-empty 'Text'.  Subject to fusion. foldl1 :: (Char -> Char -> Char) -> Text -> Char foldl1 f t = S.foldl1 f (stream t) {-# INLINE foldl1 #-} --- | /O(n)/ A strict version of 'foldl1'.--- Subject to array fusion.+-- | /O(n)/ A strict version of 'foldl1'.  Subject to fusion. foldl1' :: (Char -> Char -> Char) -> Text -> Char foldl1' f t = S.foldl1' f (stream t) {-# INLINE foldl1' #-}@@ -474,13 +540,13 @@ -- | /O(n)/ 'foldr', applied to a binary operator, a starting value -- (typically the right-identity of the operator), and a 'Text', -- reduces the 'Text' using the binary operator, from right to left.--- Subject to array fusion.+-- Subject to fusion. foldr :: (Char -> b -> b) -> b -> Text -> b foldr f z t = S.foldr f z (stream t) {-# INLINE foldr #-} --- | /O(n)/ A variant of 'foldr' that has no starting value argument, and--- thust must be applied to a non-empty 'Text'.  Subject to array+-- | /O(n)/ A variant of 'foldr' that has no starting value argument,+-- and thust must be applied to a non-empty 'Text'.  Subject to -- fusion. foldr1 :: (Char -> Char -> Char) -> Text -> Char foldr1 f t = S.foldr1 f (stream t)@@ -489,13 +555,13 @@ -- ----------------------------------------------------------------------------- -- ** Special folds --- | /O(n)/ Concatenate a list of 'Text's. Subject to array fusion.+-- | /O(n)/ Concatenate a list of 'Text's. Subject to fusion. concat :: [Text] -> Text concat ts = unstream (S.concat (L.map stream ts)) {-# INLINE concat #-}  -- | /O(n)/ Map a function over a 'Text' that results in a 'Text', and--- concatenate the results.  This function is subject to array fusion.+-- concatenate the results.  Subject to fusion. -- -- Note: if in 'concatMap' @f@ @t@, @f@ is defined in terms of fusible -- functions, it will also be fusible.@@ -504,25 +570,25 @@ {-# INLINE concatMap #-}  -- | /O(n)/ 'any' @p@ @t@ determines whether any character in the--- 'Text' @t@ satisifes the predicate @p@. Subject to array fusion.+-- 'Text' @t@ satisifes the predicate @p@. Subject to fusion. any :: (Char -> Bool) -> Text -> Bool any p t = S.any p (stream t) {-# INLINE any #-}  -- | /O(n)/ 'all' @p@ @t@ determines whether all characters in the--- 'Text' @t@ satisify the predicate @p@. Subject to array fusion.+-- 'Text' @t@ satisify the predicate @p@. Subject to fusion. all :: (Char -> Bool) -> Text -> Bool all p t = S.all p (stream t) {-# INLINE all #-}  -- | /O(n)/ 'maximum' returns the maximum value from a 'Text', which--- must be non-empty. Subject to array fusion.+-- must be non-empty. Subject to fusion. maximum :: Text -> Char maximum t = S.maximum (stream t) {-# INLINE maximum #-}  -- | /O(n)/ 'minimum' returns the minimum value from a 'Text', which--- must be non-empty. Subject to array fusion.+-- must be non-empty. Subject to fusion. minimum :: Text -> Char minimum t = S.minimum (stream t) {-# INLINE minimum #-}@@ -531,8 +597,7 @@ -- * Building 'Text's  -- | /O(n)/ 'scanl' is similar to 'foldl', but returns a list of--- successive reduced values from the left. This function is subject--- to array fusion.+-- successive reduced values from the left. Subject to fusion. -- -- > scanl f z [x1, x2, ...] == [z, z `f` x1, (z `f` x1) `f` x2, ...] --@@ -544,7 +609,7 @@ {-# INLINE scanl #-}  -- | /O(n)/ 'scanl1' is a variant of 'scanl' that has no starting--- value argument.  This function is subject to array fusion.+-- value argument.  Subject to fusion. -- -- > scanl1 f [x1, x2, ...] == [x1, x1 `f` x2, ...] scanl1 :: (Char -> Char -> Char) -> Text -> Text@@ -560,7 +625,7 @@ {-# INLINE scanr #-}  -- | /O(n)/ 'scanr1' is a variant of 'scanr' that has no starting--- value argument.  This function is subject to array fusion.+-- value argument.  Subject to fusion. scanr1 :: (Char -> Char -> Char) -> Text -> Text scanr1 f t | null t    = empty            | otherwise = scanr f (last t) (init t)@@ -590,11 +655,21 @@ -- ----------------------------------------------------------------------------- -- ** Generating and unfolding 'Text's --- | /O(n)/ 'replicate' @n@ @c@ is a 'Text' of length @n@ with @c@ the+-- | /O(n*m)/ 'replicate' @n@ @t@ is a 'Text' consisting of the input+-- @t@ repeated @n@ times. Subject to fusion.+replicate :: Int -> Text -> Text+replicate n t = unstream (S.replicateI (fromIntegral n) (S.stream t))+{-# INLINE [1] replicate #-}++{-# RULES+"TEXT replicate/singleton -> replicateChar" [~1] forall n c.+    replicate n (singleton c) = replicateChar n c+  #-}++-- | /O(n)/ 'replicateChar' @n@ @c@ is a 'Text' of length @n@ with @c@ the -- value of every element. Subject to fusion.-replicate :: Int -> Char -> Text-replicate n c = unstream (S.replicate n c)-{-# INLINE replicate #-}+replicateChar :: Int -> Char -> Text+replicateChar n c = unstream (S.replicateCharI n c)  -- | /O(n)/, where @n@ is the length of the result. The 'unfoldr' -- function is analogous to the List 'L.unfoldr'. 'unfoldr' builds a@@ -663,9 +738,9 @@     unstream (S.drop n (stream t)) = drop n t   #-} --- | /O(n)/ 'takeWhile', applied to a predicate @p@ and a 'Text', returns--- the longest prefix (possibly empty) of elements that satisfy @p@.--- This function is subject to array fusion.+-- | /O(n)/ 'takeWhile', applied to a predicate @p@ and a 'Text',+-- returns the longest prefix (possibly empty) of elements that+-- satisfy @p@.  Subject to fusion. takeWhile :: (Char -> Bool) -> Text -> Text takeWhile p t@(Text arr off len) = loop 0   where loop !i | i >= len    = t@@ -681,8 +756,8 @@     unstream (S.takeWhile p (stream t)) = takeWhile p t   #-} --- | /O(n)/ 'dropWhile' @p@ @xs@ returns the suffix remaining after--- 'takeWhile' @p@ @xs@. This function is subject to array fusion.+-- | /O(n)/ 'dropWhile' @p@ @t@ returns the suffix remaining after+-- 'takeWhile' @p@ @t@. Subject to fusion. dropWhile :: (Char -> Bool) -> Text -> Text dropWhile p t@(Text arr off len) = loop 0 0   where loop !i !l | l >= len  = empty@@ -698,6 +773,56 @@     unstream (S.dropWhile p (stream t)) = dropWhile p t   #-} +-- | /O(n)/ 'dropWhileEnd' @p@ @t@ returns the prefix remaining after+-- dropping characters that fail the predicate @p@ from the end of+-- @t@.  Subject to fusion.+-- Examples:+--+-- > dropWhileEnd (=='.') "foo..." == "foo"+dropWhileEnd :: (Char -> Bool) -> Text -> Text+dropWhileEnd p t@(Text arr off len) = loop (len-1) len+  where loop !i !l | l <= 0    = empty+                   | p c       = loop (i+d) (l+d)+                   | otherwise = Text arr off l+            where (c,d)        = reverseIter t i+{-# INLINE [1] dropWhileEnd #-}++{-# RULES+"TEXT dropWhileEnd -> fused" [~1] forall p t.+    dropWhileEnd p t = S.reverse (S.dropWhile p (S.reverseStream t))+"TEXT dropWhileEnd -> unfused" [1] forall p t.+    S.reverse (S.dropWhile p (S.reverseStream t)) = dropWhileEnd p t+  #-}++-- | /O(n)/ 'dropAround' @p@ @t@ returns the substring remaining after+-- dropping characters that fail the predicate @p@ from both the+-- beginning and end of @t@.  Subject to fusion.+dropAround :: (Char -> Bool) -> Text -> Text+dropAround p = dropWhile p . dropWhileEnd p+{-# INLINE [1] dropAround #-}++-- | /O(n)/ Remove leading white space from a string.  Equivalent to:+--+-- > dropWhile isSpace+stripStart :: Text -> Text+stripStart = dropWhile isSpace+{-# INLINE [1] stripStart #-}++-- | /O(n)/ Remove trailing white space from a string.  Equivalent to:+--+-- > dropWhileEnd isSpace+stripEnd :: Text -> Text+stripEnd = dropWhileEnd isSpace+{-# INLINE [1] stripEnd #-}++-- | /O(n)/ Remove leading and trailing white space from a string.+-- Equivalent to:+--+-- > dropAround isSpace+strip :: Text -> Text+strip = dropAround isSpace+{-# INLINE [1] strip #-}+ -- | /O(n)/ 'splitAt' @n t@ returns a pair whose first element is a -- prefix of @t@ of length @n@, and whose second is the remainder of -- the string. It is equivalent to @('take' n t, 'drop' n t)@.@@ -767,40 +892,128 @@ tails t | null t    = [empty]         | otherwise = t : tails (unsafeTail t) --- | /O(n)/ Break a 'Text' into pieces separated by the 'Char'--- argument, consuming the delimiter. I.e.+-- $split ----- > split '\n' "a\nb\nd\ne" == ["a","b","d","e"]--- > split 'a'  "aXaXaXa"    == ["","X","X","X",""]--- > split 'x'  "x"          == ["",""]+-- Splitting functions in this library do not perform character-wise+-- copies to create substrings; they just construct new 'Text's that+-- are slices of the original.++-- | /O(m)*O(n)/ Break a 'Text' into pieces separated by the first+-- 'Text' argument, consuming the delimiter. An empty delimiter is+-- invalid, and will cause an error to be raised.+--+-- Examples:+--+-- > split "\r\n" "a\r\nb\r\nd\r\ne" == ["a","b","d","e"]+-- > split "aaa"  "aaaXaaaXaaaXaaa"  == ["","X","X","X",""]+-- > split "x"    "x"                == ["",""] --  -- and ----- > intercalate (singleton c) . split c == id--- > split == splitWith . (==)--- --- As for all splitting functions in this library, this function does--- not copy the substrings, it just constructs new 'Text's that are--- slices of the original.-split :: Char -> Text -> [Text]-split c = splitWith (==c)-{-# INLINE split #-}+-- > intercalate s . split s         == id+-- > split (singleton c)             == splitWith (==c)+split :: Text                   -- ^ Text to split on+      -> Text                   -- ^ Input text+      -> [Text]+split pat src0+    | l == 0    = emptyError "split"+    | l == 1    = splitWith (== (unsafeHead pat)) src0+    | otherwise = go src0+  where+    l      = length pat+    go src = search 0 src+      where+        search !n !s+            | null s             = [src]      -- not found+            | pat `isPrefixOf` s = take n src : go (drop l s)+            | otherwise          = search (n+1) (unsafeTail s)+{-# INLINE [1] split #-} +{-# RULES+"TEXT split/singleton -> splitWith/==" [~1] forall c t.+    split (singleton c) t = splitWith (==c) t+  #-}++-- | /O(m)*O(n)/ Break a 'Text' into pieces at most @k@ times,+-- treating the first 'Text' argument as the delimiter to break on,+-- and consuming the delimiter.  The last element of the list contains+-- the remaining text after the number of times to split has been+-- reached.  A value of zero or less for @k@ causes no splitting to+-- occur. An empty delimiter is invalid, and will cause an error to be+-- raised.+--+-- Examples:+--+-- > splitTimes 0   "//"  "a//b//c"   == ["a//b//c"]+-- > splitTimes 2   ":"   "a:b:c:d:e" == ["a","b","c:d:e"]+-- > splitTimes 100 "???" "a????b"    == ["a","?b"]+--+-- and+--+-- > intercalate s . splitTimes k s   == id+splitTimes :: Int               -- ^ Maximum number of times to split+           -> Text              -- ^ Text to split on+           -> Text              -- ^ Input text+           -> [Text]+splitTimes k pat src0+    | k <= 0    = [src0]+    | l == 0    = emptyError "splitTimes"+    | otherwise = go k src0+  where+    l         = length pat+    go !i src = search 0 src+      where+        search !n !s+            | i == 0 || null s   = [src]      -- not found or limit reached+            | pat `isPrefixOf` s = take n src : go (i-1) (drop l s)+            | otherwise          = search (n+1) (unsafeTail s)+{-# INLINE splitTimes #-}++-- | /O(m)*O(n)/ Break a 'Text' into pieces at most @k@ times, like+-- 'splitTimes', but start from the end of the input and work towards+-- the start.+--+-- Examples:+--+-- > splitTimes 2    "::" "a::b::c::d::e" == ["a","b","c::d::e"]+-- > splitTimesEnd 2 "::" "a::b::c::d::e" == ["a::b::c","d","e"]+splitTimesEnd :: Int               -- ^ Maximum number of times to split+              -> Text              -- ^ Text to split on+              -> Text              -- ^ Input text+              -> [Text]+splitTimesEnd k pat src =+    L.reverse . L.map reverse $ splitTimes k (reverse pat) (reverse src)+{-# INLINE splitTimesEnd #-}+ -- | /O(n)/ Splits a 'Text' into components delimited by separators, -- where the predicate returns True for a separator element.  The -- resulting components do not contain the separators.  Two adjacent -- separators result in an empty component in the output.  eg. -- -- > splitWith (=='a') "aabbaca" == ["","","bb","c",""]--- > splitWith (=='a') []        == []+-- > splitWith (=='a') ""        == [""] splitWith :: (Char -> Bool) -> Text -> [Text]-splitWith _ (Text _off _arr 0) = []+splitWith _ t@(Text _off _arr 0) = [t] splitWith p t = loop t     where loop s | null s'   = [l]                  | otherwise = l : loop (unsafeTail s')               where (l, s') = break p s {-# INLINE splitWith #-} +-- | /O(n)/ Splits a 'Text' into components of length @k@.  The last+-- element may be shorter than the other chunks, depending on the+-- length of the input. Examples:+--+-- > chunksOf 3 "foobarbaz"   == ["foo","bar","baz"]+-- > chunksOf 4 "haskell.org" == ["hask","ell.","org"]+chunksOf :: Int -> Text -> [Text]+chunksOf k = go+  where+    go t = case splitAt k t of+             (a,b) | null a    -> []+                   | otherwise -> a : go b+{-# INLINE chunksOf #-}+ -- ---------------------------------------------------------------------------- -- * Searching @@ -831,38 +1044,6 @@ partition p t = (filter p t, filter (not . p) t) {-# INLINE partition #-} --- | /O(n)/ Break a string on a substring, returning a pair of the--- part of the string prior to the match, and the rest of the string.------ The following relationship holds:------ > break (==c) l == breakSubstring (singleton c) l------ For example, to tokenise a string, dropping delimiters:------ > tokenise x y = h : if null t then [] else tokenise x (drop (length x) t)--- >     where (h,t) = breakSubstring x y------ To skip to the first occurence of a string:------ > snd (breakSubstring x y)------ To take the parts of a string before a delimiter:------ > fst (breakSubstring x y)----breakSubstring :: Text -- ^ String to search for-               -> Text -- ^ String to search in-               -> (Text,Text) -- ^ Head and tail of string broken at substring--breakSubstring pat src = search 0 src-  where-    search !n !s-        | null s             = (src,empty)      -- not found-        | pat `isPrefixOf` s = (take n src,s)-        | otherwise          = search (n+1) (unsafeTail s)-{-# INLINE breakSubstring #-}- -- | /O(n)/ 'filter', applied to a predicate and a 'Text', -- returns a 'Text' containing those characters that satisfy the -- predicate.@@ -874,6 +1055,23 @@ ------------------------------------------------------------------------------- -- ** Indexing 'Text's +-- $index+--+-- If you think of a 'Text' value as an array of 'Char' values (which+-- it is not), you run the risk of writing inefficient code.+--+-- An idiom that is common in some languages is to find the numeric+-- offset of a character or substring, then use that number to split+-- or trim the searched string.  With a 'Text' value, this approach+-- would require two /O(n)/ operations: one to perform the search, and+-- one to operate from wherever the search ended.+--+-- For example, suppose you have a string that you want to split on+-- the substring @\"::\"@, such as @\"foo::bar::quux\"@. Instead of+-- searching for the index of @\"::\"@ and taking the substrings+-- before and after that index, you would instead use @splitTimes 1+-- "::"@.+ -- | /O(n)/ 'Text' index (subscript) operator, starting from 0. index :: Text -> Int -> Char index t n = S.index (stream t) n@@ -881,39 +1079,59 @@  -- | /O(n)/ The 'findIndex' function takes a predicate and a 'Text' -- and returns the index of the first element in the 'Text' satisfying--- the predicate. This function is subject to fusion.+-- the predicate. Subject to fusion. findIndex :: (Char -> Bool) -> Text -> Maybe Int findIndex p t = S.findIndex p (stream t) {-# INLINE findIndex #-}  -- | The 'findIndices' function extends 'findIndex', by returning the -- indices of all elements satisfying the predicate, in ascending--- order. This function is subject to fusion.+-- order. Subject to fusion. findIndices :: (Char -> Bool) -> Text -> [Int] findIndices p t = S.findIndices p (stream t) {-# INLINE findIndices #-}  -- | /O(n)/ The 'elemIndex' function returns the index of the first -- element in the given 'Text' which is equal to the query element, or--- 'Nothing' if there is no such element. This function is subject to--- fusion.+-- 'Nothing' if there is no such element. Subject to fusion. elemIndex :: Char -> Text -> Maybe Int elemIndex c t = S.elemIndex c (stream t) {-# INLINE elemIndex #-}  -- | /O(n)/ The 'elemIndices' function returns the index of every -- element in the given 'Text' which is equal to the query--- element. This function is subject to fusion.+-- element. Subject to fusion. elemIndices :: Char -> Text -> [Int] elemIndices c t = S.elemIndices c (stream t) {-# INLINE elemIndices #-} --- | /O(n)/ The 'count' function returns the number of times the query--- element appears in the given 'Text'. This function is subject to--- fusion.-count :: Char -> Text -> Int-count c t = S.count c (stream t)-{-# INLINE count #-}+-- | /O(n*m)/ The 'count' function returns the number of times the+-- query string appears in the given 'Text'. An empty query string is+-- invalid, and will cause an error to be raised.+count :: Text -> Text -> Int+count pat src0+    | l == 0    = emptyError "count"+    | l == 1    = countChar (unsafeHead pat) src0+    | otherwise = go 0 src0+  where+    l = length pat+    go !n src = search src+      where+        search s | null s             = n+                 | pat `isPrefixOf` s = go (n+1) (drop l s)+                 | otherwise          = search (unsafeTail s)+{-# INLINE [1] count #-}++{-# RULES+"TEXT count/singleton -> countChar" [~1] forall c t.+    count (singleton c) t = countChar c t+  #-}++-- | /O(n)/ The 'countChar' function returns the number of times the+-- query element appears in the given 'Text'. Subject to fusion.+countChar :: Char -> Text -> Int+countChar c t = S.countChar c (stream t)+{-# INLINE countChar #-}  ------------------------------------------------------------------------------- -- * Zipping
Data/Text/Array.hs view
@@ -166,9 +166,6 @@  #if defined(__GLASGOW_HASKELL__) -iNT_SCALE :: Int# -> Int#-iNT_SCALE n# = scale# *# n# where I# scale# = SIZEOF_INT- wORD16_SCALE :: Int# -> Int# wORD16_SCALE n# = scale# *# n# where I# scale# = SIZEOF_WORD16 @@ -227,38 +224,6 @@   marr <- unsafeNew len   sequence_ [unsafeWrite marr i initVal | i <- [0..len-1]]   return marr--instance Elt Int where-#if defined(__GLASGOW_HASKELL__)--    bytesInArray (I# i#) _ = I# (iNT_SCALE i#)-    {-# INLINE bytesInArray #-}--    unsafeIndex (Array len ba#) i@(I# i#) =-      CHECK_BOUNDS("unsafeIndex",len,i)-        case indexIntArray# ba# i# of r# -> (I# r#)-    {-# INLINE unsafeIndex #-}--    unsafeRead (MArray len mba#) i@(I# i#) = ST $ \s# ->-      CHECK_BOUNDS("unsafeRead",len,i)-      case readIntArray# mba# i# s# of-        (# s2#, r# #) -> (# s2#, I# r# #)-    {-# INLINE unsafeRead #-}--    unsafeWrite (MArray len marr#) i@(I# i#) (I# e#) = ST $ \s1# ->-      CHECK_BOUNDS("unsafeWrite",len,i)-      case writeIntArray# marr# i# e# s1# of-        s2# -> (# s2#, () #)-    {-# INLINE unsafeWrite #-}--#elif defined(__HUGS__)--    bytesInArray n w = sizeOf w * n-    unsafeIndex = unsafeIndexArray-    unsafeRead = unsafeReadMArray-    unsafeWrite = unsafeWriteMArray--#endif  instance Elt Word16 where #if defined(__GLASGOW_HASKELL__)
Data/Text/Fusion.hs view
@@ -44,7 +44,7 @@     , findIndexOrEnd     , elemIndex     , elemIndices-    , count+    , countChar     ) where  import Prelude (Bool(..), Char, Eq(..), Maybe(..), Monad(..), Int,@@ -229,6 +229,6 @@  -- | /O(n)/ The 'count' function returns the number of times the query -- element appears in the given stream.-count :: Char -> Stream Char -> Int-count = S.countI-{-# INLINE [0] count #-}+countChar :: Char -> Stream Char -> Int+countChar = S.countCharI+{-# INLINE [0] countChar #-}
Data/Text/Fusion/Common.hs view
@@ -41,6 +41,9 @@     , toLower     , toUpper +    -- ** Justification+    , justifyLeftI+     -- * Folds     , foldl     , foldl'@@ -62,10 +65,11 @@     , scanl      -- ** Accumulating maps-    , mapAccumL+    -- , mapAccumL      -- ** Generation and unfolding-    , replicate+    , replicateCharI+    , replicateI     , unfoldr     , unfoldrNI @@ -90,7 +94,7 @@     , findIndicesI     , elemIndexI     , elemIndicesI-    , countI+    , countCharI      -- * Zipping and unzipping     , zipWith@@ -101,6 +105,7 @@                 fromIntegral, otherwise) import qualified Data.List as L import qualified Prelude as P+import Data.Int (Int64) import Data.Text.Fusion.Internal import Data.Text.Fusion.CaseMapping (foldMapping, lowerMapping, upperMapping) @@ -358,6 +363,23 @@ toLower = caseConvert lowerMapping {-# INLINE [0] toLower #-} +justifyLeftI :: Integral a => a -> Char -> Stream Char -> Stream Char+justifyLeftI k c (Stream next0 s0 len) = Stream next (s0 :!: S1 :!: 0) newLen+  where+    j = fromIntegral k+    newLen | j > len   = j+           | otherwise = len+    next (s :!: S1 :!: n) =+        case next0 s of+          Done       -> next (s :!: S2 :!: n)+          Skip s'    -> Skip (s' :!: S1 :!: n)+          Yield x s' -> Yield x (s' :!: S1 :!: n+1)+    next (s :!: S2 :!: n)+        | n < k       = Yield c (s :!: S2 :!: n+1)+        | otherwise   = Done+    {-# INLINE next #-}+{-# INLINE [0] justifyLeftI #-}+ -- ---------------------------------------------------------------------------- -- * Reducing Streams (folds) @@ -533,6 +555,7 @@ -- ----------------------------------------------------------------------------- -- ** Accumulating maps +{- -- | /O(n)/ Like a combination of 'map' and 'foldl'. Applies a -- function to each element of a stream, passing an accumulating -- parameter from left to right, and returns a final stream.@@ -550,20 +573,33 @@                        Skip s'    -> Skip (s' :!: z)                        Done       -> Done {-# INLINE [0] mapAccumL #-}+-}  -- ----------------------------------------------------------------------------- -- ** Generating and unfolding streams -replicate :: Int -> Char -> Stream Char-replicate n c+replicateCharI :: Integral a => a -> Char -> Stream Char+replicateCharI n c     | n < 0     = empty-    | otherwise = Stream next 0 n -- HINT maybe too low+    | otherwise = Stream next 0 (fromIntegral n) -- HINT maybe too low   where     {-# INLINE next #-}     next i | i >= n    = Done            | otherwise = Yield c (i + 1)-{-# INLINE [0] replicate #-}+{-# INLINE [0] replicateCharI #-} +replicateI :: Int64 -> Stream Char -> Stream Char+replicateI n (Stream next0 s0 len) =+    Stream next (0 :!: s0) (max 0 (fromIntegral n * len))+  where+    next (k :!: s)+        | k >= n = Done+        | otherwise = case next0 s of+                        Done       -> Skip    (k+1 :!: s0)+                        Skip s'    -> Skip    (k :!: s')+                        Yield x s' -> Yield x (k :!: s')+{-# INLINE [0] replicateI #-}+ -- | /O(n)/, where @n@ is the length of the result. The unfoldr function -- is analogous to the List 'unfoldr'. unfoldr builds a stream -- from a seed value. The function takes the element and returns@@ -804,17 +840,17 @@                  | otherwise -> loop (i+1) s' {-# INLINE [0] elemIndicesI #-} --- | /O(n)/ The 'count' function returns the number of times the query--- element appears in the given stream.-countI :: Integral a => Char -> Stream Char -> a-countI a (Stream next s0 _len) = loop 0 s0+-- | /O(n)/ The 'countCharI' function returns the number of times the+-- query element appears in the given stream.+countCharI :: Integral a => Char -> Stream Char -> a+countCharI a (Stream next s0 _len) = loop 0 s0   where     loop !i !s = case next s of       Done                   -> i       Skip    s'             -> loop i s'       Yield x s' | a == x    -> loop (i+1) s'                  | otherwise -> loop i s'-{-# INLINE [0] countI #-}+{-# INLINE [0] countCharI #-}  streamError :: String -> String -> a streamError func msg = P.error $ "Data.Text.Fusion.Common." ++ func ++ ": " ++ msg
Data/Text/Fusion/Internal.hs view
@@ -30,31 +30,29 @@ -- | Specialised, strict Maybe-like type. data M a = N          | J {-# UNPACK #-} !a-           deriving (Eq, Ord, Show)  type M8 = M Word8  -- Restreaming state. data S s = S {-# UNPACK #-} !s     {-# UNPACK #-} !M8 {-# UNPACK #-} !M8 {-# UNPACK #-} !M8-           deriving (Eq, Ord, Show)  infixl 2 :!: data PairS a b = !a :!: !b-               deriving (Eq, Ord, Read, Show)  -- | Allow a function over a stream to switch between two states. data Switch = S1 | S2-            deriving (Eq, Ord, Show)  data Step s a = Done               | Skip !s               | Yield !a !s +{- instance Show a => Show (Step s a)     where show Done        = "Done"           show (Skip _)    = "Skip"           show (Yield x _) = "Yield " ++ show x+-}  instance (Eq a) => Eq (Stream a) where     (==) = eq
Data/Text/Internal.hs view
@@ -45,7 +45,7 @@ text arr off len =     assert (len >= 0) .     assert (off >= 0) .-    assert (alen == 0 || off < alen) .+    assert (alen == 0 || len == 0 || off < alen) .     assert (len == 0 || c < 0xDC00 || c > 0xDFFF) $     Text arr off len   where c    = A.unsafeIndex arr off
Data/Text/Lazy.hs view
@@ -1,4 +1,5 @@ {-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE BangPatterns #-} -- | -- Module      : Data.Text.Lazy -- Copyright   : (c) Bryan O'Sullivan 2009@@ -57,6 +58,7 @@     , intersperse     , transpose     , reverse+    , replace      -- ** Case conversion     -- $case@@ -64,6 +66,11 @@     , toLower     , toUpper +    -- ** Justification+    , justifyLeft+    , justifyRight+    , center+     -- * Folds     , foldl     , foldl'@@ -94,6 +101,7 @@      -- ** Generation and unfolding     , replicate+    , replicateChar     , unfoldr     , unfoldrN @@ -104,6 +112,11 @@     , drop     , takeWhile     , dropWhile+    , dropWhileEnd+    , dropAround+    , strip+    , stripStart+    , stripEnd     , splitAt     , span     , break@@ -113,8 +126,12 @@     , tails      -- ** Breaking into many substrings+    -- $split     , split+    , splitTimes+    , splitTimesEnd     , splitWith+    , chunksOf     -- , breakSubstring      -- ** Breaking into lines and words@@ -154,12 +171,13 @@  import Prelude (Char, Bool(..), Int, Maybe(..), String,                 Eq(..), Ord(..), Read(..), Show(..),-                (&&), (+), (-), (.), ($), (++),-                flip, fromIntegral, not, otherwise)+                (&&), (||), (+), (-), (.), ($), (++),+                div, flip, fromIntegral, not, otherwise) import qualified Prelude as P import Data.Int (Int64) import qualified Data.List as L import Data.Char (isSpace)+import Data.Monoid (Monoid(..)) import Data.String (IsString(..)) import qualified Data.Text as T import qualified Data.Text.Fusion.Common as S@@ -182,6 +200,11 @@ instance Read Text where     readsPrec p str = [(pack x,y) | (x,y) <- readsPrec p str] +instance Monoid Text where+    mempty  = empty+    mappend = append+    mconcat = concat+ instance IsString Text where     fromString = pack @@ -339,8 +362,8 @@     S.length (stream t) = length t  #-} --- | /O(n)/ 'map' @f @xs is the 'Text' obtained by applying @f@ to--- each element of @xs@.  Subject to array fusion.+-- | /O(n)/ 'map' @f@ @t@ is the 'Text' obtained by applying @f@ to+-- each element of @t@.  Subject to array fusion. map :: (Char -> Char) -> Text -> Text map f t = unstream (S.map f (stream t)) {-# INLINE [1] map #-}@@ -358,6 +381,51 @@ intersperse c t = unstream (S.intersperse c (stream t)) {-# INLINE intersperse #-} +-- | /O(n)/ Left-justify a string to the given length, using the+-- specified fill character on the right. Subject to fusion. Examples:+--+-- > justifyLeft 7 'x' "foo"    == "fooxxxx"+-- > justifyLeft 3 'x' "foobar" == "foobar"+justifyLeft :: Int64 -> Char -> Text -> Text+justifyLeft k c t+    | len >= k  = t+    | otherwise = t `append` replicateChar (k-len) c+  where len = length t+{-# INLINE [1] justifyLeft #-}++{-# RULES+"LAZY TEXT justifyLeft -> fused" [~1] forall k c t.+    justifyLeft k c t = unstream (S.justifyLeftI k c (stream t))+"LAZY TEXT justifyLeft -> unfused" [1] forall k c t.+    unstream (S.justifyLeftI k c (stream t)) = justifyLeft k c t+  #-}++-- | /O(n)/ Right-justify a string to the given length, using the+-- specified fill character on the left. Examples:+--+-- > justifyRight 7 'x' "bar"    == "xxxxbar"+-- > justifyRight 3 'x' "foobar" == "foobar"+justifyRight :: Int64 -> Char -> Text -> Text+justifyRight k c t+    | len >= k  = t+    | otherwise = replicateChar (k-len) c `append` t+  where len = length t+{-# INLINE justifyRight #-}++-- | /O(n)/ Center a string to the given length, using the+-- specified fill character on either side. Examples:+--+-- > center 8 'x' "HS" = "xxxHSxxx"+center :: Int64 -> Char -> Text -> Text+center k c t+    | len >= k  = t+    | otherwise = replicateChar l c `append` t `append` replicateChar r c+  where len = length t+        d   = k - len+        r   = d `div` 2+        l   = d - r+{-# INLINE center #-}+ -- | /O(n)/ The 'transpose' function transposes the rows and columns -- of its 'Text' argument.  Note that this function uses 'pack', -- 'unpack', and the list version of transpose, and is thus not very@@ -373,6 +441,14 @@   where rev a Empty        = a         rev a (Chunk t ts) = rev (Chunk (T.reverse t) a) ts +-- | /O(m)*O(n)/ Replace every occurrence of one substring with another.+replace :: Text                 -- ^ Text to search for+        -> Text                 -- ^ Replacement text+        -> Text                 -- ^ Input text+        -> Text+replace s d = intercalate d . split s+{-# INLINE replace #-}+ -- ---------------------------------------------------------------------------- -- ** Case conversions (folds) @@ -556,12 +632,22 @@                         where (s'',y ) = f s' x                               (s', ys) = mapAccumR f s xs --- | /O(n)/ 'replicate' @n@ @c@ is a 'Text' of length @n@ with @c@ the--- value of every element.-replicate :: Int -> Char -> Text-replicate n c = unstream (S.replicate n c)+-- | /O(n*m)/ 'replicate' @n@ @t@ is a 'Text' consisting of the input+-- @t@ repeated @n@ times. Subject to fusion.+replicate :: Int64 -> Text -> Text+replicate n t = unstream (S.replicateI n (S.stream t)) {-# INLINE replicate #-} +-- | /O(n)/ 'replicateChar' @n@ @c@ is a 'Text' of length @n@ with @c@ the+-- value of every element. Subject to fusion.+replicateChar :: Int64 -> Char -> Text+replicateChar n c = unstream (S.replicateCharI n c)++{-# RULES+"LAZY TEXT replicate/singleton -> replicateChar" [~1] forall n c.+    replicate n (singleton c) = replicateChar n c+  #-}+ -- | /O(n)/, where @n@ is the length of the result. The 'unfoldr' -- function is analogous to the List 'L.unfoldr'. 'unfoldr' builds a -- 'Text' from a seed value. The function takes the element and@@ -605,7 +691,7 @@ -- | /O(n)/ 'drop' @n@, applied to a 'Text', returns the suffix of the -- 'Text' of length @n@, or the empty 'Text' if @n@ is greater than the -- length of the 'Text'. Subject to fusion.-drop :: Int -> Text -> Text+drop :: Int64 -> Text -> Text drop i t0     | i <= 0 = t0     | otherwise = drop' i t0@@ -644,8 +730,8 @@     unstream (S.takeWhile p (stream t)) = takeWhile p t   #-} --- | /O(n)/ 'dropWhile' @p@ @xs@ returns the suffix remaining after--- 'takeWhile' @p@ @xs@. This function is subject to array fusion.+-- | /O(n)/ 'dropWhile' @p@ @t@ returns the suffix remaining after+-- 'takeWhile' @p@ @t@. This function is subject to array fusion. dropWhile :: (Char -> Bool) -> Text -> Text dropWhile p t0 = dropWhile' t0   where dropWhile' Empty        = Empty@@ -661,7 +747,53 @@ "LAZY TEXT dropWhile -> unfused" [1] forall p t.     unstream (S.dropWhile p (stream t)) = dropWhile p t   #-}+-- | /O(n)/ 'dropWhileEnd' @p@ @t@ returns the prefix remaining after+-- dropping characters that fail the predicate @p@ from the end of+-- @t@.+-- Examples:+--+-- > dropWhileEnd (=='.') "foo..." == "foo"+dropWhileEnd :: (Char -> Bool) -> Text -> Text+dropWhileEnd p = go+  where go Empty = Empty+        go (Chunk t Empty) = if T.null t'+                             then Empty+                             else Chunk t' Empty+            where t' = T.dropWhileEnd p t+        go (Chunk t ts) = case go ts of+                            Empty -> go (Chunk t Empty)+                            ts' -> Chunk t ts'+{-# INLINE dropWhileEnd #-} +-- | /O(n)/ 'dropAround' @p@ @t@ returns the substring remaining after+-- dropping characters that fail the predicate @p@ from both the+-- beginning and end of @t@.  Subject to fusion.+dropAround :: (Char -> Bool) -> Text -> Text+dropAround p = dropWhile p . dropWhileEnd p+{-# INLINE [1] dropAround #-}++-- | /O(n)/ Remove leading white space from a string.  Equivalent to:+--+-- > dropWhile isSpace+stripStart :: Text -> Text+stripStart = dropWhile isSpace+{-# INLINE [1] stripStart #-}++-- | /O(n)/ Remove trailing white space from a string.  Equivalent to:+--+-- > dropWhileEnd isSpace+stripEnd :: Text -> Text+stripEnd = dropWhileEnd isSpace+{-# INLINE [1] stripEnd #-}++-- | /O(n)/ Remove leading and trailing white space from a string.+-- Equivalent to:+--+-- > dropAround isSpace+strip :: Text -> Text+strip = dropAround isSpace+{-# INLINE [1] strip #-}+ -- | /O(n)/ 'splitAt' @n t@ returns a pair whose first element is a -- prefix of @t@ of length @n@, and whose second is the remainder of -- the string. It is equivalent to @('take' n t, 'drop' n t)@.@@ -734,34 +866,108 @@   | T.length t == 1 = ts : tails ts'   | otherwise       = ts : tails (Chunk (T.unsafeTail t) ts') --- | /O(n)/ Break a 'Text' into pieces separated by the byte--- argument, consuming the delimiter. I.e.+-- $split ----- > split '\n' "a\nb\nd\ne" == ["a","b","d","e"]--- > split 'a'  "aXaXaXa"    == ["","X","X","X",""]--- > split 'x'  "x"          == ["",""]+-- Splitting functions in this library do not perform character-wise+-- copies to create substrings; they just construct new 'Text's that+-- are slices of the original.++-- | /O(m)*O(n)/ Break a 'Text' into pieces separated by the first+-- 'Text' argument, consuming the delimiter.  An empty delimiter is+-- invalid, and will cause an error to be raised.+--+-- Examples:+--+-- > split "\r\n" "a\r\nb\r\nd\r\ne" == ["a","b","d","e"]+-- > split "aaa"  "aaaXaaaXaaaXaaa"  == ["","X","X","X",""]+-- > split "x"    "x"                == ["",""] --  -- and ----- > intercalate [c] . split c == id--- > split == splitWith . (==)--- --- As for all splitting functions in this library, this function does--- not copy the substrings, it just constructs new 'Text's that are--- slices of the original.-split :: Char -> Text -> [Text]-split c = splitWith (==c)-{-# INLINE split #-}+-- > intercalate s . split s         == id+-- > split (singleton c)             == splitWith (==c)+split :: Text                   -- ^ Text to split on+      -> Text                   -- ^ Input text+      -> [Text]+split pat src0+    | l == 0    = emptyError "split"+    | l == 1    = splitWith (== (head pat)) src0+    | otherwise = go src0+  where+    l      = length pat+    go src = search 0 src+      where+        search !n !s+            | null s             = [src]      -- not found+            | pat `isPrefixOf` s = take n src : go (drop l s)+            | otherwise          = search (n+1) (tail s)+{-# INLINE [1] split #-} +{-# RULES+"LAZY TEXT split/singleton -> splitWith/==" [~1] forall c t.+    split (singleton c) t = splitWith (==c) t+  #-}++-- | /O(m)*O(n)/ Break a 'Text' into pieces at most @k@ times,+-- treating the first 'Text' argument as the delimiter to break on,+-- and consuming the delimiter.  The last element of the list contains+-- the remaining text after the number of times to split has been+-- reached.  A value of zero or less for @k@ causes no splitting to+-- occur.  An empty delimiter is invalid, and will cause an error to+-- be raised.+--+-- Examples:+--+-- > splitTimes 0   "//"  "a//b//c"   == ["a//b//c"]+-- > splitTimes 2   ":"   "a:b:c:d:e" == ["a","b","c:d:e"]+-- > splitTimes 100 "???" "a????b"    == ["a","?b"]+--+-- and+--+-- > intercalate s . splitTimes k s   == id+splitTimes :: Int64             -- ^ Maximum number of times to split+           -> Text              -- ^ Text to split on+           -> Text              -- ^ Input text+           -> [Text]+splitTimes k pat src0+    | k <= 0    = [src0]+    | l == 0    = emptyError "splitTimes"+    | otherwise = go k src0+  where+    l         = length pat+    go !i src = search 0 src+      where+        search !n !s+            | i == 0 || null s   = [src]      -- not found or limit reached+            | pat `isPrefixOf` s = take n src : go (i-1) (drop l s)+            | otherwise          = search (n+1) (tail s)+{-# INLINE splitTimes #-}++-- | /O(m)*O(n)/ Break a 'Text' into pieces at most @k@ times, like+-- 'splitTimes', but start from the end of the input and work towards+-- the start.+--+-- Examples:+--+-- > splitTimes 2    "::" "a::b::c::d::e" == ["a","b","c::d::e"]+-- > splitTimesEnd 2 "::" "a::b::c::d::e" == ["a::b::c","d","e"]+splitTimesEnd :: Int64             -- ^ Maximum number of times to split+              -> Text              -- ^ Text to split on+              -> Text              -- ^ Input text+              -> [Text]+splitTimesEnd k pat src =+    L.reverse . L.map reverse $ splitTimes k (reverse pat) (reverse src)+{-# INLINE splitTimesEnd #-}+ -- | /O(n)/ Splits a 'Text' into components delimited by separators, -- where the predicate returns True for a separator element.  The -- resulting components do not contain the separators.  Two adjacent -- separators result in an empty component in the output.  eg. -- -- > splitWith (=='a') "aabbaca" == ["","","bb","c",""]--- > splitWith (=='a') []        == []+-- > splitWith (=='a') []        == [""] splitWith :: (Char -> Bool) -> Text -> [Text]-splitWith _ Empty = []+splitWith _ Empty = [Empty] splitWith p (Chunk t0 ts0) = comb [] (T.splitWith p t0) ts0   where comb acc (s:[]) Empty        = revChunks (s:acc) : []         comb acc (s:[]) (Chunk t ts) = comb (s:acc) (T.splitWith p t) ts@@ -769,6 +975,20 @@         comb _   []     _            = impossibleError "splitWith" {-# INLINE splitWith #-} +-- | /O(n)/ Splits a 'Text' into components of length @k@.  The last+-- element may be shorter than the other chunks, depending on the+-- length of the input. Examples:+--+-- > chunksOf 3 "foobarbaz"   == ["foo","bar","baz"]+-- > chunksOf 4 "haskell.org" == ["hask","ell.","org"]+chunksOf :: Int64 -> Text -> [Text]+chunksOf k = go+  where+    go t = case splitAt k t of+             (a,b) | null a    -> []+                   | otherwise -> a : go b+{-# INLINE chunksOf #-}+ -- | /O(n)/ Breaks a 'Text' up into a list of 'Text's at -- newline 'Char's. The resulting strings do not contain newlines. lines :: Text -> [Text]@@ -894,12 +1114,33 @@ elemIndices c t = S.elemIndices c (stream t) {-# INLINE elemIndices #-} --- | /O(n)/ The 'count' function returns the number of times the query--- element appears in the given 'Text'. This function is subject to--- fusion.-count :: Char -> Text -> Int64-count c t = S.count c (stream t)-{-# INLINE count #-}+-- | /O(n*m)/ The 'count' function returns the number of times the+-- query string appears in the given 'Text'. An empty query string is+-- invalid, and will cause an error to be raised.+count :: Text -> Text -> Int64+count pat src0+    | l == 0    = emptyError "count"+    | l == 1    = countChar (head pat) src0+    | otherwise = go 0 src0+  where+    l = length pat+    go !n src = search src+      where+        search s | null s             = n+                 | pat `isPrefixOf` s = go (n+1) (drop l s)+                 | otherwise          = search (tail s)+{-# INLINE [1] count #-}++{-# RULES+"LAZY TEXT count/singleton -> countChar" [~1] forall c t.+    count (singleton c) t = countChar c t+  #-}++-- | /O(n)/ The 'countChar' function returns the number of times the+-- query element appears in the given 'Text'. This function is subject+-- to fusion.+countChar :: Char -> Text -> Int64+countChar c t = S.countChar c (stream t)  -- | /O(n)/ 'zip' takes two 'Text's and returns a list of -- corresponding pairs of bytes. If one input 'Text' is short,
Data/Text/Lazy/Fusion.hs view
@@ -22,7 +22,7 @@     , findIndices     , elemIndex     , elemIndices-    , count+    , countChar     ) where  import Prelude hiding (length)@@ -134,6 +134,6 @@  -- | /O(n)/ The 'count' function returns the number of times the query -- element appears in the given stream.-count :: Char -> Stream Char -> Int64-count = S.countI-{-# INLINE [0] count #-}+countChar :: Char -> Stream Char -> Int64+countChar = S.countCharI+{-# INLINE [0] countChar #-}
Data/Text/UnsafeChar.hs view
@@ -19,7 +19,7 @@     , unsafeChr8     , unsafeChr32     , unsafeWrite-    , unsafeWriteRev+    -- , unsafeWriteRev     ) where  import Control.Exception (assert)@@ -60,6 +60,7 @@           hi = fromIntegral $ (m .&. 0x3FF) + 0xDC00 {-# INLINE unsafeWrite #-} +{- unsafeWriteRev :: A.MArray s Word16 -> Int -> Char -> ST s Int unsafeWriteRev marr i c     | n < 0x10000 = do@@ -76,3 +77,4 @@           lo = fromIntegral $ (m `shiftR` 10) + 0xD800           hi = fromIntegral $ (m .&. 0x3FF) + 0xDC00 {-# INLINE unsafeWriteRev #-}+-}
Data/Text/UnsafeShift.hs view
@@ -17,7 +17,7 @@       UnsafeShift(..)     ) where -import qualified Data.Bits as Bits+-- import qualified Data.Bits as Bits import GHC.Base import GHC.Word @@ -33,24 +33,26 @@  instance UnsafeShift Word16 where     {-# INLINE shiftL #-}-    shiftL (W16# x#) (I# i#) = W16# (x# `uncheckedShiftL#` i#)+    shiftL (W16# x#) (I# i#) = W16# (narrow16Word# (x# `uncheckedShiftL#` i#))      {-# INLINE shiftR #-}     shiftR (W16# x#) (I# i#) = W16# (x# `uncheckedShiftRL#` i#)  instance UnsafeShift Word32 where     {-# INLINE shiftL #-}-    shiftL (W32# x#) (I# i#) = W32# (x# `uncheckedShiftL#` i#)+    shiftL (W32# x#) (I# i#) = W32# (narrow32Word# (x# `uncheckedShiftL#` i#))      {-# INLINE shiftR #-}     shiftR (W32# x#) (I# i#) = W32# (x# `uncheckedShiftRL#` i#) +{- instance UnsafeShift Word64 where     {-# INLINE shiftL #-}     shiftL (W64# x#) (I# i#) = W64# (x# `uncheckedShiftL64#` i#)      {-# INLINE shiftR #-}     shiftR (W64# x#) (I# i#) = W64# (x# `uncheckedShiftRL64#` i#)+-}  instance UnsafeShift Int where     {-# INLINE shiftL #-}@@ -59,9 +61,11 @@     {-# INLINE shiftR #-}     shiftR (I# x#) (I# i#) = I# (x# `iShiftRA#` i#) +{- instance UnsafeShift Integer where     {-# INLINE shiftL #-}     shiftL = Bits.shiftL      {-# INLINE shiftR #-}     shiftR = Bits.shiftR+-}
README view
@@ -31,5 +31,7 @@  The base code for this library was originally written by Tom Harper, based on the stream fusion framework developed by Roman Leshchinskiy,-Duncan Coutts, and Don Stewart.  The core library was fleshed out,-debugged, and tested by Bryan O'Sullivan.+Duncan Coutts, and Don Stewart.++The core library was fleshed out, debugged, and tested by Bryan+O'Sullivan, and he is the current maintainer.
text.cabal view
@@ -1,5 +1,5 @@ name:           text-version:        0.3+version:        0.4 synopsis:       An efficient packed Unicode text type description:    An efficient packed Unicode text type. license:        BSD3@@ -19,27 +19,27 @@     Data.Text     Data.Text.Encoding     Data.Text.Encoding.Error-    Data.Text.Encoding.Fusion     Data.Text.Foreign-    Data.Text.Fusion-    Data.Text.Fusion.Common     Data.Text.Lazy     Data.Text.Lazy.Encoding-    Data.Text.Lazy.Encoding.Fusion-    Data.Text.Lazy.Fusion   other-modules:     Data.Text.Array-    Data.Text.Internal+    Data.Text.Encoding.Fusion     Data.Text.Encoding.Fusion.Common-    Data.Text.Fusion.Internal+    Data.Text.Encoding.Utf16+    Data.Text.Encoding.Utf32+    Data.Text.Encoding.Utf8+    Data.Text.Fusion     Data.Text.Fusion.CaseMapping+    Data.Text.Fusion.Common+    Data.Text.Fusion.Internal+    Data.Text.Internal+    Data.Text.Lazy.Encoding.Fusion+    Data.Text.Lazy.Fusion     Data.Text.Lazy.Internal     Data.Text.Unsafe     Data.Text.UnsafeChar     Data.Text.UnsafeShift-    Data.Text.Encoding.Utf8-    Data.Text.Encoding.Utf16-    Data.Text.Encoding.Utf32    build-depends:     base       < 5,