text 0.11.2.3 → 0.11.3.0
raw patch · 34 files changed
+790/−425 lines, 34 filesdep −text
Dependencies removed: text
Files
- Data/Text.hs +33/−11
- Data/Text/Array.hs +5/−1
- Data/Text/Encoding.hs +25/−6
- Data/Text/Encoding/Error.hs +8/−0
- Data/Text/Encoding/Fusion.hs +2/−2
- Data/Text/Encoding/Fusion/Common.hs +1/−27
- Data/Text/Fusion/Common.hs +6/−2
- Data/Text/Fusion/Size.hs +2/−2
- Data/Text/IO.hs +30/−9
- Data/Text/Internal.hs +25/−4
- Data/Text/Lazy.hs +5/−2
- Data/Text/Lazy/Builder.hs +6/−269
- Data/Text/Lazy/Builder/Int.hs +89/−25
- Data/Text/Lazy/Builder/Int/Digits.hs +22/−0
- Data/Text/Lazy/Builder/Internal.hs +321/−0
- Data/Text/Lazy/Encoding.hs +12/−4
- Data/Text/Lazy/Encoding/Fusion.hs +2/−2
- Data/Text/Lazy/Fusion.hs +26/−24
- Data/Text/Lazy/IO.hs +3/−0
- Data/Text/Lazy/Read.hs +4/−1
- Data/Text/Read.hs +4/−1
- Data/Text/Search.hs +1/−1
- Data/Text/Unsafe.hs +6/−0
- benchmarks/haskell/Benchmarks.hs +2/−1
- benchmarks/haskell/Benchmarks/Builder.hs +36/−9
- benchmarks/haskell/Benchmarks/DecodeUtf8.hs +1/−1
- benchmarks/haskell/Benchmarks/Pure.hs +14/−8
- benchmarks/haskell/Benchmarks/Stream.hs +0/−1
- benchmarks/text-benchmarks.cabal +10/−3
- cbits/cbits.c +38/−0
- tests/Tests/Properties.hs +17/−1
- tests/Tests/Regressions.hs +12/−1
- tests/text-tests.cabal +2/−0
- text.cabal +20/−7
Data/Text.hs view
@@ -1,5 +1,8 @@ {-# LANGUAGE BangPatterns, CPP, MagicHash, Rank2Types, UnboxedTuples #-} {-# OPTIONS_GHC -fno-warn-orphans #-}+#if __GLASGOW_HASKELL__ >= 702+{-# LANGUAGE Trustworthy #-}+#endif -- | -- Module : Data.Text@@ -180,13 +183,16 @@ -- -* Ordered text -- , sort++ -- * Low level operations+ , copy ) where import Prelude (Char, Bool(..), Int, Maybe(..), String, Eq(..), Ord(..), Ordering(..), (++), Read(..), Show(..), (&&), (||), (+), (-), (.), ($), ($!), (>>), (*),- div, maxBound, not, return, otherwise)+ maxBound, not, return, otherwise, quot) #if defined(HAVE_DEEPSEQ) import Control.DeepSeq (NFData) #endif@@ -370,7 +376,7 @@ -- | /O(n)/ Convert a 'String' into a 'Text'. Subject to -- fusion. Performs replacement on invalid scalar values. pack :: String -> Text-pack = unstream . S.streamList . L.map safe+pack = unstream . S.map safe . S.streamList {-# INLINE [1] pack #-} -- | /O(n)/ Convert a Text into a String. Subject to fusion.@@ -378,17 +384,17 @@ unpack = S.unstreamList . stream {-# INLINE [1] unpack #-} --- | /O(n)/ Convert a literal string into a Text.+-- | /O(n)/ Convert a literal string into a Text. Subject to fusion. unpackCString# :: Addr# -> Text unpackCString# addr# = unstream (S.streamCString# addr#) {-# NOINLINE unpackCString# #-} {-# RULES "TEXT literal" forall a.- unstream (S.streamList (L.map safe (GHC.unpackCString# a)))+ unstream (S.map safe (S.streamList (GHC.unpackCString# a))) = unpackCString# a #-} {-# RULES "TEXT literal UTF8" forall a.- unstream (S.streamList (L.map safe (GHC.unpackCStringUtf8# a)))+ unstream (S.map safe (S.streamList (GHC.unpackCStringUtf8# a))) = unpackCString# a #-} -- | /O(1)/ Convert a character into a Text. Subject to fusion.@@ -731,7 +737,7 @@ | otherwise = replicateChar l c `append` t `append` replicateChar r c where len = length t d = k - len- r = d `div` 2+ r = d `quot` 2 l = d - r {-# INLINE center #-} @@ -903,11 +909,11 @@ -- @t@ repeated @n@ times. replicate :: Int -> Text -> Text replicate n t@(Text a o l)- | n <= 0 || l <= 0 = empty- | n == 1 = t- | isSingleton t = replicateChar n (unsafeHead t)- | n <= maxBound `div` l = Text (A.run x) 0 len- | otherwise = overflowError "replicate"+ | n <= 0 || l <= 0 = empty+ | n == 1 = t+ | isSingleton t = replicateChar n (unsafeHead t)+ | n <= maxBound `quot` l = Text (A.run x) 0 len+ | otherwise = overflowError "replicate" where len = l * n x = do@@ -1574,3 +1580,19 @@ overflowError :: String -> a overflowError fun = P.error $ "Data.Text." ++ fun ++ ": size overflow"++-- | /O(n)/ Make a distinct copy of the given string, sharing no+-- storage with the original string.+--+-- As an example, suppose you read a large string, of which you need+-- only a small portion. If you do not use 'copy', the entire original+-- array will be kept alive in memory by the smaller string. Making a+-- copy \"breaks the link\" to the original array, allowing it to be+-- garbage collected if there are no other live references to it.+copy :: Text -> Text+copy (Text arr off len) = Text (A.run go) 0 len+ where+ go = do+ marr <- A.new len+ A.copyI marr 0 arr off len+ return marr
Data/Text/Array.hs view
@@ -114,7 +114,7 @@ -- | Create an uninitialized mutable array. new :: forall s. Int -> ST s (MArray s) new n- | n < 0 || n .&. highBit /= 0 = error $ "Data.Text.Array.new: size overflow"+ | n < 0 || n .&. highBit /= 0 = array_size_error | otherwise = ST $ \s1# -> case newByteArray# len# s1# of (# s2#, marr# #) -> (# s2#, MArray marr#@@ -126,6 +126,9 @@ highBit = maxBound `xor` (maxBound `shiftR` 1) {-# INLINE new #-} +array_size_error :: a+array_size_error = error "Data.Text.Array.new: size overflow"+ -- | Freeze a mutable array. Do not mutate the 'MArray' afterwards! unsafeFreeze :: MArray s -> ST s Array unsafeFreeze MArray{..} = ST $ \s# ->@@ -181,6 +184,7 @@ (marr,b) <- k arr <- unsafeFreeze marr return (arr,b))+{-# INLINE run2 #-} -- | Copy some elements of a mutable array. copyM :: MArray s -- ^ Destination
Data/Text/Encoding.hs view
@@ -1,5 +1,8 @@ {-# LANGUAGE BangPatterns, CPP, ForeignFunctionInterface, MagicHash, UnliftedFFITypes #-}+#if __GLASGOW_HASKELL__ >= 702+{-# LANGUAGE Trustworthy #-}+#endif -- | -- Module : Data.Text.Encoding -- Copyright : (c) 2009, 2010, 2011 Bryan O'Sullivan,@@ -23,6 +26,7 @@ -- * Decoding ByteStrings to Text -- $strict decodeASCII+ , decodeLatin1 , decodeUtf8 , decodeUtf16LE , decodeUtf16BE@@ -57,7 +61,7 @@ import Data.ByteString as B import Data.ByteString.Internal as B import Data.Text.Encoding.Error (OnDecodeError, UnicodeException, strictDecode)-import Data.Text.Internal (Text(..))+import Data.Text.Internal (Text(..), safe, textP) import Data.Text.Private (runText) import Data.Text.UnsafeChar (ord, unsafeWrite) import Data.Text.UnsafeShift (shiftL, shiftR)@@ -68,11 +72,11 @@ import Foreign.Ptr (Ptr, minusPtr, plusPtr) import Foreign.Storable (peek, poke) import GHC.Base (MutableByteArray#)-import System.IO.Unsafe (unsafePerformIO) import qualified Data.Text.Array as A import qualified Data.Text.Encoding.Fusion as E import qualified Data.Text.Encoding.Utf16 as U16 import qualified Data.Text.Fusion as F+import Data.Text.Unsafe (unsafeDupablePerformIO) -- $strict --@@ -90,11 +94,23 @@ -- | /Deprecated/. Decode a 'ByteString' containing 7-bit ASCII -- encoded text. ----- This function is deprecated. Use 'decodeUtf8' instead.+-- This function is deprecated. Use 'decodeLatin1' instead. decodeASCII :: ByteString -> Text decodeASCII = decodeUtf8 {-# DEPRECATED decodeASCII "Use decodeUtf8 instead" #-} +-- | Decode a 'ByteString' containing Latin-1 (aka ISO-8859-1) encoded text.+--+-- 'decodeLatin1' is semantically equivalent to+-- @Data.Text.pack . Data.ByteString.Char8.unpack@+decodeLatin1 :: ByteString -> Text+decodeLatin1 (PS fp off len) = textP a 0 len+ where+ a = A.run (A.new len >>= unsafeIOToST . go)+ go dest = withForeignPtr fp $ \ptr -> do+ c_decode_latin1 (A.maBA dest) (ptr `plusPtr` off) (ptr `plusPtr` (off+len))+ return dest+ -- | Decode a 'ByteString' containing UTF-8 encoded text. decodeUtf8With :: OnDecodeError -> ByteString -> Text decodeUtf8With onErr (PS fp off len) = runText $ \done -> do@@ -114,7 +130,7 @@ Just c -> do destOff <- peek destOffPtr w <- unsafeSTToIO $- unsafeWrite dest (fromIntegral destOff) c+ unsafeWrite dest (fromIntegral destOff) (safe c) poke destOffPtr (destOff + fromIntegral w) loop $ curPtr' `plusPtr` 1 loop (ptr `plusPtr` off)@@ -141,12 +157,12 @@ -- If the input contains any invalid UTF-8 data, the relevant -- exception will be returned, otherwise the decoded text. decodeUtf8' :: ByteString -> Either UnicodeException Text-decodeUtf8' = unsafePerformIO . try . evaluate . decodeUtf8With strictDecode+decodeUtf8' = unsafeDupablePerformIO . try . evaluate . decodeUtf8With strictDecode {-# INLINE decodeUtf8' #-} -- | Encode text using UTF-8 encoding. encodeUtf8 :: Text -> ByteString-encodeUtf8 (Text arr off len) = unsafePerformIO $ do+encodeUtf8 (Text arr off len) = unsafeDupablePerformIO $ do let size0 = max len 4 mallocByteString size0 >>= start size0 off 0 where@@ -279,3 +295,6 @@ 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_latin1" c_decode_latin1+ :: MutableByteArray# s -> Ptr Word8 -> Ptr Word8 -> IO ()
Data/Text/Encoding/Error.hs view
@@ -1,4 +1,7 @@ {-# LANGUAGE CPP, DeriveDataTypeable #-}+#if __GLASGOW_HASKELL__ >= 702+{-# LANGUAGE Trustworthy #-}+#endif -- | -- Module : Data.Text.Encoding.Error -- Copyright : (c) Bryan O'Sullivan 2009@@ -35,6 +38,7 @@ , replace ) where +import Control.DeepSeq (NFData (..)) #if __GLASGOW_HASKELL__ >= 610 import Control.Exception (Exception, throw) #else@@ -93,6 +97,10 @@ show = showUnicodeException instance Exception UnicodeException++instance NFData UnicodeException where+ rnf (DecodeError desc w) = rnf desc `seq` rnf w `seq` ()+ rnf (EncodeError desc c) = rnf desc `seq` rnf c `seq` () -- | Throw a 'UnicodeException' if decoding fails. strictDecode :: OnDecodeError
Data/Text/Encoding/Fusion.hs view
@@ -44,12 +44,12 @@ import Data.Word (Word8, Word16, Word32) import Foreign.ForeignPtr (withForeignPtr, ForeignPtr) import Foreign.Storable (pokeByteOff)-import System.IO.Unsafe (unsafePerformIO) import qualified Data.ByteString as B import qualified Data.ByteString.Unsafe as B 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 Data.Text.Unsafe (unsafeDupablePerformIO) streamASCII :: ByteString -> Stream Char streamASCII bs = Stream next 0 (maxSize l)@@ -163,7 +163,7 @@ -- | /O(n)/ Convert a 'Stream' 'Word8' to a 'ByteString'. unstream :: Stream Word8 -> ByteString-unstream (Stream next s0 len) = unsafePerformIO $ do+unstream (Stream next s0 len) = unsafeDupablePerformIO $ do let mlen = upperBound 4 len mallocByteString mlen >>= loop mlen 0 s0 where
Data/Text/Encoding/Fusion/Common.hs view
@@ -21,8 +21,7 @@ -- * Restreaming -- Restreaming is the act of converting from one 'Stream' -- representation to another.- restreamUtf8- , restreamUtf16LE+ restreamUtf16LE , restreamUtf16BE , restreamUtf32LE , restreamUtf32BE@@ -34,31 +33,6 @@ import Data.Text.UnsafeChar (ord) import Data.Text.UnsafeShift (shiftR) import Data.Word (Word8)-import qualified Data.Text.Encoding.Utf8 as U8---- | /O(n)/ Convert a Stream Char into a UTF-8 encoded Stream Word8.-restreamUtf8 :: Stream Char -> Stream Word8-restreamUtf8 (Stream next0 s0 len) = Stream next (RS0 s0) (len * 2)- where- next (RS0 s) = case next0 s of- Done -> Done- Skip s' -> Skip (RS0 s')- Yield x s'- | n <= 0x7F -> Yield c (RS0 s')- | n <= 0x07FF -> Yield a2 (RS1 s' b2)- | n <= 0xFFFF -> Yield a3 (RS2 s' b3 c3)- | otherwise -> Yield a4 (RS3 s' b4 c4 d4)- where- n = ord x- c = fromIntegral n- (a2,b2) = U8.ord2 x- (a3,b3,c3) = U8.ord3 x- (a4,b4,c4,d4) = U8.ord4 x- next (RS1 s x2) = Yield x2 (RS0 s)- next (RS2 s x2 x3) = Yield x2 (RS1 s x3)- next (RS3 s x2 x3 x4) = Yield x2 (RS2 s x3 x4)- {-# INLINE next #-}-{-# INLINE restreamUtf8 #-} restreamUtf16BE :: Stream Char -> Stream Word8 restreamUtf16BE (Stream next0 s0 len) = Stream next (RS0 s0) (len * 2)
Data/Text/Fusion/Common.hs view
@@ -172,7 +172,7 @@ -- | /O(n)/ Adds a character to the front of a Stream Char. cons :: Char -> Stream Char -> Stream Char-cons w (Stream next0 s0 len) = Stream next (C1 s0) (len+1)+cons !w (Stream next0 s0 len) = Stream next (C1 s0) (len+1) where next (C1 s) = Yield w (C0 s) next (C0 s) = case next0 s of@@ -218,8 +218,12 @@ loop_head !s = case next s of Yield x _ -> x Skip s' -> loop_head s'- Done -> streamError "head" "Empty stream"+ Done -> head_empty {-# INLINE [0] head #-}++head_empty :: a+head_empty = streamError "head" "Empty stream"+{-# NOINLINE head_empty #-} -- | /O(1)/ Returns the first character and remainder of a 'Stream -- Char', or 'Nothing' if empty. Subject to array fusion.
Data/Text/Fusion/Size.hs view
@@ -93,8 +93,8 @@ mul :: Int -> Int -> Int mul m n- | m <= maxBound `div` n = m * n- | otherwise = overflowError+ | m <= maxBound `quot` n = m * n+ | otherwise = overflowError {-# INLINE mul #-} mulSize :: Size -> Size -> Size
Data/Text/IO.hs view
@@ -1,4 +1,7 @@ {-# LANGUAGE BangPatterns, CPP, RecordWildCards, ScopedTypeVariables #-}+#if __GLASGOW_HASKELL__ >= 702+{-# LANGUAGE Trustworthy #-}+#endif -- | -- Module : Data.Text.IO -- Copyright : (c) 2009, 2010 Bryan O'Sullivan,@@ -26,6 +29,7 @@ , appendFile -- * Operations on handles , hGetContents+ , hGetChunk , hGetLine , hPutStr , hPutStrLn@@ -97,6 +101,30 @@ appendFile :: FilePath -> Text -> IO () appendFile p = withFile p AppendMode . flip hPutStr +catchError :: String -> Handle -> Handle__ -> IOError -> IO Text+catchError caller h Handle__{..} err+ | isEOFError err = do+ buf <- readIORef haCharBuffer+ return $ if isEmptyBuffer buf+ then T.empty+ else T.singleton '\r'+ | otherwise = E.throwIO (augmentIOError err caller h)++-- | /Experimental./ Read a single chunk of strict text from a+-- 'Handle'. The size of the chunk depends on the amount of input+-- currently buffered.+--+-- This function blocks only if there is no data available, and EOF+-- has not yet been reached. Once EOF is reached, this function+-- returns an empty string instead of throwing an exception.+hGetChunk :: Handle -> IO Text+hGetChunk h = wantReadableHandle "hGetChunk" h readSingleChunk+ where+ readSingleChunk hh@Handle__{..} = do+ buf <- readIORef haCharBuffer+ t <- readChunk hh buf `E.catch` catchError "hGetChunk" h hh+ return (hh, t)+ -- | Read the remaining contents of a 'Handle' as a string. The -- 'Handle' is closed once the contents have been read, or if an -- exception is thrown.@@ -117,16 +145,9 @@ wantReadableHandle "hGetContents" h readAll where readAll hh@Handle__{..} = do- let catchError e- | isEOFError e = do- buf <- readIORef haCharBuffer- return $ if isEmptyBuffer buf- then T.empty- else T.singleton '\r'- | otherwise = E.throwIO (augmentIOError e "hGetContents" h)- readChunks = do+ let readChunks = do buf <- readIORef haCharBuffer- t <- readChunk hh buf `E.catch` catchError+ t <- readChunk hh buf `E.catch` catchError "hGetContents" h hh if T.null t then return [t] else (t:) `fmap` readChunks
Data/Text/Internal.hs view
@@ -23,6 +23,7 @@ module Data.Text.Internal ( -- * Types+ -- $internals Text(..) -- * Construction , text@@ -47,9 +48,9 @@ -- | A space efficient, packed, unboxed Unicode text type. data Text = Text- {-# UNPACK #-} !A.Array -- payload- {-# UNPACK #-} !Int -- offset- {-# UNPACK #-} !Int -- length+ {-# UNPACK #-} !A.Array -- payload (Word16 elements)+ {-# UNPACK #-} !Int -- offset (units of Word16, not Char)+ {-# UNPACK #-} !Int -- length (units of Word16, not Char) deriving (Typeable) -- | Smart constructor.@@ -90,7 +91,8 @@ -- scalar values, but are unfortunately admitted as valid 'Char' -- values by Haskell. They cannot be represented in a 'Text'. This -- function remaps those code points to the Unicode replacement--- character \"�\", and leaves other code points unchanged.+-- character (U+FFFD, \'�\'), and leaves other code points+-- unchanged. safe :: Char -> Char safe c | ord c .&. 0x1ff800 /= 0xd800 = c@@ -101,3 +103,22 @@ firstf :: (a -> c) -> Maybe (a,b) -> Maybe (c,b) firstf f (Just (a, b)) = Just (f a, b) firstf _ Nothing = Nothing++-- $internals+--+-- Internally, the 'Text' type is represented as an array of 'Word16'+-- UTF-16 code units. The offset and length fields in the constructor+-- are in these units, /not/ units of 'Char'.+--+-- Invariants that all functions must maintain:+--+-- * Since the 'Text' type uses UTF-16 internally, it cannot represent+-- characters in the reserved surrogate code point range U+D800 to+-- U+DFFF. To maintain this invariant, the 'safe' function maps+-- 'Char' values in this range to the replacement character (U+FFFD,+-- \'�\').+--+-- * A leading (or \"high\") surrogate code unit (0xD800–0xDBFF) must+-- always be followed by a trailing (or \"low\") surrogate code unit+-- (0xDC00-0xDFFF). A trailing surrogate code unit must always be+-- preceded by a leading surrogate code unit.
Data/Text/Lazy.hs view
@@ -1,5 +1,8 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} {-# LANGUAGE BangPatterns, MagicHash, CPP #-}+#if __GLASGOW_HASKELL__ >= 702+{-# LANGUAGE Trustworthy #-}+#endif -- | -- Module : Data.Text.Lazy -- Copyright : (c) 2009, 2010, 2012 Bryan O'Sullivan@@ -190,7 +193,7 @@ import Prelude (Char, Bool(..), Maybe(..), String, Eq(..), Ord(..), Ordering(..), Read(..), Show(..), (&&), (||), (+), (-), (.), ($), (++),- div, error, flip, fmap, fromIntegral, not, otherwise)+ error, flip, fmap, fromIntegral, not, otherwise, quot) import qualified Prelude as P #if defined(HAVE_DEEPSEQ) import Control.DeepSeq (NFData(..))@@ -625,7 +628,7 @@ | otherwise = replicateChar l c `append` t `append` replicateChar r c where len = length t d = k - len- r = d `div` 2+ r = d `quot` 2 l = d - r {-# INLINE center #-}
Data/Text/Lazy/Builder.hs view
@@ -3,7 +3,8 @@ ----------------------------------------------------------------------------- -- | -- Module : Data.Text.Lazy.Builder--- Copyright : (c) 2010 Johan Tibell+-- Copyright : (c) 2013 Bryan O'Sullivan+-- (c) 2010 Johan Tibell -- License : BSD3-style (see LICENSE) -- -- Maintainer : Johan Tibell <johan.tibell@gmail.com>@@ -15,7 +16,9 @@ -- @fromLazyText@, which construct new builders, and 'mappend', which -- concatenates two builders. ----- To get maximum performance when building lazy @Text@ values using a builder, associate @mappend@ calls to the right. For example, prefer+-- To get maximum performance when building lazy @Text@ values using a+-- builder, associate @mappend@ calls to the right. For example,+-- prefer -- -- > singleton 'a' `mappend` (singleton 'b' `mappend` singleton 'c') --@@ -43,270 +46,4 @@ , flush ) where -import Control.Monad.ST (ST, runST)-import Data.Bits ((.&.))-import Data.Monoid (Monoid(..))-import Data.Text.Internal (Text(..))-import Data.Text.Lazy.Internal (smallChunkSize)-import Data.Text.Unsafe (inlineInterleaveST)-import Data.Text.UnsafeChar (ord, unsafeWrite)-import Data.Text.UnsafeShift (shiftR)-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------------------------------------------------------------------------------ | A @Builder@ is an efficient way to build lazy @Text@ values.--- There are several functions for constructing builders, but only one--- to inspect them: to extract any data, you have to turn them into--- lazy @Text@ values using @toLazyText@.------ Internally, a builder constructs a lazy @Text@ by filling arrays--- piece by piece. As each buffer is filled, it is \'popped\' off, to--- become a new chunk of the resulting lazy @Text@. All this is--- hidden from the user of the @Builder@.-newtype Builder = Builder {- -- Invariant (from Data.Text.Lazy):- -- The lists include no null Texts.- runBuilder :: forall s. (Buffer s -> ST s [S.Text])- -> Buffer s- -> ST s [S.Text]- }--instance Monoid Builder where- mempty = empty- {-# INLINE mempty #-}- mappend = append- {-# INLINE mappend #-}- mconcat = foldr mappend mempty- {-# INLINE mconcat #-}--instance String.IsString Builder where- fromString = fromString- {-# INLINE fromString #-}--instance Show Builder where- show = show . toLazyText--instance Eq Builder where- a == b = toLazyText a == toLazyText b--instance Ord Builder where- a <= b = toLazyText a <= toLazyText b------------------------------------------------------------------------------ | /O(1)./ The empty @Builder@, satisfying------ * @'toLazyText' 'empty' = 'L.empty'@----empty :: Builder-empty = Builder (\ k buf -> k buf)-{-# INLINE empty #-}---- | /O(1)./ A @Builder@ taking a single character, satisfying------ * @'toLazyText' ('singleton' c) = 'L.singleton' c@----singleton :: Char -> Builder-singleton c = writeAtMost 2 $ \ marr o ->- if n < 0x10000- then A.unsafeWrite marr o (fromIntegral n) >> return 1- else do- A.unsafeWrite marr o lo- A.unsafeWrite marr (o+1) hi- return 2- where n = ord c- m = n - 0x10000- lo = fromIntegral $ (m `shiftR` 10) + 0xD800- hi = fromIntegral $ (m .&. 0x3FF) + 0xDC00-{-# INLINE singleton #-}------------------------------------------------------------------------------ | /O(1)./ The concatenation of two builders, an associative--- operation with identity 'empty', satisfying------ * @'toLazyText' ('append' x y) = 'L.append' ('toLazyText' x) ('toLazyText' y)@----append :: Builder -> Builder -> Builder-append (Builder f) (Builder g) = Builder (f . g)-{-# INLINE [0] append #-}---- TODO: Experiment to find the right threshold.-copyLimit :: Int-copyLimit = 128---- This function attempts to merge small @Text@ values instead of--- treating each value as its own chunk. We may not always want this.---- | /O(1)./ A @Builder@ taking a 'S.Text', satisfying------ * @'toLazyText' ('fromText' t) = 'L.fromChunks' [t]@----fromText :: S.Text -> Builder-fromText t@(Text arr off l)- | S.null t = empty- | l <= copyLimit = writeN l $ \marr o -> A.copyI marr o arr off (l+o)- | otherwise = flush `append` mapBuilder (t :)-{-# INLINE [1] fromText #-}--{-# RULES-"fromText/pack" forall s .- fromText (S.pack s) = fromString s- #-}---- | /O(1)./ A Builder taking a @String@, satisfying------ * @'toLazyText' ('fromString' s) = 'L.fromChunks' [S.pack s]@----fromString :: String -> Builder-fromString str = Builder $ \k (Buffer p0 o0 u0 l0) ->- let loop !marr !o !u !l [] = k (Buffer marr o u l)- loop marr o u l s@(c:cs)- | l <= 1 = do- arr <- A.unsafeFreeze marr- let !t = Text arr o u- marr' <- A.new 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 @Text@, satisfying------ * @'toLazyText' ('fromLazyText' t) = t@----fromLazyText :: L.Text -> Builder-fromLazyText ts = flush `append` mapBuilder (L.toChunks ts ++)-{-# INLINE fromLazyText #-}------------------------------------------------------------------------------ Our internal buffer type-data Buffer s = Buffer {-# UNPACK #-} !(A.MArray s)- {-# UNPACK #-} !Int -- offset- {-# UNPACK #-} !Int -- used units- {-# UNPACK #-} !Int -- length left------------------------------------------------------------------------------ | /O(n)./ Extract a lazy @Text@ from a @Builder@ with a default--- buffer size. The construction work takes place if and when the--- relevant part of the lazy @Text@ is demanded.-toLazyText :: Builder -> L.Text-toLazyText = toLazyTextWith smallChunkSize---- | /O(n)./ Extract a lazy @Text@ from a @Builder@, using the given--- size for the initial buffer. The construction work takes place if--- and when the relevant part of the lazy @Text@ is demanded.------ If the initial buffer is too small to hold all data, subsequent--- buffers will be the default buffer size.-toLazyTextWith :: Int -> Builder -> L.Text-toLazyTextWith chunkSize m = L.fromChunks (runST $- newBuffer chunkSize >>= runBuilder (m `append` flush) (const (return [])))---- | /O(1)./ Pop the strict @Text@ we have constructed so far, if any,--- yielding a new chunk in the result lazy @Text@.-flush :: Builder-flush = Builder $ \ k buf@(Buffer p o u l) ->- if u == 0- then k buf- else do arr <- A.unsafeFreeze p- let !b = Buffer p (o+u) 0 l- !t = Text arr o u- ts <- inlineInterleaveST (k b)- return $! t : ts------------------------------------------------------------------------------ | Sequence an ST operation on the buffer-withBuffer :: (forall s. Buffer s -> ST s (Buffer s)) -> Builder-withBuffer f = Builder $ \k buf -> f buf >>= k-{-# INLINE withBuffer #-}---- | Get the size of the buffer-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-mapBuilder f = Builder (fmap f .)------------------------------------------------------------------------------ | Ensure that there are at least @n@ many elements available.-ensureFree :: Int -> Builder-ensureFree !n = withSize $ \ l ->- if n <= l- then empty- else flush `append'` withBuffer (const (newBuffer (max n smallChunkSize)))-{-# INLINE [0] ensureFree #-}--writeAtMost :: Int -> (forall s. A.MArray s -> Int -> ST s Int) -> Builder-writeAtMost n f = ensureFree n `append'` withBuffer (writeBuffer f)-{-# INLINE [0] writeAtMost #-}---- | 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 -> Int -> ST s ()) -> Builder-writeN n f = writeAtMost n (\ p o -> f p o >> return n)-{-# INLINE writeN #-}--writeBuffer :: (A.MArray s -> Int -> ST s Int) -> Buffer s -> ST s (Buffer s)-writeBuffer f (Buffer p o u l) = do- n <- f p (o+u)- return $! Buffer p o (u+n) (l-n)-{-# INLINE writeBuffer #-}--newBuffer :: Int -> ST s (Buffer s)-newBuffer size = do- arr <- A.new size- return $! Buffer arr 0 0 size-{-# INLINE newBuffer #-}----------------------------------------------------------------------------- Some nice rules for Builder---- This function makes GHC understand that 'writeN' and 'ensureFree'--- are *not* recursive in the precense of the rewrite rules below.--- This is not needed with GHC 7+.-append' :: Builder -> Builder -> Builder-append' (Builder f) (Builder g) = Builder (f . g)-{-# INLINE append' #-}--{-# RULES--"append/writeAtMost" forall a b (f::forall s. A.MArray s -> Int -> ST s Int)- (g::forall s. A.MArray s -> Int -> ST s Int) ws.- append (writeAtMost a f) (append (writeAtMost b g) ws) =- append (writeAtMost (a+b) (\marr o -> f marr o >>= \ n ->- g marr (o+n) >>= \ m ->- let s = n+m in s `seq` return s)) ws--"writeAtMost/writeAtMost" forall a b (f::forall s. A.MArray s -> Int -> ST s Int)- (g::forall s. A.MArray s -> Int -> ST s Int).- append (writeAtMost a f) (writeAtMost b g) =- writeAtMost (a+b) (\marr o -> f marr o >>= \ n ->- g marr (o+n) >>= \ m ->- let s = n+m in s `seq` return s)--"ensureFree/ensureFree" forall a b .- append (ensureFree a) (ensureFree b) = ensureFree (max a b)--"flush/flush"- append flush flush = flush-- #-}+import Data.Text.Lazy.Builder.Internal
Data/Text/Lazy/Builder/Int.hs view
@@ -1,7 +1,8 @@-{-# LANGUAGE BangPatterns, CPP, MagicHash, UnboxedTuples #-}+{-# LANGUAGE BangPatterns, CPP, MagicHash, RankNTypes, UnboxedTuples #-} -- Module: Data.Text.Lazy.Builder.Int--- Copyright: (c) 2011 MailRank, Inc.+-- Copyright: (c) 2013 Bryan O'Sullivan+-- (c) 2011 MailRank, Inc. -- License: BSD3 -- Maintainer: Bryan O'Sullivan <bos@serpentine.com> -- Stability: experimental@@ -17,12 +18,16 @@ import Data.Int (Int8, Int16, Int32, Int64) import Data.Monoid (mempty)+import qualified Data.ByteString.Unsafe as B import Data.Text.Lazy.Builder.Functions ((<>), i2d)-import Data.Text.Lazy.Builder+import Data.Text.Lazy.Builder.Internal+import Data.Text.Lazy.Builder.Int.Digits (digits)+import Data.Text.Array import Data.Word (Word, Word8, Word16, Word32, Word64) import GHC.Base (quotInt, remInt) import GHC.Num (quotRemInteger) import GHC.Types (Int(..))+import Control.Monad.ST #ifdef __GLASGOW_HASKELL__ # if __GLASGOW_HASKELL__ < 611@@ -54,12 +59,7 @@ {-# RULES "decimal/Word32" decimal = positive :: Word32 -> Builder #-} {-# RULES "decimal/Word64" decimal = positive :: Word64 -> Builder #-} {-# RULES "decimal/Integer" decimal = integer 10 :: Integer -> Builder #-}-decimal i- | i < 0 = singleton '-' <>- if i <= -128- then positive (-(i `quot` 10)) <> digit (-(i `rem` 10))- else positive (-i)- | otherwise = positive i+decimal i = decimal' (<= -128) i boundedDecimal :: (Integral a, Bounded a) => a -> Builder {-# SPECIALIZE boundedDecimal :: Int -> Builder #-}@@ -67,11 +67,23 @@ {-# SPECIALIZE boundedDecimal :: Int16 -> Builder #-} {-# SPECIALIZE boundedDecimal :: Int32 -> Builder #-} {-# SPECIALIZE boundedDecimal :: Int64 -> Builder #-}-boundedDecimal i- | i < 0 = singleton '-' <>- if i == minBound- then positive (-(i `quot` 10)) <> digit (-(i `rem` 10))- else positive (-i)+boundedDecimal i = decimal' (== minBound) i++decimal' :: (Integral a) => (a -> Bool) -> a -> Builder+{-# INLINE decimal' #-}+decimal' p i+ | i < 0 = if p i+ then let (q, r) = i `quotRem` 10+ qq = -q+ !n = countDigits qq+ in writeN (n + 2) $ \marr off -> do+ unsafeWrite marr off minus+ posDecimal marr (off+1) n qq+ unsafeWrite marr (off+n+1) (i2w (-r))+ else let j = -i+ !n = countDigits j+ in writeN (n + 1) $ \marr off ->+ unsafeWrite marr off minus >> posDecimal marr (off+1) n j | otherwise = positive i positive :: (Integral a) => a -> Builder@@ -85,10 +97,58 @@ {-# SPECIALIZE positive :: Word16 -> Builder #-} {-# SPECIALIZE positive :: Word32 -> Builder #-} {-# SPECIALIZE positive :: Word64 -> Builder #-}-positive = go- where go n | n < 10 = digit n- | otherwise = go (n `quot` 10) <> digit (n `rem` 10)+positive i+ | i < 10 = writeN 1 $ \marr off -> unsafeWrite marr off (i2w i)+ | otherwise = let !n = countDigits i+ in writeN n $ \marr off -> posDecimal marr off n i +posDecimal :: (Integral a) =>+ forall s. MArray s -> Int -> Int -> a -> ST s ()+{-# INLINE posDecimal #-}+posDecimal marr off0 ds v0 = go (off0 + ds - 1) v0+ where go off v+ | v >= 100 = do+ let (q, r) = v `quotRem` 100+ write2 off r+ go (off - 2) q+ | v < 10 = unsafeWrite marr off (i2w v)+ | otherwise = write2 off v+ write2 off i0 = do+ let i = fromIntegral i0; j = i + i+ unsafeWrite marr off $ get (j + 1)+ unsafeWrite marr (off - 1) $ get j+ get = fromIntegral . B.unsafeIndex digits++minus, zero :: Word16+{-# INLINE minus #-}+{-# INLINE zero #-}+minus = 45+zero = 48++i2w :: (Integral a) => a -> Word16+{-# INLINE i2w #-}+i2w v = zero + fromIntegral v++countDigits :: (Integral a) => a -> Int+{-# INLINE countDigits #-}+countDigits v0 = go 1 (fromIntegral v0 :: Word64)+ where go !k v+ | v < 10 = k+ | v < 100 = k + 1+ | v < 1000 = k + 2+ | v < 1000000000000 =+ k + if v < 100000000+ then if v < 1000000+ then if v < 10000+ then 3+ else 4 + fin v 100000+ else 6 + fin v 10000000+ else if v < 10000000000+ then 8 + fin v 1000000000+ else 10 + fin v 100000000000+ | otherwise = go (k + 12) (v `quot` 1000000000000)+ fin v n = if v >= n then 1 else 0+ hexadecimal :: Integral a => a -> Builder {-# SPECIALIZE hexadecimal :: Int -> Builder #-} {-# SPECIALIZE hexadecimal :: Int8 -> Builder #-}@@ -100,19 +160,23 @@ {-# SPECIALIZE hexadecimal :: Word16 -> Builder #-} {-# SPECIALIZE hexadecimal :: Word32 -> Builder #-} {-# SPECIALIZE hexadecimal :: Word64 -> Builder #-}-{-# RULES "hexadecimal/Integer" hexadecimal = integer 16 :: Integer -> Builder #-}+{-# RULES "hexadecimal/Integer"+ hexadecimal = hexInteger :: Integer -> Builder #-} hexadecimal i- | i < 0 = error msg+ | i < 0 = error hexErrMsg | otherwise = go i where go n | n < 16 = hexDigit n | otherwise = go (n `quot` 16) <> hexDigit (n `rem` 16)- msg = "Data.Text.Lazy.Builder.Int.hexadecimal: applied to negative number" -digit :: Integral a => a -> Builder-digit n = singleton $! i2d (fromIntegral n)-{-# INLINE digit #-}+hexInteger :: Integer -> Builder+hexInteger i+ | i < 0 = error hexErrMsg+ | otherwise = integer 16 i +hexErrMsg :: String+hexErrMsg = "Data.Text.Lazy.Builder.Int.hexadecimal: applied to negative number"+ hexDigit :: Integral a => a -> Builder hexDigit n | n <= 9 = singleton $! i2d (fromIntegral n)@@ -184,7 +248,7 @@ pblock = loop maxDigits where loop !d !n- | d == 1 = digit n- | otherwise = loop (d-1) q <> digit r+ | d == 1 = hexDigit n+ | otherwise = loop (d-1) q <> hexDigit r where q = n `quotInt` base r = n `remInt` base
+ Data/Text/Lazy/Builder/Int/Digits.hs view
@@ -0,0 +1,22 @@+{-# LANGUAGE OverloadedStrings #-}++-- Module: Data.Text.Lazy.Builder.Int.Digits+-- Copyright: (c) 2013 Bryan O'Sullivan+-- License: BSD3+-- Maintainer: Bryan O'Sullivan <bos@serpentine.com>+-- Stability: experimental+-- Portability: portable+--+-- This module exists because the C preprocessor does things that we+-- shall not speak of when confronted with Haskell multiline strings.++module Data.Text.Lazy.Builder.Int.Digits (digits) where++import Data.ByteString.Char8 (ByteString)++digits :: ByteString+digits = "0001020304050607080910111213141516171819\+ \2021222324252627282930313233343536373839\+ \4041424344454647484950515253545556575859\+ \6061626364656667686970717273747576777879\+ \8081828384858687888990919293949596979899"
+ Data/Text/Lazy/Builder/Internal.hs view
@@ -0,0 +1,321 @@+{-# LANGUAGE BangPatterns, CPP, Rank2Types #-}++-----------------------------------------------------------------------------+-- |+-- Module : Data.Text.Lazy.Builder.Internal+-- Copyright : (c) 2013 Bryan O'Sullivan+-- (c) 2010 Johan Tibell+-- License : BSD3-style (see LICENSE)+--+-- Maintainer : Johan Tibell <johan.tibell@gmail.com>+-- Stability : experimental+-- Portability : portable to Hugs and GHC+--+-- Efficient construction of lazy @Text@ values. The principal+-- operations on a @Builder@ are @singleton@, @fromText@, and+-- @fromLazyText@, which construct new builders, and 'mappend', which+-- concatenates two builders.+--+-- To get maximum performance when building lazy @Text@ values using a+-- builder, associate @mappend@ calls to the right. For example,+-- prefer+--+-- > singleton 'a' `mappend` (singleton 'b' `mappend` singleton 'c')+--+-- to+--+-- > singleton 'a' `mappend` singleton 'b' `mappend` singleton 'c'+--+-- as the latter associates @mappend@ to the left.+--+-----------------------------------------------------------------------------++module Data.Text.Lazy.Builder.Internal+ ( -- * Public API+ -- ** The Builder type+ Builder+ , toLazyText+ , toLazyTextWith++ -- ** Constructing Builders+ , singleton+ , fromText+ , fromLazyText+ , fromString++ -- ** Flushing the buffer state+ , flush++ -- * Internal functions+ , append'+ , ensureFree+ , writeN+ ) where++import Control.Monad.ST (ST, runST)+import Data.Bits ((.&.))+import Data.Monoid (Monoid(..))+import Data.Text.Internal (Text(..))+import Data.Text.Lazy.Internal (smallChunkSize)+import Data.Text.Unsafe (inlineInterleaveST)+import Data.Text.UnsafeChar (ord, unsafeWrite)+import Data.Text.UnsafeShift (shiftR)+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++------------------------------------------------------------------------++-- | A @Builder@ is an efficient way to build lazy @Text@ values.+-- There are several functions for constructing builders, but only one+-- to inspect them: to extract any data, you have to turn them into+-- lazy @Text@ values using @toLazyText@.+--+-- Internally, a builder constructs a lazy @Text@ by filling arrays+-- piece by piece. As each buffer is filled, it is \'popped\' off, to+-- become a new chunk of the resulting lazy @Text@. All this is+-- hidden from the user of the @Builder@.+newtype Builder = Builder {+ -- Invariant (from Data.Text.Lazy):+ -- The lists include no null Texts.+ runBuilder :: forall s. (Buffer s -> ST s [S.Text])+ -> Buffer s+ -> ST s [S.Text]+ }++instance Monoid Builder where+ mempty = empty+ {-# INLINE mempty #-}+ mappend = append+ {-# INLINE mappend #-}+ mconcat = foldr mappend mempty+ {-# INLINE mconcat #-}++instance String.IsString Builder where+ fromString = fromString+ {-# INLINE fromString #-}++instance Show Builder where+ show = show . toLazyText++instance Eq Builder where+ a == b = toLazyText a == toLazyText b++instance Ord Builder where+ a <= b = toLazyText a <= toLazyText b++------------------------------------------------------------------------++-- | /O(1)./ The empty @Builder@, satisfying+--+-- * @'toLazyText' 'empty' = 'L.empty'@+--+empty :: Builder+empty = Builder (\ k buf -> k buf)+{-# INLINE empty #-}++-- | /O(1)./ A @Builder@ taking a single character, satisfying+--+-- * @'toLazyText' ('singleton' c) = 'L.singleton' c@+--+singleton :: Char -> Builder+singleton c = writeAtMost 2 $ \ marr o ->+ if n < 0x10000+ then A.unsafeWrite marr o (fromIntegral n) >> return 1+ else do+ A.unsafeWrite marr o lo+ A.unsafeWrite marr (o+1) hi+ return 2+ where n = ord c+ m = n - 0x10000+ lo = fromIntegral $ (m `shiftR` 10) + 0xD800+ hi = fromIntegral $ (m .&. 0x3FF) + 0xDC00+{-# INLINE singleton #-}++------------------------------------------------------------------------++-- | /O(1)./ The concatenation of two builders, an associative+-- operation with identity 'empty', satisfying+--+-- * @'toLazyText' ('append' x y) = 'L.append' ('toLazyText' x) ('toLazyText' y)@+--+append :: Builder -> Builder -> Builder+append (Builder f) (Builder g) = Builder (f . g)+{-# INLINE [0] append #-}++-- TODO: Experiment to find the right threshold.+copyLimit :: Int+copyLimit = 128++-- This function attempts to merge small @Text@ values instead of+-- treating each value as its own chunk. We may not always want this.++-- | /O(1)./ A @Builder@ taking a 'S.Text', satisfying+--+-- * @'toLazyText' ('fromText' t) = 'L.fromChunks' [t]@+--+fromText :: S.Text -> Builder+fromText t@(Text arr off l)+ | S.null t = empty+ | l <= copyLimit = writeN l $ \marr o -> A.copyI marr o arr off (l+o)+ | otherwise = flush `append` mapBuilder (t :)+{-# INLINE [1] fromText #-}++{-# RULES+"fromText/pack" forall s .+ fromText (S.pack s) = fromString s+ #-}++-- | /O(1)./ A Builder taking a @String@, satisfying+--+-- * @'toLazyText' ('fromString' s) = 'L.fromChunks' [S.pack s]@+--+fromString :: String -> Builder+fromString str = Builder $ \k (Buffer p0 o0 u0 l0) ->+ let loop !marr !o !u !l [] = k (Buffer marr o u l)+ loop marr o u l s@(c:cs)+ | l <= 1 = do+ arr <- A.unsafeFreeze marr+ let !t = Text arr o u+ marr' <- A.new 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 @Text@, satisfying+--+-- * @'toLazyText' ('fromLazyText' t) = t@+--+fromLazyText :: L.Text -> Builder+fromLazyText ts = flush `append` mapBuilder (L.toChunks ts ++)+{-# INLINE fromLazyText #-}++------------------------------------------------------------------------++-- Our internal buffer type+data Buffer s = Buffer {-# UNPACK #-} !(A.MArray s)+ {-# UNPACK #-} !Int -- offset+ {-# UNPACK #-} !Int -- used units+ {-# UNPACK #-} !Int -- length left++------------------------------------------------------------------------++-- | /O(n)./ Extract a lazy @Text@ from a @Builder@ with a default+-- buffer size. The construction work takes place if and when the+-- relevant part of the lazy @Text@ is demanded.+toLazyText :: Builder -> L.Text+toLazyText = toLazyTextWith smallChunkSize++-- | /O(n)./ Extract a lazy @Text@ from a @Builder@, using the given+-- size for the initial buffer. The construction work takes place if+-- and when the relevant part of the lazy @Text@ is demanded.+--+-- If the initial buffer is too small to hold all data, subsequent+-- buffers will be the default buffer size.+toLazyTextWith :: Int -> Builder -> L.Text+toLazyTextWith chunkSize m = L.fromChunks (runST $+ newBuffer chunkSize >>= runBuilder (m `append` flush) (const (return [])))++-- | /O(1)./ Pop the strict @Text@ we have constructed so far, if any,+-- yielding a new chunk in the result lazy @Text@.+flush :: Builder+flush = Builder $ \ k buf@(Buffer p o u l) ->+ if u == 0+ then k buf+ else do arr <- A.unsafeFreeze p+ let !b = Buffer p (o+u) 0 l+ !t = Text arr o u+ ts <- inlineInterleaveST (k b)+ return $! t : ts++------------------------------------------------------------------------++-- | Sequence an ST operation on the buffer+withBuffer :: (forall s. Buffer s -> ST s (Buffer s)) -> Builder+withBuffer f = Builder $ \k buf -> f buf >>= k+{-# INLINE withBuffer #-}++-- | Get the size of the buffer+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+mapBuilder f = Builder (fmap f .)++------------------------------------------------------------------------++-- | Ensure that there are at least @n@ many elements available.+ensureFree :: Int -> Builder+ensureFree !n = withSize $ \ l ->+ if n <= l+ then empty+ else flush `append'` withBuffer (const (newBuffer (max n smallChunkSize)))+{-# INLINE [0] ensureFree #-}++writeAtMost :: Int -> (forall s. A.MArray s -> Int -> ST s Int) -> Builder+writeAtMost n f = ensureFree n `append'` withBuffer (writeBuffer f)+{-# INLINE [0] writeAtMost #-}++-- | 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 -> Int -> ST s ()) -> Builder+writeN n f = writeAtMost n (\ p o -> f p o >> return n)+{-# INLINE writeN #-}++writeBuffer :: (A.MArray s -> Int -> ST s Int) -> Buffer s -> ST s (Buffer s)+writeBuffer f (Buffer p o u l) = do+ n <- f p (o+u)+ return $! Buffer p o (u+n) (l-n)+{-# INLINE writeBuffer #-}++newBuffer :: Int -> ST s (Buffer s)+newBuffer size = do+ arr <- A.new size+ return $! Buffer arr 0 0 size+{-# INLINE newBuffer #-}++------------------------------------------------------------------------+-- Some nice rules for Builder++-- This function makes GHC understand that 'writeN' and 'ensureFree'+-- are *not* recursive in the precense of the rewrite rules below.+-- This is not needed with GHC 7+.+append' :: Builder -> Builder -> Builder+append' (Builder f) (Builder g) = Builder (f . g)+{-# INLINE append' #-}++{-# RULES++"append/writeAtMost" forall a b (f::forall s. A.MArray s -> Int -> ST s Int)+ (g::forall s. A.MArray s -> Int -> ST s Int) ws.+ append (writeAtMost a f) (append (writeAtMost b g) ws) =+ append (writeAtMost (a+b) (\marr o -> f marr o >>= \ n ->+ g marr (o+n) >>= \ m ->+ let s = n+m in s `seq` return s)) ws++"writeAtMost/writeAtMost" forall a b (f::forall s. A.MArray s -> Int -> ST s Int)+ (g::forall s. A.MArray s -> Int -> ST s Int).+ append (writeAtMost a f) (writeAtMost b g) =+ writeAtMost (a+b) (\marr o -> f marr o >>= \ n ->+ g marr (o+n) >>= \ m ->+ let s = n+m in s `seq` return s)++"ensureFree/ensureFree" forall a b .+ append (ensureFree a) (ensureFree b) = ensureFree (max a b)++"flush/flush"+ append flush flush = flush++ #-}
Data/Text/Lazy/Encoding.hs view
@@ -1,4 +1,7 @@-{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE BangPatterns,CPP #-}+#if __GLASGOW_HASKELL__ >= 702+{-# LANGUAGE Trustworthy #-}+#endif -- | -- Module : Data.Text.Lazy.Encoding -- Copyright : (c) 2009, 2010 Bryan O'Sullivan@@ -20,6 +23,7 @@ -- * Decoding ByteStrings to Text -- $strict decodeASCII+ , decodeLatin1 , decodeUtf8 , decodeUtf16LE , decodeUtf16BE@@ -48,7 +52,6 @@ import Data.Bits ((.&.)) import Data.Text.Encoding.Error (OnDecodeError, UnicodeException, strictDecode) import Data.Text.Lazy.Internal (Text(..), chunk, empty, foldrChunks)-import System.IO.Unsafe (unsafePerformIO) import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as B import qualified Data.ByteString.Lazy.Internal as B@@ -57,6 +60,7 @@ import qualified Data.Text.Encoding as TE import qualified Data.Text.Lazy.Encoding.Fusion as E import qualified Data.Text.Lazy.Fusion as F+import Data.Text.Unsafe (unsafeDupablePerformIO) -- $strict --@@ -74,11 +78,15 @@ -- | /Deprecated/. Decode a 'ByteString' containing 7-bit ASCII -- encoded text. ----- This function is deprecated. Use 'decodeUtf8' instead.+-- This function is deprecated. Use 'decodeLatin1' instead. decodeASCII :: B.ByteString -> Text decodeASCII = decodeUtf8 {-# DEPRECATED decodeASCII "Use decodeUtf8 instead" #-} +-- | Decode a 'ByteString' containing Latin-1 (aka ISO-8859-1) encoded text.+decodeLatin1 :: B.ByteString -> Text+decodeLatin1 = foldr (chunk . TE.decodeLatin1) empty . B.toChunks+ -- | Decode a 'ByteString' containing UTF-8 encoded text. decodeUtf8With :: OnDecodeError -> B.ByteString -> Text decodeUtf8With onErr bs0 = fast bs0@@ -141,7 +149,7 @@ -- input before it can return a result. If you need lazy (streaming) -- decoding, use 'decodeUtf8With' in lenient mode. decodeUtf8' :: B.ByteString -> Either UnicodeException Text-decodeUtf8' bs = unsafePerformIO $ do+decodeUtf8' bs = unsafeDupablePerformIO $ do let t = decodeUtf8 bs try (evaluate (rnf t `seq` t)) where
Data/Text/Lazy/Encoding/Fusion.hs view
@@ -42,7 +42,7 @@ 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 Data.Text.Unsafe (unsafeDupablePerformIO) import Foreign.ForeignPtr (withForeignPtr, ForeignPtr) import Foreign.Storable (pokeByteOff) import Data.ByteString.Internal (mallocByteString, memcpy)@@ -272,7 +272,7 @@ -- | /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)- where chunk s1 len1 = unsafePerformIO $ do+ where chunk s1 len1 = unsafeDupablePerformIO $ do let len = max 4 (min len1 chunkSize) mallocByteString len >>= loop len 0 s1 where
Data/Text/Lazy/Fusion.hs view
@@ -24,6 +24,7 @@ import Prelude hiding (length) import qualified Data.Text.Fusion.Common as S+import Control.Monad.ST (runST) import Data.Text.Fusion.Internal import Data.Text.Fusion.Size (isEmpty, unknownSize) import Data.Text.Lazy.Internal@@ -47,38 +48,39 @@ where Iter c d = iter t i {-# INLINE [0] stream #-} -data UC s = UC s {-# UNPACK #-} !Int- -- | /O(n)/ Convert a 'Stream Char' into a 'Text', using the given -- chunk size. unstreamChunks :: Int -> Stream Char -> Text-unstreamChunks chunkSize (Stream next s0 len0)+unstreamChunks !chunkSize (Stream next s0 len0) | isEmpty len0 = Empty | otherwise = outer s0 where- outer s = {-# SCC "unstreamChunks/outer" #-}- case next s of+ outer so = {-# SCC "unstreamChunks/outer" #-}+ case next so of Done -> Empty Skip s' -> outer s'- Yield x s' -> I.Text arr 0 len `chunk` outer s''- where (arr, UC s'' len) = A.run2 fill- fill = do a <- A.new unknownLength- unsafeWrite a 0 x >>= inner a unknownLength s'- unknownLength = 4- inner marr len s !i- | i + 1 >= chunkSize = return (marr, UC s i)- | i + 1 >= len = {-# SCC "unstreamChunks/resize" #-} do- let newLen = min (len `shiftL` 1) chunkSize- marr' <- A.new newLen- A.copyM marr' 0 marr 0 len- inner marr' newLen s i- | otherwise =- {-# SCC "unstreamChunks/inner" #-}- case next s of- Done -> return (marr, UC s i)- Skip s' -> inner marr len s' i- Yield x s' -> do d <- unsafeWrite marr i x- inner marr len s' (i+d)+ Yield x s' -> runST $ do+ a <- A.new unknownLength+ unsafeWrite a 0 x >>= inner a unknownLength s'+ where unknownLength = 4+ where+ inner marr !len s !i+ | i + 1 >= chunkSize = finish marr i s+ | i + 1 >= len = {-# SCC "unstreamChunks/resize" #-} do+ let newLen = min (len `shiftL` 1) chunkSize+ marr' <- A.new newLen+ A.copyM marr' 0 marr 0 len+ inner marr' newLen s i+ | otherwise =+ {-# SCC "unstreamChunks/inner" #-}+ case next s of+ Done -> finish marr i s+ Skip s' -> inner marr len s' i+ Yield x s' -> do d <- unsafeWrite marr i x+ inner marr len s' (i+d)+ finish marr len s' = do+ arr <- A.unsafeFreeze marr+ return (I.Text arr 0 len `Chunk` outer s') {-# INLINE [0] unstreamChunks #-} -- | /O(n)/ Convert a 'Stream Char' into a 'Text', using
Data/Text/Lazy/IO.hs view
@@ -1,4 +1,7 @@ {-# LANGUAGE BangPatterns, CPP, RecordWildCards #-}+#if __GLASGOW_HASKELL__ >= 702+{-# LANGUAGE Trustworthy #-}+#endif -- | -- Module : Data.Text.Lazy.IO -- Copyright : (c) 2009, 2010 Bryan O'Sullivan,
Data/Text/Lazy/Read.hs view
@@ -1,4 +1,7 @@-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE OverloadedStrings, CPP #-}+#if __GLASGOW_HASKELL__ >= 702+{-# LANGUAGE Trustworthy #-}+#endif -- | -- Module : Data.Text.Lazy.Read
Data/Text/Read.hs view
@@ -1,4 +1,7 @@-{-# LANGUAGE OverloadedStrings, UnboxedTuples #-}+{-# LANGUAGE OverloadedStrings, UnboxedTuples, CPP #-}+#if __GLASGOW_HASKELL__ >= 702+{-# LANGUAGE Trustworthy #-}+#endif -- | -- Module : Data.Text.Read
Data/Text/Search.hs view
@@ -86,4 +86,4 @@ where loop !i | i >= hlen = [] | hindex i == c = i : loop (i+1) | otherwise = loop (i+1)-{- INLINE indices #-}+{-# INLINE indices #-}
Data/Text/Unsafe.hs view
@@ -14,6 +14,7 @@ ( inlineInterleaveST , inlinePerformIO+ , unsafeDupablePerformIO , Iter(..) , iter , iter_@@ -33,6 +34,11 @@ 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
benchmarks/haskell/Benchmarks.hs view
@@ -49,7 +49,8 @@ , Equality.benchmark (tf "japanese.txt") , FileRead.benchmark (tf "russian.txt") , FoldLines.benchmark (tf "russian.txt")- , Pure.benchmark (tf "japanese.txt")+ , Pure.benchmark "tiny "(tf "tiny.txt")+ , Pure.benchmark "japanese" (tf "japanese.txt") , ReadNumbers.benchmark (tf "numbers.txt") , Replace.benchmark (tf "russian.txt") "принимая" "своем" , Search.benchmark (tf "russian.txt") "принимая"
benchmarks/haskell/Benchmarks/Builder.hs view
@@ -12,7 +12,7 @@ import Criterion (Benchmark, bgroup, bench, nf) import Data.Binary.Builder as B import Data.ByteString.Char8 ()-import Data.Monoid (mconcat)+import Data.Monoid (mconcat, mempty) import qualified Blaze.ByteString.Builder as Blaze import qualified Blaze.ByteString.Builder.Char.Utf8 as Blaze import qualified Data.ByteString as SB@@ -20,18 +20,45 @@ import qualified Data.Text as T import qualified Data.Text.Lazy as LT import qualified Data.Text.Lazy.Builder as LTB+import qualified Data.Text.Lazy.Builder.Int as Int+import Data.Int (Int64) benchmark :: IO Benchmark benchmark = return $ bgroup "Builder"- [ bench "LazyText" $ nf- (LT.length . LTB.toLazyText . mconcat . map LTB.fromText) texts- , bench "Binary" $ nf- (LB.length . B.toLazyByteString . mconcat . map B.fromByteString)- byteStrings- , bench "Blaze" $ nf- (LB.length . Blaze.toLazyByteString . mconcat . map Blaze.fromString)- strings+ [ bgroup "Comparison"+ [ bench "LazyText" $ nf+ (LT.length . LTB.toLazyText . mconcat . map LTB.fromText) texts+ , bench "Binary" $ nf+ (LB.length . B.toLazyByteString . mconcat . map B.fromByteString)+ byteStrings+ , bench "Blaze" $ nf+ (LB.length . Blaze.toLazyByteString . mconcat . map Blaze.fromString)+ strings+ ]+ , bgroup "Int"+ [ bgroup "Decimal"+ [ bgroup "Positive" .+ flip map numbers $ \n ->+ (bench (show (length (show n))) $ nf (LTB.toLazyText . Int.decimal) n)+ , bgroup "Negative" .+ flip map numbers $ \m ->+ let n = negate m in+ (bench (show (length (show n))) $ nf (LTB.toLazyText . Int.decimal) n)+ , bench "Empty" $ nf LTB.toLazyText mempty+ , bgroup "Show" .+ flip map numbers $ \n ->+ (bench (show (length (show n))) $ nf show n)+ ]+ ] ]+ where+ numbers :: [Int64]+ numbers = [+ 6, 14, 500, 9688, 10654, 620735, 5608880, 37010612,+ 731223504, 5061580596, 24596952933, 711732309084, 2845910093839,+ 54601756118340, 735159434806159, 3619097625502435, 95777227510267124,+ 414944309510675693, 8986407456998704019+ ] texts :: [T.Text] texts = take 200000 $ cycle ["foo", "λx", "由の"]
benchmarks/haskell/Benchmarks/DecodeUtf8.hs view
@@ -18,7 +18,7 @@ ( benchmark ) where -import Foreign.C.Types (CInt, CSize)+import Foreign.C.Types import Data.ByteString.Internal (ByteString(..)) import Foreign.Ptr (Ptr, plusPtr) import Foreign.ForeignPtr (withForeignPtr)
benchmarks/haskell/Benchmarks/Pure.hs view
@@ -4,7 +4,7 @@ -- -- * Most pure functions defined the string types ---{-# LANGUAGE BangPatterns, GADTs, MagicHash #-}+{-# LANGUAGE BangPatterns, CPP, GADTs, MagicHash #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Benchmarks.Pure ( benchmark@@ -27,8 +27,8 @@ import qualified Data.Text.Lazy.Builder as TB import qualified Data.Text.Lazy.Encoding as TL -benchmark :: FilePath -> IO Benchmark-benchmark fp = do+benchmark :: String -> FilePath -> IO Benchmark+benchmark kind fp = do -- Evaluate stuff before actually running the benchmark, we don't want to -- count it here. @@ -100,6 +100,10 @@ , benchBSL $ nf BL.unpack bla , benchS $ nf UTF8.toString bsa ]+ , bgroup "decode'"+ [ benchT $ nf T.decodeUtf8' bsa+ , benchTL $ nf TL.decodeUtf8' bla+ ] , bgroup "drop" [ benchT $ nf (T.drop (ta_len `div` 3)) ta , benchTL $ nf (TL.drop (tla_len `div` 3)) tla@@ -405,11 +409,11 @@ ] ] where- benchS = bench "String"- benchT = bench "Text"- benchTL = bench "LazyText"- benchBS = bench "ByteString"- benchBSL = bench "LazyByteString"+ benchS = bench ("String+" ++ kind)+ benchT = bench ("Text+" ++ kind)+ benchTL = bench ("LazyText+" ++ kind)+ benchBS = bench ("ByteString+" ++ kind)+ benchBSL = bench ("LazyByteString+" ++ kind) c = 'й' p0 = (== c)@@ -425,11 +429,13 @@ replicat n = concat . L.replicate n short = T.pack "short" +#if !MIN_VERSION_bytestring(0,10,0) instance NFData BS.ByteString instance NFData BL.ByteString where rnf BL.Empty = () rnf (BL.Chunk _ ts) = rnf ts+#endif data B where B :: NFData a => a -> B
benchmarks/haskell/Benchmarks/Stream.hs view
@@ -86,7 +86,6 @@ ] -- Encoding.Fusion.Common- , bench "restreamUtf8" $ nf F.restreamUtf8 s , bench "restreamUtf16LE" $ nf F.restreamUtf16LE s , bench "restreamUtf16BE" $ nf F.restreamUtf16BE s , bench "restreamUtf32LE" $ nf F.restreamUtf32LE s
benchmarks/text-benchmarks.cabal view
@@ -15,13 +15,19 @@ cabal-version: >=1.2 +flag llvm+ description: use LLVM+ default: False+ executable text-benchmarks hs-source-dirs: haskell .. c-sources: ../cbits/cbits.c cbits/time_iconv.c main-is: Benchmarks.hs- ghc-options: -Wall -O2 -fllvm- cpp-options: -DHAVE_DEEPSEQ+ ghc-options: -Wall -O2+ if flag(llvm)+ ghc-options: -fllvm+ cpp-options: -DHAVE_DEEPSEQ -DINTEGER_GMP build-depends: base == 4.*, binary, blaze-builder,@@ -33,13 +39,14 @@ directory, filepath, ghc-prim,+ integer-gmp, stringsearch, utf8-string executable text-multilang hs-source-dirs: haskell main-is: Multilang.hs- ghc-options: -Wall -O2 -fllvm+ ghc-options: -Wall -O2 build-depends: base == 4.*, bytestring, text,
cbits/cbits.c view
@@ -62,6 +62,44 @@ } /*+ * The ISO 8859-1 (aka latin-1) code points correspond exactly to the first 256 unicode+ * code-points, therefore we can trivially convert from a latin-1 encoded bytestring to+ * an UTF16 array+ */+void+_hs_text_decode_latin1(uint16_t *dest, const uint8_t const *src,+ const uint8_t const *srcend)+{+ const uint8_t *p = src;++#if defined(__i386__) || defined(__x86_64__)+ /* This optimization works on a little-endian systems by using+ (aligned) 32-bit loads instead of 8-bit loads+ */++ /* consume unaligned prefix */+ while (p != srcend && (uintptr_t)p & 0x3)+ *dest++ = *p++;++ /* iterate over 32-bit aligned loads */+ while (p < srcend - 3) {+ const uint32_t w = *((const uint32_t *)p);++ *dest++ = w & 0xff;+ *dest++ = (w >> 8) & 0xff;+ *dest++ = (w >> 16) & 0xff;+ *dest++ = (w >> 24) & 0xff;++ p += 4;+ }+#endif++ /* handle unaligned suffix */+ while (p != srcend)+ *dest++ = *p++;+}++/* * A best-effort decoder. Runs until it hits either end of input or * the start of an invalid byte sequence. *
tests/Tests/Properties.hs view
@@ -32,6 +32,7 @@ import Test.Framework.Providers.QuickCheck2 (testProperty) import qualified Data.Bits as Bits (shiftL, shiftR) import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as BL import qualified Data.List as L import qualified Data.Text as T import qualified Data.Text.Encoding as E@@ -65,10 +66,20 @@ tl_chunk_unchunk = (TL.fromChunks . TL.toChunks) `eq` id tl_from_to_strict = (TL.fromStrict . TL.toStrict) `eq` id +-- Note: this silently truncates code-points > 255 to 8-bit due to 'B.pack'+encodeL1 :: T.Text -> B.ByteString+encodeL1 = B.pack . map (fromIntegral . fromEnum) . T.unpack+encodeLazyL1 :: TL.Text -> BL.ByteString+encodeLazyL1 = BL.fromChunks . map encodeL1 . TL.toChunks+ 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_latin1 t = E.decodeLatin1 (encodeL1 a) == a+ where a = T.map (\c -> chr (ord c `mod` 256)) t+tl_latin1 t = EL.decodeLatin1 (encodeLazyL1 a) == a+ where a = TL.map (\c -> chr (ord c `mod` 256)) t t_utf8 = forAll genUnicode $ (E.decodeUtf8 . E.encodeUtf8) `eq` id t_utf8' = forAll genUnicode $ (E.decodeUtf8' . E.encodeUtf8) `eq` (id . Right) tl_utf8 = forAll genUnicode $ (EL.decodeUtf8 . EL.encodeUtf8) `eq` id@@ -722,6 +733,8 @@ where n = small m t_use_from t = monadicIO $ assert . (==t) =<< run (useAsPtr t fromPtr) +t_copy t = T.copy t == t+ -- Regression tests. s_filter_eq s = S.filter p t == S.streamList (filter p s) where p = (/= S.last t)@@ -754,6 +767,8 @@ testGroup "transcoding" [ testProperty "t_ascii" t_ascii, testProperty "tl_ascii" tl_ascii,+ testProperty "t_latin1" t_latin1,+ testProperty "tl_latin1" tl_latin1, testProperty "t_utf8" t_utf8, testProperty "t_utf8'" t_utf8', testProperty "tl_utf8" tl_utf8,@@ -1184,6 +1199,7 @@ 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+ testProperty "t_use_from" t_use_from,+ testProperty "t_copy" t_copy ] ]
tests/Tests/Regressions.hs view
@@ -8,10 +8,12 @@ import Control.Exception (SomeException, handle) import System.IO-import Test.HUnit (assertFailure)+import Test.HUnit (assertBool, assertFailure) import qualified Data.ByteString as B+import Data.ByteString.Char8 () import qualified Data.ByteString.Lazy as LB import qualified Data.Text as T+import qualified Data.Text.Encoding as TE import qualified Data.Text.IO as T import qualified Data.Text.Lazy as LT import qualified Data.Text.Lazy.Encoding as LE@@ -49,9 +51,18 @@ power | maxBound == (2147483647::Int) = 28 | otherwise = 60 :: Int +-- Reported by John Millikin: a UTF-8 decode error handler could+-- return a bogus substitution character, which we would write without+-- checking.+utf8_decode_unsafe :: IO ()+utf8_decode_unsafe = do+ let t = TE.decodeUtf8With (\_ _ -> Just '\xdc00') "\x80"+ assertBool "broken error recovery shouldn't break us" (t == "\xfffd")+ tests :: F.Test tests = F.testGroup "Regressions" [ F.testCase "hGetContents_crash" hGetContents_crash , F.testCase "lazy_encode_crash" lazy_encode_crash , F.testCase "replicate_crash" replicate_crash+ , F.testCase "utf8_decode_unsafe" utf8_decode_unsafe ]
tests/text-tests.cabal view
@@ -87,6 +87,8 @@ Data.Text.Lazy.Builder Data.Text.Lazy.Builder.Functions Data.Text.Lazy.Builder.Int+ Data.Text.Lazy.Builder.Int.Digits+ Data.Text.Lazy.Builder.Internal Data.Text.Lazy.Builder.RealFloat Data.Text.Lazy.Builder.RealFloat.Functions Data.Text.Lazy.Encoding
text.cabal view
@@ -1,5 +1,5 @@ name: text-version: 0.11.2.3+version: 0.11.3.0 homepage: https://github.com/bos/text bug-reports: https://github.com/bos/text/issues synopsis: An efficient packed Unicode text type.@@ -109,6 +109,8 @@ Data.Text.Fusion.Size Data.Text.IO.Internal Data.Text.Lazy.Builder.Functions+ Data.Text.Lazy.Builder.Int.Digits+ Data.Text.Lazy.Builder.Internal Data.Text.Lazy.Builder.RealFloat.Functions Data.Text.Lazy.Encoding.Fusion Data.Text.Lazy.Fusion@@ -131,7 +133,7 @@ cpp-options: -DHAVE_DEEPSEQ else build-depends: extensible-exceptions- extensions: PatternSignatures+ extensions: ScopedTypeVariables ghc-options: -Wall -funbox-strict-fields -O2 if impl(ghc >= 6.8)@@ -149,16 +151,15 @@ cpp-options: -DINTEGER_GMP build-depends: integer-gmp >= 0.2 - if impl(ghc >= 6.9) && impl(ghc < 6.11) cpp-options: -DINTEGER_GMP build-depends: integer >= 0.1 && < 0.2 test-suite tests type: exitcode-stdio-1.0- hs-source-dirs: tests+ hs-source-dirs: tests . main-is: Tests.hs- -- c-sources: cbits/cbits.c+ c-sources: cbits/cbits.c ghc-options: -Wall -threaded -O0 -rtsopts@@ -169,6 +170,7 @@ build-depends: HUnit >= 1.2, QuickCheck >= 2.4,+ array, base, bytestring, deepseq,@@ -177,8 +179,19 @@ random, test-framework >= 0.4, test-framework-hunit >= 0.2,- test-framework-quickcheck2 >= 0.2,- text+ 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)+ cpp-options: -DINTEGER_GMP+ build-depends: integer >= 0.1 && < 0.2 source-repository head type: git