text 0.3 → 0.4
raw patch · 12 files changed
+709/−243 lines, 12 files
Files
- Data/Text.hs +351/−133
- Data/Text/Array.hs +0/−35
- Data/Text/Fusion.hs +4/−4
- Data/Text/Fusion/Common.hs +48/−12
- Data/Text/Fusion/Internal.hs +2/−4
- Data/Text/Internal.hs +1/−1
- Data/Text/Lazy.hs +274/−33
- Data/Text/Lazy/Fusion.hs +4/−4
- Data/Text/UnsafeChar.hs +3/−1
- Data/Text/UnsafeShift.hs +7/−3
- README +4/−2
- text.cabal +11/−11
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 \"ﬓ\" (men now, U+FB13) is case+-- folded to the sequence \"մ\" (men, U+0574) followed by+-- \"ն\" (now, U+0576), while the Greek \"µ\" (micro sign,+-- U+00B5) is case folded to \"μ\" (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, \"İ\" (Latin capital letter I with dot above,+-- U+0130) maps to the sequence \"i\" (Latin small letter i, U+0069) followed+-- by \" ̇\" (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 \"ß\" (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,