packages feed

text 0.7.2.1 → 0.8.0.0

raw patch · 31 files changed

+1688/−755 lines, 31 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Data.Text: compareLength :: Text -> Int -> Ordering
+ Data.Text: prefixed :: Text -> Text -> Maybe Text
+ Data.Text: suffixed :: Text -> Text -> Maybe Text
+ Data.Text.Foreign: dropWord16 :: Int -> Text -> Text
+ Data.Text.Foreign: takeWord16 :: Int -> Text -> Text
+ Data.Text.Lazy: compareLength :: Text -> Int64 -> Ordering
+ Data.Text.Lazy: foldlChunks :: (a -> Text -> a) -> a -> Text -> a
+ Data.Text.Lazy: foldrChunks :: (Text -> a -> a) -> a -> Text -> a
+ Data.Text.Lazy: fromStrict :: Text -> Text
+ Data.Text.Lazy: prefixed :: Text -> Text -> Maybe Text
+ Data.Text.Lazy: suffixed :: Text -> Text -> Maybe Text
+ Data.Text.Lazy: toStrict :: Text -> Text
+ Data.Text.Lazy.Builder: data Builder
+ Data.Text.Lazy.Builder: flush :: Builder
+ Data.Text.Lazy.Builder: fromLazyText :: Text -> Builder
+ Data.Text.Lazy.Builder: fromText :: Text -> Builder
+ Data.Text.Lazy.Builder: instance IsString Builder
+ Data.Text.Lazy.Builder: instance Monoid Builder
+ Data.Text.Lazy.Builder: instance Show Builder
+ Data.Text.Lazy.Builder: singleton :: Char -> Builder
+ Data.Text.Lazy.Builder: toLazyText :: Builder -> Text
+ Data.Text.Lazy.Builder: toLazyTextWith :: Int -> Builder -> Text
+ Data.Text.Lazy.Encoding: decodeASCII :: ByteString -> Text
+ Data.Text.Lazy.Encoding: decodeUtf16BE :: ByteString -> Text
+ Data.Text.Lazy.Encoding: decodeUtf16BEWith :: OnDecodeError -> ByteString -> Text
+ Data.Text.Lazy.Encoding: decodeUtf16LE :: ByteString -> Text
+ Data.Text.Lazy.Encoding: decodeUtf16LEWith :: OnDecodeError -> ByteString -> Text
+ Data.Text.Lazy.Encoding: decodeUtf32BE :: ByteString -> Text
+ Data.Text.Lazy.Encoding: decodeUtf32BEWith :: OnDecodeError -> ByteString -> Text
+ Data.Text.Lazy.Encoding: decodeUtf32LE :: ByteString -> Text
+ Data.Text.Lazy.Encoding: decodeUtf32LEWith :: OnDecodeError -> ByteString -> Text
+ Data.Text.Lazy.Encoding: encodeUtf16BE :: Text -> ByteString
+ Data.Text.Lazy.Encoding: encodeUtf16LE :: Text -> ByteString
+ Data.Text.Lazy.Encoding: encodeUtf32BE :: Text -> ByteString
+ Data.Text.Lazy.Encoding: encodeUtf32LE :: Text -> ByteString
- Data.Text: find :: Text -> Text -> (Text, [(Text, Text)])
+ Data.Text: find :: Text -> Text -> [(Text, Text)]
- Data.Text.Lazy: find :: Text -> Text -> (Text, [(Text, Text)])
+ Data.Text.Lazy: find :: Text -> Text -> [(Text, Text)]

Files

Data/Text.hs view
@@ -3,9 +3,9 @@  -- | -- Module      : Data.Text--- Copyright   : (c) Tom Harper 2008-2009,---               (c) Bryan O'Sullivan 2009,---               (c) Duncan Coutts 2009+-- Copyright   : (c) 2008, 2009 Tom Harper,+--               (c) 2009, 2010 Bryan O'Sullivan,+--               (c) 2009 Duncan Coutts -- -- License     : BSD-style -- Maintainer  : bos@serpentine.com, rtomharper@googlemail.com,@@ -47,6 +47,7 @@     , init     , null     , length+    , compareLength      -- * Transformations     , map@@ -140,6 +141,10 @@     , isSuffixOf     , isInfixOf +    -- ** View patterns+    , prefixed+    , suffixed+     -- * Searching     , filter     , find@@ -163,14 +168,16 @@     ) where  import Prelude (Char, Bool(..), Functor(..), Int, Maybe(..), String,-                Eq(..), Ord(..), (++),+                Eq(..), Ord(..), Ordering(..), (++),                 Read(..), Show(..),-                (&&), (||), (+), (-), (.), ($), (>>), (*),+                (&&), (||), (+), (-), (.), ($), ($!), (>>), (*),                 div, error, not, return, otherwise) #if defined(HAVE_DEEPSEQ) import Control.DeepSeq (NFData) #endif+#if defined(ASSERTS) import Control.Exception (assert)+#endif import Data.Char (isSpace) import Data.Data (Data(gfoldl, toConstr, gunfold, dataTypeOf)) #if __GLASGOW_HASKELL__ >= 612@@ -179,18 +186,17 @@ import Data.Data (mkNorepType) #endif import Control.Monad (foldM)-import Control.Monad.ST (ST) import qualified Data.Text.Array as A import qualified Data.List as L import Data.Monoid (Monoid(..))-import Data.Word (Word16) import Data.String (IsString(..)) import qualified Data.Text.Fusion as S import qualified Data.Text.Fusion.Common as S import Data.Text.Fusion (stream, reverseStream, unstream) import Data.Text.Internal (Text(..), empty, text, textP) import qualified Prelude as P-import Data.Text.Unsafe (iter, iter_, reverseIter, unsafeHead, unsafeTail)+import Data.Text.Unsafe (Iter(..), iter, iter_, lengthWord16, reverseIter,+                         unsafeHead, unsafeTail) import Data.Text.UnsafeChar (unsafeChr) import qualified Data.Text.Encoding.Utf16 as U16 import Data.Text.Search (indices)@@ -292,8 +298,8 @@       len = len1+len2       x = do         arr <- A.unsafeNew len-        copy arr 0 arr1 off1 len1-        copy arr len1 arr2 off2 (len1+len2)+        A.copyI arr 0 arr1 off1 len1+        A.copyI arr len1 arr2 off2 (len1+len2)         return arr {-# INLINE append #-} @@ -304,14 +310,6 @@     unstream (S.append (stream t1) (stream t2)) = append t1 t2  #-} -copy :: forall s. A.MArray s Word16 -> Int -> A.Array Word16 -> Int -> Int-     -> ST s ()-copy dest i0 src j0 top = go i0 j0-  where-    go i j | i >= top  = return ()-           | otherwise = do A.unsafeWrite dest i (src `A.unsafeIndex` j)-                            go (i+1) (j+1)- -- | /O(1)/ Returns the first character of a 'Text', which must be -- non-empty.  Subject to fusion. head :: Text -> Char@@ -324,7 +322,7 @@ uncons t@(Text arr off len)     | len <= 0  = Nothing     | otherwise = Just (c, textP arr (off+d) (len-d))-    where (c,d) = iter t 0+    where Iter c d = iter t 0 {-# INLINE [1] uncons #-}  -- | Lifted from Control.Arrow and specialized.@@ -392,7 +390,11 @@ -- | /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+null (Text _arr _off len) =+#if defined(ASSERTS)+    assert (len >= 0) $+#endif+    len <= 0 {-# INLINE [1] null #-}  {-# RULES@@ -414,6 +416,51 @@ length t = S.length (stream t) {-# INLINE length #-} +-- | /O(n)/ Compare the count of characters in a 'Text' to a number.+-- Subject to fusion.+--+-- This function gives the same answer as comparing against the result+-- of 'length', but can short circuit if the count of characters is+-- greater than the number, and hence be more efficient.+compareLength :: Text -> Int -> Ordering+compareLength t n = S.compareLengthI (stream t) n+{-# INLINE [1] compareLength #-}++{-# RULES+"TEXT compareN/length -> compareLength" [~1] forall t n.+    compare (length t) n = compareLength t n+  #-}++{-# RULES+"TEXT ==N/length -> compareLength/==EQ" [~1] forall t n.+    (==) (length t) n = compareLength t n == EQ+  #-}++{-# RULES+"TEXT /=N/length -> compareLength//=EQ" [~1] forall t n.+    (/=) (length t) n = compareLength t n /= EQ+  #-}++{-# RULES+"TEXT <N/length -> compareLength/==LT" [~1] forall t n.+    (<) (length t) n = compareLength t n == LT+  #-}++{-# RULES+"TEXT <=N/length -> compareLength//=GT" [~1] forall t n.+    (<=) (length t) n = compareLength t n /= GT+  #-}++{-# RULES+"TEXT >N/length -> compareLength/==GT" [~1] forall t n.+    (>) (length t) n = compareLength t n == GT+  #-}++{-# RULES+"TEXT >=N/length -> compareLength//=LT" [~1] forall t n.+    (>=) (length t) n = compareLength t n /= LT+  #-}+ -- ----------------------------------------------------------------------------- -- * Transformations -- | /O(n)/ 'map' @f@ @t@ is the 'Text' obtained by applying @f@ to@@ -461,6 +508,12 @@ -- 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.+--+-- /Note/: In some languages, case conversion is a locale- and+-- context-dependent operation. The case conversion functions in this+-- module are /not/ locale sensitive. Programs that require locale+-- sensitivity should use appropriate versions of the case mapping+-- functions from the @text-icu@ package.  -- | /O(n)/ Convert a string to folded case.  This function is mainly -- useful for performing caseless (also known as case insensitive)@@ -597,14 +650,18 @@  -- | /O(n)/ Concatenate a list of 'Text's. concat :: [Text] -> Text-concat ts = Text (A.run go) 0 len+concat ts = case ts' of+              [] -> empty+              [t] -> t+              _ -> Text (A.run go) 0 len   where-    len = L.sum (L.map (\(Text _ _ l) -> l) ts)+    ts' = L.filter (not . null) ts+    len = L.sum $ L.map lengthWord16 ts'     go = do       arr <- A.unsafeNew len-      let step i (Text a o l) = let j = i + l in copy arr i a o j >> return j-      foldM step 0 ts >> return arr-{-# INLINE concat #-}+      let step i (Text a o l) =+            let !j = i + l in A.copyI arr i a o j >> return j+      foldM step 0 ts' >> return arr  -- | /O(n)/ Map a function over a 'Text' that results in a 'Text', and -- concatenate the results.@@ -712,7 +769,7 @@       arr <- A.unsafeNew len       let loop !d !i | i >= n    = return arr                      | otherwise = let m = d + l-                                   in copy arr d a o m >> loop m (i+1)+                                   in A.copyI arr d a o m >> loop m (i+1)       loop 0 0 {-# INLINE [1] replicate #-} @@ -774,8 +831,8 @@   #-}  -- | /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.+-- 'Text' after the first @n@ characters, or the empty 'Text' if @n@+-- is greater than the length of the 'Text'. Subject to fusion. drop :: Int -> Text -> Text drop n t@(Text arr off len)     | n <= 0    = t@@ -802,7 +859,7 @@   where loop !i | i >= len    = t                 | p c         = loop (i+d)                 | otherwise   = textP arr off i-            where (c,d)       = iter t i+            where Iter c d    = iter t i {-# INLINE [1] takeWhile #-}  {-# RULES@@ -819,7 +876,7 @@   where loop !i !l | l >= len  = empty                    | p c       = loop (i+d) (l+d)                    | otherwise = Text arr (off+i) (len-l)-            where (c,d)        = iter t i+            where Iter c d     = iter t i {-# INLINE [1] dropWhile #-}  {-# RULES@@ -903,7 +960,7 @@   where k = loop 0         loop !i | i >= len || not (p c) = i                 | otherwise             = loop (i+d)-            where (c,d)                 = iter t i+            where Iter c d              = iter t i {-# INLINE spanBy #-}  -- | /O(n)/ 'breakBy' is like 'spanBy', but the prefix returned is@@ -919,7 +976,7 @@     loop t@(Text arr off len)         | null t    = []         | otherwise = text arr off n : loop (text arr (off+n) (len-n))-        where (c,d) = iter t 0+        where Iter c d = iter t 0               n     = d + findAIndexOrEnd (not . p c) (Text arr (off+d) (len-d))  -- | Returns the /array/ index (in units of 'Word16') at which a@@ -929,7 +986,7 @@ findAIndexOrEnd q t@(Text _arr _off len) = go 0     where go !i | i >= len || q c       = i                 | otherwise             = go (i+d)-                where (c,d)             = iter t i+                where Iter c d          = iter t i      -- | /O(n)/ Group characters in a string by equality. group :: Text -> [Text]@@ -1086,34 +1143,31 @@ {-# INLINE breakEnd #-}  -- | /O(n+m)/ Find all non-overlapping instances of @needle@ in--- @haystack@.  The first element of the returned pair is the prefix--- of @haystack@ prior to any matches of @needle@.  The second is a--- list of pairs.+-- @haystack@.  Each element of the returned list consists of a pair: ----- The first element of each pair in the list is a span from the--- beginning of a match to the beginning of the next match, while the--- second is a span from the beginning of the match to the end of the--- input.+-- * The entire string prior to the /k/th match (i.e. the prefix) --+-- * The /k/th match, followed by the remainder of the string+-- -- Examples: -- -- > find "::" ""--- > ==> ("", [])--- > find "/" "a/b/c/d"--- > ==> ("a", [("/b","/b/c/d"), ("/c","/c/d"), ("/d","/d")])+-- > ==> []+-- > find "/" "a/b/c/"+-- > ==> [("a", "/b/c/"), ("a/b", "/c/"), ("a/b/c", "/")] -- -- In (unlikely) bad cases, this function's time complexity degrades -- towards /O(n*m)/.-find :: Text -> Text -> (Text, [(Text, Text)])-find pat src@(Text arr off len)+--+-- The @needle@ parameter may not be empty.+find :: Text                    -- ^ @needle@ to search for+     -> Text                    -- ^ @haystack@ in which to search+     -> [(Text, Text)]+find pat src@(Text arr off slen)     | null pat  = emptyError "find"-    | otherwise = case indices pat src of-                    []     -> (src, [])-                    (x:xs) -> (chunk 0 x, go x xs)+    | otherwise = L.map step (indices pat src)   where-    go !s (x:xs) = (chunk s (x-s), chunk s (len-s)) : go x xs-    go  s _      = let c = chunk s (len-s)-                   in [(c,c)]+    step       x = (chunk 0 x, chunk x (slen-x))     chunk !n !l  = textP arr (n+off) l {-# INLINE find #-} @@ -1203,7 +1257,7 @@             then loop (start+1) (start+1)             else Text arr (start+off) (n-start) : loop (n+d) (n+d)         | otherwise = loop start (n+d)-        where (c,d) = iter t n+        where Iter c d = iter t n {-# INLINE words #-}  -- | /O(n)/ Breaks a 'Text' up into a list of 'Text's at@@ -1291,6 +1345,53 @@ "TEXT isInfixOf/singleton -> S.elem/S.stream" [~1] forall n h.     isInfixOf (singleton n) h = S.elem n (S.stream h)   #-}++-------------------------------------------------------------------------------+-- * View patterns++-- | /O(n)/ Returns the suffix of the second string if its prefix+-- matches the first.+--+-- Examples:+--+-- > prefixed "foo" "foobar" == Just "bar"+-- > prefixed "foo" "quux"   == Nothing+--+-- This is particularly useful with the @ViewPatterns@ extension to+-- GHC, as follows:+--+-- > {-# LANGUAGE ViewPatterns #-}+-- > import Data.Text as T+-- >+-- > fnordLength :: Text -> Int+-- > fnordLength (prefixed "fnord" -> Just suf) = T.length suf+-- > fnordLength _                              = -1+prefixed :: Text -> Text -> Maybe Text+prefixed p@(Text _arr _off plen) t@(Text arr off len)+    | p `isPrefixOf` t = Just $! textP arr (off+plen) (len-plen)+    | otherwise        = Nothing++-- | /O(n)/ Returns the prefix of the second string if its suffix+-- matches the first.+--+-- Examples:+--+-- > suffixed "bar" "foobar" == Just "foo"+-- > suffixed "foo" "quux"   == Nothing+--+-- This is particularly useful with the @ViewPatterns@ extension to+-- GHC, as follows:+--+-- > {-# LANGUAGE ViewPatterns #-}+-- > import Data.Text as T+-- >+-- > quuxLength :: Text -> Int+-- > quuxLength (suffixed "quux" -> Just pre) = T.length pre+-- > quuxLength _                             = -1+suffixed :: Text -> Text -> Maybe Text+suffixed p@(Text _arr _off plen) t@(Text arr off len)+    | p `isSuffixOf` t = Just $! textP arr off (len-plen)+    | otherwise        = Nothing  emptyError :: String -> a emptyError fun = P.error ("Data.Text." ++ fun ++ ": empty input")
Data/Text/Array.hs view
@@ -1,9 +1,9 @@-{-# LANGUAGE BangPatterns, CPP, ExistentialQuantification, MagicHash,-             Rank2Types, ScopedTypeVariables, UnboxedTuples #-}+{-# LANGUAGE BangPatterns, CPP, MagicHash, Rank2Types, RecordWildCards,+    UnboxedTuples #-} {-# OPTIONS_GHC -fno-warn-unused-matches #-} -- | -- Module      : Data.Text.Array--- Copyright   : (c) Bryan O'Sullivan 2009+-- Copyright   : (c) 2009, 2010 Bryan O'Sullivan -- -- License     : BSD-style -- Maintainer  : bos@serpentine.com, rtomharper@googlemail.com,@@ -26,286 +26,247 @@ module Data.Text.Array     (     -- * Types-      IArray(..)-    , Elt(..)-    , Array+      Array     , MArray      -- * Functions+    , copyM+    , copyI     , empty-    , new-    , unsafeNew-    , unsafeFreeze+#if defined(ASSERTS)+    , length+#endif     , run     , run2     , toList-    , copy-    , unsafeCopy+    , unsafeFreeze+    , unsafeIndex+    , unsafeNew+    , unsafeWrite     ) where -#if 0-#define BOUNDS_CHECKING+#if defined(ASSERTS) -- This fugly hack is brought by GHC's apparent reluctance to deal -- with MagicHash and UnboxedTuples when inferring types. Eek!-#define CHECK_BOUNDS(_func_,_len_,_k_) \+# define CHECK_BOUNDS(_func_,_len_,_k_) \ if (_k_) < 0 || (_k_) >= (_len_) then error ("Data.Text.Array." ++ (_func_) ++ ": bounds error, offset " ++ show (_k_) ++ ", length " ++ show (_len_)) else #else-#define CHECK_BOUNDS(_func_,_len_,_k_)+# define CHECK_BOUNDS(_func_,_len_,_k_) #endif -#if defined(__GLASGOW_HASKELL__) #include "MachDeps.h" +#if defined(ASSERTS)+import Control.Exception (assert)+#endif+import Data.Bits ((.&.))+import Data.Text.UnsafeShift (shiftL, shiftR) import GHC.Base (ByteArray#, MutableByteArray#, Int(..),-                 indexWord16Array#, newByteArray#,-                 readWord16Array#, unsafeCoerce#,-                 writeWord16Array#, (*#))-import GHC.Prim (Int#)+                 indexWord16Array#, indexWordArray#, newByteArray#,+                 readWord16Array#, readWordArray#, unsafeCoerce#,+                 writeWord16Array#, writeWordArray#) import GHC.ST (ST(..), runST)-import GHC.Word (Word16(..))--#elif defined(__HUGS__)--import Hugs.ByteArray (ByteArray, MutableByteArray, readByteArray,-                       newMutableByteArray, readMutableByteArray,-                       unsafeFreezeMutableByteArray, writeMutableByteArray)-import Foreign.Storable (Storable, sizeOf)-import Hugs.ST (ST(..), runST)--#else-# error not implemented for this compiler-#endif--import Control.Exception (assert)-import Data.Typeable (Typeable1(..), Typeable2(..), TyCon, mkTyCon, mkTyConApp)+import GHC.Word (Word16(..), Word(..)) import Prelude hiding (length, read) -#include "Typeable.h"- -- | Immutable array type.-data Array e = Array-    {-# UNPACK #-} !Int -- length (in units of e, not bytes)-#if defined(__GLASGOW_HASKELL__)-    ByteArray#-#elif defined(__HUGS__)-    !ByteArray+data Array = Array {+      aBA :: ByteArray#+#if defined(ASSERTS)+    , aLen :: {-# UNPACK #-} !Int -- length (in units of Word16, not bytes) #endif--INSTANCE_TYPEABLE1(Array,arrayTc,"Array")+    }  -- | Mutable array type, for use in the ST monad.-data MArray s e = MArray-    {-# UNPACK #-} !Int -- length (in units of e, not bytes)-#if defined(__GLASGOW_HASKELL__)-    (MutableByteArray# s)-#elif defined(__HUGS__)-    !(MutableByteArray s)+data MArray s = MArray {+      maBA :: MutableByteArray# s+#if defined(ASSERTS)+    , maLen :: {-# UNPACK #-} !Int -- length (in units of Word16, not bytes) #endif--INSTANCE_TYPEABLE2(MArray,mArrayTc,"MArray")+    } +#if defined(ASSERTS) -- | Operations supported by all arrays. class IArray a where     -- | Return the length of an array.     length :: a -> Int -instance IArray (Array e) where-    length (Array len _ba) = len+instance IArray Array where+    length = aLen     {-# INLINE length #-} -instance (Elt e, Show e) => Show (Array e) where-    show = show . toList--instance IArray (MArray s e) where-    length (MArray len _ba) = len+instance IArray (MArray s) where+    length = maLen     {-# INLINE length #-}--check :: IArray a => String -> a -> Int -> (a -> Int -> b) -> b-check func ary i f-    | i >= 0 && i < length ary = f ary i-    | otherwise = error ("Data.Array.Flat." ++ func ++ ": index out of bounds")-{-# INLINE check #-}---- | Operations supported by all elements that can be stored in--- arrays.-class Elt e where-    -- | Indicate how many bytes would be used for an array of the-    -- given size.-    bytesInArray :: Int -> e -> Int-    -- | Unchecked read of an immutable array.  May return garbage or-    -- crash on an out-of-bounds access.-    unsafeIndex :: Array e -> Int -> e-    -- | Unchecked read of a mutable array.  May return garbage or-    -- crash on an out-of-bounds access.-    unsafeRead :: MArray s e -> Int -> ST s e-    -- | Unchecked write of a mutable array.  May return garbage or-    -- crash on an out-of-bounds access.-    unsafeWrite :: MArray s e -> Int -> e -> ST s ()--    -- | Read an immutable array. An invalid index results in a-    -- runtime error.-    index :: Array e -> Int -> e-    index ary i = check "index" ary i unsafeIndex-    {-# INLINE index #-}--    -- | Read a mutable array. An invalid index results in a runtime-    -- error.-    read :: Array e -> Int -> ST s e-    read ary i = check "read" ary i read-    {-# INLINE read #-}--    -- | Write a mutable array. An invalid index results in a runtime-    -- error.-    write :: Array e -> Int -> ST s e-    write ary i = check "write" ary i write-    {-# INLINE write #-}---- | Freeze a mutable array. Do not mutate the 'MArray' afterwards!-unsafeFreeze :: MArray s e -> ST s (Array e)--#if defined(__GLASGOW_HASKELL__)--wORD16_SCALE :: Int# -> Int#-wORD16_SCALE n# = scale# *# n# where !(I# scale#) = SIZEOF_WORD16+#endif  -- | Create an uninitialized mutable array.-unsafeNew :: forall s e. Elt e => Int -> ST s (MArray s e)-unsafeNew n = assert (n >= 0) . ST $ \s1# ->-   case bytesInArray n (undefined :: e) of-     len@(I# len#) ->-#if defined(BOUNDS_CHECKING)+unsafeNew :: forall s. Int -> ST s (MArray s)+unsafeNew n =+#if defined(ASSERTS)+    assert (n >= 0) .+#endif+    ST $ \s1# ->+    case bytesInArray n of+      len@(I# len#) ->+#if defined(ASSERTS)          if len < 0 then error (show ("unsafeNew",len)) else #endif          case newByteArray# len# s1# of-           (# s2#, marr# #) -> (# s2#, MArray n marr# #)+           (# s2#, marr# #) -> (# s2#, MArray marr#+#if defined(ASSERTS)+                                  n+#endif+                                  #) {-# INLINE unsafeNew #-} -unsafeFreeze (MArray len mba#) = ST $ \s# ->-                                 (# s#, Array len (unsafeCoerce# mba#) #)-{-# INLINE unsafeFreeze #-}---- | Create a mutable array, with its elements initialized with the--- given value.-new :: forall s e. Elt e => Int -> e -> ST s (MArray s e)--#elif defined(__HUGS__)--unsafeIndexArray :: Storable e => Array e -> Int -> e-unsafeIndexArray (Array off len arr) i =-    assert (i >= 0 && i < len) $ readByteArray arr (off + i)--unsafeReadMArray :: Storable e => MArray s e -> Int -> ST s e-unsafeReadMArray (MArray _len marr) i =-    assert (i >= 0 && i < len) $ readMutableByteArray marr--unsafeWriteMArray :: Storable e => MArray s e -> Int -> e -> ST s ()-unsafeWriteMArray (MArray len marr) i =-    assert (i >= 0 && i < len) $ writeMutableByteArray marr---- | Create an uninitialized mutable array.-unsafeNew :: (Storable e) => Int -> ST s (MArray s e)-unsafeNew n = new undefined-  where new :: (Storable e) => e -> ST s (MArray s e)-        new unused = do-          marr <- newMutableByteArray (n * sizeOf unused)-          return (MArray n marr)--unsafeFreeze (MArray len mba) = do-  ba <- unsafeFreezeMutableByteArray mba-  return (Array 0 len ba)---- | Create a mutable array, with its elements initialized with the--- given value.-new :: (Storable e) => Int -> e -> ST s (MArray s e)+-- | Freeze a mutable array. Do not mutate the 'MArray' afterwards!+unsafeFreeze :: MArray s -> ST s Array+unsafeFreeze MArray{..} = ST $ \s# ->+                          (# s#, Array (unsafeCoerce# maBA)+#if defined(ASSERTS)+                             maLen #endif--new len initVal = do-  marr <- unsafeNew len-  sequence_ [unsafeWrite marr i initVal | i <- [0..len-1]]-  return marr--instance Elt Word16 where-#if defined(__GLASGOW_HASKELL__)+                             #)+{-# INLINE unsafeFreeze #-} -    bytesInArray (I# i#) _ = I# (wORD16_SCALE i#)-    {-# INLINE bytesInArray #-}+-- | Indicate how many bytes would be used for an array of the given+-- size.+bytesInArray :: Int -> Int+bytesInArray n = n `shiftL` 1+{-# INLINE bytesInArray #-} -    unsafeIndex (Array len ba#) i@(I# i#) =-      CHECK_BOUNDS("unsafeIndex",len,i)-        case indexWord16Array# ba# i# of r# -> (W16# r#)-    {-# INLINE unsafeIndex #-}+-- | Unchecked read of an immutable array.  May return garbage or+-- crash on an out-of-bounds access.+unsafeIndex :: Array -> Int -> Word16+unsafeIndex Array{..} i@(I# i#) =+  CHECK_BOUNDS("unsafeIndex",aLen,i)+    case indexWord16Array# aBA i# of r# -> (W16# r#)+{-# INLINE unsafeIndex #-} -    unsafeRead (MArray len mba#) i@(I# i#) = ST $ \s# ->-      CHECK_BOUNDS("unsafeRead",len,i)-      case readWord16Array# mba# i# s# of-        (# s2#, r# #) -> (# s2#, W16# r# #)-    {-# INLINE unsafeRead #-}+-- | Unchecked read of an immutable array.  May return garbage or+-- crash on an out-of-bounds access.+unsafeIndexWord :: Array -> Int -> Word+unsafeIndexWord Array{..} i@(I# i#) =+  CHECK_BOUNDS("unsafeIndexWord",aLen`div`wordFactor,i)+    case indexWordArray# aBA i# of r# -> (W# r#)+{-# INLINE unsafeIndexWord #-} -    unsafeWrite (MArray len marr#) i@(I# i#) (W16# e#) = ST $ \s1# ->-      CHECK_BOUNDS("unsafeWrite",len,i)-      case writeWord16Array# marr# i# e# s1# of-        s2# -> (# s2#, () #)-    {-# INLINE unsafeWrite #-}+-- | Unchecked read of a mutable array.  May return garbage or+-- crash on an out-of-bounds access.+unsafeRead :: MArray s -> Int -> ST s Word16+unsafeRead MArray{..} i@(I# i#) = ST $ \s# ->+  CHECK_BOUNDS("unsafeRead",maLen,i)+  case readWord16Array# maBA i# s# of+    (# s2#, r# #) -> (# s2#, W16# r# #)+{-# INLINE unsafeRead #-} -#elif defined(__HUGS__)+-- | Unchecked write of a mutable array.  May return garbage or crash+-- on an out-of-bounds access.+unsafeWrite :: MArray s -> Int -> Word16 -> ST s ()+unsafeWrite MArray{..} i@(I# i#) (W16# e#) = ST $ \s1# ->+  CHECK_BOUNDS("unsafeWrite",maLen,i)+  case writeWord16Array# maBA i# e# s1# of+    s2# -> (# s2#, () #)+{-# INLINE unsafeWrite #-} -    bytesInArray n w = sizeOf w * n-    unsafeIndex = unsafeIndexArray-    unsafeRead = unsafeReadMArray-    unsafeWrite = unsafeWriteMArray+-- | Unchecked read of a mutable array.  May return garbage or+-- crash on an out-of-bounds access.+unsafeReadWord :: MArray s -> Int -> ST s Word+unsafeReadWord MArray{..} i@(I# i#) = ST $ \s# ->+  CHECK_BOUNDS("unsafeRead64",maLen`div`wordFactor,i)+  case readWordArray# maBA i# s# of+    (# s2#, r# #) -> (# s2#, W# r# #)+{-# INLINE unsafeReadWord #-} -#endif+-- | Unchecked write of a mutable array.  May return garbage or crash+-- on an out-of-bounds access.+unsafeWriteWord :: MArray s -> Int -> Word -> ST s ()+unsafeWriteWord MArray{..} i@(I# i#) (W# e#) = ST $ \s1# ->+  CHECK_BOUNDS("unsafeWriteWord",maLen`div`wordFactor,i)+  case writeWordArray# maBA i# e# s1# of+    s2# -> (# s2#, () #)+{-# INLINE unsafeWriteWord #-}  -- | Convert an immutable array to a list.-toList :: Elt e => Array e -> [e]-toList a = loop 0-    where loop i | i < len   = unsafeIndex a i : loop (i+1)+toList :: Array -> Int -> Int -> [Word16]+toList ary off len = loop 0+    where loop i | i < len   = unsafeIndex ary (off+i) : loop (i+1)                  | otherwise = []-          len = length a  -- | An empty immutable array.-empty :: Elt e => Array e+empty :: Array empty = runST (unsafeNew 0 >>= unsafeFreeze)  -- | Run an action in the ST monad and return an immutable array of -- its result.-run :: Elt e => (forall s. ST s (MArray s e)) -> Array e+run :: (forall s. ST s (MArray s)) -> Array run k = runST (k >>= unsafeFreeze)  -- | Run an action in the ST monad and return an immutable array of -- its result paired with whatever else the action returns.-run2 :: Elt e => (forall s. ST s (MArray s e, a)) -> (Array e, a)+run2 :: (forall s. ST s (MArray s, a)) -> (Array, a) run2 k = runST (do                  (marr,b) <- k                  arr <- unsafeFreeze marr                  return (arr,b)) --- | Copy an array in its entirety. The destination array must be at--- least as big as the source.-copy :: Elt e => MArray s e     -- ^ source array-     -> MArray s e              -- ^ destination array-     -> ST s ()-copy src dest-    | length dest >= length src = copy_loop 0-    | otherwise                 = fail "Data.Text.Array.copy: array too small"-    where-      len = length src-      copy_loop i-          | i >= len  = return ()-          | otherwise = do unsafeRead src i >>= unsafeWrite dest i-                           copy_loop (i+1)-{-# INLINE copy #-}+-- | The amount to divide or multiply by to switch between units of+-- 'Word16' and units of 'Word'.+wordFactor :: Int+wordFactor = SIZEOF_HSWORD `shiftR` 1 --- | Unsafely copy the elements of an array.-unsafeCopy :: Elt e =>-              MArray s e -> Int -> MArray s e -> Int -> Int -> ST s ()-unsafeCopy src sidx dest didx count =+-- | Indicate whether an offset is word-aligned.+wordAligned :: Int -> Bool+wordAligned i = i .&. (wordFactor - 1) == 0++-- | Copy some elements of a mutable array.+copyM :: MArray s               -- ^ Destination+      -> Int                    -- ^ Destination offset+      -> MArray s               -- ^ Source+      -> Int                    -- ^ Source offset+      -> Int                    -- ^ Count+      -> ST s ()+copyM dest didx src sidx count =+#if defined(ASSERTS)     assert (sidx + count <= length src) .     assert (didx + count <= length dest) $-    copy_loop sidx didx 0+#endif+    if srem == 0 && drem == 0+    then fast_loop 0+    else slow_loop 0     where-      copy_loop !i !j !c-          | c >= count  = return ()-          | otherwise = do unsafeRead src i >>= unsafeWrite dest j-                           copy_loop (i+1) (j+1) (c+1)-{-# INLINE unsafeCopy #-}+      (swidx,srem) = sidx `divMod` wordFactor+      (dwidx,drem) = didx `divMod` wordFactor+      nwds         = count `div` wordFactor+      fast_loop !i+          | i >= nwds = slow_loop (i * wordFactor)+          | otherwise = do w <- unsafeReadWord src (swidx+i)+                           unsafeWriteWord dest (dwidx+i) w+                           fast_loop (i+1)+      slow_loop !i+          | i >= count= return ()+          | otherwise = do unsafeRead src (sidx+i) >>= unsafeWrite dest (didx+i)+                           slow_loop (i+1)++-- | Copy some elements of an immutable array.+copyI :: MArray s               -- ^ Destination+      -> Int                    -- ^ Destination offset+      -> Array                  -- ^ Source+      -> Int                    -- ^ Source offset+      -> Int                    -- ^ First offset in source /not/ to+                                -- copy (i.e. /not/ length)+      -> ST s ()+copyI dest i0 src j0 top+    | wordAligned i0 && wordAligned j0 = fast (i0 `div` wordFactor) (j0 `div` wordFactor)+    | otherwise = slow i0 j0+  where+    topwds = top `div` wordFactor+    fast !i !j+        | i >= topwds = slow (i * wordFactor) (j * wordFactor)+        | otherwise   = do unsafeWriteWord dest i (src `unsafeIndexWord` j)+                           fast (i+1) (j+1)+    slow !i !j+        | i >= top  = return ()+        | otherwise = do unsafeWrite dest i (src `unsafeIndex` j)+                         slow (i+1) (j+1)
Data/Text/Encoding/Fusion.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE BangPatterns, Rank2Types #-}+{-# LANGUAGE BangPatterns, CPP, Rank2Types #-}  -- | -- Module      : Data.Text.Encoding.Fusion@@ -31,7 +31,9 @@     , module Data.Text.Encoding.Fusion.Common     ) where +#if defined(ASSERTS) import Control.Exception (assert)+#endif import Data.ByteString.Internal (ByteString(..), mallocByteString, memcpy) import Data.Text.Fusion (Step(..), Stream(..)) import Data.Text.Fusion.Size@@ -73,9 +75,8 @@           | i+1 < l && U8.validate2 x1 x2 = Yield (U8.chr2 x1 x2) (i+2)           | i+2 < l && U8.validate3 x1 x2 x3 = Yield (U8.chr3 x1 x2 x3) (i+3)           | i+3 < l && U8.validate4 x1 x2 x3 x4 = Yield (U8.chr4 x1 x2 x3 x4) (i+4)-          | otherwise = decodeError "streamUtf8" "UTF-8" onErr mx (i+1)+          | otherwise = decodeError "streamUtf8" "UTF-8" onErr (Just x1) (i+1)           where-            mx = if i >= l then Nothing else Just x1             x1 = idx i             x2 = idx (i + 1)             x3 = idx (i + 2)@@ -182,7 +183,11 @@       {-# NOINLINE trimUp #-}       trimUp fp _ off = return $! PS fp 0 off       copy0 :: ForeignPtr Word8 -> Int -> Int -> IO (ForeignPtr Word8)-      copy0 !src !srcLen !destLen = assert (srcLen <= destLen) $ do+      copy0 !src !srcLen !destLen =+#if defined(ASSERTS)+        assert (srcLen <= destLen) $+#endif+        do           dest <- mallocByteString destLen           withForeignPtr src  $ \src'  ->               withForeignPtr dest $ \dest' ->
Data/Text/Encoding/Fusion/Common.hs view
@@ -28,9 +28,9 @@     ) where  import Data.Bits ((.&.))-import Data.Char (ord) import Data.Text.Fusion (Step(..), Stream(..)) import Data.Text.Fusion.Internal (M(..), S(..))+import Data.Text.UnsafeChar (ord) import Data.Text.UnsafeShift (shiftR) import Data.Word (Word8) import qualified Data.Text.Encoding.Utf8 as U8
Data/Text/Encoding/Utf16.hs view
@@ -2,9 +2,9 @@  -- | -- Module      : Data.Text.Encoding.Utf16--- Copyright   : (c) Tom Harper 2008-2009,---               (c) Bryan O'Sullivan 2009,---               (c) Duncan Coutts 2009+-- Copyright   : (c) 2008, 2009 Tom Harper,+--               (c) 2009 Bryan O'Sullivan,+--               (c) 2009 Duncan Coutts -- -- License     : BSD-style -- Maintainer  : bos@serpentine.com, rtomharper@googlemail.com,@@ -33,7 +33,7 @@ {-# INLINE chr2 #-}  validate1    :: Word16 -> Bool-validate1 x1 = (x1 >= 0 && x1 < 0xD800) || x1 > 0xDFFF+validate1 x1 = x1 < 0xD800 || x1 > 0xDFFF {-# INLINE validate1 #-}  validate2       ::  Word16 -> Word16 -> Bool
Data/Text/Encoding/Utf32.hs view
@@ -1,8 +1,8 @@ -- |--- Module      : Data.Text.Encoding.Utf16--- Copyright   : (c) Tom Harper 2008-2009,---               (c) Bryan O'Sullivan 2009,---               (c) Duncan Coutts 2009+-- Module      : Data.Text.Encoding.Utf32+-- Copyright   : (c) 2008, 2009 Tom Harper,+--               (c) 2009, 2010 Bryan O'Sullivan,+--               (c) 2009 Duncan Coutts -- -- License     : BSD-style -- Maintainer  : bos@serpentine.com, rtomharper@googlemail.com,@@ -19,5 +19,5 @@ import Data.Word (Word32)  validate    :: Word32 -> Bool-validate x1 = (x1 >= 0x0 && x1 < 0xD800) || (x1 > 0xDFFF && x1 <= 0x10FFFF)+validate x1 = x1 < 0xD800 || (x1 > 0xDFFF && x1 <= 0x10FFFF) {-# INLINE validate #-}
Data/Text/Encoding/Utf8.hs view
@@ -1,10 +1,10 @@-{-# LANGUAGE MagicHash #-}+{-# LANGUAGE CPP, MagicHash #-}  -- |--- Module      : Data.Text.Encoding.Utf16--- Copyright   : (c) Tom Harper 2008-2009,---               (c) Bryan O'Sullivan 2009,---               (c) Duncan Coutts 2009+-- Module      : Data.Text.Encoding.Utf8+-- Copyright   : (c) 2008, 2009 Tom Harper,+--               (c) 2009, 2010 Bryan O'Sullivan,+--               (c) 2009 Duncan Coutts -- -- License     : BSD-style -- Maintainer  : bos@serpentine.com, rtomharper@googlemail.com,@@ -30,9 +30,11 @@     , validate4     ) where +#if defined(ASSERTS) import Control.Exception (assert)-import Data.Char (ord)+#endif import Data.Bits ((.&.))+import Data.Text.UnsafeChar (ord) import Data.Text.UnsafeShift (shiftR) import GHC.Exts import GHC.Word (Word8(..))@@ -47,14 +49,22 @@ {-# INLINE between #-}  ord2   :: Char -> (Word8,Word8)-ord2 c = assert (n >= 0x80 && n <= 0x07ff) (x1,x2)+ord2 c =+#if defined(ASSERTS)+    assert (n >= 0x80 && n <= 0x07ff)+#endif+    (x1,x2)     where       n  = ord c       x1 = fromIntegral $ (n `shiftR` 6) + 0xC0       x2 = fromIntegral $ (n .&. 0x3F)   + 0x80  ord3   :: Char -> (Word8,Word8,Word8)-ord3 c = assert (n >= 0x0800 && n <= 0xffff) (x1,x2,x3)+ord3 c =+#if defined(ASSERTS)+    assert (n >= 0x0800 && n <= 0xffff)+#endif+    (x1,x2,x3)     where       n  = ord c       x1 = fromIntegral $ (n `shiftR` 12) + 0xE0@@ -62,7 +72,11 @@       x3 = fromIntegral $ (n .&. 0x3F) + 0x80  ord4   :: Char -> (Word8,Word8,Word8,Word8)-ord4 c = assert (n >= 0x10000) (x1,x2,x3,x4)+ord4 c =+#if defined(ASSERTS)+    assert (n >= 0x10000)+#endif+    (x1,x2,x3,x4)     where       n  = ord c       x1 = fromIntegral $ (n `shiftR` 18) + 0xF0@@ -105,7 +119,7 @@ {-# INLINE chr4 #-}  validate1    :: Word8 -> Bool-validate1 x1 = between x1 0x00 0x7F+validate1 x1 = x1 <= 0x7F {-# INLINE validate1 #-}  validate2       :: Word8 -> Word8 -> Bool
Data/Text/Foreign.hs view
@@ -1,7 +1,7 @@-{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE BangPatterns, CPP #-} -- | -- Module      : Data.Text.Foreign--- Copyright   : (c) Bryan O'Sullivan 2009+-- Copyright   : (c) 2009, 2010 Bryan O'Sullivan -- -- License     : BSD-style -- Maintainer  : bos@serpentine.com, rtomharper@googlemail.com,@@ -23,11 +23,18 @@     -- * Unsafe conversion code     , lengthWord16     , unsafeCopyToPtr+    -- * Low-level manipulation+    -- $lowlevel+    , dropWord16+    , takeWord16     ) where +#if defined(ASSERTS) import Control.Exception (assert)+#endif import Control.Monad.ST (unsafeIOToST) import Data.Text.Internal (Text(..), empty)+import Data.Text.Unsafe (lengthWord16) import qualified Data.Text.Array as A import Data.Word (Word16) import Foreign.Marshal.Alloc (allocaBytes)@@ -53,7 +60,11 @@         -> Int                  -- ^ length of source array (in 'Word16' units)         -> IO Text fromPtr _   0   = return empty-fromPtr ptr len = assert (len > 0) $ return (Text arr 0 len)+fromPtr ptr len =+#if defined(ASSERTS)+    assert (len > 0) $+#endif+    return $! Text arr 0 len   where     arr = A.run (A.unsafeNew len >>= copy)     copy marr = loop ptr 0@@ -63,11 +74,44 @@           A.unsafeWrite marr i =<< unsafeIOToST (peek p)           loop (p `plusPtr` 2) (i + 1) --- | /O(1)/ Return the length of a 'Text' in units of 'Word16'.  This--- is useful for sizing a target array appropriately before using--- 'unsafeCopyToPtr'.-lengthWord16 :: Text -> Int-lengthWord16 (Text _arr _off len) = len+-- $lowlevel+--+-- Foreign functions that use UTF-16 internally may return indices in+-- units of 'Word16' instead of characters.  These functions may+-- safely be used with such indices, as they will adjust offsets if+-- necessary to preserve the validity of a Unicode string.++-- | /O(1)/ Return the prefix of the 'Text' of @n@ 'Word16' units in+-- length.+--+-- If @n@ would cause the 'Text' to end inside a surrogate pair, the+-- end of the prefix will be advanced by one additional 'Word16' unit+-- to maintain its validity.+takeWord16 :: Int -> Text -> Text+takeWord16 n t@(Text arr off len)+    | n <= 0               = empty+    | n >= len || m >= len = t+    | otherwise            = Text arr off m+  where+    m | w < 0xDB00 || w > 0xD8FF = n+      | otherwise                = n+1+    w = A.unsafeIndex arr (off+n-1)++-- | /O(1)/ Return the suffix of the 'Text', with @n@ 'Word16' units+-- dropped from its beginning.+--+-- If @n@ would cause the 'Text' to begin inside a surrogate pair, the+-- beginning of the suffix will be advanced by one additional 'Word16'+-- unit to maintain its validity.+dropWord16 :: Int -> Text -> Text+dropWord16 n t@(Text arr off len)+    | n <= 0               = t+    | n >= len || m >= len = empty+    | otherwise            = Text arr (off+m) (len-m)+  where+    m | w < 0xD800 || w > 0xDBFF = n+      | otherwise                = n+1+    w = A.unsafeIndex arr (off+n-1)  -- | /O(n)/ Copy a 'Text' to an array.  The array is assumed to be big -- enough to hold the contents of the entire 'Text'.
Data/Text/Fusion.hs view
@@ -3,7 +3,7 @@ -- | -- Module      : Data.Text.Fusion -- Copyright   : (c) Tom Harper 2008-2009,---               (c) Bryan O'Sullivan 2009,+--               (c) Bryan O'Sullivan 2009-2010, --               (c) Duncan Coutts 2009 -- -- License     : BSD-style@@ -47,9 +47,8 @@                 Num(..), Ord(..), ($), (&&),                 fromIntegral, otherwise) import Data.Bits ((.&.))-import Data.Char (ord) import Data.Text.Internal (Text(..))-import Data.Text.UnsafeChar (unsafeChr, unsafeWrite)+import Data.Text.UnsafeChar (ord, unsafeChr, unsafeWrite) import Data.Text.UnsafeShift (shiftL, shiftR) import qualified Data.Text.Array as A import qualified Data.Text.Fusion.Common as S@@ -65,8 +64,7 @@ stream :: Text -> Stream Char stream (Text arr off len) = Stream next off (maxSize len)     where-      end = off+len-      {-# INLINE next #-}+      !end = off+len       next !i           | i >= end                   = Done           | n >= 0xD800 && n <= 0xDBFF = Yield (U16.chr2 n n2) (i + 2)@@ -94,20 +92,25 @@ -- | /O(n)/ Convert a 'Stream Char' into a 'Text'. unstream :: Stream Char -> Text unstream (Stream next0 s0 len) = I.textP (P.fst a) 0 (P.snd a)-    where-      mlen = upperBound 4 len-      a = A.run2 (A.unsafeNew mlen >>= (\arr -> loop arr mlen s0 0))-      loop arr !top !s !i-          | i + 1 >= top = case next0 s of-                            Done -> return (arr, i)-                            _    -> do-                              let top' = (top `shiftL` 1) + 1-                              arr' <- A.unsafeNew top'-                              A.copy arr arr' >> loop arr' top' s i-          | otherwise = case next0 s of-               Done       -> return (arr, i)-               Skip s'    -> loop arr top s' i-               Yield x s' -> unsafeWrite arr i x >>= loop arr top s'+  where+    a = A.run2 (A.unsafeNew mlen >>= \arr -> outer arr mlen s0 0)+      where mlen = upperBound 4 len+    outer arr top = loop+      where+        loop !s !i =+            case next0 s of+              Done          -> return (arr, i)+              Skip s'       -> loop s' i+              Yield x s'+                | j >= top  -> {-# SCC "unstream/resize" #-} do+                               let top' = (top + 1) `shiftL` 1+                               arr' <- A.unsafeNew top'+                               A.copyM arr' 0 arr 0 top+                               outer arr' top' s i+                | otherwise -> do d <- unsafeWrite arr i x+                                  loop s' (i+d)+                where j | ord x < 0x10000 = i+                        | otherwise       = i + 1 {-# INLINE [0] unstream #-} {-# RULES "STREAM stream/unstream fusion" forall s. stream (unstream s) = s #-} @@ -132,10 +135,10 @@           Done -> return (marr, (j, len-j))               where j = i + 1           Skip s1    -> loop s1 i len marr-          Yield x s1 | i < least -> do-                       let newLen = len * 2+          Yield x s1 | i < least -> {-# SCC "reverse/resize" #-} do+                       let newLen = len `shiftL` 1                        marr' <- A.unsafeNew newLen-                       A.unsafeCopy marr 0 marr' (newLen-len) len+                       A.copyM marr' (newLen-len) marr 0 len                        write s1 (len+i) newLen marr'                      | otherwise -> write s1 i len marr             where n = ord x
Data/Text/Fusion/Common.hs view
@@ -29,6 +29,7 @@     , init     , null     , lengthI+    , compareLengthI     , isSingleton      -- * Transformations@@ -99,8 +100,8 @@     ) where  import Prelude (Bool(..), Char, Eq(..), Int, Integral, Maybe(..),-                Ord(..), String, (.), ($), (+), (-), (*), (++), (&&),-                fromIntegral, otherwise)+                Ord(..), Ordering(..), String, (.), ($), (+), (-), (*), (++),+                (&&), fromIntegral, otherwise) import qualified Data.List as L import qualified Prelude as P import Data.Int (Int64)@@ -158,8 +159,8 @@     next N = Done {-# INLINE [0] snoc #-} -data E l r = L {-# UNPACK #-} !l-           | R {-# UNPACK #-} !r+data E l r = L !l+           | R !r  -- | /O(n)/ Appends one Stream to the other. append :: Stream Char -> Stream Char -> Stream Char@@ -266,6 +267,25 @@                            Yield _ s' -> loop_length (z + 1) s' {-# INLINE[0] lengthI #-} +-- | /O(n)/ Compares the count of characters in a string to a number.+-- Subject to fusion.+--+-- This function gives the same answer as comparing against the result+-- of 'lengthI', but can short circuit if the count of characters is+-- greater than the number, and hence be more efficient.+compareLengthI :: Integral a => Stream Char -> a -> Ordering+compareLengthI (Stream next s0 len) n = +    case exactly len of+      Nothing -> loop_cmp 0 s0+      Just i  -> compare (fromIntegral i) n+    where+      loop_cmp !z s  = case next s of+                         Done       -> compare z n+                         Skip    s' -> loop_cmp z s'+                         Yield _ s' | z > n     -> GT+                                    | otherwise -> loop_cmp (z + 1) s'+{-# INLINE[0] compareLengthI #-}+ -- | /O(n)/ Indicate whether a string contains exactly one element. isSingleton :: Stream Char -> Bool isSingleton (Stream next s0 _len) = loop 0 s0@@ -662,8 +682,8 @@ {-# INLINE [0] take #-}  -- | /O(n)/ drop n, applied to a stream, returns the suffix of the--- stream of length @n@, or the empty stream if @n@ is greater than the--- length of the stream.+-- stream after the first @n@ characters, or the empty stream if @n@+-- is greater than the length of the stream. drop :: Integral a => a -> Stream Char -> Stream Char drop n0 (Stream next0 s0 len) =     Stream next (J n0 :*: s0) (len - fromIntegral (max 0 n0))
Data/Text/Fusion/Internal.hs view
@@ -34,16 +34,15 @@  -- | Specialised, strict Maybe-like type. data M a = N-         | J {-# UNPACK #-} !a+         | J !a  type M8 = M Word8  -- Restreaming state.-data S s = S {-# UNPACK #-} !s-    {-# UNPACK #-} !M8 {-# UNPACK #-} !M8 {-# UNPACK #-} !M8+data S s = S !s {-# UNPACK #-} !M8 {-# UNPACK #-} !M8 {-# UNPACK #-} !M8  infixl 2 :*:-data PairS a b = {-# UNPACK #-} !a :*: {-# UNPACK #-} !b+data PairS a b = !a :*: !b                  deriving (Eq, Ord, Show)  -- | Allow a function over a stream to switch between two states.
Data/Text/Fusion/Size.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-# OPTIONS_GHC -fno-warn-missing-methods #-} -- | -- Module      : Data.Text.Fusion.Internal@@ -15,6 +16,7 @@ module Data.Text.Fusion.Size     (       Size+    , exactly     , exactSize     , maxSize     , unknownSize@@ -26,19 +28,34 @@     , isEmpty     ) where +#if defined(ASSERTS) import Control.Exception (assert)+#endif  data Size = Exact {-# UNPACK #-} !Int -- ^ Exact size.           | Max   {-# UNPACK #-} !Int -- ^ Upper bound on size.           | Unknown                   -- ^ Unknown size.             deriving (Eq, Show) +exactly :: Size -> Maybe Int+exactly (Exact n) = Just n+exactly _         = Nothing+{-# INLINE exactly #-}+ exactSize :: Int -> Size-exactSize n = assert (n >= 0) Exact n+exactSize n =+#if defined(ASSERTS)+    assert (n >= 0)+#endif+    Exact n {-# INLINE exactSize #-}  maxSize :: Int -> Size-maxSize n = assert (n >= 0) Max n+maxSize n =+#if defined(ASSERTS)+    assert (n >= 0)+#endif+    Max n {-# INLINE maxSize #-}  unknownSize :: Size
Data/Text/IO.hs view
@@ -1,8 +1,8 @@-{-# LANGUAGE BangPatterns, CPP, RecordWildCards #-}+{-# LANGUAGE BangPatterns, CPP, RecordWildCards, ScopedTypeVariables #-} -- | -- Module      : Data.Text.IO--- Copyright   : (c) Bryan O'Sullivan 2009,---               (c) Simon Marlow 2009+-- Copyright   : (c) 2009, 2010 Bryan O'Sullivan,+--               (c) 2009 Simon Marlow -- License     : BSD-style -- Maintainer  : bos@serpentine.com -- Stability   : experimental@@ -32,15 +32,16 @@     ) where  import Data.Text (Text)-import Prelude hiding (appendFile, getContents, getLine, interact, putStr,-                       putStrLn, readFile, writeFile)+import Prelude hiding (appendFile, catch, getContents, getLine, interact,+                       putStr, putStrLn, readFile, writeFile) import System.IO (Handle, IOMode(..), hPutChar, openFile, stdin, stdout,                   withFile) #if __GLASGOW_HASKELL__ <= 610 import qualified Data.ByteString.Char8 as B import Data.Text.Encoding (decodeUtf8, encodeUtf8) #else-import Control.Exception (throw)+import Control.Exception (catch, throw)+import Control.Monad (liftM2, when) import Data.IORef (readIORef, writeIORef) import qualified Data.Text as T import Data.Text.Fusion (stream)@@ -49,11 +50,13 @@ import GHC.IO.Buffer (Buffer(..), BufferState(..), CharBufElem, CharBuffer,                       RawCharBuffer, emptyBuffer, isEmptyBuffer, newCharBuffer,                       writeCharBuf)+import GHC.IO.Exception (IOException(ioe_type), IOErrorType(InappropriateType)) import GHC.IO.Handle.Internals (augmentIOError, hClose_help, wantReadableHandle,                                 wantWritableHandle) import GHC.IO.Handle.Text (commitBuffer') import GHC.IO.Handle.Types (BufferList(..), BufferMode(..), Handle__(..),-                            Newline(..))+                            HandleType(..), Newline(..))+import System.IO (hGetBuffering, hFileSize, hSetBuffering, hTell) import System.IO.Error (isEOFError) #endif @@ -87,25 +90,46 @@ #if __GLASGOW_HASKELL__ <= 610 hGetContents = fmap decodeUtf8 . B.hGetContents #else-hGetContents h = wantReadableHandle "hGetContents" h $ \hh -> do-                   (hh',ts) <- readAll hh-                   return (hh',T.concat ts)+hGetContents h = do+  chooseGoodBuffering h+  wantReadableHandle "hGetContents" h readAll  where   readAll hh@Handle__{..} = do-    buf <- readIORef haCharBuffer-    let readChunks = do-          t <- readChunk hh buf-          (hh',ts) <- readAll hh-          return (hh', t:ts)-    readChunks `catch` \e -> do-      (hh', _) <- hClose_help hh-      if isEOFError e-        then return $ if isEmptyBuffer buf-                      then (hh', [])-                      else (hh', [T.singleton '\r'])-        else throw (augmentIOError e "hGetContents" h)+    let catchError e+          | isEOFError e = do+              buf <- readIORef haCharBuffer+              return $ if isEmptyBuffer buf+                       then T.empty+                       else T.singleton '\r'+          | otherwise = throw (augmentIOError e "hGetContents" h)+        readChunks = do+          buf <- readIORef haCharBuffer+          t <- readChunk hh buf `catch` catchError+          if T.null t+            then return [t]+            else (t:) `fmap` readChunks+    ts <- readChunks+    (hh', _) <- hClose_help hh+    return (hh'{haType=ClosedHandle}, T.concat ts) #endif   +-- | Use a more efficient buffer size if we're reading in+-- block-buffered mode with the default buffer size.  When we can+-- determine the size of the handle we're reading, set the buffer size+-- to that, so that we can read the entire file in one chunk.+-- Otherwise, use a buffer size of at least 16KB.+chooseGoodBuffering :: Handle -> IO ()+chooseGoodBuffering h = do+  bufMode <- hGetBuffering h+  case bufMode of+    BlockBuffering Nothing -> do+      d <- catch (liftM2 (-) (hFileSize h) (hTell h)) $ \(e::IOException) ->+           if ioe_type e == InappropriateType+           then return 16384 -- faster than the 2KB default+           else throw e+      when (d > 0) . hSetBuffering h . BlockBuffering . Just . fromIntegral $ d+    _ -> return ()+ -- | Read a single line from a handle. hGetLine :: Handle -> IO Text #if __GLASGOW_HASKELL__ <= 610@@ -128,8 +152,10 @@   let str = stream t   case buffer_mode of      (NoBuffering, _)        -> hPutChars h str-     (LineBuffering, buf)    -> writeBlocks h True  nl buf str-     (BlockBuffering _, buf) -> writeBlocks h False nl buf str+     (LineBuffering, buf)    -> writeLines h nl buf str+     (BlockBuffering _, buf)+         | nl == CRLF        -> writeBlocksCRLF h buf str+         | otherwise         -> writeBlocksRaw h buf str  hPutChars :: Handle -> Stream Char -> IO () hPutChars h (Stream next0 s0 _len) = loop s0@@ -139,12 +165,18 @@                 Skip s'    -> loop s'                 Yield x s' -> hPutChar h x >> loop s' --- This function is largely lifted from GHC.IO.Handle.Text, but--- adapted to a coinductive stream of data instead of an inductive+-- The following functions are largely lifted from GHC.IO.Handle.Text,+-- but adapted to a coinductive stream of data instead of an inductive -- list.-writeBlocks :: Handle -> Bool -> Newline -> Buffer CharBufElem -> Stream Char-            -> IO ()-writeBlocks h lineBuffered nl buf0 (Stream next0 s0 _len) = outer s0 buf0+--+-- We have several variations of more or less the same code for+-- performance reasons.  Splitting the original buffered write+-- function into line- and block-oriented versions gave us a 2.1x+-- performance improvement.  Lifting out the raw/cooked newline+-- handling gave a few more percent on top.++writeLines :: Handle -> Newline -> Buffer CharBufElem -> Stream Char -> IO ()+writeLines h nl buf0 (Stream next0 s0 _len) = outer s0 buf0  where   outer s1 Buffer{bufRaw=raw, bufSize=len} = inner s1 (0::Int)    where@@ -159,12 +191,40 @@                          then do n1 <- writeCharBuf raw n '\r'                                  writeCharBuf raw n1 '\n'                          else writeCharBuf raw n x-                   if lineBuffered-                     then commit n' True{-needs flush-} False >>= outer s'-                     else inner s' n'+                   commit n' True{-needs flush-} False >>= outer s'           | otherwise    -> writeCharBuf raw n x >>= inner s'     commit = commitBuffer h raw len +writeBlocksCRLF :: Handle -> Buffer CharBufElem -> Stream Char -> IO ()+writeBlocksCRLF h buf0 (Stream next0 s0 _len) = outer s0 buf0+ where+  outer s1 Buffer{bufRaw=raw, bufSize=len} = inner s1 (0::Int)+   where+    inner !s !n =+      case next0 s of+        Done -> commit n False{-no flush-} True{-release-} >> return ()+        Skip s' -> inner s' n+        Yield x s'+          | n + 1 >= len -> commit n True{-needs flush-} False >>= outer s+          | x == '\n'    -> do n1 <- writeCharBuf raw n '\r'+                               writeCharBuf raw n1 '\n' >>= inner s'+          | otherwise    -> writeCharBuf raw n x >>= inner s'+    commit = commitBuffer h raw len++writeBlocksRaw :: Handle -> Buffer CharBufElem -> Stream Char -> IO ()+writeBlocksRaw h buf0 (Stream next0 s0 _len) = outer s0 buf0+ where+  outer s1 Buffer{bufRaw=raw, bufSize=len} = inner s1 (0::Int)+   where+    inner !s !n =+      case next0 s of+        Done -> commit n False{-no flush-} True{-release-} >> return ()+        Skip s' -> inner s' n+        Yield x s'+          | n + 1 >= len -> commit n True{-needs flush-} False >>= outer s+          | otherwise    -> writeCharBuf raw n x >>= inner s'+    commit = commitBuffer h raw len+ -- This function is completely lifted from GHC.IO.Handle.Text. getSpareBuffer :: Handle__ -> IO (BufferMode, CharBuffer) getSpareBuffer Handle__{haCharBuffer=ref, @@ -191,7 +251,7 @@ commitBuffer hdl !raw !sz !count flush release =    wantWritableHandle "commitAndReleaseBuffer" hdl $      commitBuffer' raw sz count flush release-{-# NOINLINE commitBuffer #-}+{-# INLINE commitBuffer #-} #endif  -- | Write a string to a handle, followed by a newline.
Data/Text/IO/Internal.hs view
@@ -1,8 +1,8 @@ {-# LANGUAGE BangPatterns, CPP, RecordWildCards #-} -- | -- Module      : Data.Text.IO.Internal--- Copyright   : (c) Bryan O'Sullivan 2009,---               (c) Simon Marlow 2009+-- Copyright   : (c) 2009, 2010 Bryan O'Sullivan,+--               (c) 2009 Simon Marlow -- License     : BSD-style -- Maintainer  : bos@serpentine.com -- Stability   : experimental@@ -72,8 +72,8 @@               let pre | isEmptyBuffer buf1 = T.empty                       | otherwise          = T.singleton '\r'               writeIORef haCharBuffer buf1{ bufL=0, bufR=0 }-              let str = reverse $ pre:t:ts-              if all T.null str+              let str = reverse . filter (not . T.null) $ pre:t:ts+              if null str                 then ioe_EOF                 else return str          Just new_buf -> hGetLineLoop hh (t:ts) new_buf@@ -105,7 +105,7 @@  | otherwise     = withRawBuffer buf $ go  where   go pbuf = do-    let t = unstream (Stream next r (maxSize (w-r)))+    let !t = unstream (Stream next r (maxSize (w-r)))         w' = w - 1     return $ if ix w' == '\r'              then (t,w')@@ -127,7 +127,7 @@ getSomeCharacters handle_@Handle__{..} buf@Buffer{..} =   case bufferElems buf of     -- buffer empty: read some more-    0 -> readTextDevice handle_ buf+    0 -> {-# SCC "readTextDevice" #-} readTextDevice handle_ buf      -- if the buffer has a single '\r' in it and we're doing newline     -- translation: read some more@@ -145,7 +145,7 @@                  return buf      -- buffer has some chars in it already: just return it-    _otherwise -> return buf+    _otherwise -> {-# SCC "otherwise" #-} return buf  -- | Read a single chunk of strict text from a buffer. Used by both -- the strict and lazy implementations of hGetContents.
Data/Text/Internal.hs view
@@ -1,10 +1,10 @@-{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE CPP, DeriveDataTypeable #-}  -- | -- Module      : Data.Text.Internal--- Copyright   : (c) Tom Harper 2008-2009,---               (c) Bryan O'Sullivan 2009,---               (c) Duncan Coutts 2009+-- Copyright   : (c) 2008, 2009 Tom Harper,+--               (c) 2009, 2010 Bryan O'Sullivan,+--               (c) 2009 Duncan Coutts -- -- License     : BSD-style -- Maintainer  : bos@serpentine.com, rtomharper@googlemail.com,@@ -28,28 +28,31 @@     , showText     ) where +#if defined(ASSERTS) import Control.Exception (assert)+#endif import qualified Data.Text.Array as A import Data.Typeable (Typeable)-import Data.Word (Word16)  -- | A space efficient, packed, unboxed Unicode text type. data Text = Text-    {-# UNPACK #-} !(A.Array Word16) -- payload+    {-# UNPACK #-} !A.Array          -- payload     {-# UNPACK #-} !Int              -- offset     {-# UNPACK #-} !Int              -- length     deriving (Typeable)  -- | Smart constructor.-text :: A.Array Word16 -> Int -> Int -> Text+text :: A.Array -> Int -> Int -> Text text arr off len =-    assert (len >= 0) .-    assert (off >= 0) .-    assert (alen == 0 || len == 0 || off < alen) .-    assert (len == 0 || c < 0xDC00 || c > 0xDFFF) $-    Text arr off len-  where c    = A.unsafeIndex arr off-        alen = A.length arr+#if defined(ASSERTS)+  let c    = A.unsafeIndex arr off+      alen = A.length arr+  in assert (len >= 0) .+     assert (off >= 0) .+     assert (alen == 0 || len == 0 || off < alen) .+     assert (len == 0 || c < 0xDC00 || c > 0xDFFF) $+#endif+     Text arr off len {-# INLINE text #-}  -- | /O(1)/ The empty 'Text'.@@ -59,7 +62,7 @@  -- | Construct a 'Text' without invisibly pinning its byte array in -- memory if its length has dwindled to zero.-textP :: A.Array Word16 -> Int -> Int -> Text+textP :: A.Array -> Int -> Int -> Text textP arr off len | len == 0  = empty                   | otherwise = text arr off len {-# INLINE textP #-}@@ -67,5 +70,5 @@ -- | A useful 'show'-like function for debugging purposes. showText :: Text -> String showText (Text arr off len) =-    "Text " ++ (show . take (off+len) . A.toList) arr ++ ' ' :+    "Text " ++ show (A.toList arr off len) ++ ' ' :             show off ++ ' ' : show len
Data/Text/Lazy.hs view
@@ -2,7 +2,7 @@ {-# LANGUAGE BangPatterns, CPP #-} -- | -- Module      : Data.Text.Lazy--- Copyright   : (c) Bryan O'Sullivan 2009+-- Copyright   : (c) 2009, 2010 Bryan O'Sullivan -- -- License     : BSD-style -- Maintainer  : bos@serpentine.com, rtomharper@googlemail.com,@@ -39,6 +39,10 @@     , empty     , fromChunks     , toChunks+    , toStrict+    , fromStrict+    , foldrChunks+    , foldlChunks      -- * Basic interface     , cons@@ -51,6 +55,7 @@     , init     , null     , length+    , compareLength      -- * Transformations     , map@@ -144,6 +149,10 @@     , isSuffixOf     , isInfixOf +    -- ** View patterns+    , prefixed+    , suffixed+     -- * Searching     , filter     , find@@ -165,7 +174,7 @@     ) where  import Prelude (Char, Bool(..), Maybe(..), String,-                Eq(..), Ord(..), Read(..), Show(..),+                Eq(..), Ord(..), Ordering, Read(..), Show(..),                 (&&), (+), (-), (.), ($), (++),                 div, error, flip, fromIntegral, not, otherwise) import qualified Prelude as P@@ -245,6 +254,8 @@ unpack t = S.unstreamList (stream t) {-# INLINE [1] unpack #-} +-- | /O(1)/ Convert a character into a Text.+-- Subject to fusion. singleton :: Char -> Text singleton c = Chunk (T.singleton c) Empty {-# INLINE [1] singleton #-}@@ -264,6 +275,22 @@ toChunks :: Text -> [T.Text] toChunks cs = foldrChunks (:) [] cs +-- | /O(n)/ Convert a lazy 'Text' into a strict 'T.Text'.+toStrict :: Text -> T.Text+toStrict t = T.concat (toChunks t)+{-# INLINE [1] toStrict #-}++-- | /O(c)/ Convert a strict 'T.Text' into a lazy 'Text'.+fromStrict :: T.Text -> Text+fromStrict t = chunk t Empty+{-# INLINE [1] fromStrict #-}++-- -----------------------------------------------------------------------------+-- * Basic functions++-- | /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 fusion. cons :: Char -> Text -> Text cons c t = Chunk (T.singleton c) t {-# INLINE [1] cons #-}@@ -275,6 +302,8 @@     unstream (S.cons c (stream t)) = cons c t  #-} +-- | /O(n)/ Adds a character to the end of a 'Text'.  This copies the+-- entire array in the process, unless fused.  Subject to fusion. snoc :: Text -> Char -> Text snoc t c = foldrChunks Chunk (singleton c) t {-# INLINE [1] snoc #-}@@ -380,6 +409,8 @@     S.last (stream t) = last t   #-} +-- | /O(n)/ Returns the number of characters in a 'Text'.+-- Subject to fusion. length :: Text -> Int64 length = foldlChunks go 0     where go l t = l + fromIntegral (T.length t)@@ -392,6 +423,20 @@     S.length (stream t) = length t  #-} +-- | /O(n)/ Compare the count of characters in a 'Text' to a number.+-- Subject to fusion.+--+-- This function gives the same answer as comparing against the result+-- of 'length', but can short circuit if the count of characters is+-- greater than the number, and hence be more efficient.+compareLength :: Text -> Int64 -> Ordering+compareLength t n = S.compareLengthI (stream t) n+{-# INLINE [1] compareLength #-}++-- We don't apply those otherwise appealing length-to-compareLength+-- rewrite rules here, because they can change the strictness+-- properties of code.+ -- | /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@@ -724,8 +769,8 @@   #-}  -- | /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.+-- 'Text' after the first @n@ characters, or the empty 'Text' if @n@+-- is greater than the length of the 'Text'. Subject to fusion. drop :: Int64 -> Text -> Text drop i t0     | i <= 0    = t0@@ -915,38 +960,37 @@ {-# INLINE breakEnd #-}  -- | /O(n+m)/ Find all non-overlapping instances of @needle@ in--- @haystack@.  The first element of the returned pair is the prefix--- of @haystack@ prior to any matches of @needle@.  The second is a--- list of pairs.+-- @haystack@.  Each element of the returned list consists of a pair: ----- The first element of each pair in the list is a span from the--- beginning of a match to the beginning of the next match, while the--- second is a span from the beginning of the match to the end of the--- input.+-- * The entire string prior to the /k/th match (i.e. the prefix) --+-- * The /k/th match, followed by the remainder of the string+-- -- Examples: -- -- > find "::" ""--- > ==> ("", [])--- > find "/" "a/b/c/d"--- > ==> ("a", [("/b","/b/c/d"), ("/c","/c/d"), ("/d","/d")])+-- > ==> []+-- > find "/" "a/b/c/"+-- > ==> [("a", "/b/c/"), ("a/b", "/c/"), ("a/b/c", "/")] -- -- This function is strict in its first argument, and lazy in its -- second. -- -- In (unlikely) bad cases, this function's time complexity degrades -- towards /O(n*m)/.-find :: Text -> Text -> (Text, [(Text, Text)])+--+-- The @needle@ parameter may not be empty.+find :: Text                    -- ^ @needle@ to search for+     -> Text                    -- ^ @haystack@ in which to search+     -> [(Text, Text)] find pat src     | null pat  = emptyError "find"-    | otherwise = case indices pat src of-                    []     -> (src, [])-                    (x:xs) -> let h :*: t = splitAtWord x src-                              in (h, go x xs t)+    | otherwise = go 0 empty src (indices pat src)   where-    go !i (x:xs) cs = let h :*: t = splitAtWord (x-i) cs-                      in (h, cs) : go x xs t-    go  _ _      cs = [(cs,cs)]+    go !n p s (x:xs) = let h :*: t = splitAtWord (x-n) s+                           h'      = append p h+                       in (h',t) : go x h' t xs+    go _  _ _ _      = []  -- | /O(n)/ 'breakBy' is like 'spanBy', but the prefix returned is over -- elements that fail the predicate @p@.@@ -1157,6 +1201,55 @@     isInfixOf (singleton n) h = S.elem n (S.stream h)   #-} +-------------------------------------------------------------------------------+-- * View patterns++-- | /O(n)/ Returns the suffix of the second string if its prefix+-- matches the first.+--+-- Examples:+--+-- > prefixed "foo" "foobar" == Just "bar"+-- > prefixed "foo" "quux"   == Nothing+--+-- This is particularly useful with the @ViewPatterns@ extension to+-- GHC, as follows:+--+-- > {-# LANGUAGE ViewPatterns #-}+-- > import Data.Text as T+-- >+-- > fnordLength :: Text -> Int+-- > fnordLength (prefixed "fnord" -> Just suf) = T.length suf+-- > fnordLength _                              = -1+prefixed :: Text -> Text -> Maybe Text+-- Yes, this could be much more efficient.+prefixed p t+    | p `isPrefixOf` t = Just (drop (length p) t)+    | otherwise        = Nothing++-- | /O(n)/ Returns the prefix of the second string if its suffix+-- matches the first.+--+-- Examples:+--+-- > suffixed "bar" "foobar" == Just "foo"+-- > suffixed "foo" "quux"   == Nothing+--+-- This is particularly useful with the @ViewPatterns@ extension to+-- GHC, as follows:+--+-- > {-# LANGUAGE ViewPatterns #-}+-- > import Data.Text as T+-- >+-- > quuxLength :: Text -> Int+-- > quuxLength (suffixed "quux" -> Just pre) = T.length pre+-- > quuxLength _                             = -1+suffixed :: Text -> Text -> Maybe Text+-- Yes, this could be much more efficient.+suffixed p t+    | p `isSuffixOf` t = Just (take (length t - length p) t)+    | otherwise        = Nothing+ -- | /O(n)/ 'filter', applied to a predicate and a 'Text', -- returns a 'Text' containing those characters that satisfy the -- predicate.@@ -1194,9 +1287,9 @@ count :: Text -> Text -> Int64 count pat src     | null pat        = emptyError "count"-    | otherwise       = len (indices pat src)-  where len []     = 0-        len (_:xs) = 1 + len xs+    | otherwise       = go 0 (indices pat src)+  where go !n []     = n+        go !n (_:xs) = go (n+1) xs {-# INLINE [1] count #-}  {-# RULES
Data/Text/Lazy/Builder.hs view
@@ -1,9 +1,9 @@-{-# LANGUAGE BangPatterns, Rank2Types #-}+{-# LANGUAGE BangPatterns, CPP, Rank2Types #-}  ----------------------------------------------------------------------------- -- | -- Module      : Data.Text.Lazy.Builder--- Copyright   : Johan Tibell+-- Copyright   : (c) 2010 Johan Tibell -- License     : BSD3-style (see LICENSE) --  -- Maintainer  : Johan Tibell <johan.tibell@gmail.com>@@ -29,18 +29,17 @@    , flush    ) where -import Control.Exception (assert) import Control.Monad.ST (ST, runST) import Data.Bits ((.&.))-import Data.Char (ord) import Data.Monoid (Monoid(..)) import Data.Text.Internal (Text(..))-import Data.Text.Lazy.Internal (defaultChunkSize)+import Data.Text.Lazy.Internal (smallChunkSize) import Data.Text.Unsafe (inlineInterleaveST)+import Data.Text.UnsafeChar (ord, unsafeWrite) import Data.Text.UnsafeShift (shiftR)-import Data.Word (Word16) import Prelude hiding (map, putChar) +import qualified Data.String as String import qualified Data.Text as S import qualified Data.Text.Array as A import qualified Data.Text.Lazy as L@@ -70,6 +69,13 @@    mappend = append    {-# INLINE mappend #-} +instance String.IsString Builder where+    fromString = fromString+    {-# INLINE fromString #-}++instance Show Builder where+    show = L.unpack . toLazyText+ ------------------------------------------------------------------------  -- | /O(1)./ The empty Builder, satisfying@@ -113,7 +119,7 @@ fromText :: S.Text -> Builder fromText t@(Text arr off l)     | S.null t       = empty-    | l <= copyLimit = writeN l $ \marr o -> unsafeCopy arr off marr o l+    | l <= copyLimit = writeN l $ \marr o -> A.copyI marr o arr off (l+o)     | otherwise      = flush `append` mapBuilder (t :) {-# INLINE [1] fromText #-} @@ -133,13 +139,15 @@             | l <= 1 = do                 arr <- A.unsafeFreeze marr                 let !t = Text arr o u-                marr' <- A.unsafeNew defaultChunkSize-                ts <- inlineInterleaveST (loop marr' 0 0 defaultChunkSize s)+                marr' <- A.unsafeNew chunkSize+                ts <- inlineInterleaveST (loop marr' 0 0 chunkSize s)                 return $ t : ts             | otherwise = do                 n <- unsafeWrite marr (o+u) c                 loop marr o (u+n) (l-n) cs     in loop p0 o0 u0 l0 str+  where+    chunkSize = smallChunkSize {-# INLINE fromString #-}  -- | /O(1)./ A Builder taking a lazy 'L.Text', satisfying@@ -153,7 +161,7 @@ ------------------------------------------------------------------------  -- Our internal buffer type-data Buffer s = Buffer {-# UNPACK #-} !(A.MArray s Word16)+data Buffer s = Buffer {-# UNPACK #-} !(A.MArray s)                        {-# UNPACK #-} !Int  -- offset                        {-# UNPACK #-} !Int  -- used units                        {-# UNPACK #-} !Int  -- length left@@ -164,7 +172,7 @@ -- buffer size.  The construction work takes place if and when the -- relevant part of the lazy 'L.Text' is demanded. toLazyText :: Builder -> L.Text-toLazyText = toLazyTextWith defaultChunkSize+toLazyText = toLazyTextWith smallChunkSize  -- | /O(n)./ Extract a lazy 'L.Text' from a 'Builder', using the given -- size for the initial buffer.  The construction work takes place if@@ -199,6 +207,7 @@ withSize :: (Int -> Builder) -> Builder withSize f = Builder $ \ k buf@(Buffer _ _ _ l) ->     runBuilder (f l) k buf+{-# INLINE withSize #-}  -- | Map the resulting list of texts. mapBuilder :: ([S.Text] -> [S.Text]) -> Builder@@ -225,16 +234,16 @@ ensureFree !n = withSize $ \ l ->     if n <= l     then empty-    else flush `append'` withBuffer (const (newBuffer (max n defaultChunkSize)))+    else flush `append'` withBuffer (const (newBuffer (max n smallChunkSize))) {-# INLINE [0] ensureFree #-}  -- | Ensure that @n@ many elements are available, and then use @f@ to -- write some elements into the memory.-writeN :: Int -> (forall s. A.MArray s Word16 -> Int -> ST s ()) -> Builder+writeN :: Int -> (forall s. A.MArray s -> Int -> ST s ()) -> Builder writeN n f = ensureFree n `append'` withBuffer (writeNBuffer n f) {-# INLINE [0] writeN #-} -writeNBuffer :: Int -> (A.MArray s Word16 -> Int -> ST s ()) -> (Buffer s)+writeNBuffer :: Int -> (A.MArray s -> Int -> ST s ()) -> (Buffer s)              -> ST s (Buffer s) writeNBuffer n f (Buffer p o u l) = do     f p (o+u)@@ -247,39 +256,6 @@     return $! Buffer arr 0 0 size {-# INLINE newBuffer #-} --- | Unsafely copy the elements of an array.-unsafeCopy :: A.Elt e =>-              A.Array e -> Int -> A.MArray s e -> Int -> Int -> ST s ()-unsafeCopy src sidx dest didx count =-    assert (sidx + count <= A.length src) .-    assert (didx + count <= A.length dest) $-    copy_loop sidx didx 0-  where-    copy_loop !i !j !c-        | c >= count  = return ()-        | otherwise = do A.unsafeWrite dest j (A.unsafeIndex src i)-                         copy_loop (i+1) (j+1) (c+1)-{-# INLINE unsafeCopy #-}---- Write a character to the array, starting at the specified offset--- @i@.  Returns the number of elements written.-unsafeWrite :: A.MArray s Word16 -> Int -> Char -> ST s Int-unsafeWrite marr i c-    | n < 0x10000 = do-        assert (i >= 0) . assert (i < A.length marr) $-          A.unsafeWrite marr i (fromIntegral n)-        return 1-    | otherwise = do-        assert (i >= 0) . assert (i < A.length marr - 1) $-          A.unsafeWrite marr i lo-        A.unsafeWrite marr (i+1) hi-        return 2-    where n = ord c-          m = n - 0x10000-          lo = fromIntegral $ (m `shiftR` 10) + 0xD800-          hi = fromIntegral $ (m .&. 0x3FF) + 0xDC00-{-# INLINE unsafeWrite #-}- ------------------------------------------------------------------------ -- Some nice rules for Builder @@ -292,13 +268,13 @@  {-# RULES -"append/writeN" forall a b (f::forall s. A.MArray s Word16 -> Int -> ST s ())-                           (g::forall s. A.MArray s Word16 -> Int -> ST s ()) ws.+"append/writeN" forall a b (f::forall s. A.MArray s -> Int -> ST s ())+                           (g::forall s. A.MArray s -> Int -> ST s ()) ws.         append (writeN a f) (append (writeN b g) ws) =             append (writeN (a+b) (\marr o -> f marr o >> g marr (o+a))) ws -"writeN/writeN" forall a b (f::forall s. A.MArray s Word16 -> Int -> ST s ())-                           (g::forall s. A.MArray s Word16 -> Int -> ST s ()).+"writeN/writeN" forall a b (f::forall s. A.MArray s -> Int -> ST s ())+                           (g::forall s. A.MArray s -> Int -> ST s ()).         append (writeN a f) (writeN b g) =             writeN (a+b) (\marr o -> f marr o >> g marr (o+a)) 
Data/Text/Lazy/Encoding.hs view
@@ -1,6 +1,6 @@ -- | -- Module      : Data.Text.Lazy.Encoding--- Copyright   : (c) Bryan O'Sullivan 2009+-- Copyright   : (c) 2009, 2010 Bryan O'Sullivan -- -- License     : BSD-style -- Maintainer  : bos@serpentine.com, rtomharper@googlemail.com,@@ -17,39 +17,110 @@ module Data.Text.Lazy.Encoding     (     -- * Decoding ByteStrings to Text-    --  decodeASCII-      decodeUtf8+      decodeASCII+    , decodeUtf8+    , decodeUtf16LE+    , decodeUtf16BE+    , decodeUtf32LE+    , decodeUtf32BE+    -- ** Controllable error handling     , decodeUtf8With-    --, decodeUtf16LE-    --, decodeUtf16BE-    --, decodeUtf32LE-    --, decodeUtf32BE+    , decodeUtf16LEWith+    , decodeUtf16BEWith+    , decodeUtf32LEWith+    , decodeUtf32BEWith      -- * Encoding Text to ByteStrings     , encodeUtf8-    --, encodeUtf16LE-    --, encodeUtf16BE-    --, encodeUtf32LE-    --, encodeUtf32BE+    , encodeUtf16LE+    , encodeUtf16BE+    , encodeUtf32LE+    , encodeUtf32BE     ) where -import Data.ByteString.Lazy (ByteString)+import qualified Data.ByteString.Lazy as B import Data.Text.Encoding.Error (OnDecodeError, strictDecode)-import Data.Text.Lazy (Text)+import qualified Data.Text.Encoding as TE import qualified Data.Text.Lazy.Fusion as F+import Data.Text.Lazy.Internal (Text(..), chunk, foldrChunks) import qualified Data.Text.Lazy.Encoding.Fusion as E +-- | Decode a 'ByteString' containing 7-bit ASCII encoded text.+decodeASCII :: B.ByteString -> Text+decodeASCII bs = foldr (chunk . TE.decodeASCII) Empty (B.toChunks bs)+{-# INLINE decodeASCII #-}+ -- | Decode a 'ByteString' containing UTF-8 encoded text.-decodeUtf8With :: OnDecodeError -> ByteString -> Text+decodeUtf8With :: OnDecodeError -> B.ByteString -> Text decodeUtf8With onErr bs = F.unstream (E.streamUtf8 onErr bs) {-# INLINE decodeUtf8With #-}  -- | Decode a 'ByteString' containing UTF-8 encoded text.-decodeUtf8 :: ByteString -> Text+decodeUtf8 :: B.ByteString -> Text decodeUtf8 = decodeUtf8With strictDecode {-# INLINE decodeUtf8 #-}  -- | Encode text using UTF-8 encoding.-encodeUtf8 :: Text -> ByteString+encodeUtf8 :: Text -> B.ByteString encodeUtf8 txt = E.unstream (E.restreamUtf8 (F.stream txt)) {-# INLINE encodeUtf8 #-}++-- | Decode text from little endian UTF-16 encoding.+decodeUtf16LEWith :: OnDecodeError -> B.ByteString -> Text+decodeUtf16LEWith onErr bs = F.unstream (E.streamUtf16LE onErr bs)+{-# INLINE decodeUtf16LEWith #-}++-- | Decode text from little endian UTF-16 encoding.+decodeUtf16LE :: B.ByteString -> Text+decodeUtf16LE = decodeUtf16LEWith strictDecode+{-# INLINE decodeUtf16LE #-}++-- | Decode text from big endian UTF-16 encoding.+decodeUtf16BEWith :: OnDecodeError -> B.ByteString -> Text+decodeUtf16BEWith onErr bs = F.unstream (E.streamUtf16BE onErr bs)+{-# INLINE decodeUtf16BEWith #-}++-- | Decode text from big endian UTF-16 encoding.+decodeUtf16BE :: B.ByteString -> Text+decodeUtf16BE = decodeUtf16BEWith strictDecode+{-# INLINE decodeUtf16BE #-}++-- | Encode text using little endian UTF-16 encoding.+encodeUtf16LE :: Text -> B.ByteString+encodeUtf16LE txt = B.fromChunks (foldrChunks ((:) . TE.encodeUtf16LE) [] txt)+{-# INLINE encodeUtf16LE #-}++-- | Encode text using big endian UTF-16 encoding.+encodeUtf16BE :: Text -> B.ByteString+encodeUtf16BE txt = B.fromChunks (foldrChunks ((:) . TE.encodeUtf16BE) [] txt)+{-# INLINE encodeUtf16BE #-}++-- | Decode text from little endian UTF-32 encoding.+decodeUtf32LEWith :: OnDecodeError -> B.ByteString -> Text+decodeUtf32LEWith onErr bs = F.unstream (E.streamUtf32LE onErr bs)+{-# INLINE decodeUtf32LEWith #-}++-- | Decode text from little endian UTF-32 encoding.+decodeUtf32LE :: B.ByteString -> Text+decodeUtf32LE = decodeUtf32LEWith strictDecode+{-# INLINE decodeUtf32LE #-}++-- | Decode text from big endian UTF-32 encoding.+decodeUtf32BEWith :: OnDecodeError -> B.ByteString -> Text+decodeUtf32BEWith onErr bs = F.unstream (E.streamUtf32BE onErr bs)+{-# INLINE decodeUtf32BEWith #-}++-- | Decode text from big endian UTF-32 encoding.+decodeUtf32BE :: B.ByteString -> Text+decodeUtf32BE = decodeUtf32BEWith strictDecode+{-# INLINE decodeUtf32BE #-}++-- | Encode text using little endian UTF-32 encoding.+encodeUtf32LE :: Text -> B.ByteString+encodeUtf32LE txt = B.fromChunks (foldrChunks ((:) . TE.encodeUtf32LE) [] txt)+{-# INLINE encodeUtf32LE #-}++-- | Encode text using big endian UTF-32 encoding.+encodeUtf32BE :: Text -> B.ByteString+encodeUtf32BE txt = B.fromChunks (foldrChunks ((:) . TE.encodeUtf32BE) [] txt)+{-# INLINE encodeUtf32BE #-}
Data/Text/Lazy/Encoding/Fusion.hs view
@@ -1,8 +1,8 @@-{-# LANGUAGE BangPatterns, Rank2Types #-}+{-# LANGUAGE BangPatterns, CPP, Rank2Types #-}  -- | -- Module      : Data.Text.Lazy.Encoding.Fusion--- Copyright   : (c) Bryan O'Sullivan 2009+-- Copyright   : (c) 2009, 2010 Bryan O'Sullivan -- -- License     : BSD-style -- Maintainer  : bos@serpentine.com, rtomharper@googlemail.com, @@ -17,11 +17,11 @@     (     -- * Streaming     --  streamASCII-     streamUtf8-    --, streamUtf16LE-    --, streamUtf16BE-    --, streamUtf32LE-    --, streamUtf32BE+      streamUtf8+    , streamUtf16LE+    , streamUtf16BE+    , streamUtf32LE+    , streamUtf32BE      -- * Unstreaming     , unstream@@ -36,14 +36,19 @@ import Data.Text.Encoding.Error import Data.Text.Fusion (Step(..), Stream(..)) import Data.Text.Fusion.Size-import Data.Text.UnsafeChar (unsafeChr8)-import Data.Word (Word8)+import Data.Text.UnsafeChar (unsafeChr, unsafeChr8, unsafeChr32)+import Data.Text.UnsafeShift (shiftL)+import Data.Word (Word8, Word16, Word32) import qualified Data.Text.Encoding.Utf8 as U8+import qualified Data.Text.Encoding.Utf16 as U16+import qualified Data.Text.Encoding.Utf32 as U32 import System.IO.Unsafe (unsafePerformIO) import Foreign.ForeignPtr (withForeignPtr, ForeignPtr) import Foreign.Storable (pokeByteOff) import Data.ByteString.Internal (mallocByteString, memcpy)+#if defined(ASSERTS) import Control.Exception (assert)+#endif import qualified Data.ByteString.Internal as B  data S = S0@@ -52,7 +57,7 @@        | S3 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8        | S4 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 -data T = T {-# UNPACK #-} !ByteString {-# UNPACK #-} !S {-# UNPACK #-} !Int+data T = T !ByteString !S {-# UNPACK #-} !Int  -- | /O(n)/ Convert a lazy 'ByteString' into a 'Stream Char', using -- UTF-8 encoding.@@ -66,7 +71,7 @@           Yield (U8.chr2 a b)     (T bs S0 (i+2))       | i + 2 < len && U8.validate3 a b c =           Yield (U8.chr3 a b c)   (T bs S0 (i+3))-      | i + 4 < len && U8.validate4 a b c d =+      | i + 3 < len && U8.validate4 a b c d =           Yield (U8.chr4 a b c d) (T bs S0 (i+4))       where len = B.length ps             a = B.unsafeIndex ps i@@ -96,6 +101,174 @@     consume st             = decodeError "streamUtf8" "UTF-8" onErr Nothing st {-# INLINE [0] streamUtf8 #-} +-- | /O(n)/ Convert a 'ByteString' into a 'Stream Char', using little+-- endian UTF-16 encoding.+streamUtf16LE :: OnDecodeError -> ByteString -> Stream Char+streamUtf16LE onErr bs0 = Stream next (T bs0 S0 0) unknownSize+  where+    next (T bs@(Chunk ps _) S0 i)+      | i + 1 < len && U16.validate1 x1 =+          Yield (unsafeChr x1)         (T bs S0 (i+2))+      | i + 3 < len && U16.validate2 x1 x2 =+          Yield (U16.chr2 x1 x2)       (T bs S0 (i+4))+      where len = B.length ps+            x1   = c (idx  i)      (idx (i + 1))+            x2   = c (idx (i + 2)) (idx (i + 3))+            c w1 w2 = w1 + (w2 `shiftL` 8)+            idx = fromIntegral . B.unsafeIndex ps :: Int -> Word16+    next st@(T bs s i) =+      case s of+        S2 w1 w2       | U16.validate1 (c w1 w2)           ->+          Yield (unsafeChr (c w1 w2))   es+        S4 w1 w2 w3 w4 | U16.validate2 (c w1 w2) (c w3 w4) ->+          Yield (U16.chr2 (c w1 w2) (c w3 w4)) es+        _ -> consume st+       where es = T bs S0 i+             c :: Word8 -> Word8 -> Word16+             c w1 w2 = fromIntegral w1 + (fromIntegral w2 `shiftL` 8)+    consume (T bs@(Chunk ps rest) s i)+        | i >= B.length ps = consume (T rest s 0)+        | otherwise =+      case s of+        S0             -> next (T bs (S1 x)          (i+1))+        S1 w1          -> next (T bs (S2 w1 x)       (i+1))+        S2 w1 w2       -> next (T bs (S3 w1 w2 x)    (i+1))+        S3 w1 w2 w3    -> next (T bs (S4 w1 w2 w3 x) (i+1))+        S4 w1 w2 w3 w4 -> decodeError "streamUtf16LE" "UTF-16LE" onErr (Just w1)+                           (T bs (S3 w2 w3 w4)       (i+1))+        where x = B.unsafeIndex ps i+    consume (T Empty S0 _) = Done+    consume st             = decodeError "streamUtf16LE" "UTF-16LE" onErr Nothing st+{-# INLINE [0] streamUtf16LE #-}++-- | /O(n)/ Convert a 'ByteString' into a 'Stream Char', using big+-- endian UTF-16 encoding.+streamUtf16BE :: OnDecodeError -> ByteString -> Stream Char+streamUtf16BE onErr bs0 = Stream next (T bs0 S0 0) unknownSize+  where+    next (T bs@(Chunk ps _) S0 i)+      | i + 1 < len && U16.validate1 x1 =+          Yield (unsafeChr x1)         (T bs S0 (i+2))+      | i + 3 < len && U16.validate2 x1 x2 =+          Yield (U16.chr2 x1 x2)       (T bs S0 (i+4))+      where len = B.length ps+            x1   = c (idx  i)      (idx (i + 1))+            x2   = c (idx (i + 2)) (idx (i + 3))+            c w1 w2 = (w1 `shiftL` 8) + w2+            idx = fromIntegral . B.unsafeIndex ps :: Int -> Word16+    next st@(T bs s i) =+      case s of+        S2 w1 w2       | U16.validate1 (c w1 w2)           ->+          Yield (unsafeChr (c w1 w2))   es+        S4 w1 w2 w3 w4 | U16.validate2 (c w1 w2) (c w3 w4) ->+          Yield (U16.chr2 (c w1 w2) (c w3 w4)) es+        _ -> consume st+       where es = T bs S0 i+             c :: Word8 -> Word8 -> Word16+             c w1 w2 = (fromIntegral w1 `shiftL` 8) + fromIntegral w2+    consume (T bs@(Chunk ps rest) s i)+        | i >= B.length ps = consume (T rest s 0)+        | otherwise =+      case s of+        S0             -> next (T bs (S1 x)          (i+1))+        S1 w1          -> next (T bs (S2 w1 x)       (i+1))+        S2 w1 w2       -> next (T bs (S3 w1 w2 x)    (i+1))+        S3 w1 w2 w3    -> next (T bs (S4 w1 w2 w3 x) (i+1))+        S4 w1 w2 w3 w4 -> decodeError "streamUtf16BE" "UTF-16BE" onErr (Just w1)+                           (T bs (S3 w2 w3 w4)       (i+1))+        where x = B.unsafeIndex ps i+    consume (T Empty S0 _) = Done+    consume st             = decodeError "streamUtf16BE" "UTF-16BE" onErr Nothing st+{-# INLINE [0] streamUtf16BE #-}++-- | /O(n)/ Convert a 'ByteString' into a 'Stream Char', using big+-- endian UTF-32 encoding.+streamUtf32BE :: OnDecodeError -> ByteString -> Stream Char+streamUtf32BE onErr bs0 = Stream next (T bs0 S0 0) unknownSize+  where+    next (T bs@(Chunk ps _) S0 i)+      | i + 3 < len && U32.validate x =+          Yield (unsafeChr32 x)       (T bs S0 (i+4))+      where len = B.length ps+            x = shiftL x1 24 + shiftL x2 16 + shiftL x3 8 + x4+            x1    = idx i+            x2    = idx (i+1)+            x3    = idx (i+2)+            x4    = idx (i+3)+            idx = fromIntegral . B.unsafeIndex ps :: Int -> Word32+    next st@(T bs s i) =+      case s of+        S4 w1 w2 w3 w4 | U32.validate (c w1 w2 w3 w4) ->+          Yield (unsafeChr32 (c w1 w2 w3 w4)) es+        _ -> consume st+       where es = T bs S0 i+             c :: Word8 -> Word8 -> Word8 -> Word8 -> Word32+             c w1 w2 w3 w4 = shifted+              where+               shifted = shiftL x1 24 + shiftL x2 16 + shiftL x3 8 + x4+               x1 = fromIntegral w1+               x2 = fromIntegral w2+               x3 = fromIntegral w3+               x4 = fromIntegral w4+    consume (T bs@(Chunk ps rest) s i)+        | i >= B.length ps = consume (T rest s 0)+        | otherwise =+      case s of+        S0             -> next (T bs (S1 x)          (i+1))+        S1 w1          -> next (T bs (S2 w1 x)       (i+1))+        S2 w1 w2       -> next (T bs (S3 w1 w2 x)    (i+1))+        S3 w1 w2 w3    -> next (T bs (S4 w1 w2 w3 x) (i+1))+        S4 w1 w2 w3 w4 -> decodeError "streamUtf32BE" "UTF-32BE" onErr (Just w1)+                           (T bs (S3 w2 w3 w4)       (i+1))+        where x = B.unsafeIndex ps i+    consume (T Empty S0 _) = Done+    consume st             = decodeError "streamUtf32BE" "UTF-32BE" onErr Nothing st+{-# INLINE [0] streamUtf32BE #-}++-- | /O(n)/ Convert a 'ByteString' into a 'Stream Char', using little+-- endian UTF-32 encoding.+streamUtf32LE :: OnDecodeError -> ByteString -> Stream Char+streamUtf32LE onErr bs0 = Stream next (T bs0 S0 0) unknownSize+  where+    next (T bs@(Chunk ps _) S0 i)+      | i + 3 < len && U32.validate x =+          Yield (unsafeChr32 x)       (T bs S0 (i+4))+      where len = B.length ps+            x = shiftL x4 24 + shiftL x3 16 + shiftL x2 8 + x1+            x1    = idx i+            x2    = idx (i+1)+            x3    = idx (i+2)+            x4    = idx (i+3)+            idx = fromIntegral . B.unsafeIndex ps :: Int -> Word32+    next st@(T bs s i) =+      case s of+        S4 w1 w2 w3 w4 | U32.validate (c w1 w2 w3 w4) ->+          Yield (unsafeChr32 (c w1 w2 w3 w4)) es+        _ -> consume st+       where es = T bs S0 i+             c :: Word8 -> Word8 -> Word8 -> Word8 -> Word32+             c w1 w2 w3 w4 = shifted+              where+               shifted = shiftL x4 24 + shiftL x3 16 + shiftL x2 8 + x1+               x1 = fromIntegral w1+               x2 = fromIntegral w2+               x3 = fromIntegral w3+               x4 = fromIntegral w4+    consume (T bs@(Chunk ps rest) s i)+        | i >= B.length ps = consume (T rest s 0)+        | otherwise =+      case s of+        S0             -> next (T bs (S1 x)          (i+1))+        S1 w1          -> next (T bs (S2 w1 x)       (i+1))+        S2 w1 w2       -> next (T bs (S3 w1 w2 x)    (i+1))+        S3 w1 w2 w3    -> next (T bs (S4 w1 w2 w3 x) (i+1))+        S4 w1 w2 w3 w4 -> decodeError "streamUtf32LE" "UTF-32LE" onErr (Just w1)+                           (T bs (S3 w2 w3 w4)       (i+1))+        where x = B.unsafeIndex ps i+    consume (T Empty S0 _) = Done+    consume st             = decodeError "streamUtf32LE" "UTF-32LE" onErr Nothing st+{-# INLINE [0] streamUtf32LE #-}+ -- | /O(n)/ Convert a 'Stream' 'Word8' to a lazy 'ByteString'. unstreamChunks :: Int -> Stream Word8 -> ByteString unstreamChunks chunkSize (Stream next s0 len0) = chunk s0 (upperBound 4 len0)@@ -123,7 +296,11 @@               loop n' (off+1) s fp'             trimUp fp off = B.PS fp 0 off             copy0 :: ForeignPtr Word8 -> Int -> Int -> IO (ForeignPtr Word8)-            copy0 !src !srcLen !destLen = assert (srcLen <= destLen) $ do+            copy0 !src !srcLen !destLen =+#if defined(ASSERTS)+              assert (srcLen <= destLen) $+#endif+              do                 dest <- mallocByteString destLen                 withForeignPtr src  $ \src'  ->                     withForeignPtr dest $ \dest' ->
Data/Text/Lazy/Fusion.hs view
@@ -1,6 +1,7 @@+{-# LANGUAGE BangPatterns #-} -- | -- Module      : Data.Text.Lazy.Fusion--- Copyright   : (c) Bryan O'Sullivan 2009+-- Copyright   : (c) 2009, 2010 Bryan O'Sullivan -- -- License     : BSD-style -- Maintainer  : bos@serpentine.com, rtomharper@googlemail.com,@@ -29,7 +30,8 @@ import qualified Data.Text.Internal as I import qualified Data.Text.Array as A import Data.Text.UnsafeChar (unsafeWrite)-import Data.Text.Unsafe (iter)+import Data.Text.UnsafeShift (shiftL)+import Data.Text.Unsafe (Iter(..), iter) import Data.Int (Int64)  default(Int64)@@ -42,7 +44,7 @@     next (txt@(Chunk t@(I.Text _ _ len) ts) :*: i)         | i >= len  = next (ts :*: 0)         | otherwise = Yield c (txt :*: i+d)-        where (c,d) = iter t i+        where Iter c d = iter t i {-# INLINE [0] stream #-}  -- | /O(n)/ Convert a 'Stream Char' into a 'Text', using the given@@ -58,21 +60,21 @@                 Yield x s' -> I.Text arr 0 len `chunk` outer s''                   where (arr,(s'',len)) = A.run2 fill                         fill = do a <- A.unsafeNew unknownLength-                                  i <- unsafeWrite a 0 x-                                  inner a unknownLength s' i+                                  unsafeWrite a 0 x >>= inner a unknownLength s'                         unknownLength = 4-    inner marr len s i+    inner marr len s !i         | i + 1 >= chunkSize = return (marr, (s,i))         | i + 1 >= len       = do-            let newLen = min (len * 2) chunkSize+            let newLen = min (len `shiftL` 1) chunkSize             marr' <- A.unsafeNew newLen-            A.copy marr marr'+            A.copyM marr' 0 marr 0 len             inner marr' newLen s i         | otherwise =             case next s of               Done        -> return (marr,(s,i))               Skip s'     -> inner marr len s' i-              Yield x s'  -> unsafeWrite marr i x >>= inner marr len s'+              Yield x s'  -> do d <- unsafeWrite marr i x+                                inner marr len s' (i+d) {-# INLINE [0] unstreamChunks #-}  -- | /O(n)/ Convert a 'Stream Char' into a 'Text', using@@ -89,9 +91,9 @@ {-# RULES "LAZY STREAM stream/unstream fusion" forall s.     stream (unstream s) = s #-} --- | /O(n)/ Like 'unfoldr', 'unfoldrN64' builds a stream from a seed+-- | /O(n)/ Like 'unfoldr', 'unfoldrN' builds a stream from a seed -- value. However, the length of the result is limited by the--- first argument to 'unfoldrN64'. This function is more efficient than+-- first argument to 'unfoldrN'. This function is more efficient than -- 'unfoldr' when the length of the result is known. unfoldrN :: Int64 -> (a -> Maybe (Char,a)) -> a -> Stream Char unfoldrN n = S.unfoldrNI n
Data/Text/Lazy/IO.hs view
@@ -1,8 +1,8 @@ {-# LANGUAGE BangPatterns, CPP, RecordWildCards #-} -- | -- Module      : Data.Text.Lazy.IO--- Copyright   : (c) Bryan O'Sullivan 2009,---               (c) Simon Marlow 2009+-- Copyright   : (c) 2009, 2010 Bryan O'Sullivan,+--               (c) 2009 Simon Marlow -- License     : BSD-style -- Maintainer  : bos@serpentine.com -- Stability   : experimental@@ -44,6 +44,7 @@ import qualified Data.ByteString.Lazy.Char8 as L8 #else import Control.Exception (throw)+import Control.Monad (when) import Data.IORef (readIORef) import Data.Text.IO.Internal (hGetLineWith, readChunk) import Data.Text.Lazy.Internal (chunk, empty)@@ -52,6 +53,7 @@ import GHC.IO.Handle.Internals (augmentIOError, hClose_help,                                 wantReadableHandle, withHandle) import GHC.IO.Handle.Types (Handle__(..), HandleType(..))+import System.IO (BufferMode(..), hGetBuffering, hSetBuffering) import System.IO.Error (isEOFError) import System.IO.Unsafe (unsafeInterleaveIO) #endif@@ -76,10 +78,19 @@ #if __GLASGOW_HASKELL__ <= 610 hGetContents = fmap decodeUtf8 . L8.hGetContents #else-hGetContents h =-    wantReadableHandle "hGetContents" h $ \hh -> do-      ts <- lazyRead h-      return (hh{haType=SemiClosedHandle}, ts)+hGetContents h = do+  chooseGoodBuffering h+  wantReadableHandle "hGetContents" h $ \hh -> do+    ts <- lazyRead h+    return (hh{haType=SemiClosedHandle}, ts)++-- | Use a more efficient buffer size if we're reading in+-- block-buffered mode with the default buffer size.+chooseGoodBuffering :: Handle -> IO ()+chooseGoodBuffering h = do+  bufMode <- hGetBuffering h+  when (bufMode == BlockBuffering Nothing) $+    hSetBuffering h (BlockBuffering (Just 16384))  lazyRead :: Handle -> IO Text lazyRead h = unsafeInterleaveIO $
Data/Text/Lazy/Internal.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE BangPatterns, DeriveDataTypeable #-} -- | -- Module      : Data.Text.Lazy.Internal--- Copyright   : (c) Bryan O'Sullivan 2009+-- Copyright   : (c) 2009, 2010 Bryan O'Sullivan -- -- License     : BSD-style -- Maintainer  : bos@serpentine.com, rtomharper@googlemail.com,@@ -21,9 +21,12 @@     , foldrChunks     , foldlChunks     -- * Data type invariant and abstraction functions-    , invariant-    , checkInvariant++    -- $invariant+    , strictInvariant+    , lazyInvariant     , showStructure+     -- * Chunk allocation sizes     , defaultChunkSize     , smallChunkSize@@ -32,35 +35,43 @@  import qualified Data.Text.Internal as T import Data.Text ()+import Data.Text.UnsafeShift import Data.Typeable (Typeable)-import Data.Word (Word16) import Foreign.Storable (sizeOf)  data Text = Empty           | Chunk {-# UNPACK #-} !T.Text Text             deriving (Typeable) --- | The data type invariant: Every 'Text' is either 'Empty' or+-- $invariant+--+-- The data type invariant for lazy 'Text': Every 'Text' is either 'Empty' or -- consists of non-null 'T.Text's.  All functions must preserve this, -- and the QC properties must check this.-invariant :: Text -> Bool-invariant Empty                       = True-invariant (Chunk (T.Text _ _ len) cs) = len > 0 && invariant cs +-- | Check the invariant strictly.+strictInvariant :: Text -> Bool+strictInvariant Empty = True+strictInvariant x@(Chunk (T.Text _ _ len) cs)+    | len > 0   = strictInvariant cs+    | otherwise = error $ "Data.Text.Lazy: invariant violation: "+                  ++ showStructure x++-- | Check the invariant lazily.+lazyInvariant :: Text -> Text+lazyInvariant Empty = Empty+lazyInvariant x@(Chunk c@(T.Text _ _ len) cs)+    | len > 0   = Chunk c (lazyInvariant cs)+    | otherwise = error $ "Data.Text.Lazy: invariant violation: "+                  ++ showStructure x++-- | Display the internal structure of a lazy 'Text'. showStructure :: Text -> String showStructure Empty           = "Empty" showStructure (Chunk t Empty) = "Chunk " ++ show t ++ " Empty" showStructure (Chunk t ts)    =     "Chunk " ++ show t ++ " (" ++ showStructure ts ++ ")" --- | In a form that checks the invariant lazily.-checkInvariant :: Text -> Text-checkInvariant Empty = Empty-checkInvariant (Chunk c@(T.Text _ _ len) cs)-    | len > 0   = Chunk c (checkInvariant cs)-    | otherwise = error $ "Data.Text.Lazy: invariant violation: "-               ++ showStructure (Chunk c cs)- -- | Smart constructor for 'Chunk'. Guarantees the data type invariant. chunk :: T.Text -> Text -> Text {-# INLINE chunk #-}@@ -87,17 +98,17 @@         go !a (Chunk c cs) = go (f a c) cs {-# INLINE foldlChunks #-} --- | Currently set to 32k, less the memory management overhead.+-- | Currently set to 16 KiB, less the memory management overhead. defaultChunkSize :: Int-defaultChunkSize = 32 * k - chunkOverhead-   where k = 1024 `div` sizeOf (undefined :: Word16)+defaultChunkSize = 16384 - chunkOverhead {-# INLINE defaultChunkSize #-} --- | Currently set to 4k, less the memory management overhead.+-- | Currently set to 128 bytes, less the memory management overhead. smallChunkSize :: Int-smallChunkSize = 4 * k - chunkOverhead-   where k = 1024 `div` sizeOf (undefined :: Word16)+smallChunkSize = 128 - chunkOverhead+{-# INLINE smallChunkSize #-}  -- | The memory management overhead. Currently this is tuned for GHC only. chunkOverhead :: Int-chunkOverhead = 2 * sizeOf (undefined :: Int)+chunkOverhead = sizeOf (undefined :: Int) `shiftL` 1+{-# INLINE chunkOverhead #-}
Data/Text/Lazy/Search.hs view
@@ -2,7 +2,7 @@  -- | -- Module      : Data.Text.Lazy.Search--- Copyright   : (c) Bryan O'Sullivan 2009+-- Copyright   : (c) 2009, 2010 Bryan O'Sullivan -- -- License     : BSD-style -- Maintainer  : bos@serpentine.com, rtomharper@googlemail.com,@@ -41,7 +41,7 @@         -> [Int64] indices needle@(Chunk n ns) _haystack@(Chunk k ks)     | nlen <= 0  = []-    | nlen == 1  = scanOne (nindex 0) 0 k ks+    | nlen == 1  = indicesOne (nindex 0) 0 k ks     | otherwise  = scan 0 0 k ks   where     scan !g !i x@(T.Text _ _ l) xs@@ -81,14 +81,6 @@                   skp' | c == z    = nlen - fromIntegral g - 2                        | otherwise = skp                   xlast = xlen - 1-    scanOne c i (T.Text oarr ooff olen) os = go 0-      where-        go h | h >= olen = case os of-                             Empty      -> []-                             Chunk y ys -> scanOne c (i+fromIntegral olen) y ys-             | on == c = i + fromIntegral h : go (h+1)-             | otherwise = go (h+1)-             where on = A.unsafeIndex oarr (ooff+h)     -- | Check whether an attempt to index into the haystack at the     -- given offset would fail.     lackingHay q = go 0@@ -103,7 +95,7 @@ -- the possibility that the caller might try to access one element -- past the end. index :: T.Text -> Text -> Int64 -> Word16-index (T.Text arr off len) xs i+index (T.Text arr off len) xs !i     | j < len   = A.unsafeIndex arr (off+j)     | otherwise = case xs of                     Empty@@ -113,6 +105,19 @@                         | otherwise -> emptyError "index"                     Chunk c cs -> index c cs (i-fromIntegral len)     where j = fromIntegral i++-- | A variant of 'indices' that scans linearly for a single 'Word16'.+indicesOne :: Word16 -> Int64 -> T.Text -> Text -> [Int64]+indicesOne c = chunk+  where+    chunk !i (T.Text oarr ooff olen) os = go 0+      where+        go h | h >= olen = case os of+                             Empty      -> []+                             Chunk y ys -> chunk (i+fromIntegral olen) y ys+             | on == c = i + fromIntegral h : go (h+1)+             | otherwise = go (h+1)+             where on = A.unsafeIndex oarr (ooff+h)  -- | The number of 'Word16' values in a 'Text'. wordLength :: Text -> Int64
Data/Text/Search.hs view
@@ -40,7 +40,8 @@ import Data.Text.UnsafeShift (shiftL)  -- | /O(n+m)/ Find the offsets of all non-overlapping indices of--- @needle@ within @haystack@.+-- @needle@ within @haystack@.  The offsets returned represent+-- locations in the low-level array. -- -- In (unlikely) bad cases, this algorithm's complexity degrades -- towards /O(n*m)/.@@ -57,6 +58,8 @@     z        = nindex nlast     nindex k = A.unsafeIndex narr (noff+k)     hindex k = A.unsafeIndex harr (hoff+k)+    hindex' k | k == hlen  = 0+              | otherwise = A.unsafeIndex harr (hoff+k)     (mask :: Word64) :*: skip  = buildTable 0 0 (nlen-2)     buildTable !i !msk !skp         | i >= nlast           = (msk .|. swizzle z) :*: skp@@ -77,7 +80,7 @@               delta | nextInPattern = nlen + 1                     | c == z        = skip + 1                     | otherwise     = 1-              nextInPattern         = mask .&. swizzle (hindex (i+nlen)) == 0+              nextInPattern         = mask .&. swizzle (hindex' (i+nlen)) == 0     scanOne c = loop 0         where loop !i | i >= hlen     = []                       | hindex i == c = i : loop (i+1)
Data/Text/Unsafe.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE CPP, MagicHash, UnboxedTuples #-} -- | -- Module      : Data.Text.Unsafe--- Copyright   : (c) Bryan O'Sullivan 2009+-- Copyright   : (c) 2009, 2010 Bryan O'Sullivan -- License     : BSD-style -- Maintainer  : bos@serpentine.com, rtomharper@googlemail.com, --               duncan@haskell.org@@ -14,14 +14,18 @@     (       inlineInterleaveST     , inlinePerformIO+    , Iter(..)     , iter     , iter_     , reverseIter     , unsafeHead     , unsafeTail+    , lengthWord16     ) where      +#if defined(ASSERTS) import Control.Exception (assert)+#endif import Data.Text.Encoding.Utf16 (chr2) import Data.Text.Internal (Text(..)) import Data.Text.UnsafeChar (unsafeChr)@@ -40,11 +44,11 @@ -- omits the check for the empty case, so there is an obligation on -- the programmer to provide a proof that the 'Text' is non-empty. unsafeHead :: Text -> Char-unsafeHead (Text arr off len)+unsafeHead (Text arr off _len)     | m < 0xD800 || m > 0xDBFF = unsafeChr m     | otherwise                = chr2 m n-    where m = assert (len > 0) $ A.unsafeIndex arr off-          n = assert (len > 1) $ A.unsafeIndex arr (off+1)+    where m = A.unsafeIndex arr off+          n = A.unsafeIndex arr (off+1) {-# INLINE unsafeHead #-}  -- | /O(1)/ A variant of 'tail' for non-empty 'Text'. 'unsafeHead'@@ -52,42 +56,47 @@ -- the programmer to provide a proof that the 'Text' is non-empty. unsafeTail :: Text -> Text unsafeTail t@(Text arr off len) =-    assert (d <= len) $ Text arr (off+d) (len-d)+#if defined(ASSERTS)+    assert (d <= len) $+#endif+    Text arr (off+d) (len-d)   where d = iter_ t 0 {-# INLINE unsafeTail #-} --- | /O(1)/ Iterate one step forwards through a UTF-16 array,--- returning the current character and the delta to add to give the--- next offset to iterate at.-iter :: Text -> Int -> (Char,Int)-iter (Text arr off len) i-    | m < 0xD800 || m > 0xDBFF = (unsafeChr m, 1)-    | otherwise                = (chr2 m n,    2)-  where m = assert (i < len)     $ A.unsafeIndex arr j-        n = assert (i + 1 < len) $ A.unsafeIndex arr k-        j = assert (i >= 0)      $ off + i-        k =                        j + 1+data Iter = Iter {-# UNPACK #-} !Char {-# UNPACK #-} !Int++-- | /O(1)/ Iterate (unsafely) one step forwards through a UTF-16+-- array, returning the current character and the delta to add to give+-- the next offset to iterate at.+iter :: Text -> Int -> Iter+iter (Text arr off _len) i+    | m < 0xD800 || m > 0xDBFF = Iter (unsafeChr m) 1+    | otherwise                = Iter (chr2 m n) 2+  where m = A.unsafeIndex arr j+        n = A.unsafeIndex arr k+        j = off + i+        k = j + 1 {-# INLINE iter #-}  -- | /O(1)/ Iterate one step through a UTF-16 array, returning the -- delta to add to give the next offset to iterate at. iter_ :: Text -> Int -> Int-iter_ (Text arr off len) i | m < 0xD800 || m > 0xDBFF = 1-                           | otherwise                = 2-  where m = assert (i >= 0 && i < len) $ A.unsafeIndex arr (off+i)+iter_ (Text arr off _len) i | m < 0xD800 || m > 0xDBFF = 1+                            | otherwise                = 2+  where m = A.unsafeIndex arr (off+i) {-# INLINE iter_ #-}  -- | /O(1)/ Iterate one step backwards through a UTF-16 array, -- returning the current character and the delta to add (i.e. a -- negative number) to give the next offset to iterate at. reverseIter :: Text -> Int -> (Char,Int)-reverseIter (Text arr off len) i+reverseIter (Text arr off _len) i     | m < 0xDC00 || m > 0xDFFF = (unsafeChr m, -1)     | otherwise                = (chr2 n m,    -2)-  where m = assert (i < len)    $ A.unsafeIndex arr j-        n = assert (i - 1 >= 0) $ A.unsafeIndex arr k-        j = assert (i >= 0)     $ off + i-        k =                       j - 1+  where m = A.unsafeIndex arr j+        n = A.unsafeIndex arr k+        j = off + i+        k = j - 1 {-# INLINE reverseIter #-}  -- | Just like unsafePerformIO, but we inline it. Big performance gains as@@ -116,3 +125,10 @@ inlineInterleaveST (ST m) = ST $ \ s ->     let r = case m s of (# _, res #) -> res in (# s, r #) {-# INLINE inlineInterleaveST #-}++-- | /O(1)/ Return the length of a 'Text' in units of 'Word16'.  This+-- is useful for sizing a target array appropriately before using+-- 'unsafeCopyToPtr'.+lengthWord16 :: Text -> Int+lengthWord16 (Text _arr _off len) = len+{-# INLINE lengthWord16 #-}
Data/Text/UnsafeChar.hs view
@@ -1,10 +1,10 @@-{-# LANGUAGE MagicHash #-}+{-# LANGUAGE CPP, MagicHash #-}  -- | -- Module      : Data.Text.UnsafeChar--- Copyright   : (c) Tom Harper 2008-2009,---               (c) Bryan O'Sullivan 2009,---               (c) Duncan Coutts 2009+-- Copyright   : (c) 2008, 2009 Tom Harper,+--               (c) 2009, 2010 Bryan O'Sullivan,+--               (c) 2009 Duncan Coutts -- -- License     : BSD-style -- Maintainer  : bos@serpentine.com, rtomharper@googlemail.com,@@ -15,22 +15,28 @@ -- Fast character manipulation functions. module Data.Text.UnsafeChar     (-      unsafeChr+      ord+    , unsafeChr     , unsafeChr8     , unsafeChr32     , unsafeWrite     -- , unsafeWriteRev     ) where +#ifdef ASSERTS import Control.Exception (assert)+#endif import Control.Monad.ST (ST) import Data.Bits ((.&.))-import Data.Char (ord) import Data.Text.UnsafeShift (shiftR)-import GHC.Exts (Char(..), chr#, word2Int#)+import GHC.Exts (Char(..), Int(..), chr#, ord#, word2Int#) import GHC.Word (Word8(..), Word16(..), Word32(..)) import qualified Data.Text.Array as A +ord :: Char -> Int+ord (C# c#) = I# (ord# c#)+{-# INLINE ord #-}+ unsafeChr :: Word16 -> Char unsafeChr (W16# w#) = C# (chr# (word2Int# w#)) {-# INLINE unsafeChr #-}@@ -43,17 +49,23 @@ unsafeChr32 (W32# w#) = C# (chr# (word2Int# w#)) {-# INLINE unsafeChr32 #-} -unsafeWrite :: A.MArray s Word16 -> Int -> Char -> ST s Int+-- | Write a character into the array at the given offset.  Returns+-- the number of 'Word16's written.+unsafeWrite :: A.MArray s -> Int -> Char -> ST s Int unsafeWrite marr i c     | n < 0x10000 = do-        assert (i >= 0) . assert (i < A.length marr) $-          A.unsafeWrite marr i (fromIntegral n)-        return (i+1)+#if defined(ASSERTS)+        assert (i >= 0) . assert (i < A.length marr) $ return ()+#endif+        A.unsafeWrite marr i (fromIntegral n)+        return 1     | otherwise = do-        assert (i >= 0) . assert (i < A.length marr - 1) $-          A.unsafeWrite marr i lo+#if defined(ASSERTS)+        assert (i >= 0) . assert (i < A.length marr - 1) $ return ()+#endif+        A.unsafeWrite marr i lo         A.unsafeWrite marr (i+1) hi-        return (i+2)+        return 2     where n = ord c           m = n - 0x10000           lo = fromIntegral $ (m `shiftR` 10) + 0xD800
tests/Benchmarks.hs view
@@ -8,7 +8,7 @@ import Criterion.Main import Data.Char import Data.Monoid (mappend, mempty)-import qualified Codec.Binary.UTF8.Generic as UTF8+import qualified Data.ByteString.UTF8 as UTF8 import qualified Data.Text as TS import qualified Data.Text.IO as TS import qualified Data.Text.Lazy as TL@@ -113,6 +113,13 @@       , bench "bs" $ nf (BS.drop (bsa_len `div` 3)) bsa       , bench "bl" $ nf (BL.drop (bla_len `div` 3)) bla       , bench "l" $ nf (L.drop (la_len `div` 3)) la+      ],+      bgroup "encode" [+        bench "ts" $ nf TS.encodeUtf8 tsa+      , bench "tl" $ nf TL.encodeUtf8 tla+      , bench "bs" $ nf BS.pack la+      , bench "bl" $ nf BL.pack la+      , bench "l" $ nf UTF8.fromString la       ],       bgroup "filter" [         bench "ts" $ nf (TS.filter p0) tsa
tests/Makefile view
@@ -1,11 +1,13 @@ version := $(shell awk '/^version:/{print $$2}' ../text.cabal) ghc := ghc ghc-opt-flags = -O0-ghc-base-flags := -funbox-strict-fields -package criterion \+ghc-base-flags := -funbox-strict-fields -hide-all-packages \+	-package base -package mtl -package random -package directory \+	-package criterion -package deepseq -DASSERTS -DHAVE_DEEPSEQ \ 	-package bytestring -ignore-package text \ 	-fno-ignore-asserts-ghc-test-flags := -package QuickCheck -package test-framework \-	-package test-framework-quickcheck -package test-framework-hunit \+ghc-test-flags := -package QuickCheck -package test-framework -package deepseq \+	-package test-framework-quickcheck2 -package test-framework-hunit \ 	-package HUnit ghc-base-flags += -Wall -fno-warn-orphans -fno-warn-missing-signatures ghc-flags := $(ghc-base-flags) -i../dist/build -package-name text-$(version)@@ -30,36 +32,50 @@ 	cd .. && cabal build endif -Properties.o qc qc-hpc: ghc-flags += $(ghc-test-flags)-Properties.o: QuickCheckUtils.o SlowFunctions.o+Properties.o Regressions.o qc qc-hpc: ghc-flags += $(ghc-test-flags)+Properties.o: QuickCheckUtils.o SlowFunctions.o TestUtils.o  QuickCheckUtils.o: $(lib) -qc: Properties.o QuickCheckUtils.o SlowFunctions.o+qc: Properties.o QuickCheckUtils.o SlowFunctions.o TestUtils.o 	$(ghc) $(ghc-flags) -threaded -o $@ $^ $(lib)  sb: SearchBench.o SlowFunctions.o 	$(ghc) $(ghc-flags) -threaded -o $@ $^ $(lib) -qc-hpc: Properties.hs QuickCheckUtils.hs $(lib-srcs:%=../%)+qc-hpc: Properties.hs QuickCheckUtils.hs SlowFunctions.hs TestUtils.hs $(lib-srcs:%=../%) 	-mkdir -p hpcdir 	@rm -f $@.tix-	$(ghc) $(ghc-hpc-flags) $(ghc-opt-flags) -ihpcdir \+	$(ghc) $(ghc-hpc-flags) $(ghc-test-flags) $(ghc-opt-flags) -ihpcdir \ 	  --make -threaded -o $@ $< -regressions: Regressions.o-	$(ghc) $(ghc-test-flags) -o $@ $^ $(lib)+stdio-hpc: StdioCoverage.hs $(lib-srcs:%=../%)+	-mkdir -p hpcdir+	@rm -f $@.tix+	$(ghc) $(ghc-hpc-flags) $(ghc-test-flags) $(ghc-opt-flags) -ihpcdir \+	  --make -threaded -o $@ $< -coverage: qc-hpc-html/hpc_index.html+coverage: coverage-html/hpc_index.html -qc-hpc-html/hpc_index.html: qc-hpc-	./qc-hpc -a 100 +RTS -N2-	hpc markup qc-hpc --exclude=Main --exclude=Properties \-	  --exclude=Data.Text.Fusion.CaseMapping \+coverage-html/hpc_index.html: qc-hpc+	@rm -f qc-hpc.tix stdio-hpc.tix coverage.tix+	./qc-hpc -a 100 +RTS -N+	bash ./cover-stdio.sh+	hpc combine --output=coverage.tix --exclude=Main \+	  qc-hpc.tix stdio-hpc.tix+	hpc markup coverage --exclude=Main --exclude=Properties --exclude=Main \+	  --exclude=Data.Text.Fusion.CaseMapping --exclude StdioCoverage \+	  --exclude=SlowFunctions --exclude=TestUtils \ 	  --exclude=QuickCheckUtils --srcdir=.. --srcdir=. --destdir=$(dir $@)+	@echo xdg-open $@ +Regressions.o: TestUtils.o++regressions: Regressions.o TestUtils.o+	$(ghc) $(ghc-test-flags) -o $@ $^ $(lib)+ Benchmarks.o: ghc-opt-flags = -O-bm Benchmarks.o: ghc-flags += -hide-package transformers -package utf8-string+bm Benchmarks.o: ghc-flags += -package utf8-string bm: Benchmarks.o 	$(ghc) $(ghc-flags) -o $@ $^ $(lib) @@ -78,4 +94,4 @@ 	curl -O http://projects.haskell.org/text/text-testdata.tar.bz2  clean:-	-rm -rf *.o *.hi *.tix bm qc qc-hpc hpcdir .hpc qc-hpc-html+	-rm -rf *.o *.hi *.tix bm qc qc-hpc stdio-hpc hpcdir .hpc coverage-html
tests/Properties.hs view
@@ -2,7 +2,8 @@              ScopedTypeVariables, TypeSynonymInstances #-} {-# OPTIONS_GHC -fno-enable-rewrite-rules #-} -import Test.QuickCheck hiding (evaluate)+import Test.QuickCheck+import Test.QuickCheck.Monadic import Text.Show.Functions ()  import qualified Data.Bits as Bits (shiftL, shiftR)@@ -11,12 +12,18 @@ import Data.String (fromString) import Debug.Trace (trace) import Control.Arrow ((***), second)+import Control.DeepSeq import Data.Word (Word8, Word16, Word32) import qualified Data.Text as T+import qualified Data.Text.IO as T import qualified Data.Text.Lazy as TL+import qualified Data.Text.Lazy.IO as TL+import qualified Data.Text.Lazy.Internal as TL import qualified Data.Text.Lazy.Builder as TB import qualified Data.Text.Encoding as E-import Control.Exception (SomeException, evaluate, try)+import Data.Text.Encoding.Error+import Control.Exception (SomeException, bracket, catch, evaluate, try)+import Data.Text.Foreign import qualified Data.Text.Fusion as S import qualified Data.Text.Fusion.Common as S import Data.Text.Fusion.Size@@ -24,15 +31,17 @@ import qualified Data.Text.Lazy.Fusion as SL import qualified Data.Text.UnsafeShift as U import qualified Data.List as L-import Prelude hiding (replicate)+import Prelude hiding (catch, replicate)+import System.IO import System.IO.Unsafe (unsafePerformIO) import Test.Framework (defaultMain, testGroup)-import Test.Framework.Providers.QuickCheck (testProperty)+import Test.Framework.Providers.QuickCheck2 (testProperty) import Data.Text.Search (indices) import qualified Data.Text.Lazy.Search as S (indices) import qualified SlowFunctions as Slow -import QuickCheckUtils (NotEmpty(..), small)+import QuickCheckUtils (NotEmpty(..), genUnicode, small, unsquare)+import TestUtils (withRedirect, withTempFile)  -- Ensure that two potentially bottom values (in the sense of crashing -- for some inputs, not looping infinitely) either both crash, or both@@ -59,16 +68,48 @@ tl_unstreamChunks x = f 11 x == f 1000 x     where f n = SL.unstreamChunks n . S.streamList tl_chunk_unchunk    = (TL.fromChunks . TL.toChunks) `eq` id+tl_from_to_strict   = (TL.fromStrict . TL.toStrict) `eq` id -t_ascii t           = E.decodeASCII (E.encodeUtf8 a) == a-    where a              = T.map (\c -> chr (ord c `mod` 128)) t-t_utf8              = (E.decodeUtf8 . E.encodeUtf8) `eq` id-tl_utf8             = (EL.decodeUtf8 . EL.encodeUtf8) `eq` id-t_utf16LE           = (E.decodeUtf16LE . E.encodeUtf16LE) `eq` id-t_utf16BE           = (E.decodeUtf16BE . E.encodeUtf16BE) `eq` id-t_utf32LE           = (E.decodeUtf32LE . E.encodeUtf32LE) `eq` id-t_utf32BE           = (E.decodeUtf32BE . E.encodeUtf32BE) `eq` id+t_ascii t    = E.decodeASCII (E.encodeUtf8 a) == a+    where a  = T.map (\c -> chr (ord c `mod` 128)) t+tl_ascii t   = EL.decodeASCII (EL.encodeUtf8 a) == a+    where a  = TL.map (\c -> chr (ord c `mod` 128)) t+t_utf8       = forAll genUnicode $ (E.decodeUtf8 . E.encodeUtf8) `eq` id+tl_utf8      = forAll genUnicode $ (EL.decodeUtf8 . EL.encodeUtf8) `eq` id+t_utf16LE    = forAll genUnicode $ (E.decodeUtf16LE . E.encodeUtf16LE) `eq` id+tl_utf16LE   = forAll genUnicode $ (EL.decodeUtf16LE . EL.encodeUtf16LE) `eq` id+t_utf16BE    = forAll genUnicode $ (E.decodeUtf16BE . E.encodeUtf16BE) `eq` id+tl_utf16BE   = forAll genUnicode $ (EL.decodeUtf16BE . EL.encodeUtf16BE) `eq` id+t_utf32LE    = forAll genUnicode $ (E.decodeUtf32LE . E.encodeUtf32LE) `eq` id+tl_utf32LE   = forAll genUnicode $ (EL.decodeUtf32LE . EL.encodeUtf32LE) `eq` id+t_utf32BE    = forAll genUnicode $ (E.decodeUtf32BE . E.encodeUtf32BE) `eq` id+tl_utf32BE   = forAll genUnicode $ (EL.decodeUtf32BE . EL.encodeUtf32BE) `eq` id +data DecodeErr = DE String OnDecodeError++instance Show DecodeErr where+    show (DE d _) = "DE " ++ d++instance CoArbitrary Word8 where+    coarbitrary = coarbitraryIntegral++instance Arbitrary DecodeErr where+    arbitrary = oneof [ return $ DE "lenient" lenientDecode+                      , return $ DE "ignore" ignore+                      , return $ DE "strict" strictDecode+                      , DE "replace" `fmap` arbitrary ]++-- This is a poor attempt to ensure that the error handling paths on+-- decode are exercised in some way.  Proper testing would be rather+-- more involved.+t_utf8_err (DE _ de) bs = monadicIO $ do+  l <- run $ let len = T.length (E.decodeUtf8With de bs)+             in (len `seq` return (Right len)) `catch`+                (\(e::UnicodeException) -> return (Left e))+  case l of+    Left err -> assert $ length (show err) >= 0+    Right n  -> assert $ n >= 0+ class Stringy s where     packS    :: String -> s     unpackS  :: s -> String@@ -95,7 +136,8 @@     packSChunkSize k = SL.unstreamChunks k . S.streamList     packS    = TL.pack     unpackS  = TL.unpack-    splitAtS = TL.splitAt . fromIntegral+    splitAtS = ((TL.lazyInvariant *** TL.lazyInvariant) .) .+               TL.splitAt . fromIntegral  -- Do two functions give the same answer? eq :: (Eq a, Show a) => (t -> a) -> (t -> a) -> t -> Bool@@ -116,14 +158,9 @@           m | l == 0    = n             | otherwise = n `mod` l           n             = fromIntegral w-          eql d a b | a =^= b   = True-                    | otherwise = trace (d ++ ": " ++ show a ++ " /= " ++ show b) False---- For tests that have O(n^2) running times or input sizes, resize--- their inputs to the square root of the originals.-unsquare :: (Arbitrary a, Show a, Testable b) => (a -> b) -> Property-unsquare = forAll . sized $ \n -> resize (smallish n) arbitrary-    where smallish = round . (sqrt :: Double -> Double) . fromIntegral+          eql d a b+            | a =^= b   = True+            | otherwise = trace (d ++ ": " ++ show a ++ " /= " ++ show b) False  s_Eq s            = (s==)    `eq` ((S.streamList s==) . S.streamList)     where _types = s :: String@@ -145,8 +182,8 @@ tl_Show           = show     `eq` (show . TL.pack) t_mappend s       = mappend s`eqP` (unpackS . mappend (T.pack s)) tl_mappend s      = mappend s`eqP` (unpackS . mappend (TL.pack s))-t_mconcat         = unsquare (mconcat `eq` (unpackS . mconcat . L.map T.pack))-tl_mconcat        = unsquare (mconcat `eq` (unpackS . mconcat . L.map TL.pack))+t_mconcat         = mconcat `eq` (unpackS . mconcat . L.map T.pack)+tl_mconcat        = mconcat `eq` (unpackS . mconcat . L.map TL.pack) t_IsString        = fromString  `eqP` (T.unpack . fromString) tl_IsString       = fromString  `eqP` (TL.unpack . fromString) @@ -159,15 +196,18 @@ t_snoc x          = (++ [x]) `eqP` (unpackS . (flip T.snoc) x) tl_snoc x         = (++ [x]) `eqP` (unpackS . (flip TL.snoc) x) s_append s        = (s++)    `eqP` (unpackS . S.append (S.streamList s))-s_append_s s      = (s++)    `eqP` (unpackS . S.unstream . S.append (S.streamList s))-sf_append p s     = (L.filter p s++) `eqP` (unpackS . S.append (S.filter p $ S.streamList s))+s_append_s s      = (s++)    `eqP`+                    (unpackS . S.unstream . S.append (S.streamList s))+sf_append p s     = (L.filter p s++) `eqP`+                    (unpackS . S.append (S.filter p $ S.streamList s)) t_append s        = (s++)    `eqP` (unpackS . T.append (packS s))  uncons (x:xs) = Just (x,xs) uncons _      = Nothing  s_uncons          = uncons   `eqP` (fmap (second unpackS) . S.uncons)-sf_uncons p       = (uncons . L.filter p) `eqP` (fmap (second unpackS) . S.uncons . S.filter p)+sf_uncons p       = (uncons . L.filter p) `eqP`+                    (fmap (second unpackS) . S.uncons . S.filter p) t_uncons          = uncons   `eqP` (fmap (second unpackS) . T.uncons) tl_uncons         = uncons   `eqP` (fmap (second unpackS) . TL.uncons) s_head            = head   `eqP` S.head@@ -194,29 +234,39 @@ tl_null           = null   `eqP` TL.null s_length          = length `eqP` S.length sf_length p       = (length . L.filter p) `eqP` (S.length . S.filter p)+sl_length         = (fromIntegral . length) `eqP` SL.length t_length          = length `eqP` T.length-tl_length         = length `eqP` (fromIntegral . TL.length)+tl_length         = L.genericLength `eqP` TL.length+t_compareLength t = (compare (T.length t)) `eq` T.compareLength t+tl_compareLength t= (compare (TL.length t)) `eq` TL.compareLength t  s_map f           = map f  `eqP` (unpackS . S.map f) s_map_s f         = map f  `eqP` (unpackS . S.unstream . S.map f) sf_map p f        = (map f . L.filter p)  `eqP` (unpackS . S.map f . S.filter p) t_map f           = map f  `eqP` (unpackS . T.map f) tl_map f          = map f  `eqP` (unpackS . TL.map f)-t_intercalate c   = unsquare (L.intercalate c `eq` (unpackS . T.intercalate (packS c) . map packS))-tl_intercalate c  = unsquare (L.intercalate c `eq` (unpackS . TL.intercalate (TL.pack c) . map TL.pack))-s_intersperse c   = L.intersperse c `eqP` (unpackS . S.intersperse c)-s_intersperse_s c = L.intersperse c `eqP` (unpackS . S.unstream . S.intersperse c)-sf_intersperse p c= (L.intersperse c . L.filter p) `eqP` (unpackS . S.intersperse c . S.filter p)+t_intercalate c   = L.intercalate c `eq`+                    (unpackS . T.intercalate (packS c) . map packS)+tl_intercalate c  = L.intercalate c `eq`+                    (unpackS . TL.intercalate (TL.pack c) . map TL.pack)+s_intersperse c   = L.intersperse c `eqP`+                    (unpackS . S.intersperse c)+s_intersperse_s c = L.intersperse c `eqP`+                    (unpackS . S.unstream . S.intersperse c)+sf_intersperse p c= (L.intersperse c . L.filter p) `eqP`+                   (unpackS . S.intersperse c . S.filter p) t_intersperse c   = L.intersperse c `eqP` (unpackS . T.intersperse c) tl_intersperse c  = L.intersperse c `eqP` (unpackS . TL.intersperse c)-t_transpose       = unsquare (L.transpose `eq` (map unpackS . T.transpose . map packS))-tl_transpose      = unsquare (L.transpose `eq` (map unpackS . TL.transpose . map TL.pack))+t_transpose       = L.transpose `eq` (map unpackS . T.transpose . map packS)+tl_transpose      = L.transpose `eq` (map unpackS . TL.transpose . map TL.pack) t_reverse         = L.reverse `eqP` (unpackS . T.reverse) tl_reverse        = L.reverse `eqP` (unpackS . TL.reverse) t_reverse_short n = L.reverse `eqP` (unpackS . S.reverse . shorten n . S.stream) -t_replace s d     = (L.intercalate d . split s) `eqP` (unpackS . T.replace (T.pack s) (T.pack d))-tl_replace s d     = (L.intercalate d . split s) `eqP` (unpackS . TL.replace (TL.pack s) (TL.pack d))+t_replace s d     = (L.intercalate d . split s) `eqP`+                    (unpackS . T.replace (T.pack s) (T.pack d))+tl_replace s d     = (L.intercalate d . split s) `eqP`+                     (unpackS . TL.replace (TL.pack s) (TL.pack d))  split :: (Eq a) => [a] -> [a] -> [[a]] split pat src0@@ -249,25 +299,47 @@ tl_toUpper_upper t = p (TL.toUpper t) >= p t     where p = TL.length . TL.filter isUpper -justifyLeft k c s  = s ++ L.replicate (k - length s) c-justifyRight m n s = L.replicate (m - length s) n ++ s+justifyLeft k c xs  = xs ++ L.replicate (k - length xs) c+justifyRight m n xs = L.replicate (m - length xs) n ++ xs+center k c xs+    | len >= k  = xs+    | otherwise = L.replicate l c ++ xs ++ L.replicate r c+   where len = length xs+         d   = k - len+         r   = d `div` 2+         l   = d - r -s_justifyLeft k c = justifyLeft k c `eqP` (unpackS . S.justifyLeftI k c)-s_justifyLeft_s k c = justifyLeft k c `eqP` (unpackS . S.unstream . S.justifyLeftI k c)-sf_justifyLeft p k c =-    (justifyLeft k c . L.filter p) `eqP` (unpackS . S.justifyLeftI k c . S.filter p)-t_justifyLeft k c = justifyLeft k c `eqP` (unpackS . T.justifyLeft k c)-tl_justifyLeft k c = justifyLeft k c `eqP` (unpackS . TL.justifyLeft (fromIntegral k) c)-t_justifyRight k c = justifyRight k c `eqP` (unpackS . T.justifyRight k c)-tl_justifyRight k c = justifyRight k c `eqP` (unpackS . TL.justifyRight (fromIntegral k) c)+s_justifyLeft k c = justifyLeft j c `eqP` (unpackS . S.justifyLeftI j c)+    where j = fromIntegral (k :: Word8)+s_justifyLeft_s k c = justifyLeft j c `eqP`+                      (unpackS . S.unstream . S.justifyLeftI j c)+    where j = fromIntegral (k :: Word8)+sf_justifyLeft p k c = (justifyLeft j c . L.filter p) `eqP`+                       (unpackS . S.justifyLeftI j c . S.filter p)+    where j = fromIntegral (k :: Word8)+t_justifyLeft k c = justifyLeft j c `eqP` (unpackS . T.justifyLeft j c)+    where j = fromIntegral (k :: Word8)+tl_justifyLeft k c = justifyLeft j c `eqP`+                     (unpackS . TL.justifyLeft (fromIntegral j) c)+    where j = fromIntegral (k :: Word8)+t_justifyRight k c = justifyRight j c `eqP` (unpackS . T.justifyRight j c)+    where j = fromIntegral (k :: Word8)+tl_justifyRight k c = justifyRight j c `eqP`+                      (unpackS . TL.justifyRight (fromIntegral j) c)+    where j = fromIntegral (k :: Word8)+t_center k c = center j c `eqP` (unpackS . T.center j c)+    where j = fromIntegral (k :: Word8)+tl_center k c = center j c `eqP` (unpackS . TL.center (fromIntegral j) c)+    where j = fromIntegral (k :: Word8) -sf_foldl p f z     = (L.foldl f z . L.filter p)  `eqP` (S.foldl f z . S.filter p)+sf_foldl p f z    = (L.foldl f z . L.filter p) `eqP` (S.foldl f z . S.filter p)     where _types  = f :: Char -> Char -> Char t_foldl f z       = L.foldl f z  `eqP` (T.foldl f z)     where _types  = f :: Char -> Char -> Char tl_foldl f z      = L.foldl f z  `eqP` (TL.foldl f z)     where _types  = f :: Char -> Char -> Char-sf_foldl' p f z   = (L.foldl' f z . L.filter p) `eqP` (S.foldl' f z . S.filter p)+sf_foldl' p f z   = (L.foldl' f z . L.filter p) `eqP`+                    (S.foldl' f z . S.filter p)     where _types  = f :: Char -> Char -> Char t_foldl' f z      = L.foldl' f z `eqP` T.foldl' f z     where _types  = f :: Char -> Char -> Char@@ -289,13 +361,17 @@ t_foldr1 f        = L.foldr1 f   `eqP` T.foldr1 f tl_foldr1 f       = L.foldr1 f   `eqP` TL.foldr1 f -s_concat_s        = unsquare (L.concat `eq` (unpackS . S.unstream . S.concat . map packS))-sf_concat p       = unsquare ((L.concat . map (L.filter p)) `eq` (unpackS . S.concat . map (S.filter p . packS)))-t_concat          = unsquare (L.concat `eq` (unpackS . T.concat . map packS))-tl_concat         = unsquare (L.concat `eq` (unpackS . TL.concat . map TL.pack))-sf_concatMap p f  = unsquare ((L.concatMap f . L.filter p) `eqP` (unpackS . S.concatMap (packS . f) . S.filter p))-t_concatMap f     = unsquare (L.concatMap f `eqP` (unpackS . T.concatMap (packS . f)))-tl_concatMap f    = unsquare (L.concatMap f `eqP` (unpackS . TL.concatMap (TL.pack . f)))+s_concat_s        = L.concat `eq` (unpackS . S.unstream . S.concat . map packS)+sf_concat p       = (L.concat . map (L.filter p)) `eq`+                    (unpackS . S.concat . map (S.filter p . packS))+t_concat          = L.concat `eq` (unpackS . T.concat . map packS)+tl_concat         = L.concat `eq` (unpackS . TL.concat . map TL.pack)+sf_concatMap p f  = unsquare $ (L.concatMap f . L.filter p) `eqP`+                               (unpackS . S.concatMap (packS . f) . S.filter p)+t_concatMap f     = unsquare $+                    L.concatMap f `eqP` (unpackS . T.concatMap (packS . f))+tl_concatMap f    = unsquare $+                    L.concatMap f `eqP` (unpackS . TL.concatMap (TL.pack . f)) sf_any q p        = (L.any p . L.filter q) `eqP` (S.any p . S.filter q) t_any p           = L.any p       `eqP` T.any p tl_any p          = L.any p       `eqP` TL.any p@@ -309,7 +385,8 @@ t_minimum         = L.minimum     `eqP` T.minimum tl_minimum        = L.minimum     `eqP` TL.minimum -sf_scanl p f z    = (L.scanl f z . L.filter p) `eqP` (unpackS . S.scanl f z . S.filter p)+sf_scanl p f z    = (L.scanl f z . L.filter p) `eqP`+                    (unpackS . S.scanl f z . S.filter p) t_scanl f z       = L.scanl f z   `eqP` (unpackS . T.scanl f z) tl_scanl f z      = L.scanl f z   `eqP` (unpackS . TL.scanl f z) t_scanl1 f        = L.scanl1 f    `eqP` (unpackS . T.scanl1 f)@@ -319,30 +396,39 @@ t_scanr1 f        = L.scanr1 f    `eqP` (unpackS . T.scanr1 f) tl_scanr1 f       = L.scanr1 f    `eqP` (unpackS . TL.scanr1 f) -t_mapAccumL f z   = unsquare (L.mapAccumL f z `eqP` (second unpackS . T.mapAccumL f z))-    where _types = f :: Int -> Char -> (Int,Char)-tl_mapAccumL f z  = unsquare (L.mapAccumL f z `eqP` (second unpackS . TL.mapAccumL f z))-    where _types = f :: Int -> Char -> (Int,Char)-t_mapAccumR f z   = unsquare (L.mapAccumR f z `eqP` (second unpackS . T.mapAccumR f z))-    where _types = f :: Int -> Char -> (Int,Char)-tl_mapAccumR f z   = unsquare (L.mapAccumR f z `eqP` (second unpackS . TL.mapAccumR f z))-    where _types = f :: Int -> Char -> (Int,Char)+t_mapAccumL f z   = L.mapAccumL f z `eqP` (second unpackS . T.mapAccumL f z)+    where _types  = f :: Int -> Char -> (Int,Char)+tl_mapAccumL f z  = L.mapAccumL f z `eqP` (second unpackS . TL.mapAccumL f z)+    where _types  = f :: Int -> Char -> (Int,Char)+t_mapAccumR f z   = L.mapAccumR f z `eqP` (second unpackS . T.mapAccumR f z)+    where _types  = f :: Int -> Char -> (Int,Char)+tl_mapAccumR f z  = L.mapAccumR f z `eqP` (second unpackS . TL.mapAccumR f z)+    where _types  = f :: Int -> Char -> (Int,Char)  replicate n l = concat (L.replicate n l) -t_replicate n     = replicate n `eq` (unpackS . T.replicate n . packS)-tl_replicate n    = replicate n `eq` (unpackS . TL.replicate (fromIntegral n) . packS)+t_replicate n     = replicate m `eq` (unpackS . T.replicate m . packS)+    where m = fromIntegral (n :: Word8)+tl_replicate n    = replicate m `eq`+                    (unpackS . TL.replicate (fromIntegral m) . packS)+    where m = fromIntegral (n :: Word8)  unf :: Int -> Char -> Maybe (Char, Char) unf n c | fromEnum c * 100 > n = Nothing         | otherwise            = Just (c, succ c) -t_unfoldr n       = L.unfoldr (unf n) `eq` (unpackS . T.unfoldr (unf n))-tl_unfoldr n      = L.unfoldr (unf n) `eq` (unpackS . TL.unfoldr (unf n))-t_unfoldrN n m    = (L.take n . L.unfoldr (unf m)) `eq`-                         (unpackS . T.unfoldrN n (unf m))-tl_unfoldrN n m   = (L.take n . L.unfoldr (unf m)) `eq`-                         (unpackS . TL.unfoldrN (fromIntegral n) (unf m))+t_unfoldr n       = L.unfoldr (unf m) `eq` (unpackS . T.unfoldr (unf m))+    where m = fromIntegral (n :: Word16)+tl_unfoldr n      = L.unfoldr (unf m) `eq` (unpackS . TL.unfoldr (unf m))+    where m = fromIntegral (n :: Word16)+t_unfoldrN n m    = (L.take i . L.unfoldr (unf j)) `eq`+                         (unpackS . T.unfoldrN i (unf j))+    where i = fromIntegral (n :: Word16)+          j = fromIntegral (m :: Word16)+tl_unfoldrN n m   = (L.take i . L.unfoldr (unf j)) `eq`+                         (unpackS . TL.unfoldrN (fromIntegral i) (unf j))+    where i = fromIntegral (n :: Word16)+          j = fromIntegral (m :: Word16)  unpack2 :: (Stringy s) => (s,s) -> (String,String) unpack2 = unpackS *** unpackS@@ -350,33 +436,42 @@ s_take n          = L.take n      `eqP` (unpackS . S.take n) s_take_s m        = L.take n      `eqP` (unpackS . S.unstream . S.take n)   where n = small m-sf_take p n       = (L.take n . L.filter p) `eqP` (unpackS . S.take n . S.filter p)+sf_take p n       = (L.take n . L.filter p) `eqP`+                    (unpackS . S.take n . S.filter p) t_take n          = L.take n      `eqP` (unpackS . T.take n) tl_take n         = L.take n      `eqP` (unpackS . TL.take (fromIntegral n)) s_drop n          = L.drop n      `eqP` (unpackS . S.drop n) s_drop_s m        = L.drop n      `eqP` (unpackS . S.unstream . S.drop n)   where n = small m-sf_drop p n       = (L.drop n . L.filter p) `eqP` (unpackS . S.drop n . S.filter p)+sf_drop p n       = (L.drop n . L.filter p) `eqP`+                    (unpackS . S.drop n . S.filter p) t_drop n          = L.drop n      `eqP` (unpackS . T.drop n) tl_drop n         = L.drop n      `eqP` (unpackS . TL.drop (fromIntegral n)) s_take_drop m     = (L.take n . L.drop n) `eqP` (unpackS . S.take n . S.drop n)   where n = small m-s_take_drop_s m   = (L.take n . L.drop n) `eqP` (unpackS . S.unstream . S.take n . S.drop n)+s_take_drop_s m   = (L.take n . L.drop n) `eqP`+                    (unpackS . S.unstream . S.take n . S.drop n)   where n = small m s_takeWhile p     = L.takeWhile p `eqP` (unpackS . S.takeWhile p) s_takeWhile_s p   = L.takeWhile p `eqP` (unpackS . S.unstream . S.takeWhile p)-sf_takeWhile q p  = (L.takeWhile p . L.filter q) `eqP` (unpackS . S.takeWhile p . S.filter q)+sf_takeWhile q p  = (L.takeWhile p . L.filter q) `eqP`+                    (unpackS . S.takeWhile p . S.filter q) t_takeWhile p     = L.takeWhile p `eqP` (unpackS . T.takeWhile p) tl_takeWhile p    = L.takeWhile p `eqP` (unpackS . TL.takeWhile p) s_dropWhile p     = L.dropWhile p `eqP` (unpackS . S.dropWhile p) s_dropWhile_s p   = L.dropWhile p `eqP` (unpackS . S.unstream . S.dropWhile p)-sf_dropWhile q p  = (L.dropWhile p . L.filter q) `eqP` (unpackS . S.dropWhile p . S.filter q)+sf_dropWhile q p  = (L.dropWhile p . L.filter q) `eqP`+                    (unpackS . S.dropWhile p . S.filter q) t_dropWhile p     = L.dropWhile p `eqP` (unpackS . T.dropWhile p) tl_dropWhile p    = L.dropWhile p `eqP` (unpackS . S.dropWhile p)-t_dropWhileEnd p  = (L.reverse . L.dropWhile p . L.reverse) `eqP` (unpackS . T.dropWhileEnd p)-tl_dropWhileEnd p = (L.reverse . L.dropWhile p . L.reverse) `eqP` (unpackS . TL.dropWhileEnd p)-t_dropAround p    = (L.dropWhile p . L.reverse . L.dropWhile p . L.reverse) `eqP` (unpackS . T.dropAround p)-tl_dropAround p   = (L.dropWhile p . L.reverse . L.dropWhile p . L.reverse) `eqP` (unpackS . TL.dropAround p)+t_dropWhileEnd p  = (L.reverse . L.dropWhile p . L.reverse) `eqP`+                    (unpackS . T.dropWhileEnd p)+tl_dropWhileEnd p = (L.reverse . L.dropWhile p . L.reverse) `eqP`+                    (unpackS . TL.dropWhileEnd p)+t_dropAround p    = (L.dropWhile p . L.reverse . L.dropWhile p . L.reverse)+                    `eqP` (unpackS . T.dropAround p)+tl_dropAround p   = (L.dropWhile p . L.reverse . L.dropWhile p . L.reverse)+                    `eqP` (unpackS . TL.dropAround p) t_stripStart      = T.dropWhile isSpace `eq` T.stripStart tl_stripStart     = TL.dropWhile isSpace `eq` TL.stripStart t_stripEnd        = T.dropWhileEnd isSpace `eq` T.stripEnd@@ -398,6 +493,10 @@                                in T.null m || s `T.isPrefixOf` m tl_break_start (NotEmpty s) t = let (_,m) = TL.break s t                                 in TL.null m || s `TL.isPrefixOf` m+t_breakEnd_end (NotEmpty s) t = let (m,_) = T.breakEnd s t+                                in T.null m || s `T.isSuffixOf` m+tl_breakEnd_end (NotEmpty s) t = let (m,_) = TL.breakEnd s t+                                in TL.null m || s `TL.isSuffixOf` m t_breakBy p       = L.break p     `eqP` (unpack2 . T.breakBy p) tl_breakBy p      = L.break p     `eqP` (unpack2 . TL.breakBy p) t_group           = L.group       `eqP` (map unpackS . T.group)@@ -408,25 +507,29 @@ tl_inits          = L.inits       `eqP` (map unpackS . TL.inits) t_tails           = L.tails       `eqP` (map unpackS . T.tails) tl_tails          = L.tails       `eqP` (map unpackS . TL.tails)+t_findAppendId (NotEmpty s) = unsquare $ \ts ->+    let t = T.intercalate s ts+    in all (==t) $ map (uncurry T.append) (T.find s t)+tl_findAppendId (NotEmpty s) = unsquare $ \ts ->+    let t = TL.intercalate s ts+    in all (==t) $ map (uncurry TL.append) (TL.find s t)+t_findContains (NotEmpty s) = all (T.isPrefixOf s . snd) . T.find s .+                              T.intercalate s+tl_findContains (NotEmpty s) = all (TL.isPrefixOf s . snd) .+                               TL.find s . TL.intercalate s+sl_filterCount c  = (L.genericLength . L.filter (==c)) `eqP` SL.countChar c+t_findCount s     = (L.length . T.find s) `eq` T.count s+tl_findCount s    = (L.genericLength . TL.find s) `eq` TL.count s -t_findSplit s t         = (T.split s `eq` splitty) u-  where splitty v       = case T.find s v  of-                            (x,xs) -> x : L.map (T.drop (T.length s) . fst) xs-        u = T.concat [t,s,t]-tl_findSplit s t        = (TL.split s `eq` splitty) u-  where splitty v       = case TL.find s v of-                            (x,xs) -> x : L.map (TL.drop (TL.length s) . fst) xs-        u = TL.concat [t,s,t]-t_split_split s         = unsquare ((T.split s `eq` Slow.split s) .-                                    T.intercalate s)-tl_split_split s        = unsquare (((TL.split (chunkify s) . chunkify) `eq`-                                     (map chunkify . T.split s)) .-                                    T.intercalate s)+t_split_split s         = (T.split s `eq` Slow.split s) . T.intercalate s+tl_split_split s        = ((TL.split (TL.fromStrict s) . TL.fromStrict) `eq`+                           (map TL.fromStrict . T.split s)) . T.intercalate s t_split_i (NotEmpty t)  = id `eq` (T.intercalate t . T.split t) tl_split_i (NotEmpty t) = id `eq` (TL.intercalate t . TL.split t)  t_splitBy p       = splitBy p `eqP` (map unpackS . T.splitBy p)-t_splitBy_count c = (L.length . T.splitBy (==c)) `eq` ((1+) . T.count (T.singleton c))+t_splitBy_count c = (L.length . T.splitBy (==c)) `eq`+                    ((1+) . T.count (T.singleton c)) t_splitBy_split c = T.splitBy (==c) `eq` T.split (T.singleton c) tl_splitBy p      = splitBy p `eqP` (map unpackS . TL.splitBy p) @@ -444,9 +547,8 @@ t_chunksOf_length k t = len == T.length t || (k <= 0 && len == 0)   where len = L.sum . L.map T.length $ T.chunksOf k t -chunkify = TL.fromChunks . (:[])--tl_chunksOf k = T.chunksOf k `eq` (map (T.concat . TL.toChunks) . TL.chunksOf (fromIntegral k) . chunkify)+tl_chunksOf k = T.chunksOf k `eq` (map (T.concat . TL.toChunks) .+                                   TL.chunksOf (fromIntegral k) . TL.fromStrict)  t_lines           = L.lines       `eqP` (map unpackS . T.lines) tl_lines          = L.lines       `eqP` (map unpackS . TL.lines)@@ -463,13 +565,15 @@ t_words           = L.words       `eqP` (map unpackS . T.words)  tl_words          = L.words       `eqP` (map unpackS . TL.words)-t_unlines         = unsquare (L.unlines `eq` (unpackS . T.unlines . map packS))-tl_unlines        = unsquare (L.unlines `eq` (unpackS . TL.unlines . map packS))-t_unwords         = unsquare (L.unwords `eq` (unpackS . T.unwords . map packS))-tl_unwords        = unsquare (L.unwords `eq` (unpackS . TL.unwords . map packS))+t_unlines         = L.unlines `eq` (unpackS . T.unlines . map packS)+tl_unlines        = L.unlines `eq` (unpackS . TL.unlines . map packS)+t_unwords         = L.unwords `eq` (unpackS . T.unwords . map packS)+tl_unwords        = L.unwords `eq` (unpackS . TL.unwords . map packS) -s_isPrefixOf s    = L.isPrefixOf s `eqP` (S.isPrefixOf (S.stream $ packS s) . S.stream)-sf_isPrefixOf p s = (L.isPrefixOf s . L.filter p) `eqP` (S.isPrefixOf (S.stream $ packS s) . S.filter p . S.stream)+s_isPrefixOf s    = L.isPrefixOf s `eqP`+                    (S.isPrefixOf (S.stream $ packS s) . S.stream)+sf_isPrefixOf p s = (L.isPrefixOf s . L.filter p) `eqP`+                    (S.isPrefixOf (S.stream $ packS s) . S.filter p . S.stream) t_isPrefixOf s    = L.isPrefixOf s`eqP` T.isPrefixOf (packS s) tl_isPrefixOf s   = L.isPrefixOf s`eqP` TL.isPrefixOf (packS s) t_isSuffixOf s    = L.isSuffixOf s`eqP` T.isSuffixOf (packS s)@@ -477,8 +581,22 @@ t_isInfixOf s     = L.isInfixOf s `eqP` T.isInfixOf (packS s) tl_isInfixOf s    = L.isInfixOf s `eqP` TL.isInfixOf (packS s) +prefixed (p:ps) (t:ts)+    | p == t = prefixed ps ts+prefixed [] ts = Just ts+prefixed _  _  = Nothing++t_prefixed s      = (fmap packS . prefixed s) `eqP` T.prefixed (packS s)+tl_prefixed s     = (fmap packS . prefixed s) `eqP` TL.prefixed (packS s)++suffixed p t = reverse `fmap` prefixed (reverse p) (reverse t)++t_suffixed s      = (fmap packS . suffixed s) `eqP` T.suffixed (packS s)+tl_suffixed s     = (fmap packS . suffixed s) `eqP` TL.suffixed (packS s)+ sf_elem p c       = (L.elem c . L.filter p) `eqP` (S.elem c . S.filter p)-sf_filter q p     = (L.filter p . L.filter q) `eqP` (unpackS . S.filter p . S.filter q)+sf_filter q p     = (L.filter p . L.filter q) `eqP`+                    (unpackS . S.filter p . S.filter q) t_filter p        = L.filter p    `eqP` (unpackS . T.filter p) tl_filter p       = L.filter p    `eqP` (unpackS . TL.filter p) sf_findBy q p     = (L.find p . L.filter q) `eqP` (S.findBy p . S.filter q)@@ -487,29 +605,33 @@ t_partition p     = L.partition p `eqP` (unpack2 . T.partitionBy p) tl_partition p    = L.partition p `eqP` (unpack2 . TL.partitionBy p) -sf_index p s      = forAll (choose (-l,l*2)) ((L.filter p s L.!!) `eq` S.index (S.filter p $ packS s))+sf_index p s      = forAll (choose (-l,l*2))+                    ((L.filter p s L.!!) `eq` S.index (S.filter p $ packS s))     where l = L.length s t_index s         = forAll (choose (-l,l*2)) ((s L.!!) `eq` T.index (packS s))     where l = L.length s -tl_index s        = forAll (choose (-l,l*2)) ((s L.!!) `eq` (TL.index (packS s) . fromIntegral))+tl_index s        = forAll (choose (-l,l*2))+                    ((s L.!!) `eq` (TL.index (packS s) . fromIntegral))     where l = L.length s  t_findIndex p     = L.findIndex p `eqP` T.findIndex p-t_count t         = (subtract 1 . L.length . T.split t) `eq` T.count t-tl_count t        = (subtract 1 . L.genericLength . TL.split t) `eq` TL.count t+t_count (NotEmpty t)  = (subtract 1 . L.length . T.split t) `eq` T.count t+tl_count (NotEmpty t) = (subtract 1 . L.genericLength . TL.split t) `eq`+                        TL.count t t_zip s           = L.zip s `eqP` T.zip (packS s) tl_zip s          = L.zip s `eqP` TL.zip (packS s)-sf_zipWith p c s  = (L.zipWith c (L.filter p s) . L.filter p) `eqP` (unpackS . S.zipWith c (S.filter p $ packS s) . S.filter p)+sf_zipWith p c s  = (L.zipWith c (L.filter p s) . L.filter p) `eqP`+                    (unpackS . S.zipWith c (S.filter p $ packS s) . S.filter p) t_zipWith c s     = L.zipWith c s `eqP` (unpackS . T.zipWith c (packS s)) tl_zipWith c s    = L.zipWith c s `eqP` (unpackS . TL.zipWith c (packS s)) -t_indices = unsquare (\s -> Slow.indices s `eq` indices s)-tl_indices = unsquare (\s -> lazyIndices s `eq` S.indices s)-    where lazyIndices s t = map fromIntegral $ Slow.indices (conc s) (conc t)+t_indices  (NotEmpty s) = Slow.indices s `eq` indices s+tl_indices (NotEmpty s) = lazyIndices s `eq` S.indices s+    where lazyIndices ss t = map fromIntegral $ Slow.indices (conc ss) (conc t)           conc = T.concat . TL.toChunks-t_indices_occurs t = unsquare (\ts -> let s = T.intercalate t ts-                                      in Slow.indices t s == indices t s)+t_indices_occurs (NotEmpty t) ts = let s = T.intercalate t ts+                                   in Slow.indices t s == indices t s  -- Bit shifts. shiftL w = forAll (choose (0,width-1)) $ \k -> Bits.shiftL w k == U.shiftL w k@@ -526,15 +648,103 @@ shiftR_Word16 = shiftR :: Word16 -> Property shiftR_Word32 = shiftR :: Word32 -> Property --- Builder-t_builderSingleton = id `eqP` (unpackS . TB.toLazyText . mconcat . map TB.singleton)-t_builderFromText = L.concat `eq` (unpackS . TB.toLazyText . mconcat . map (TB.fromText . packS))-t_builderAssociative s1 s2 s3 = TB.toLazyText (b1 `mappend` (b2 `mappend` b3)) ==-                                TB.toLazyText ((b1 `mappend` b2) `mappend` b3)-    where b1 = TB.fromText (packS s1)-          b2 = TB.fromText (packS s2)-          b3 = TB.fromText (packS s3)+-- Builder. +t_builderSingleton = id `eqP`+                     (unpackS . TB.toLazyText . mconcat . map TB.singleton)+t_builderFromText = L.concat `eq` (unpackS . TB.toLazyText . mconcat .+                                   map (TB.fromText . packS))+t_builderAssociative s1 s2 s3 =+    TB.toLazyText (b1 `mappend` (b2 `mappend` b3)) ==+    TB.toLazyText ((b1 `mappend` b2) `mappend` b3)+  where b1 = TB.fromText (packS s1)+        b2 = TB.fromText (packS s2)+        b3 = TB.fromText (packS s3)++-- Input and output.++-- Work around lack of Show instance for TextEncoding.+data Encoding = E String TextEncoding++instance Show Encoding where show (E n _) = "utf" ++ n++instance Arbitrary Encoding where+    arbitrary = oneof . map return $+      [ E "8" utf8, E "8_bom" utf8_bom, E "16" utf16, E "16le" utf16le,+        E "16be" utf16be, E "32" utf32, E "32le" utf32le, E "32be" utf32be ]++windowsNewlineMode  = NewlineMode { inputNL = CRLF, outputNL = CRLF }++instance Show Newline where+    show CRLF = "CRLF"+    show LF   = "LF"++instance Show NewlineMode where+    show (NewlineMode i o) = "NewlineMode { inputNL = " ++ show i +++                             ", outputNL = " ++ show o ++ " }"++instance Arbitrary NewlineMode where+    arbitrary = oneof . map return $+      [ noNewlineTranslation, universalNewlineMode, nativeNewlineMode,+        windowsNewlineMode ]++instance Arbitrary BufferMode where+    arbitrary = oneof [ return NoBuffering,+                        return LineBuffering,+                        return (BlockBuffering Nothing),+                        (BlockBuffering . Just . (+1) . fromIntegral) `fmap`+                        (arbitrary :: Gen Word16) ]++-- This test harness is complex!  What property are we checking?+--+-- Reading after writing a multi-line file should give the same+-- results as were written.+--+-- What do we vary while checking this property?+-- * The lines themselves, scrubbed to contain neither CR nor LF.  (By+--   working with a list of lines, we ensure that the data will+--   sometimes contain line endings.)+-- * Encoding.+-- * Newline translation mode.+-- * Buffering.+write_read unline filt writer reader (E _ enc) nl buf ts =+    monadicIO $ assert . (==t) =<< run act+  where t = unline . map (filt (not . (`elem` "\r\n"))) $ ts+        act = withTempFile $ \path h -> do+                hSetEncoding h enc+                hSetNewlineMode h nl+                hSetBuffering h buf+                () <- writer h t+                hClose h+                bracket (openFile path ReadMode) hClose $ \h' -> do+                  hSetEncoding h' enc+                  hSetNewlineMode h' nl+                  hSetBuffering h' buf+                  r <- reader h'+                  r `deepseq` return r++t_put_get = write_read T.unlines T.filter put get+  where put h = withRedirect h stdout . T.putStr+        get h = withRedirect h stdin T.getContents+tl_put_get = write_read TL.unlines TL.filter put get+  where put h = withRedirect h stdout . TL.putStr+        get h = withRedirect h stdin TL.getContents+t_write_read = write_read T.unlines T.filter T.hPutStr T.hGetContents+tl_write_read = write_read TL.unlines TL.filter TL.hPutStr TL.hGetContents++t_write_read_line e m b t = write_read head T.filter T.hPutStrLn+                            T.hGetLine e m b [t]+tl_write_read_line e m b t = write_read head TL.filter TL.hPutStrLn+                             TL.hGetLine e m b [t]++-- Low-level.++t_dropWord16 m t = dropWord16 m t `T.isSuffixOf` t+t_takeWord16 m t = takeWord16 m t `T.isPrefixOf` t+t_take_drop_16 m t = T.append (takeWord16 n t) (dropWord16 n t) == t+  where n = small m+t_use_from t = monadicIO $ assert . (==t) =<< run (useAsPtr t fromPtr)+ -- Regression tests. s_filter_eq s = S.filter p t == S.streamList (filter p s)     where p = (/= S.last t)@@ -560,17 +770,26 @@     testProperty "t_singleton" t_singleton,     testProperty "tl_singleton" tl_singleton,     testProperty "tl_unstreamChunks" tl_unstreamChunks,-    testProperty "tl_chunk_unchunk" tl_chunk_unchunk+    testProperty "tl_chunk_unchunk" tl_chunk_unchunk,+    testProperty "tl_from_to_strict" tl_from_to_strict   ],    testGroup "transcoding" [     testProperty "t_ascii" t_ascii,+    testProperty "tl_ascii" tl_ascii,     testProperty "t_utf8" t_utf8,     testProperty "tl_utf8" tl_utf8,     testProperty "t_utf16LE" t_utf16LE,+    testProperty "tl_utf16LE" tl_utf16LE,     testProperty "t_utf16BE" t_utf16BE,+    testProperty "tl_utf16BE" tl_utf16BE,     testProperty "t_utf32LE" t_utf32LE,-    testProperty "t_utf32BE" t_utf32BE+    testProperty "tl_utf32LE" tl_utf32LE,+    testProperty "t_utf32BE" t_utf32BE,+    testProperty "tl_utf32BE" tl_utf32BE,+    testGroup "errors" [+      testProperty "t_utf8_err" t_utf8_err+    ]   ],    testGroup "instances" [@@ -635,8 +854,11 @@     testProperty "tl_null" tl_null,     testProperty "s_length" s_length,     testProperty "sf_length" sf_length,+    testProperty "sl_length" sl_length,     testProperty "t_length" t_length,-    testProperty "tl_length" tl_length+    testProperty "tl_length" tl_length,+    testProperty "t_compareLength" t_compareLength,+    testProperty "tl_compareLength" tl_compareLength   ],    testGroup "transformations" [@@ -680,7 +902,9 @@       testProperty "t_justifyLeft" t_justifyLeft,       testProperty "tl_justifyLeft" tl_justifyLeft,       testProperty "t_justifyRight" t_justifyRight,-      testProperty "tl_justifyRight" tl_justifyRight+      testProperty "tl_justifyRight" tl_justifyRight,+      testProperty "t_center" t_center,+      testProperty "tl_center" tl_center     ]   ], @@ -799,6 +1023,8 @@       testProperty "tl_break_id" tl_break_id,       testProperty "t_break_start" t_break_start,       testProperty "tl_break_start" tl_break_start,+      testProperty "t_breakEnd_end" t_breakEnd_end,+      testProperty "tl_breakEnd_end" tl_breakEnd_end,       testProperty "t_breakBy" t_breakBy,       testProperty "tl_breakBy" tl_breakBy,       testProperty "t_group" t_group,@@ -812,8 +1038,13 @@     ],      testGroup "breaking many" [-      testProperty "t_findSplit" t_findSplit,-      testProperty "tl_findSplit" tl_findSplit,+      testProperty "t_findAppendId" t_findAppendId,+      testProperty "tl_findAppendId" tl_findAppendId,+      testProperty "t_findContains" t_findContains,+      testProperty "tl_findContains" tl_findContains,+      testProperty "sl_filterCount" sl_filterCount,+      testProperty "t_findCount" t_findCount,+      testProperty "tl_findCount" tl_findCount,       testProperty "t_split_split" t_split_split,       testProperty "tl_split_split" tl_split_split,       testProperty "t_split_i" t_split_i,@@ -848,7 +1079,14 @@     testProperty "t_isSuffixOf" t_isSuffixOf,     testProperty "tl_isSuffixOf" tl_isSuffixOf,     testProperty "t_isInfixOf" t_isInfixOf,-    testProperty "tl_isInfixOf" tl_isInfixOf+    testProperty "tl_isInfixOf" tl_isInfixOf,++    testGroup "view" [+      testProperty "t_prefixed" t_prefixed,+      testProperty "tl_prefixed" tl_prefixed,+      testProperty "t_suffixed" t_suffixed,+      testProperty "tl_suffixed" tl_suffixed+    ]   ],    testGroup "searching" [@@ -900,5 +1138,23 @@     testProperty "t_builderSingleton" t_builderSingleton,     testProperty "t_builderFromText" t_builderFromText,     testProperty "t_builderAssociative" t_builderAssociative+  ],++  testGroup "input-output" [+    testProperty "t_write_read" t_write_read,+    testProperty "tl_write_read" tl_write_read,+    testProperty "t_write_read_line" t_write_read_line,+    testProperty "tl_write_read_line" tl_write_read_line+    -- These tests are subject to I/O race conditions when run under+    -- test-framework-quickcheck2.+    -- testProperty "t_put_get" t_put_get+    -- testProperty "tl_put_get" tl_put_get+  ],++  testGroup "lowlevel" [+    testProperty "t_dropWord16" t_dropWord16,+    testProperty "t_takeWord16" t_takeWord16,+    testProperty "t_take_drop_16" t_take_drop_16,+    testProperty "t_use_from" t_use_from   ]  ]
tests/QuickCheckUtils.hs view
@@ -3,12 +3,15 @@ module QuickCheckUtils where  import Control.Arrow (first)+import Data.Char (chr)+import Data.Bits ((.&.)) import Data.Int (Int64) import Data.Word (Word8, Word16, Word32)+import Data.String (IsString, fromString) import qualified Data.Text as T import qualified Data.Text.Lazy as TL import System.Random (Random(..), RandomGen)-import Test.QuickCheck (Arbitrary(..), choose, oneof, sized, variant, vector)+import Test.QuickCheck hiding ((.&.)) import qualified Data.ByteString as B  instance Random Int64 where@@ -17,7 +20,6 @@  instance Arbitrary Int64 where     arbitrary     = choose (minBound,maxBound)-    coarbitrary c = variant (fromEnum c `rem` 4)  instance Random Word8 where     randomR = integralRandomR@@ -25,11 +27,9 @@  instance Arbitrary Word8 where     arbitrary     = choose (minBound,maxBound)-    coarbitrary c = variant (fromEnum c `rem` 4)  instance Arbitrary B.ByteString where     arbitrary     = B.pack `fmap` arbitrary-    coarbitrary s = coarbitrary (B.unpack s)  instance Random Word16 where     randomR = integralRandomR@@ -37,7 +37,6 @@  instance Arbitrary Word16 where     arbitrary     = choose (minBound,maxBound)-    coarbitrary c = variant (fromEnum c `rem` 4)  instance Random Word32 where     randomR = integralRandomR@@ -45,41 +44,92 @@  instance Arbitrary Word32 where     arbitrary     = choose (minBound,maxBound)-    coarbitrary c = variant (fromEnum c `rem` 4) -instance Arbitrary Char where-    arbitrary     = oneof [choose ('\0','\55295'), choose ('\57344','\1114111')]-    coarbitrary c = variant (fromEnum c `rem` 4)+genUnicode :: IsString a => Gen a+genUnicode = fmap fromString string where+    string = sized $ \n ->+        do k <- choose (0,n)+           sequence [ char | _ <- [1..k] ]+    +    excluding :: [a -> Bool] -> Gen a -> Gen a+    excluding bad gen = loop+      where+        loop = do+          x <- gen+          if or (map ($ x) bad)+            then loop+            else return x+    +    reserved = [lowSurrogate, highSurrogate, noncharacter]+    lowSurrogate c = c >= 0xDC00 && c <= 0xDFFF+    highSurrogate c = c >= 0xD800 && c <= 0xDBFF+    noncharacter c = masked == 0xFFFE || masked == 0xFFFF+      where+        masked = c .&. 0xFFFF +    +    ascii = choose (0,0x7F)+    plane0 = choose (0xF0, 0xFFFF)+    plane1 = oneof [ choose (0x10000, 0x10FFF)+                   , choose (0x11000, 0x11FFF)+                   , choose (0x12000, 0x12FFF)+                   , choose (0x13000, 0x13FFF)+                   , choose (0x1D000, 0x1DFFF)+                   , choose (0x1F000, 0x1FFFF)+                   ]+    plane2 = oneof [ choose (0x20000, 0x20FFF)+                   , choose (0x21000, 0x21FFF)+                   , choose (0x22000, 0x22FFF)+                   , choose (0x23000, 0x23FFF)+                   , choose (0x24000, 0x24FFF)+                   , choose (0x25000, 0x25FFF)+                   , choose (0x26000, 0x26FFF)+                   , choose (0x27000, 0x27FFF)+                   , choose (0x28000, 0x28FFF)+                   , choose (0x29000, 0x29FFF)+                   , choose (0x2A000, 0x2AFFF)+                   , choose (0x2B000, 0x2BFFF)+                   , choose (0x2F000, 0x2FFFF)+                   ]+    plane14 = choose (0xE0000, 0xE0FFF)+    planes = [ascii, plane0, plane1, plane2, plane14]+    +    char = chr `fmap` excluding reserved (oneof planes) +-- For tests that have O(n^2) running times or input sizes, resize+-- their inputs to the square root of the originals.+unsquare :: (Arbitrary a, Show a, Testable b) => (a -> b) -> Property+unsquare = forAll smallArbitrary++smallArbitrary :: (Arbitrary a, Show a) => Gen a+smallArbitrary = sized $ \n -> resize (smallish n) arbitrary+  where smallish = round . (sqrt :: Double -> Double) . fromIntegral . abs+ instance Arbitrary T.Text where-    arbitrary     = T.pack `fmap` arbitrary-    coarbitrary s = coarbitrary (T.unpack s)+    arbitrary = T.pack `fmap` arbitrary  instance Arbitrary TL.Text where-    arbitrary     = TL.pack `fmap` arbitrary-    coarbitrary s = coarbitrary (TL.unpack s)+    arbitrary = (TL.fromChunks . map notEmpty) `fmap` smallArbitrary  newtype NotEmpty a = NotEmpty { notEmpty :: a }-    deriving (Eq, Ord, Show)+    deriving (Eq, Ord) +instance Show a => Show (NotEmpty a) where+    show (NotEmpty a) = show a+ instance Functor NotEmpty where     fmap f (NotEmpty a) = NotEmpty (f a)  instance Arbitrary a => Arbitrary (NotEmpty [a]) where     arbitrary   = sized (\n -> NotEmpty `fmap` (choose (1,n+1) >>= vector))-    coarbitrary = coarbitrary . notEmpty  instance Arbitrary (NotEmpty T.Text) where     arbitrary   = (fmap T.pack) `fmap` arbitrary-    coarbitrary = coarbitrary . notEmpty  instance Arbitrary (NotEmpty TL.Text) where     arbitrary   = (fmap TL.pack) `fmap` arbitrary-    coarbitrary = coarbitrary . notEmpty  instance Arbitrary (NotEmpty B.ByteString) where     arbitrary   = (fmap B.pack) `fmap` arbitrary-    coarbitrary = coarbitrary . notEmpty  data Small = S0  | S1  | S2  | S3  | S4  | S5  | S6  | S7            | S8  | S9  | S10 | S11 | S12 | S13 | S14 | S15@@ -120,7 +170,6 @@  instance Arbitrary Small where     arbitrary     = choose (minBound,maxBound)-    coarbitrary c = variant (fromEnum c `rem` 4)  integralRandomR :: (Integral a, RandomGen g) => (a,a) -> g -> (a,g) integralRandomR  (a,b) g = case randomR (fromIntegral a :: Integer,
text.cabal view
@@ -1,12 +1,12 @@ name:           text-version:        0.7.2.1+version:        0.8.0.0 synopsis:       An efficient packed Unicode text type description:    An efficient packed Unicode text type. license:        BSD3 license-file:   LICENSE-author:         Tom Harper <rtharper@aftereternity.co.uk>+author:         Tom Harper <rtomharper@googlemail.com> maintainer:     Bryan O'Sullivan <bos@serpentine.com>-                Tom Harper <rtharper@aftereternity.co.uk>+                Tom Harper <rrtomharper@googlemail.com>                 Duncan Coutts <duncan@haskell.org> copyright:      2008-2009 Tom Harper, 2009-2010 Bryan O'Sullivan category:       Data, Text@@ -40,6 +40,7 @@     Data.Text.Foreign     Data.Text.IO     Data.Text.Lazy+    Data.Text.Lazy.Builder     Data.Text.Lazy.Encoding     Data.Text.Lazy.IO   other-modules:@@ -56,7 +57,6 @@     Data.Text.Fusion.Size     Data.Text.IO.Internal     Data.Text.Internal-    Data.Text.Lazy.Builder     Data.Text.Lazy.Encoding.Fusion     Data.Text.Lazy.Fusion     Data.Text.Lazy.Internal@@ -85,3 +85,4 @@     ghc-options: -fwarn-tabs   if flag(developer)     ghc-options: -Werror+    cpp-options: -DASSERTS