text 0.11.3.1 → 1.0.0.0
raw patch · 39 files changed
+1031/−236 lines, 39 filesdep −extensible-exceptionsdep −integerdep ~arraydep ~basedep ~ghc-prim
Dependencies removed: extensible-exceptions, integer
Dependency ranges changed: array, base, ghc-prim
Files
- Data/Text.hs +61/−24
- Data/Text/Encoding.hs +140/−5
- Data/Text/Encoding/Error.hs +0/−4
- Data/Text/Foreign.hs +26/−2
- Data/Text/Fusion.hs +3/−3
- Data/Text/Fusion/CaseMapping.hs +114/−0
- Data/Text/Fusion/Common.hs +39/−2
- Data/Text/IO.hs +0/−17
- Data/Text/IO/Internal.hs +0/−4
- Data/Text/Lazy.hs +38/−17
- Data/Text/Lazy/Builder.hs +10/−1
- Data/Text/Lazy/Builder/Int.hs +4/−3
- Data/Text/Lazy/Builder/RealFloat.hs +4/−1
- Data/Text/Lazy/Encoding.hs +14/−36
- Data/Text/Lazy/IO.hs +0/−14
- Data/Text/Search.hs +2/−1
- Data/Text/Unsafe.hs +1/−5
- Data/Text/Unsafe/Base.hs +0/−4
- benchmarks/haskell/Benchmarks/DecodeUtf8.hs +9/−1
- benchmarks/haskell/Benchmarks/Programs/BigTable.hs +42/−0
- benchmarks/haskell/Benchmarks/Programs/Cut.hs +98/−0
- benchmarks/haskell/Benchmarks/Programs/Fold.hs +68/−0
- benchmarks/haskell/Benchmarks/Programs/Sort.hs +70/−0
- benchmarks/haskell/Benchmarks/Programs/StripTags.hs +53/−0
- benchmarks/haskell/Benchmarks/Programs/Throughput.hs +41/−0
- benchmarks/haskell/Benchmarks/Pure.hs +0/−1
- benchmarks/haskell/Benchmarks/Replace.hs +19/−7
- benchmarks/text-benchmarks.cabal +1/−0
- cbits/cbits.c +50/−11
- changelog +16/−0
- include/text_cbits.h +11/−0
- scripts/Arsec.hs +4/−9
- scripts/CaseFolding.hs +10/−6
- scripts/CaseMapping.hs +8/−2
- scripts/SpecialCasing.hs +12/−6
- tests/Tests/Properties.hs +22/−4
- tests/Tests/Regressions.hs +15/−1
- tests/text-tests.cabal +1/−0
- text.cabal +25/−45
Data/Text.hs view
@@ -79,6 +79,7 @@ , toCaseFold , toLower , toUpper+ , toTitle -- ** Justification , justifyLeft@@ -177,7 +178,7 @@ , findIndex , count - -- * Zipping and unzipping+ -- * Zipping , zip , zipWith @@ -201,11 +202,7 @@ #endif import Data.Char (isSpace) import Data.Data (Data(gfoldl, toConstr, gunfold, dataTypeOf))-#if __GLASGOW_HASKELL__ >= 612 import Data.Data (mkNoRepType)-#else-import Data.Data (mkNorepType)-#endif import Control.Monad (foldM) import qualified Data.Text.Array as A import qualified Data.List as L@@ -350,11 +347,7 @@ gfoldl f z txt = z pack `f` (unpack txt) toConstr _ = P.error "Data.Text.Text.toConstr" gunfold _ _ = P.error "Data.Text.Text.gunfold"-#if __GLASGOW_HASKELL__ >= 612 dataTypeOf _ = mkNoRepType "Data.Text.Text"-#else- dataTypeOf _ = mkNorepType "Data.Text.Text"-#endif -- | /O(n)/ Compare two 'Text' values lexicographically. compareText :: Text -> Text -> Ordering@@ -623,12 +616,29 @@ -- -- In (unlikely) bad cases, this function's time complexity degrades -- towards /O(n*m)/.-replace :: Text -- ^ Text to search for- -> Text -- ^ Replacement text- -> Text -- ^ Input text- -> Text-replace s d = intercalate d . splitOn s-{-# INLINE replace #-}+replace :: Text -> Text -> Text -> Text+replace needle@(Text _ _ neeLen)+ (Text repArr repOff repLen)+ haystack@(Text hayArr hayOff hayLen)+ | neeLen == 0 = emptyError "replace"+ | L.null ixs = haystack+ | len > 0 = Text (A.run x) 0 len+ | len < 0 = overflowError "replace"+ | otherwise = empty+ where+ ixs = indices needle haystack+ len = hayLen - (neeLen - repLen) * L.length ixs+ x = do+ marr <- A.new len+ let loop (i:is) o d = do+ let d0 = d + i - o+ d1 = d0 + repLen+ A.copyI marr d hayArr (hayOff+o) d0+ A.copyI marr d0 repArr repOff d1+ loop is (i + neeLen) d1+ loop [] o d = A.copyI marr d hayArr (hayOff+o) len+ loop ixs 0 0+ return marr -- ---------------------------------------------------------------------------- -- ** Case conversions (folds)@@ -651,10 +661,11 @@ -- functions from the @text-icu@ package: -- <http://hackage.haskell.org/package/text-icu> --- | /O(n)/ Convert a string to folded case. This function is mainly--- useful for performing caseless (also known as case insensitive)--- string comparisons.+-- | /O(n)/ Convert a string to folded case. Subject to fusion. --+-- This function is mainly useful for performing caseless (also known+-- as case insensitive) string comparisons.+-- -- A string @x@ is a caseless match for a string @y@ if and only if: -- -- @toCaseFold x == toCaseFold y@@@ -671,21 +682,47 @@ {-# INLINE [0] toCaseFold #-} -- | /O(n)/ Convert a string to lower case, using simple case--- conversion. The result string may be longer than the input string.--- For instance, \"İ\" (Latin capital letter I with dot above,--- U+0130) maps to the sequence \"i\" (Latin small letter i, U+0069) followed--- by \" ̇\" (combining dot above, U+0307).+-- conversion. Subject to fusion.+--+-- The result string may be longer than the input string. For+-- instance, \"İ\" (Latin capital letter I with dot above,+-- U+0130) maps to the sequence \"i\" (Latin small letter i, U+0069)+-- followed by \" ̇\" (combining dot above, U+0307). toLower :: Text -> Text toLower t = unstream (S.toLower (stream t)) {-# INLINE toLower #-} -- | /O(n)/ Convert a string to upper case, using simple case--- conversion. The result string may be longer than the input string.--- For instance, the German \"ß\" (eszett, U+00DF) maps to the+-- conversion. Subject to fusion.+--+-- The result string may be longer than the input string. For+-- instance, the German \"ß\" (eszett, U+00DF) maps to the -- two-letter sequence \"SS\". toUpper :: Text -> Text toUpper t = unstream (S.toUpper (stream t)) {-# INLINE toUpper #-}++-- | /O(n)/ Convert a string to title case, using simple case+-- conversion. Subject to fusion.+--+-- The first letter of the input is converted to title case, as is+-- every subsequent letter that immediately follows a non-letter.+-- Every letter that immediately follows another letter is converted+-- to lower case.+--+-- The result string may be longer than the input string. For example,+-- the Latin small ligature fl (U+FB02) is converted to the+-- sequence Latin capital letter F (U+0046) followed by Latin small+-- letter l (U+006C).+--+-- /Note/: this function does not take language or culture specific+-- rules into account. For instance, in English, different style+-- guides disagree on whether the book name \"The Hill of the Red+-- Fox\" is correctly title cased—but this function will+-- capitalize /every/ word.+toTitle :: Text -> Text+toTitle t = unstream (S.toTitle (stream t))+{-# INLINE toTitle #-} -- | /O(n)/ Left-justify a string to the given length, using the -- specified fill character on the right. Subject to fusion.
Data/Text/Encoding.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE BangPatterns, CPP, ForeignFunctionInterface, MagicHash,+{-# LANGUAGE BangPatterns, CPP, ForeignFunctionInterface, GeneralizedNewtypeDeriving, MagicHash, UnliftedFFITypes #-} #if __GLASGOW_HASKELL__ >= 702 {-# LANGUAGE Trustworthy #-}@@ -43,6 +43,12 @@ , decodeUtf32LEWith , decodeUtf32BEWith + -- ** Stream oriented decoding+ -- $stream+ , streamDecodeUtf8+ , streamDecodeUtf8With+ , Decoding(..)+ -- * Encoding Text to ByteStrings , encodeUtf8 , encodeUtf16LE@@ -57,20 +63,22 @@ #else import Control.Monad.ST (unsafeIOToST, unsafeSTToIO) #endif+import Control.Monad.ST (runST) import Data.Bits ((.&.)) import Data.ByteString as B import Data.ByteString.Internal as B+import Data.Text () import Data.Text.Encoding.Error (OnDecodeError, UnicodeException, strictDecode) import Data.Text.Internal (Text(..), safe, textP) import Data.Text.Private (runText) import Data.Text.UnsafeChar (ord, unsafeWrite) import Data.Text.UnsafeShift (shiftL, shiftR)-import Data.Word (Word8)+import Data.Word (Word8, Word32) import Foreign.C.Types (CSize) import Foreign.ForeignPtr (withForeignPtr) import Foreign.Marshal.Utils (with)-import Foreign.Ptr (Ptr, minusPtr, plusPtr)-import Foreign.Storable (peek, poke)+import Foreign.Ptr (Ptr, minusPtr, nullPtr, plusPtr)+import Foreign.Storable (Storable, peek, poke) import GHC.Base (MutableByteArray#) import qualified Data.Text.Array as A import qualified Data.Text.Encoding.Fusion as E@@ -78,6 +86,8 @@ import qualified Data.Text.Fusion as F import Data.Text.Unsafe (unsafeDupablePerformIO) +#include "text_cbits.h"+ -- $strict -- -- All of the single-parameter functions for decoding bytestrings@@ -139,6 +149,126 @@ desc = "Data.Text.Encoding.decodeUtf8: Invalid UTF-8 stream" {- INLINE[0] decodeUtf8With #-} +-- $stream+--+-- The 'streamDecodeUtf8' and 'streamDecodeUtf8With' functions accept+-- a 'ByteString' that represents a possibly incomplete input (e.g. a+-- packet from a network stream) that may not end on a UTF-8 boundary.+--+-- The first element of the result is the maximal chunk of 'Text' that+-- can be decoded from the given input. The second is a function which+-- accepts another 'ByteString'. That string will be assumed to+-- directly follow the string that was passed as input to the original+-- function, and it will in turn be decoded.+--+-- To help understand the use of these functions, consider the Unicode+-- string @\"hi ☃\"@. If encoded as UTF-8, this becomes @\"hi+-- \\xe2\\x98\\x83\"@; the final @\'☃\'@ is encoded as 3 bytes.+--+-- Now suppose that we receive this encoded string as 3 packets that+-- are split up on untidy boundaries: @[\"hi \\xe2\", \"\\x98\",+-- \"\\x83\"]@. We cannot decode the entire Unicode string until we+-- have received all three packets, but we would like to make progress+-- as we receive each one.+--+-- @+-- let 'Some' t0 f0 = 'streamDecodeUtf8' \"hi \\xe2\"+-- t0 == \"hi \" :: 'Text'+-- @+--+-- We use the continuation @f0@ to decode our second packet.+--+-- @+-- let 'Some' t1 f1 = f0 \"\\x98\"+-- t1 == \"\"+-- @+--+-- We could not give @f0@ enough input to decode anything, so it+-- returned an empty string. Once we feed our second continuation @f1@+-- the last byte of input, it will make progress.+--+-- @+-- let 'Some' t2 f2 = f1 \"\\x83\"+-- t2 == \"☃\"+-- @+--+-- If given invalid input, an exception will be thrown by the function+-- or continuation where it is encountered.++-- | A stream oriented decoding result.+data Decoding = Some Text ByteString (ByteString -> Decoding)++instance Show Decoding where+ showsPrec d (Some t bs _) = showParen (d > prec) $+ showString "Some " . showsPrec prec' t .+ showChar ' ' . showsPrec prec' bs .+ showString " _"+ where prec = 10; prec' = prec + 1++newtype CodePoint = CodePoint Word32 deriving (Eq, Show, Num, Storable)+newtype DecoderState = DecoderState Word32 deriving (Eq, Show, Num, Storable)++-- | Decode, in a stream oriented way, a 'ByteString' containing UTF-8+-- encoded text that is known to be valid.+--+-- If the input contains any invalid UTF-8 data, an exception will be+-- thrown (either by this function or a continuation) that cannot be+-- caught in pure code. For more control over the handling of invalid+-- data, use 'streamDecodeUtf8With'.+streamDecodeUtf8 :: ByteString -> Decoding+streamDecodeUtf8 = streamDecodeUtf8With strictDecode++-- | Decode, in a stream oriented way, a 'ByteString' containing UTF-8+-- encoded text.+streamDecodeUtf8With :: OnDecodeError -> ByteString -> Decoding+streamDecodeUtf8With onErr = decodeChunk 0 0+ where+ -- We create a slightly larger than necessary buffer to accommodate a+ -- potential surrogate pair started in the last buffer+ decodeChunk :: CodePoint -> DecoderState -> ByteString -> Decoding+ decodeChunk codepoint0 state0 bs@(PS fp off len) =+ runST $ (unsafeIOToST . decodeChunkToBuffer) =<< A.new (len+1)+ where+ decodeChunkToBuffer :: A.MArray s -> IO Decoding+ decodeChunkToBuffer dest = withForeignPtr fp $ \ptr ->+ with (0::CSize) $ \destOffPtr ->+ with codepoint0 $ \codepointPtr ->+ with state0 $ \statePtr ->+ with nullPtr $ \curPtrPtr ->+ let end = ptr `plusPtr` (off + len)+ loop curPtr = do+ poke curPtrPtr curPtr+ curPtr' <- c_decode_utf8_with_state (A.maBA dest) destOffPtr+ curPtrPtr end codepointPtr statePtr+ state <- peek statePtr+ case state of+ UTF8_REJECT -> do+ -- We encountered an encoding error+ x <- peek curPtr'+ case onErr desc (Just x) of+ Nothing -> loop $ curPtr' `plusPtr` 1+ Just c -> do+ destOff <- peek destOffPtr+ w <- unsafeSTToIO $+ unsafeWrite dest (fromIntegral destOff) (safe c)+ poke destOffPtr (destOff + fromIntegral w)+ poke statePtr 0+ loop $ curPtr' `plusPtr` 1++ _ -> do+ -- We encountered the end of the buffer while decoding+ n <- peek destOffPtr+ codepoint <- peek codepointPtr+ chunkText <- unsafeSTToIO $ do+ arr <- A.unsafeFreeze dest+ return $! textP arr 0 (fromIntegral n)+ lastPtr <- peek curPtrPtr+ let left = lastPtr `minusPtr` curPtr+ return $ Some chunkText (B.drop left bs)+ (decodeChunk codepoint state)+ in loop (ptr `plusPtr` off)+ desc = "Data.Text.Encoding.streamDecodeUtf8With: Invalid UTF-8 stream"+ -- | Decode a 'ByteString' containing UTF-8 encoded text that is known -- to be valid. --@@ -152,7 +282,7 @@ {-# RULES "STREAM stream/decodeUtf8 fusion" [1] forall bs. F.stream (decodeUtf8 bs) = E.streamUtf8 strictDecode bs #-} --- | Decode a 'ByteString' containing UTF-8 encoded text..+-- | Decode a 'ByteString' containing UTF-8 encoded text. -- -- If the input contains any invalid UTF-8 data, the relevant -- exception will be returned, otherwise the decoded text.@@ -295,6 +425,11 @@ foreign import ccall unsafe "_hs_text_decode_utf8" c_decode_utf8 :: MutableByteArray# s -> Ptr CSize -> Ptr Word8 -> Ptr Word8 -> IO (Ptr Word8)++foreign import ccall unsafe "_hs_text_decode_utf8_state" c_decode_utf8_with_state+ :: MutableByteArray# s -> Ptr CSize+ -> Ptr (Ptr Word8) -> Ptr Word8+ -> Ptr CodePoint -> Ptr DecoderState -> IO (Ptr Word8) foreign import ccall unsafe "_hs_text_decode_latin1" c_decode_latin1 :: MutableByteArray# s -> Ptr Word8 -> Ptr Word8 -> IO ()
Data/Text/Encoding/Error.hs view
@@ -39,11 +39,7 @@ ) where import Control.DeepSeq (NFData (..))-#if __GLASGOW_HASKELL__ >= 610 import Control.Exception (Exception, throw)-#else-import Control.Exception.Extensible (Exception, throw)-#endif import Data.Typeable (Typeable) import Data.Word (Word8) import Numeric (showHex)
Data/Text/Foreign.hs view
@@ -21,6 +21,9 @@ , fromPtr , useAsPtr , asForeignPtr+ -- ** Encoding as UTF-8+ , peekCStringLen+ , withCStringLen -- * Unsafe conversion code , lengthWord16 , unsafeCopyToPtr@@ -38,14 +41,17 @@ #else import Control.Monad.ST (unsafeIOToST) #endif+import Data.ByteString.Unsafe (unsafePackCStringLen, unsafeUseAsCStringLen)+import Data.Text.Encoding (decodeUtf8, encodeUtf8) import Data.Text.Internal (Text(..), empty) import Data.Text.Unsafe (lengthWord16)-import qualified Data.Text.Array as A import Data.Word (Word16)+import Foreign.C.String (CStringLen)+import Foreign.ForeignPtr (ForeignPtr, mallocForeignPtrArray, withForeignPtr) import Foreign.Marshal.Alloc (allocaBytes) import Foreign.Ptr (Ptr, castPtr, plusPtr)-import Foreign.ForeignPtr (ForeignPtr, mallocForeignPtrArray, withForeignPtr) import Foreign.Storable (peek, poke)+import qualified Data.Text.Array as A -- $interop --@@ -148,3 +154,21 @@ fp <- mallocForeignPtrArray len withForeignPtr fp $ unsafeCopyToPtr t return (fp, I16 len)++-- | /O(n)/ Decode a C string with explicit length, which is assumed+-- to have been encoded as UTF-8. If decoding fails, a+-- 'UnicodeException' is thrown.+peekCStringLen :: CStringLen -> IO Text+peekCStringLen cs = do+ bs <- unsafePackCStringLen cs+ return $! decodeUtf8 bs++-- | Marshal a 'Text' into a C string encoded as UTF-8 in temporary+-- storage, with explicit length information. The encoded string may+-- contain NUL bytes, and is not followed by a trailing NUL byte.+--+-- The temporary storage is freed when the subcomputation terminates+-- (either normally or via an exception), so the pointer to the+-- temporary storage must /not/ be used after this function returns.+withCStringLen :: Text -> (CStringLen -> IO a) -> IO a+withCStringLen t act = unsafeUseAsCStringLen (encodeUtf8 t) act
Data/Text/Fusion.hs view
@@ -223,9 +223,9 @@ arr' <- A.new top' A.copyM arr' 0 arr 0 top outer arr' top' z s i- | otherwise -> do let (z',c) = f z x- d <- unsafeWrite arr i c+ | otherwise -> do d <- unsafeWrite arr i c loop z' s' (i+d)- where j | ord x < 0x10000 = i+ where (z',c) = f z x+ j | ord c < 0x10000 = i | otherwise = i + 1 {-# INLINE [0] mapAccumL #-}
Data/Text/Fusion/CaseMapping.hs view
@@ -1,6 +1,11 @@ {-# LANGUAGE Rank2Types #-} -- AUTOMATICALLY GENERATED - DO NOT EDIT -- Generated by scripts/SpecialCasing.hs+-- CaseFolding-6.3.0.txt+-- Date: 2012-12-20, 22:14:35 GMT [MD]+-- SpecialCasing-6.3.0.txt+-- Date: 2013-05-08, 13:54:51 GMT [MD]+ module Data.Text.Fusion.CaseMapping where import Data.Char import Data.Text.Fusion.Internal@@ -217,6 +222,105 @@ -- LATIN CAPITAL LETTER I WITH DOT ABOVE lowerMapping '\x0130' s = Yield '\x0069' (CC s '\x0307' '\x0000') lowerMapping c s = Yield (toLower c) (CC s '\0' '\0')+titleMapping :: forall s. Char -> s -> Step (CC s) Char+{-# INLINE titleMapping #-}+-- LATIN SMALL LETTER SHARP S+titleMapping '\x00df' s = Yield '\x0053' (CC s '\x0073' '\x0000')+-- LATIN SMALL LIGATURE FF+titleMapping '\xfb00' s = Yield '\x0046' (CC s '\x0066' '\x0000')+-- LATIN SMALL LIGATURE FI+titleMapping '\xfb01' s = Yield '\x0046' (CC s '\x0069' '\x0000')+-- LATIN SMALL LIGATURE FL+titleMapping '\xfb02' s = Yield '\x0046' (CC s '\x006c' '\x0000')+-- LATIN SMALL LIGATURE FFI+titleMapping '\xfb03' s = Yield '\x0046' (CC s '\x0066' '\x0069')+-- LATIN SMALL LIGATURE FFL+titleMapping '\xfb04' s = Yield '\x0046' (CC s '\x0066' '\x006c')+-- LATIN SMALL LIGATURE LONG S T+titleMapping '\xfb05' s = Yield '\x0053' (CC s '\x0074' '\x0000')+-- LATIN SMALL LIGATURE ST+titleMapping '\xfb06' s = Yield '\x0053' (CC s '\x0074' '\x0000')+-- ARMENIAN SMALL LIGATURE ECH YIWN+titleMapping '\x0587' s = Yield '\x0535' (CC s '\x0582' '\x0000')+-- ARMENIAN SMALL LIGATURE MEN NOW+titleMapping '\xfb13' s = Yield '\x0544' (CC s '\x0576' '\x0000')+-- ARMENIAN SMALL LIGATURE MEN ECH+titleMapping '\xfb14' s = Yield '\x0544' (CC s '\x0565' '\x0000')+-- ARMENIAN SMALL LIGATURE MEN INI+titleMapping '\xfb15' s = Yield '\x0544' (CC s '\x056b' '\x0000')+-- ARMENIAN SMALL LIGATURE VEW NOW+titleMapping '\xfb16' s = Yield '\x054e' (CC s '\x0576' '\x0000')+-- ARMENIAN SMALL LIGATURE MEN XEH+titleMapping '\xfb17' s = Yield '\x0544' (CC s '\x056d' '\x0000')+-- LATIN SMALL LETTER N PRECEDED BY APOSTROPHE+titleMapping '\x0149' s = Yield '\x02bc' (CC s '\x004e' '\x0000')+-- GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS+titleMapping '\x0390' s = Yield '\x0399' (CC s '\x0308' '\x0301')+-- GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS+titleMapping '\x03b0' s = Yield '\x03a5' (CC s '\x0308' '\x0301')+-- LATIN SMALL LETTER J WITH CARON+titleMapping '\x01f0' s = Yield '\x004a' (CC s '\x030c' '\x0000')+-- LATIN SMALL LETTER H WITH LINE BELOW+titleMapping '\x1e96' s = Yield '\x0048' (CC s '\x0331' '\x0000')+-- LATIN SMALL LETTER T WITH DIAERESIS+titleMapping '\x1e97' s = Yield '\x0054' (CC s '\x0308' '\x0000')+-- LATIN SMALL LETTER W WITH RING ABOVE+titleMapping '\x1e98' s = Yield '\x0057' (CC s '\x030a' '\x0000')+-- LATIN SMALL LETTER Y WITH RING ABOVE+titleMapping '\x1e99' s = Yield '\x0059' (CC s '\x030a' '\x0000')+-- LATIN SMALL LETTER A WITH RIGHT HALF RING+titleMapping '\x1e9a' s = Yield '\x0041' (CC s '\x02be' '\x0000')+-- GREEK SMALL LETTER UPSILON WITH PSILI+titleMapping '\x1f50' s = Yield '\x03a5' (CC s '\x0313' '\x0000')+-- GREEK SMALL LETTER UPSILON WITH PSILI AND VARIA+titleMapping '\x1f52' s = Yield '\x03a5' (CC s '\x0313' '\x0300')+-- GREEK SMALL LETTER UPSILON WITH PSILI AND OXIA+titleMapping '\x1f54' s = Yield '\x03a5' (CC s '\x0313' '\x0301')+-- GREEK SMALL LETTER UPSILON WITH PSILI AND PERISPOMENI+titleMapping '\x1f56' s = Yield '\x03a5' (CC s '\x0313' '\x0342')+-- GREEK SMALL LETTER ALPHA WITH PERISPOMENI+titleMapping '\x1fb6' s = Yield '\x0391' (CC s '\x0342' '\x0000')+-- GREEK SMALL LETTER ETA WITH PERISPOMENI+titleMapping '\x1fc6' s = Yield '\x0397' (CC s '\x0342' '\x0000')+-- GREEK SMALL LETTER IOTA WITH DIALYTIKA AND VARIA+titleMapping '\x1fd2' s = Yield '\x0399' (CC s '\x0308' '\x0300')+-- GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA+titleMapping '\x1fd3' s = Yield '\x0399' (CC s '\x0308' '\x0301')+-- GREEK SMALL LETTER IOTA WITH PERISPOMENI+titleMapping '\x1fd6' s = Yield '\x0399' (CC s '\x0342' '\x0000')+-- GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI+titleMapping '\x1fd7' s = Yield '\x0399' (CC s '\x0308' '\x0342')+-- GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND VARIA+titleMapping '\x1fe2' s = Yield '\x03a5' (CC s '\x0308' '\x0300')+-- GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA+titleMapping '\x1fe3' s = Yield '\x03a5' (CC s '\x0308' '\x0301')+-- GREEK SMALL LETTER RHO WITH PSILI+titleMapping '\x1fe4' s = Yield '\x03a1' (CC s '\x0313' '\x0000')+-- GREEK SMALL LETTER UPSILON WITH PERISPOMENI+titleMapping '\x1fe6' s = Yield '\x03a5' (CC s '\x0342' '\x0000')+-- GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI+titleMapping '\x1fe7' s = Yield '\x03a5' (CC s '\x0308' '\x0342')+-- GREEK SMALL LETTER OMEGA WITH PERISPOMENI+titleMapping '\x1ff6' s = Yield '\x03a9' (CC s '\x0342' '\x0000')+-- GREEK SMALL LETTER ALPHA WITH VARIA AND YPOGEGRAMMENI+titleMapping '\x1fb2' s = Yield '\x1fba' (CC s '\x0345' '\x0000')+-- GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI+titleMapping '\x1fb4' s = Yield '\x0386' (CC s '\x0345' '\x0000')+-- GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI+titleMapping '\x1fc2' s = Yield '\x1fca' (CC s '\x0345' '\x0000')+-- GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI+titleMapping '\x1fc4' s = Yield '\x0389' (CC s '\x0345' '\x0000')+-- GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI+titleMapping '\x1ff2' s = Yield '\x1ffa' (CC s '\x0345' '\x0000')+-- GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI+titleMapping '\x1ff4' s = Yield '\x038f' (CC s '\x0345' '\x0000')+-- GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI+titleMapping '\x1fb7' s = Yield '\x0391' (CC s '\x0342' '\x0345')+-- GREEK SMALL LETTER ETA WITH PERISPOMENI AND YPOGEGRAMMENI+titleMapping '\x1fc7' s = Yield '\x0397' (CC s '\x0342' '\x0345')+-- GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI+titleMapping '\x1ff7' s = Yield '\x03a9' (CC s '\x0342' '\x0345')+titleMapping c s = Yield (toTitle c) (CC s '\0' '\0') foldMapping :: forall s. Char -> s -> Step (CC s) Char {-# INLINE foldMapping #-} -- MICRO SIGN@@ -255,6 +359,10 @@ foldMapping '\x03f5' s = Yield '\x03b5' (CC s '\x0000' '\x0000') -- ARMENIAN SMALL LIGATURE ECH YIWN foldMapping '\x0587' s = Yield '\x0565' (CC s '\x0582' '\x0000')+-- GEORGIAN CAPITAL LETTER YN+foldMapping '\x10c7' s = Yield '\x2d27' (CC s '\x0000' '\x0000')+-- GEORGIAN CAPITAL LETTER AEN+foldMapping '\x10cd' s = Yield '\x2d2d' (CC s '\x0000' '\x0000') -- LATIN SMALL LETTER H WITH LINE BELOW foldMapping '\x1e96' s = Yield '\x0068' (CC s '\x0331' '\x0000') -- LATIN SMALL LETTER T WITH DIAERESIS@@ -429,6 +537,12 @@ foldMapping '\x1ff7' s = Yield '\x03c9' (CC s '\x0342' '\x03b9') -- GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI foldMapping '\x1ffc' s = Yield '\x03c9' (CC s '\x03b9' '\x0000')+-- COPTIC CAPITAL LETTER BOHAIRIC KHEI+foldMapping '\x2cf2' s = Yield '\x2cf3' (CC s '\x0000' '\x0000')+-- LATIN CAPITAL LETTER C WITH BAR+foldMapping '\xa792' s = Yield '\xa793' (CC s '\x0000' '\x0000')+-- LATIN CAPITAL LETTER H WITH HOOK+foldMapping '\xa7aa' s = Yield '\x0266' (CC s '\x0000' '\x0000') -- LATIN SMALL LIGATURE FF foldMapping '\xfb00' s = Yield '\x0066' (CC s '\x0066' '\x0000') -- LATIN SMALL LIGATURE FI
Data/Text/Fusion/Common.hs view
@@ -42,6 +42,7 @@ -- $case , toCaseFold , toLower+ , toTitle , toUpper -- ** Justification@@ -102,13 +103,15 @@ import Prelude (Bool(..), Char, Eq(..), Int, Integral, Maybe(..), Ord(..), Ordering(..), String, (.), ($), (+), (-), (*), (++),- (&&), fromIntegral, otherwise)+ (&&), fromIntegral, not, otherwise) import qualified Data.List as L import qualified Prelude as P import Data.Bits (shiftL)+import Data.Char (isLetter) import Data.Int (Int64) import Data.Text.Fusion.Internal-import Data.Text.Fusion.CaseMapping (foldMapping, lowerMapping, upperMapping)+import Data.Text.Fusion.CaseMapping (foldMapping, lowerMapping, titleMapping,+ upperMapping) import Data.Text.Fusion.Size import GHC.Prim (Addr#, chr#, indexCharOffAddr#, ord#) import GHC.Types (Char(..), Int(..))@@ -431,6 +434,40 @@ toLower :: Stream Char -> Stream Char toLower = caseConvert lowerMapping {-# INLINE [0] toLower #-}++-- | /O(n)/ Convert a string to title case, using simple case+-- conversion.+--+-- The first letter of the input is converted to title case, as is+-- every subsequent letter that immediately follows a non-letter.+-- Every letter that immediately follows another letter is converted+-- to lower case.+--+-- The result string may be longer than the input string. For example,+-- the Latin small ligature fl (U+FB02) is converted to the+-- sequence Latin capital letter F (U+0046) followed by Latin small+-- letter l (U+006C).+--+-- /Note/: this function does not take language or culture specific+-- rules into account. For instance, in English, different style+-- guides disagree on whether the book name \"The Hill of the Red+-- Fox\" is correctly title cased—but this function will+-- capitalize /every/ word.+toTitle :: Stream Char -> Stream Char+toTitle (Stream next0 s0 len) = Stream next (CC (False :*: s0) '\0' '\0') len+ where+ next (CC (letter :*: s) '\0' _) =+ case next0 s of+ Done -> Done+ Skip s' -> Skip (CC (letter :*: s') '\0' '\0')+ Yield c s'+ | letter' -> if letter+ then lowerMapping c (letter' :*: s')+ else titleMapping c (letter' :*: s')+ | otherwise -> Yield c (CC (letter' :*: s') '\0' '\0')+ where letter' = isLetter c+ next (CC s a b) = Yield a (CC s b '\0')+{-# INLINE [0] toTitle #-} justifyLeftI :: Integral a => a -> Char -> Stream Char -> Stream Char justifyLeftI k c (Stream next0 s0 len) =
Data/Text/IO.hs view
@@ -46,10 +46,6 @@ 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 qualified Control.Exception as E import Control.Monad (liftM2, when) import Data.IORef (readIORef, writeIORef)@@ -68,7 +64,6 @@ HandleType(..), Newline(..)) import System.IO (hGetBuffering, hFileSize, hSetBuffering, hTell) import System.IO.Error (isEOFError)-#endif -- $performance -- #performance#@@ -137,9 +132,6 @@ -- result to construct its result. For files more than a half of -- available RAM in size, this may result in memory exhaustion. hGetContents :: Handle -> IO Text-#if __GLASGOW_HASKELL__ <= 610-hGetContents = fmap decodeUtf8 . B.hGetContents-#else hGetContents h = do chooseGoodBuffering h wantReadableHandle "hGetContents" h readAll@@ -171,21 +163,13 @@ else E.throwIO e when (d > 0) . hSetBuffering h . BlockBuffering . Just . fromIntegral $ d _ -> return ()-#endif -- | Read a single line from a handle. hGetLine :: Handle -> IO Text-#if __GLASGOW_HASKELL__ <= 610-hGetLine = fmap decodeUtf8 . B.hGetLine-#else hGetLine = hGetLineWith T.concat-#endif -- | Write a string to a handle. hPutStr :: Handle -> Text -> IO ()-#if __GLASGOW_HASKELL__ <= 610-hPutStr h = B.hPutStr h . encodeUtf8-#else -- This function is lifted almost verbatim from GHC.IO.Handle.Text. hPutStr h t = do (buffer_mode, nl) <-@@ -295,7 +279,6 @@ wantWritableHandle "commitAndReleaseBuffer" hdl $ commitBuffer' raw sz count flush release {-# INLINE commitBuffer #-}-#endif -- | Write a string to a handle, followed by a newline. hPutStrLn :: Handle -> Text -> IO ()
Data/Text/IO/Internal.hs view
@@ -12,13 +12,10 @@ module Data.Text.IO.Internal (-#if __GLASGOW_HASKELL__ >= 612 hGetLineWith , readChunk-#endif ) where -#if __GLASGOW_HASKELL__ >= 612 import qualified Control.Exception as E import Data.IORef (readIORef, writeIORef) import Data.Text (Text)@@ -163,4 +160,3 @@ sizeError :: String -> a sizeError loc = error $ "Data.Text.IO." ++ loc ++ ": bad internal buffer size"-#endif
Data/Text/Lazy.hs view
@@ -86,6 +86,7 @@ , toCaseFold , toLower , toUpper+ , toTitle -- ** Justification , justifyLeft@@ -202,11 +203,7 @@ import qualified Data.List as L import Data.Char (isSpace) import Data.Data (Data(gfoldl, toConstr, gunfold, dataTypeOf))-#if __GLASGOW_HASKELL__ >= 612 import Data.Data (mkNoRepType)-#else-import Data.Data (mkNorepType)-#endif import Data.Monoid (Monoid(..)) import Data.String (IsString(..)) import qualified Data.Text as T@@ -341,11 +338,7 @@ gfoldl f z txt = z pack `f` (unpack txt) toConstr _ = error "Data.Text.Lazy.Text.toConstr" gunfold _ _ = error "Data.Text.Lazy.Text.gunfold"-#if __GLASGOW_HASKELL__ >= 612 dataTypeOf _ = mkNoRepType "Data.Text.Lazy.Text"-#else- dataTypeOf _ = mkNorepType "Data.Text.Lazy.Text"-#endif -- | /O(n)/ Convert a 'String' into a 'Text'. --@@ -670,10 +663,11 @@ -- functions may map one input character to two or three output -- characters. --- | /O(n)/ Convert a string to folded case. This function is mainly--- useful for performing caseless (or case insensitive) string--- comparisons.+-- | /O(n)/ Convert a string to folded case. Subject to fusion. --+-- This function is mainly useful for performing caseless (or case+-- insensitive) string comparisons.+-- -- A string @x@ is a caseless match for a string @y@ if and only if: -- -- @toCaseFold x == toCaseFold y@@@ -689,21 +683,48 @@ {-# INLINE [0] toCaseFold #-} -- | /O(n)/ Convert a string to lower case, using simple case--- conversion. The result string may be longer than the input string.--- For instance, the Latin capital letter I with dot above (U+0130)--- maps to the sequence Latin small letter i (U+0069) followed by--- combining dot above (U+0307).+-- conversion. Subject to fusion.+--+-- The result string may be longer than the input string. For+-- instance, the Latin capital letter I with dot above (U+0130) maps+-- to the sequence Latin small letter i (U+0069) followed by combining+-- dot above (U+0307). toLower :: Text -> Text toLower t = unstream (S.toLower (stream t)) {-# INLINE toLower #-} -- | /O(n)/ Convert a string to upper case, using simple case--- conversion. The result string may be longer than the input string.--- For instance, the German eszett (U+00DF) maps to the two-letter+-- conversion. Subject to fusion.+--+-- The result string may be longer than the input string. For+-- instance, the German eszett (U+00DF) maps to the two-letter -- sequence SS. toUpper :: Text -> Text toUpper t = unstream (S.toUpper (stream t)) {-# INLINE toUpper #-}+++-- | /O(n)/ Convert a string to title case, using simple case+-- conversion. Subject to fusion.+--+-- The first letter of the input is converted to title case, as is+-- every subsequent letter that immediately follows a non-letter.+-- Every letter that immediately follows another letter is converted+-- to lower case.+--+-- The result string may be longer than the input string. For example,+-- the Latin small ligature fl (U+FB02) is converted to the+-- sequence Latin capital letter F (U+0046) followed by Latin small+-- letter l (U+006C).+--+-- /Note/: this function does not take language or culture specific+-- rules into account. For instance, in English, different style+-- guides disagree on whether the book name \"The Hill of the Red+-- Fox\" is correctly title cased—but this function will+-- capitalize /every/ word.+toTitle :: Text -> Text+toTitle t = unstream (S.toTitle (stream t))+{-# INLINE toTitle #-} -- | /O(n)/ 'foldl', applied to a binary operator, a starting value -- (typically the left-identity of the operator), and a 'Text',
Data/Text/Lazy/Builder.hs view
@@ -1,4 +1,7 @@ {-# LANGUAGE BangPatterns, CPP, Rank2Types #-}+#if __GLASGOW_HASKELL__ >= 702+{-# LANGUAGE Trustworthy #-}+#endif ----------------------------------------------------------------------------- -- |@@ -26,8 +29,14 @@ -- -- > singleton 'a' `mappend` singleton 'b' `mappend` singleton 'c' ----- as the latter associates @mappend@ to the left.+-- as the latter associates @mappend@ to the left. Or, equivalently,+-- prefer --+-- > singleton 'a' <> singleton 'b' <> singleton 'c'+--+-- since the '<>' from recent versions of 'Data.Monoid' associates +-- to the right.+ ----------------------------------------------------------------------------- module Data.Text.Lazy.Builder
Data/Text/Lazy/Builder/Int.hs view
@@ -1,4 +1,7 @@ {-# LANGUAGE BangPatterns, CPP, MagicHash, RankNTypes, UnboxedTuples #-}+#if __GLASGOW_HASKELL__ >= 702+{-# LANGUAGE Trustworthy #-}+#endif -- Module: Data.Text.Lazy.Builder.Int -- Copyright: (c) 2013 Bryan O'Sullivan@@ -30,9 +33,7 @@ import Control.Monad.ST #ifdef __GLASGOW_HASKELL__-# if __GLASGOW_HASKELL__ < 611-import GHC.Integer.Internals-# elif defined(INTEGER_GMP)+# if defined(INTEGER_GMP) import GHC.Integer.GMP.Internals # elif defined(INTEGER_SIMPLE) import GHC.Integer
Data/Text/Lazy/Builder/RealFloat.hs view
@@ -1,4 +1,7 @@-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE CPP, OverloadedStrings #-}+#if __GLASGOW_HASKELL__ >= 702+{-# LANGUAGE Trustworthy #-}+#endif -- | -- Module: Data.Text.Lazy.Builder.RealFloat
Data/Text/Lazy/Encoding.hs view
@@ -49,15 +49,14 @@ ) where import Control.Exception (evaluate, try)-import Data.Bits ((.&.)) import Data.Text.Encoding.Error (OnDecodeError, UnicodeException, strictDecode) import Data.Text.Lazy.Internal (Text(..), chunk, empty, foldrChunks) import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as B import qualified Data.ByteString.Lazy.Internal as B-import qualified Data.ByteString.Unsafe as S-import qualified Data.Text as T+import qualified Data.ByteString.Unsafe as B import qualified Data.Text.Encoding as TE+import qualified Data.Text.Lazy as L import qualified Data.Text.Lazy.Encoding.Fusion as E import qualified Data.Text.Lazy.Fusion as F import Data.Text.Unsafe (unsafeDupablePerformIO)@@ -89,41 +88,20 @@ -- | Decode a 'ByteString' containing UTF-8 encoded text. decodeUtf8With :: OnDecodeError -> B.ByteString -> Text-decodeUtf8With onErr bs0 = fast bs0+decodeUtf8With onErr (B.Chunk b0 bs0) =+ case TE.streamDecodeUtf8With onErr b0 of+ TE.Some t l f -> chunk t (go f l bs0) where- decode = TE.decodeUtf8With onErr- fast (B.Chunk p ps) | isComplete p = chunk (decode p) (fast ps)- | otherwise = chunk (decode h) (slow t ps)- where (h,t) = S.splitAt pivot p- pivot | at 1 = len-1- | at 2 = len-2- | otherwise = len-3- len = S.length p- at n = len >= n && S.unsafeIndex p (len-n) .&. 0xc0 == 0xc0- fast B.Empty = empty- slow i bs = {-# SCC "decodeUtf8With'/slow" #-}- case B.uncons bs of- Just (w,bs') | isComplete i' -> chunk (decode i') (fast bs')- | otherwise -> slow i' bs'- where i' = S.snoc i w- Nothing -> case S.uncons i of- Just (j,i') ->- case onErr desc (Just j) of- Nothing -> slow i' bs- Just c -> Chunk (T.singleton c) (slow i' bs)- Nothing ->- case onErr desc Nothing of- Nothing -> empty- Just c -> Chunk (T.singleton c) empty- isComplete bs = {-# SCC "decodeUtf8With'/isComplete" #-}- ix 1 .&. 0x80 == 0 ||- (len >= 2 && ix 2 .&. 0xe0 == 0xc0) ||- (len >= 3 && ix 3 .&. 0xf0 == 0xe0) ||- (len >= 4 && ix 4 .&. 0xf8 == 0xf0)- where len = S.length bs- ix n = S.unsafeIndex bs (len-n)+ go f0 _ (B.Chunk b bs) =+ case f0 b of+ TE.Some t l f -> chunk t (go f l bs)+ go _ l _+ | S.null l = empty+ | otherwise = case onErr desc (Just (B.unsafeHead l)) of+ Nothing -> empty+ Just c -> L.singleton c desc = "Data.Text.Lazy.Encoding.decodeUtf8With: Invalid UTF-8 stream"-{-# INLINE[0] decodeUtf8With #-}+decodeUtf8With _ _ = empty -- | Decode a 'ByteString' containing UTF-8 encoded text that is known -- to be valid.
Data/Text/Lazy/IO.hs view
@@ -47,11 +47,6 @@ withFile) import qualified Data.Text.IO as T import qualified Data.Text.Lazy as L-#if __GLASGOW_HASKELL__ <= 610-import Data.Text.Lazy.Encoding (decodeUtf8)-import qualified Data.ByteString.Char8 as S8-import qualified Data.ByteString.Lazy.Char8 as L8-#else import qualified Control.Exception as E import Control.Monad (when) import Data.IORef (readIORef)@@ -65,7 +60,6 @@ import System.IO (BufferMode(..), hGetBuffering, hSetBuffering) import System.IO.Error (isEOFError) import System.IO.Unsafe (unsafeInterleaveIO)-#endif -- $performance --@@ -99,9 +93,6 @@ -- | Lazily read the remaining contents of a 'Handle'. The 'Handle' -- will be closed after the read completes, or on error. hGetContents :: Handle -> IO Text-#if __GLASGOW_HASKELL__ <= 610-hGetContents = fmap decodeUtf8 . L8.hGetContents-#else hGetContents h = do chooseGoodBuffering h wantReadableHandle "hGetContents" h $ \hh -> do@@ -138,15 +129,10 @@ then (hh', empty) else (hh', L.singleton '\r') else E.throwIO (augmentIOError e "hGetContents" h)-#endif -- | Read a single line from a handle. hGetLine :: Handle -> IO Text-#if __GLASGOW_HASKELL__ <= 610-hGetLine = fmap (decodeUtf8 . L8.fromChunks . (:[])) . S8.hGetLine-#else hGetLine = hGetLineWith L.fromChunks-#endif -- | Write a string to a handle. hPutStr :: Handle -> Text -> IO ()
Data/Text/Search.hs view
@@ -42,7 +42,8 @@ -- | /O(n+m)/ Find the offsets of all non-overlapping indices of -- @needle@ within @haystack@. The offsets returned represent--- locations in the low-level array.+-- uncorrected indices in the low-level \"needle\" array, to which its+-- offset must be added. -- -- In (unlikely) bad cases, this algorithm's complexity degrades -- towards /O(n*m)/.
Data/Text/Unsafe.hs view
@@ -34,11 +34,7 @@ import Data.Text.Unsafe.Base (inlineInterleaveST, inlinePerformIO) import Data.Text.UnsafeChar (unsafeChr) import qualified Data.Text.Array as A-#if __GLASGOW_HASKELL__ >= 611 import GHC.IO (unsafeDupablePerformIO)-#else-import GHC.IOBase (unsafeDupablePerformIO)-#endif -- | /O(1)/ A variant of 'head' for non-empty 'Text'. 'unsafeHead' -- omits the check for the empty case, so there is an obligation on@@ -51,7 +47,7 @@ n = A.unsafeIndex arr (off+1) {-# INLINE unsafeHead #-} --- | /O(1)/ A variant of 'tail' for non-empty 'Text'. 'unsafeHead'+-- | /O(1)/ A variant of 'tail' for non-empty 'Text'. 'unsafeTail' -- 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. unsafeTail :: Text -> Text
Data/Text/Unsafe/Base.hs view
@@ -18,11 +18,7 @@ import GHC.ST (ST(..)) #if defined(__GLASGOW_HASKELL__)-# if __GLASGOW_HASKELL__ >= 611 import GHC.IO (IO(IO))-# else-import GHC.IOBase (IO(IO))-# endif import GHC.Base (realWorld#) #endif
benchmarks/haskell/Benchmarks/DecodeUtf8.hs view
@@ -20,6 +20,7 @@ import Foreign.C.Types import Data.ByteString.Internal (ByteString(..))+import Data.ByteString.Lazy.Internal (ByteString(..)) import Foreign.Ptr (Ptr, plusPtr) import Foreign.ForeignPtr (withForeignPtr) import Data.Word (Word8)@@ -38,8 +39,15 @@ bs <- B.readFile fp lbs <- BL.readFile fp let bench name = C.bench (name ++ "+" ++ kind)+ decodeStream (Chunk b0 bs0) = case T.streamDecodeUtf8 b0 of+ T.Some t0 _ f0 -> t0 : go f0 bs0+ where go f (Chunk b bs1) = case f b of+ T.Some t1 _ f1 -> t1 : go f1 bs1+ go _ _ = []+ decodeStream _ = [] return $ bgroup "DecodeUtf8" [ bench "Strict" $ nf T.decodeUtf8 bs+ , bench "Stream" $ nf decodeStream lbs , bench "IConv" $ iconv bs , bench "StrictLength" $ nf (T.length . T.decodeUtf8) bs , bench "StrictInitLength" $ nf (T.length . T.init . T.decodeUtf8) bs@@ -52,7 +60,7 @@ , bench "LazyStringUtf8Length" $ nf (length . U8.toString) lbs ] -iconv :: ByteString -> IO CInt+iconv :: B.ByteString -> IO CInt iconv (PS fp off len) = withForeignPtr fp $ \ptr -> time_iconv (ptr `plusPtr` off) (fromIntegral len)
+ benchmarks/haskell/Benchmarks/Programs/BigTable.hs view
@@ -0,0 +1,42 @@+-- | Create a large HTML table and dump it to a handle+--+-- Tested in this benchmark:+--+-- * Creating a large HTML document using a builder+--+-- * Writing to a handle+--+{-# LANGUAGE OverloadedStrings #-}+module Benchmarks.Programs.BigTable+ ( benchmark+ ) where++import Criterion (Benchmark, bench)+import Data.Monoid (mappend, mconcat)+import Data.Text.Lazy.Builder (Builder, fromText, toLazyText)+import Data.Text.Lazy.IO (hPutStr)+import System.IO (Handle)+import qualified Data.Text as T++benchmark :: Handle -> IO Benchmark+benchmark sink = return $ bench "BigTable" $ do+ hPutStr sink "Content-Type: text/html\n\n<table>"+ hPutStr sink . toLazyText . makeTable =<< rows+ hPutStr sink "</table>"+ where+ -- We provide the number of rows in IO so the builder value isn't shared+ -- between the benchmark samples.+ rows :: IO Int+ rows = return 20000+ {-# NOINLINE rows #-}++makeTable :: Int -> Builder+makeTable n = mconcat $ replicate n $ mconcat $ map makeCol [1 .. 50]++makeCol :: Int -> Builder+makeCol 1 = fromText "<tr><td>1</td>"+makeCol 50 = fromText "<td>50</td></tr>"+makeCol i = fromText "<td>" `mappend` (fromInt i `mappend` fromText "</td>")++fromInt :: Int -> Builder+fromInt = fromText . T.pack . show
+ benchmarks/haskell/Benchmarks/Programs/Cut.hs view
@@ -0,0 +1,98 @@+-- | Cut into a file, selecting certain columns (e.g. columns 10 to 40)+--+-- Tested in this benchmark:+--+-- * Reading the file+--+-- * Splitting into lines+--+-- * Taking a number of characters from the lines+--+-- * Joining the lines+--+-- * Writing back to a handle+--+module Benchmarks.Programs.Cut+ ( benchmark+ ) where++import Criterion (Benchmark, bgroup, bench)+import System.IO (Handle, hPutStr)+import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as BC+import qualified Data.ByteString.Lazy as BL+import qualified Data.ByteString.Lazy.Char8 as BLC+import qualified Data.Text as T+import qualified Data.Text.Encoding as T+import qualified Data.Text.IO as T+import qualified Data.Text.Lazy as TL+import qualified Data.Text.Lazy.Encoding as TL+import qualified Data.Text.Lazy.IO as TL++benchmark :: FilePath -> Handle -> Int -> Int -> IO Benchmark+benchmark p sink from to = return $ bgroup "Cut"+ [ bench' "String" string+ , bench' "ByteString" byteString+ , bench' "LazyByteString" lazyByteString+ , bench' "Text" text+ , bench' "LazyText" lazyText+ , bench' "TextByteString" textByteString+ , bench' "LazyTextByteString" lazyTextByteString+ ]+ where+ bench' n s = bench n (s p sink from to)++string :: FilePath -> Handle -> Int -> Int -> IO ()+string fp sink from to = do+ s <- readFile fp+ hPutStr sink $ cut s+ where+ cut = unlines . map (take (to - from) . drop from) . lines++byteString :: FilePath -> Handle -> Int -> Int -> IO ()+byteString fp sink from to = do+ bs <- B.readFile fp+ B.hPutStr sink $ cut bs+ where+ cut = BC.unlines . map (B.take (to - from) . B.drop from) . BC.lines++lazyByteString :: FilePath -> Handle -> Int -> Int -> IO ()+lazyByteString fp sink from to = do+ bs <- BL.readFile fp+ BL.hPutStr sink $ cut bs+ where+ cut = BLC.unlines . map (BL.take (to' - from') . BL.drop from') . BLC.lines+ from' = fromIntegral from+ to' = fromIntegral to++text :: FilePath -> Handle -> Int -> Int -> IO ()+text fp sink from to = do+ t <- T.readFile fp+ T.hPutStr sink $ cut t+ where+ cut = T.unlines . map (T.take (to - from) . T.drop from) . T.lines++lazyText :: FilePath -> Handle -> Int -> Int -> IO ()+lazyText fp sink from to = do+ t <- TL.readFile fp+ TL.hPutStr sink $ cut t+ where+ cut = TL.unlines . map (TL.take (to' - from') . TL.drop from') . TL.lines+ from' = fromIntegral from+ to' = fromIntegral to++textByteString :: FilePath -> Handle -> Int -> Int -> IO ()+textByteString fp sink from to = do+ t <- T.decodeUtf8 `fmap` B.readFile fp+ B.hPutStr sink $ T.encodeUtf8 $ cut t+ where+ cut = T.unlines . map (T.take (to - from) . T.drop from) . T.lines++lazyTextByteString :: FilePath -> Handle -> Int -> Int -> IO ()+lazyTextByteString fp sink from to = do+ t <- TL.decodeUtf8 `fmap` BL.readFile fp+ BL.hPutStr sink $ TL.encodeUtf8 $ cut t+ where+ cut = TL.unlines . map (TL.take (to' - from') . TL.drop from') . TL.lines+ from' = fromIntegral from+ to' = fromIntegral to
+ benchmarks/haskell/Benchmarks/Programs/Fold.hs view
@@ -0,0 +1,68 @@+-- | Benchmark which formats paragraph, like the @sort@ unix utility.+--+-- Tested in this benchmark:+--+-- * Reading the file+--+-- * Splitting into paragraphs+--+-- * Reformatting the paragraphs to a certain line width+--+-- * Concatenating the results using the text builder+--+-- * Writing back to a handle+--+{-# LANGUAGE OverloadedStrings #-}+module Benchmarks.Programs.Fold+ ( benchmark+ ) where++import Data.List (foldl')+import Data.List (intersperse)+import Data.Monoid (mempty, mappend, mconcat)+import System.IO (Handle)+import Criterion (Benchmark, bench)+import qualified Data.Text as T+import qualified Data.Text.IO as T+import qualified Data.Text.Lazy.Builder as TLB+import qualified Data.Text.Lazy as TL+import qualified Data.Text.Lazy.IO as TL++benchmark :: FilePath -> Handle -> IO Benchmark+benchmark i o = return $+ bench "Fold" $ T.readFile i >>= TL.hPutStr o . fold 80++-- | We represent a paragraph by a word list+--+type Paragraph = [T.Text]++-- | Fold a text+--+fold :: Int -> T.Text -> TL.Text+fold maxWidth = TLB.toLazyText . mconcat .+ intersperse "\n\n" . map (foldParagraph maxWidth) . paragraphs++-- | Fold a paragraph+--+foldParagraph :: Int -> Paragraph -> TLB.Builder+foldParagraph _ [] = mempty+foldParagraph max' (w : ws) = fst $ foldl' go (TLB.fromText w, T.length w) ws+ where+ go (builder, width) word+ | width + len + 1 <= max' =+ (builder `mappend` " " `mappend` word', width + len + 1)+ | otherwise =+ (builder `mappend` "\n" `mappend` word', len)+ where+ word' = TLB.fromText word+ len = T.length word++-- | Divide a text into paragraphs+--+paragraphs :: T.Text -> [Paragraph]+paragraphs = splitParagraphs . map T.words . T.lines+ where+ splitParagraphs ls = case break null ls of+ ([], []) -> []+ (p, []) -> [concat p]+ (p, lr) -> concat p : splitParagraphs (dropWhile null lr)
+ benchmarks/haskell/Benchmarks/Programs/Sort.hs view
@@ -0,0 +1,70 @@+-- | This benchmark sorts the lines of a file, like the @sort@ unix utility.+--+-- Tested in this benchmark:+--+-- * Reading the file+--+-- * Splitting into lines+--+-- * Sorting the lines+--+-- * Joining the lines+--+-- * Writing back to a handle+--+{-# LANGUAGE OverloadedStrings #-}+module Benchmarks.Programs.Sort+ ( benchmark+ ) where++import Criterion (Benchmark, bgroup, bench)+import Data.Monoid (mconcat)+import System.IO (Handle, hPutStr)+import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as BC+import qualified Data.ByteString.Lazy as BL+import qualified Data.ByteString.Lazy.Char8 as BLC+import qualified Data.List as L+import qualified Data.Text as T+import qualified Data.Text.Encoding as T+import qualified Data.Text.IO as T+import qualified Data.Text.Lazy as TL+import qualified Data.Text.Lazy.Builder as TLB+import qualified Data.Text.Lazy.Encoding as TL+import qualified Data.Text.Lazy.IO as TL++benchmark :: FilePath -> Handle -> IO Benchmark+benchmark i o = return $ bgroup "Sort"+ [ bench "String" $ readFile i >>= hPutStr o . string+ , bench "ByteString" $ B.readFile i >>= B.hPutStr o . byteString+ , bench "LazyByteString" $ BL.readFile i >>= BL.hPutStr o . lazyByteString+ , bench "Text" $ T.readFile i >>= T.hPutStr o . text+ , bench "LazyText" $ TL.readFile i >>= TL.hPutStr o . lazyText+ , bench "TextByteString" $ B.readFile i >>=+ B.hPutStr o . T.encodeUtf8 . text . T.decodeUtf8+ , bench "LazyTextByteString" $ BL.readFile i >>=+ BL.hPutStr o . TL.encodeUtf8 . lazyText . TL.decodeUtf8+ , bench "TextBuilder" $ B.readFile i >>=+ BL.hPutStr o . TL.encodeUtf8 . textBuilder . T.decodeUtf8+ ]++string :: String -> String+string = unlines . L.sort . lines++byteString :: B.ByteString -> B.ByteString+byteString = BC.unlines . L.sort . BC.lines++lazyByteString :: BL.ByteString -> BL.ByteString+lazyByteString = BLC.unlines . L.sort . BLC.lines++text :: T.Text -> T.Text+text = T.unlines . L.sort . T.lines++lazyText :: TL.Text -> TL.Text+lazyText = TL.unlines . L.sort . TL.lines++-- | Text variant using a builder monoid for the final concatenation+--+textBuilder :: T.Text -> TL.Text+textBuilder = TLB.toLazyText . mconcat . L.intersperse (TLB.singleton '\n') .+ map TLB.fromText . L.sort . T.lines
+ benchmarks/haskell/Benchmarks/Programs/StripTags.hs view
@@ -0,0 +1,53 @@+-- | Program to replace HTML tags by whitespace+--+-- This program was originally contributed by Petr Prokhorenkov.+--+-- Tested in this benchmark:+--+-- * Reading the file+--+-- * Replacing text between HTML tags (<>) with whitespace+--+-- * Writing back to a handle+--+{-# OPTIONS_GHC -fspec-constr-count=5 #-}+module Benchmarks.Programs.StripTags+ ( benchmark+ ) where++import Criterion (Benchmark, bgroup, bench)+import Data.List (mapAccumL)+import System.IO (Handle, hPutStr)+import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as BC+import qualified Data.Text as T+import qualified Data.Text.Encoding as T+import qualified Data.Text.IO as T++benchmark :: FilePath -> Handle -> IO Benchmark+benchmark i o = return $ bgroup "StripTags"+ [ bench "String" $ readFile i >>= hPutStr o . string+ , bench "ByteString" $ B.readFile i >>= B.hPutStr o . byteString+ , bench "Text" $ T.readFile i >>= T.hPutStr o . text+ , bench "TextByteString" $+ B.readFile i >>= B.hPutStr o . T.encodeUtf8 . text . T.decodeUtf8+ ]++string :: String -> String+string = snd . mapAccumL step 0++text :: T.Text -> T.Text+text = snd . T.mapAccumL step 0++byteString :: B.ByteString -> B.ByteString+byteString = snd . BC.mapAccumL step 0++step :: Int -> Char -> (Int, Char)+step d c+ | d > 0 || d' > 0 = (d', ' ')+ | otherwise = (d', c)+ where+ d' = d + depth c+ depth '>' = 1+ depth '<' = -1+ depth _ = 0
+ benchmarks/haskell/Benchmarks/Programs/Throughput.hs view
@@ -0,0 +1,41 @@+-- | This benchmark simply reads and writes a file using the various string+-- libraries. The point of it is that we can make better estimations on how+-- much time the other benchmarks spend doing IO.+--+-- Note that we expect ByteStrings to be a whole lot faster, since they do not+-- do any actual encoding/decoding here, while String and Text do have UTF-8+-- encoding/decoding.+--+-- Tested in this benchmark:+--+-- * Reading the file+--+-- * Replacing text between HTML tags (<>) with whitespace+--+-- * Writing back to a handle+--+module Benchmarks.Programs.Throughput+ ( benchmark+ ) where++import Criterion (Benchmark, bgroup, bench)+import System.IO (Handle, hPutStr)+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as BL+import qualified Data.Text.Encoding as T+import qualified Data.Text.IO as T+import qualified Data.Text.Lazy.Encoding as TL+import qualified Data.Text.Lazy.IO as TL++benchmark :: FilePath -> Handle -> IO Benchmark+benchmark fp sink = return $ bgroup "Throughput"+ [ bench "String" $ readFile fp >>= hPutStr sink+ , bench "ByteString" $ B.readFile fp >>= B.hPutStr sink+ , bench "LazyByteString" $ BL.readFile fp >>= BL.hPutStr sink+ , bench "Text" $ T.readFile fp >>= T.hPutStr sink+ , bench "LazyText" $ TL.readFile fp >>= TL.hPutStr sink+ , bench "TextByteString" $+ B.readFile fp >>= B.hPutStr sink . T.encodeUtf8 . T.decodeUtf8+ , bench "LazyTextByteString" $+ BL.readFile fp >>= BL.hPutStr sink . TL.encodeUtf8 . TL.decodeUtf8+ ]
benchmarks/haskell/Benchmarks/Pure.hs view
@@ -18,7 +18,6 @@ import GHC.Base (Char (..), Int (..), chr#, ord#, (+#)) import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Lazy.Char8 as BL-import qualified Data.ByteString.Lazy.Internal as BL import qualified Data.ByteString.UTF8 as UTF8 import qualified Data.List as L import qualified Data.Text as T
benchmarks/haskell/Benchmarks/Replace.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE BangPatterns #-} -- | Replace a string by another string -- -- Tested in this benchmark:@@ -12,6 +13,9 @@ import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy.Search as BL+import qualified Data.ByteString.Search as B+import qualified Data.Text as T+import qualified Data.Text.Encoding as T import qualified Data.Text.Lazy as TL import qualified Data.Text.Lazy.Encoding as TL import qualified Data.Text.Lazy.IO as TL@@ -20,12 +24,20 @@ benchmark fp pat sub = do tl <- TL.readFile fp bl <- BL.readFile fp- return $ bgroup "Replace"- [ bench "LazyText" $ nf (TL.length . TL.replace tpat tsub) tl- , bench "LazyByteString" $ nf (BL.length . BL.replace bpat bsub) bl+ let !t = TL.toStrict tl+ !b = T.encodeUtf8 t+ return $ bgroup "Replace" [+ bench "Text" $ nf (T.length . T.replace tpat tsub) t+ , bench "ByteString" $ nf (BL.length . B.replace bpat bsub) b+ , bench "LazyText" $ nf (TL.length . TL.replace tlpat tlsub) tl+ , bench "LazyByteString" $ nf (BL.length . BL.replace blpat blsub) bl ] where- tpat = TL.pack pat- tsub = TL.pack sub- bpat = B.concat $ BL.toChunks $ TL.encodeUtf8 tpat- bsub = B.concat $ BL.toChunks $ TL.encodeUtf8 tsub+ tpat = T.pack pat+ tsub = T.pack sub+ tlpat = TL.pack pat+ tlsub = TL.pack sub+ bpat = T.encodeUtf8 tpat+ bsub = T.encodeUtf8 tsub+ blpat = B.concat $ BL.toChunks $ TL.encodeUtf8 tlpat+ blsub = B.concat $ BL.toChunks $ TL.encodeUtf8 tlsub
benchmarks/text-benchmarks.cabal view
@@ -23,6 +23,7 @@ hs-source-dirs: haskell .. c-sources: ../cbits/cbits.c cbits/time_iconv.c+ include-dirs: ../include main-is: Benchmarks.hs ghc-options: -Wall -O2 if flag(llvm)
cbits/cbits.c view
@@ -9,6 +9,7 @@ #include <string.h> #include <stdint.h> #include <stdio.h>+#include "text_cbits.h" void _hs_text_memcpy(void *dest, size_t doff, const void *src, size_t soff, size_t n)@@ -103,20 +104,41 @@ * A best-effort decoder. Runs until it hits either end of input or * the start of an invalid byte sequence. *- * At exit, updates *destoff with the next offset to write to, and- * returns the next source offset to read from.+ * At exit, we update *destoff with the next offset to write to, *src+ * with the next source location past the last one successfully+ * decoded, and return the next source location to read from.+ *+ * Moreover, we expose the internal decoder state (state0 and+ * codepoint0), allowing one to restart the decoder after it+ * terminates (say, due to a partial codepoint).+ *+ * In particular, there are a few possible outcomes,+ *+ * 1) We decoded the buffer entirely:+ * In this case we return srcend+ * state0 == UTF8_ACCEPT+ *+ * 2) We met an invalid encoding+ * In this case we return the address of the first invalid byte+ * state0 == UTF8_REJECT+ *+ * 3) We reached the end of the buffer while decoding a codepoint+ * In this case we return a pointer to the first byte of the partial codepoint+ * state0 != UTF8_ACCEPT, UTF8_REJECT+ * */-uint8_t const *-_hs_text_decode_utf8(uint16_t *dest, size_t *destoff,- const uint8_t const *src, const uint8_t const *srcend)+const uint8_t *+_hs_text_decode_utf8_state(uint16_t *const dest, size_t *destoff,+ const uint8_t **const src,+ const uint8_t *const srcend,+ uint32_t *codepoint0, uint32_t *state0) { uint16_t *d = dest + *destoff;- const uint8_t const *s = src;- uint32_t state = UTF8_ACCEPT;+ const uint8_t *s = *src, *last = *src;+ uint32_t state = *state0;+ uint32_t codepoint = *codepoint0; while (s < srcend) {- uint32_t codepoint;- #if defined(__i386__) || defined(__x86_64__) /* * This code will only work on a little-endian system that@@ -144,6 +166,7 @@ *d++ = (uint16_t) ((codepoint >> 16) & 0xff); *d++ = (uint16_t) ((codepoint >> 24) & 0xff); }+ last = s; } #endif @@ -159,13 +182,29 @@ *d++ = (uint16_t) (0xD7C0 + (codepoint >> 10)); *d++ = (uint16_t) (0xDC00 + (codepoint & 0x3FF)); }+ last = s; } - /* Error recovery - if we're not in a valid finishing state, back up. */- if (state != UTF8_ACCEPT)+ /* Invalid encoding, back up to the errant character */+ if (state == UTF8_REJECT) s -= 1; *destoff = d - dest;+ *codepoint0 = codepoint;+ *state0 = state;+ *src = last; return s;+}++/*+ * Helper to decode buffer and discard final decoder state+ */+const uint8_t *+_hs_text_decode_utf8(uint16_t *const dest, size_t *destoff,+ const uint8_t *src, const uint8_t *const srcend)+{+ uint32_t codepoint;+ uint32_t state = UTF8_ACCEPT;+ return _hs_text_decode_utf8_state(dest, destoff, &src, srcend, &codepoint, &state); }
+ changelog view
@@ -0,0 +1,16 @@+1.0.0.0++ * Added support for Unicode 6.3.0 to case conversion functions++ * New function toTitle converts words in a string to title case++ * New functions peekCStringLen and withCStringLen simplify+ interoperability with C functionns++ * Added support for decoding UTF-8 in stream-friendly fashion++ * Fixed a bug in mapAccumL++ * Added trusted Haskell support++ * Removed support for GHC 6.10 (released in 2008) and older
+ include/text_cbits.h view
@@ -0,0 +1,11 @@+/*+ * Copyright (c) 2013 Bryan O'Sullivan <bos@serpentine.com>.+ */++#ifndef _text_cbits_h+#define _text_cbits_h++#define UTF8_ACCEPT 0+#define UTF8_REJECT 12++#endif
scripts/Arsec.hs view
@@ -1,6 +1,7 @@ module Arsec (- comment+ Comment+ , comment , semi , showC , unichar@@ -23,13 +24,7 @@ import Text.ParserCombinators.Parsec.Error import Text.ParserCombinators.Parsec.Prim hiding ((<|>), many) -instance Applicative (GenParser s a) where- pure = return- (<*>) = ap--instance Alternative (GenParser s a) where- empty = mzero- (<|>) = mplus+type Comment = String unichar :: Parser Char unichar = chr . fst . head . readHex <$> many1 hexDigit@@ -40,7 +35,7 @@ semi :: Parser () semi = char ';' *> spaces *> pure () -comment :: Parser String+comment :: Parser Comment comment = (char '#' *> manyTill anyToken (char '\n')) <|> string "\n" showC :: Char -> String
scripts/CaseFolding.hs view
@@ -4,7 +4,8 @@ module CaseFolding (- Fold(..)+ CaseFolding(..)+ , Fold(..) , parseCF , mapCF ) where@@ -18,19 +19,22 @@ , name :: String } deriving (Eq, Ord, Show) -entries :: Parser [Fold]-entries = many comment *> many (entry <* many comment)+data CaseFolding = CF { cfComments :: [Comment], cfFolding :: [Fold] }+ deriving (Show)++entries :: Parser CaseFolding+entries = CF <$> many comment <*> many (entry <* many comment) where entry = Fold <$> unichar <* semi <*> oneOf "CFST" <* semi <*> unichars <*> (string "# " *> manyTill anyToken (char '\n')) -parseCF :: FilePath -> IO (Either ParseError [Fold])+parseCF :: FilePath -> IO (Either ParseError CaseFolding) parseCF name = parse entries name <$> readFile name -mapCF :: [Fold] -> [String]-mapCF ms = typ ++ (map nice . filter p $ ms) ++ [last]+mapCF :: CaseFolding -> [String]+mapCF (CF _ ms) = typ ++ (map nice . filter p $ ms) ++ [last] where typ = ["foldMapping :: forall s. Char -> s -> Step (CC s) Char" ,"{-# INLINE foldMapping #-}"]
scripts/CaseMapping.hs view
@@ -19,14 +19,20 @@ Left err -> print err >> return undefined Right ms -> return ms h <- openFile oname WriteMode- mapM_ (hPutStrLn h) ["{-# LANGUAGE Rank2Types #-}"+ let comments = map ("--" ++) $+ take 2 (cfComments cfs) ++ take 2 (scComments scs)+ mapM_ (hPutStrLn h) $+ ["{-# LANGUAGE Rank2Types #-}" ,"-- AUTOMATICALLY GENERATED - DO NOT EDIT"- ,"-- Generated by scripts/SpecialCasing.hs"+ ,"-- Generated by scripts/SpecialCasing.hs"] +++ comments +++ ["" ,"module Data.Text.Fusion.CaseMapping where" ,"import Data.Char" ,"import Data.Text.Fusion.Internal" ,""] mapM_ (hPutStrLn h) (mapSC "upper" upper toUpper scs) mapM_ (hPutStrLn h) (mapSC "lower" lower toLower scs)+ mapM_ (hPutStrLn h) (mapSC "title" title toTitle scs) mapM_ (hPutStrLn h) (mapCF cfs) hClose h
scripts/SpecialCasing.hs view
@@ -4,13 +4,17 @@ module SpecialCasing (- Case(..)+ SpecialCasing(..)+ , Case(..) , parseSC , mapSC ) where import Arsec +data SpecialCasing = SC { scComments :: [Comment], scCasing :: [Case] }+ deriving (Show)+ data Case = Case { code :: Char , lower :: [Char]@@ -20,8 +24,8 @@ , name :: String } deriving (Eq, Ord, Show) -entries :: Parser [Case]-entries = many comment *> many (entry <* many comment)+entries :: Parser SpecialCasing+entries = SC <$> many comment <*> many (entry <* many comment) where entry = Case <$> unichar <* semi <*> unichars@@ -30,11 +34,13 @@ <*> manyTill anyToken (string "# ") <*> manyTill anyToken (char '\n') -parseSC :: FilePath -> IO (Either ParseError [Case])+parseSC :: FilePath -> IO (Either ParseError SpecialCasing) parseSC name = parse entries name <$> readFile name -mapSC :: String -> (Case -> String) -> (Char -> Char) -> [Case] -> [String]-mapSC which access twiddle ms = typ ++ (map nice . filter p $ ms) ++ [last]+mapSC :: String -> (Case -> String) -> (Char -> Char) -> SpecialCasing+ -> [String]+mapSC which access twiddle (SC _ ms) =+ typ ++ (map nice . filter p $ ms) ++ [last] where typ = [which ++ "Mapping :: forall s. Char -> s -> Step (CC s) Char" ,"{-# INLINE " ++ which ++ "Mapping #-}"]
tests/Tests/Properties.hs view
@@ -1,7 +1,7 @@ -- | General quicktest properties for the text library ---{-# LANGUAGE BangPatterns, FlexibleInstances, OverloadedStrings,- ScopedTypeVariables, TypeSynonymInstances, CPP #-}+{-# LANGUAGE BangPatterns, CPP, FlexibleInstances, OverloadedStrings,+ ScopedTypeVariables, TypeSynonymInstances #-} {-# OPTIONS_GHC -fno-enable-rewrite-rules #-} {-# OPTIONS_GHC -fno-warn-missing-signatures #-} module Tests.Properties@@ -27,7 +27,6 @@ import Data.Text.Search (indices) import Data.Word (Word, Word8, Word16, Word32, Word64) import Numeric (showHex)-import Prelude hiding (catch, replicate) import Test.Framework (Test, testGroup) import Test.Framework.Providers.QuickCheck2 (testProperty) import qualified Data.Bits as Bits (shiftL, shiftR)@@ -54,6 +53,12 @@ import Tests.Utils import qualified Tests.SlowFunctions as Slow +#if MIN_VERSION_base(4,6,0)+import Prelude hiding (replicate)+#else+import Prelude hiding (catch, replicate)+#endif+ t_pack_unpack = (T.unpack . T.pack) `eq` id tl_pack_unpack = (TL.unpack . TL.pack) `eq` id t_stream_unstream = (S.unstream . S.stream) `eq` id@@ -93,6 +98,18 @@ t_utf32BE = forAll genUnicode $ (E.decodeUtf32BE . E.encodeUtf32BE) `eq` id tl_utf32BE = forAll genUnicode $ (EL.decodeUtf32BE . EL.encodeUtf32BE) `eq` id +t_utf8_incr = do+ Positive n <- arbitrary+ forAll genUnicode $ recode n `eq` id+ where recode n = T.concat . feedChunksOf n E.streamDecodeUtf8 . E.encodeUtf8+ feedChunksOf :: Int -> (B.ByteString -> E.Decoding) -> B.ByteString+ -> [T.Text]+ feedChunksOf n f bs+ | B.null bs = []+ | otherwise = let (a,b) = B.splitAt n bs+ E.Some t _ f' = f a+ in t : feedChunksOf n f' b+ -- 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.@@ -222,7 +239,7 @@ splitOn :: (Eq a) => [a] -> [a] -> [[a]] splitOn pat src0- | l == 0 = error "empty"+ | l == 0 = error "splitOn: empty" | otherwise = go src0 where l = length pat@@ -771,6 +788,7 @@ testProperty "tl_latin1" tl_latin1, testProperty "t_utf8" t_utf8, testProperty "t_utf8'" t_utf8',+ testProperty "t_utf8_incr" t_utf8_incr, testProperty "tl_utf8" tl_utf8, testProperty "tl_utf8'" tl_utf8', testProperty "t_utf16LE" t_utf16LE,
tests/Tests/Regressions.hs view
@@ -8,7 +8,7 @@ import Control.Exception (SomeException, handle) import System.IO-import Test.HUnit (assertBool, assertFailure)+import Test.HUnit (assertBool, assertEqual, assertFailure) import qualified Data.ByteString as B import Data.ByteString.Char8 () import qualified Data.ByteString.Lazy as LB@@ -17,6 +17,7 @@ import qualified Data.Text.IO as T import qualified Data.Text.Lazy as LT import qualified Data.Text.Lazy.Encoding as LE+import qualified Data.Text.Unsafe as T import qualified Test.Framework as F import qualified Test.Framework.Providers.HUnit as F @@ -59,10 +60,23 @@ let t = TE.decodeUtf8With (\_ _ -> Just '\xdc00') "\x80" assertBool "broken error recovery shouldn't break us" (t == "\xfffd") +-- Reported by Eric Seidel: we mishandled mapping Chars that fit in a+-- single Word16 to Chars that require two.+mapAccumL_resize :: IO ()+mapAccumL_resize = do+ let f a _ = (a, '\65536')+ count = 5+ val = T.mapAccumL f (0::Int) (T.replicate count "a")+ assertEqual "mapAccumL should correctly fill buffers for two-word results"+ (0, T.replicate count "\65536") val+ assertEqual "mapAccumL should correctly size buffers for two-word results"+ (count * 2) (T.lengthWord16 (snd val))+ tests :: F.Test tests = F.testGroup "Regressions" [ F.testCase "hGetContents_crash" hGetContents_crash , F.testCase "lazy_encode_crash" lazy_encode_crash+ , F.testCase "mapAccumL_resize" mapAccumL_resize , F.testCase "replicate_crash" replicate_crash , F.testCase "utf8_decode_unsafe" utf8_decode_unsafe ]
tests/text-tests.cabal view
@@ -64,6 +64,7 @@ library hs-source-dirs: .. c-sources: ../cbits/cbits.c+ include-dirs: ../include exposed-modules: Data.Text Data.Text.Array
text.cabal view
@@ -1,5 +1,5 @@ name: text-version: 0.11.3.1+version: 1.0.0.0 homepage: https://github.com/bos/text bug-reports: https://github.com/bos/text/issues synopsis: An efficient packed Unicode text type.@@ -30,16 +30,7 @@ non-standard encodings, text breaking, and locales), see the @text-icu@ package: <http://hackage.haskell.org/package/text-icu>- .- —— RELEASE NOTES ——- .- Changes in 0.11.2.0:- .- * String literals are now converted directly from the format in- which GHC stores them into 'Text', without an intermediate- transformation through 'String', and without inlining of- conversion code at each site where a string literal is declared.- .+ license: BSD3 license-file: LICENSE author: Bryan O'Sullivan <bos@serpentine.com>@@ -56,9 +47,12 @@ benchmarks/cbits/*.c benchmarks/haskell/*.hs benchmarks/haskell/Benchmarks/*.hs+ benchmarks/haskell/Benchmarks/Programs/*.hs benchmarks/python/*.py benchmarks/ruby/*.rb benchmarks/text-benchmarks.cabal+ changelog+ include/*.h scripts/*.hs tests-and-benchmarks.markdown tests/*.hs@@ -77,7 +71,8 @@ default: False library- c-sources: cbits/cbits.c+ c-sources: cbits/cbits.c+ include-dirs: include exposed-modules: Data.Text@@ -124,42 +119,32 @@ Data.Text.Util build-depends:- array,- base < 5,- bytestring >= 0.9- if impl(ghc >= 6.10)- build-depends:- ghc-prim, base >= 4, deepseq >= 1.1.0.0- cpp-options: -DHAVE_DEEPSEQ- else- build-depends: extensible-exceptions- extensions: ScopedTypeVariables+ array >= 0.3,+ base >= 4.2 && < 5,+ bytestring >= 0.9,+ deepseq >= 1.1.0.0,+ ghc-prim >= 0.2 - ghc-options: -Wall -funbox-strict-fields -O2- if impl(ghc >= 6.8)- ghc-options: -fwarn-tabs+ cpp-options: -DHAVE_DEEPSEQ+ ghc-options: -Wall -fwarn-tabs -funbox-strict-fields -O2 if flag(developer) ghc-prof-options: -auto-all ghc-options: -Werror cpp-options: -DASSERTS - if impl(ghc >= 6.11)- if flag(integer-simple)- cpp-options: -DINTEGER_SIMPLE- build-depends: integer-simple >= 0.1 && < 0.5- else- cpp-options: -DINTEGER_GMP- build-depends: integer-gmp >= 0.2-- if impl(ghc >= 6.9) && impl(ghc < 6.11)+ if flag(integer-simple)+ cpp-options: -DINTEGER_SIMPLE+ build-depends: integer-simple >= 0.1 && < 0.5+ else cpp-options: -DINTEGER_GMP- build-depends: integer >= 0.1 && < 0.2+ build-depends: integer-gmp >= 0.2 test-suite tests type: exitcode-stdio-1.0 hs-source-dirs: tests . main-is: Tests.hs c-sources: cbits/cbits.c+ include-dirs: include ghc-options: -Wall -threaded -O0 -rtsopts@@ -181,17 +166,12 @@ test-framework-hunit >= 0.2, test-framework-quickcheck2 >= 0.2 - if impl(ghc >= 6.11)- if flag(integer-simple)- cpp-options: -DINTEGER_SIMPLE- build-depends: integer-simple >= 0.1 && < 0.5- else- cpp-options: -DINTEGER_GMP- build-depends: integer-gmp >= 0.2-- if impl(ghc >= 6.9) && impl(ghc < 6.11)+ if flag(integer-simple)+ cpp-options: -DINTEGER_SIMPLE+ build-depends: integer-simple >= 0.1 && < 0.5+ else cpp-options: -DINTEGER_GMP- build-depends: integer >= 0.1 && < 0.2+ build-depends: integer-gmp >= 0.2 source-repository head type: git