{-# LANGUAGE BangPatterns, CPP, RankNTypes #-}{-# OPTIONS_HADDOCK not-home #-}------------------------------------------------------------------------------- |-- Module : Data.Text.Internal.Builder-- Copyright : (c) 2013 Bryan O'Sullivan-- (c) 2010 Johan Tibell-- License : BSD-style (see LICENSE)---- Maintainer : Johan Tibell <johan.tibell@gmail.com>-- Stability : experimental-- Portability : portable to Hugs and GHC---- /Warning/: this is an internal module, and does not have a stable-- API or name. Functions in this module may not check or enforce-- preconditions expected by public modules. Use at your own risk!---- 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.-------------------------------------------------------------------------------moduleData.Text.Internal.Builder(-- * Public API-- ** The Builder typeBuilder ,toLazyText ,toLazyTextWith -- ** Constructing Builders,singleton ,fromText ,fromLazyText ,fromString -- ** Flushing the buffer state,flush -- * Internal functions,append' ,ensureFree ,writeN )whereimportControl.Monad.ST(ST,runST)importData.Monoid(Monoid(..)) #if !MIN_VERSION_base(4,11,0) importData.Semigroup(Semigroup(..)) #endif importData.Text.Internal (Text (..),safe )importData.Text.Internal.Lazy (smallChunkSize )importData.Text.Unsafe (inlineInterleaveST )importData.Text.Internal.Unsafe.Char (unsafeWrite )importPreludehiding(map,putChar)importqualifiedData.StringasStringimportqualifiedData.Text asSimportqualifiedData.Text.Array asAimportqualifiedData.Text.Lazy asL #if defined(ASSERTS) importGHC.Stack(HasCallStack) #endif -------------------------------------------------------------------------- | 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@.newtypeBuilder =Builder {-- Invariant (from Data.Text.Lazy):-- The lists include no null Texts.Builder -> forall s. (Buffer s -> ST s [Text]) -> Buffer s -> ST s [Text] runBuilder ::foralls .(Buffer s ->STs [S.Text ])->Buffer s ->STs [S.Text ]}instanceSemigroupBuilder where<> :: Builder -> Builder -> Builder (<>)=Builder -> Builder -> Builder append {-# INLINE(<>)#-}instanceMonoidBuilder wheremempty :: Builder mempty=Builder empty {-# INLINEmempty#-}mappend :: Builder -> Builder -> Builder mappend=forall a. Semigroup a => a -> a -> a (<>){-# INLINEmappend#-}mconcat :: [Builder] -> Builder mconcat=forall (t :: * -> *) a b. Foldable t => (a -> b -> b) -> b -> t a -> b foldrforall a. Monoid a => a -> a -> a mappendforall a. Monoid a => a Data.Monoid.mempty{-# INLINEmconcat#-}-- | Performs replacement on invalid scalar values:---- >>> :set -XOverloadedStrings-- >>> "55555円" :: Builder-- "65533円"instanceString.IsStringBuilder wherefromString :: String -> Builder fromString=String -> Builder fromString {-# INLINEfromString#-}instanceShowBuilder whereshow :: Builder -> String show=forall a. Show a => a -> String showforall b c a. (b -> c) -> (a -> b) -> a -> c .Builder -> Text toLazyText instanceEqBuilder whereBuilder a == :: Builder -> Builder -> Bool ==Builder b =Builder -> Text toLazyText Builder a forall a. Eq a => a -> a -> Bool ==Builder -> Text toLazyText Builder b instanceOrdBuilder whereBuilder a <= :: Builder -> Builder -> Bool <=Builder b =Builder -> Text toLazyText Builder a forall a. Ord a => a -> a -> Bool <=Builder -> Text toLazyText Builder b -------------------------------------------------------------------------- | /O(1)./ The empty @Builder@, satisfying---- * @'toLazyText' 'empty' = 'L.empty'@--empty ::Builder empty :: Builder empty =(forall s. (Buffer s -> ST s [Text]) -> Buffer s -> ST s [Text]) -> Builder Builder (\Buffer s -> ST s [Text] k Buffer s buf ->Buffer s -> ST s [Text] k Buffer s buf ){-# INLINEempty #-}-- | /O(1)./ A @Builder@ taking a single character, satisfying---- * @'toLazyText' ('singleton' c) = 'L.singleton' c@--singleton :: #if defined(ASSERTS) HasCallStack=> #endif Char->Builder singleton :: Char -> Builder singleton Char c =Int -> (forall s. MArray s -> Int -> ST s Int) -> Builder writeAtMost Int 4forall a b. (a -> b) -> a -> b $\MArray s marr Int o ->forall s. MArray s -> Int -> Char -> ST s Int unsafeWrite MArray s marr Int o (Char -> Char safe Char c ){-# INLINEsingleton #-}-------------------------------------------------------------------------- | /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 -> Builder -> Builder append (Builder forall s. (Buffer s -> ST s [Text]) -> Buffer s -> ST s [Text] f )(Builder forall s. (Buffer s -> ST s [Text]) -> Buffer s -> ST s [Text] g )=(forall s. (Buffer s -> ST s [Text]) -> Buffer s -> ST s [Text]) -> Builder Builder (forall s. (Buffer s -> ST s [Text]) -> Buffer s -> ST s [Text] f forall b c a. (b -> c) -> (a -> b) -> a -> c .forall s. (Buffer s -> ST s [Text]) -> Buffer s -> ST s [Text] g ){-# INLINE[0]append #-}-- TODO: Experiment to find the right threshold.copyLimit ::IntcopyLimit :: Int copyLimit =Int 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 :: Text -> Builder fromText t :: Text t @(Text Array arr Int off Int l )|Text -> Bool S.null Text t =Builder empty |Int l forall a. Ord a => a -> a -> Bool <=Int copyLimit =Int -> (forall s. MArray s -> Int -> ST s ()) -> Builder writeN Int l forall a b. (a -> b) -> a -> b $\MArray s marr Int o ->forall s. Int -> MArray s -> Int -> Array -> Int -> ST s () A.copyI Int l MArray s marr Int o Array arr Int off |Bool otherwise=Builder flush Builder -> Builder -> Builder `append` ([Text] -> [Text]) -> Builder mapBuilder (Text t forall a. a -> [a] -> [a] :){-# INLINE[1]fromText #-}{-# RULES"fromText/pack"foralls .fromText (S.pack s )=fromString s #-}-- | /O(1)./ A Builder taking a @String@, satisfying---- * @'toLazyText' ('fromString' s) = 'L.fromChunks' [S.pack s]@---- Performs replacement on invalid scalar values:---- >>> fromString "55555円"-- "65533円"---- @since 1.2.0.0fromString ::String->Builder fromString :: String -> Builder fromString String str =(forall s. (Buffer s -> ST s [Text]) -> Buffer s -> ST s [Text]) -> Builder Builder forall a b. (a -> b) -> a -> b $\Buffer s -> ST s [Text] k (Buffer MArray s p0 Int o0 Int u0 Int l0 )->letloop :: MArray s -> Int -> Int -> Int -> String -> ST s [Text] loop !MArray s marr !Int o !Int u !Int l []=Buffer s -> ST s [Text] k (forall s. MArray s -> Int -> Int -> Int -> Buffer s Buffer MArray s marr Int o Int u Int l )loop MArray s marr Int o Int u Int l s :: String s @(Char c :String cs )|Int l forall a. Ord a => a -> a -> Bool <=Int 3=doforall s. MArray s -> Int -> ST s () A.shrinkM MArray s marr (Int o forall a. Num a => a -> a -> a +Int u )Array arr <-forall s. MArray s -> ST s Array A.unsafeFreeze MArray s marr let!t :: Text t =Array -> Int -> Int -> Text Text Array arr Int o Int u MArray s marr' <-forall s. Int -> ST s (MArray s) A.new Int chunkSize [Text] ts <-forall s a. ST s a -> ST s a inlineInterleaveST (MArray s -> Int -> Int -> Int -> String -> ST s [Text] loop MArray s marr' Int 0Int 0Int chunkSize String s )forall (m :: * -> *) a. Monad m => a -> m a returnforall a b. (a -> b) -> a -> b $Text t forall a. a -> [a] -> [a] :[Text] ts |Bool otherwise=doInt n <-forall s. MArray s -> Int -> Char -> ST s Int unsafeWrite MArray s marr (Int o forall a. Num a => a -> a -> a +Int u )(Char -> Char safe Char c )MArray s -> Int -> Int -> Int -> String -> ST s [Text] loop MArray s marr Int o (Int u forall a. Num a => a -> a -> a +Int n )(Int l forall a. Num a => a -> a -> a -Int n )String cs inMArray s -> Int -> Int -> Int -> String -> ST s [Text] loop MArray s p0 Int o0 Int u0 Int l0 String str wherechunkSize :: Int chunkSize =Int smallChunkSize {-# INLINEfromString #-}-- | /O(1)./ A @Builder@ taking a lazy @Text@, satisfying---- * @'toLazyText' ('fromLazyText' t) = t@--fromLazyText ::L.Text ->Builder fromLazyText :: Text -> Builder fromLazyText Text ts =Builder flush Builder -> Builder -> Builder `append` ([Text] -> [Text]) -> Builder mapBuilder (Text -> [Text] L.toChunks Text ts forall a. [a] -> [a] -> [a] ++){-# INLINEfromLazyText #-}-------------------------------------------------------------------------- Our internal buffer typedataBuffer 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 :: Builder -> Text toLazyText =Int -> Builder -> Text toLazyTextWith Int 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 :: Int -> Builder -> Text toLazyTextWith Int chunkSize Builder m =[Text] -> Text L.fromChunks (forall a. (forall s. ST s a) -> a runSTforall a b. (a -> b) -> a -> b $forall s. Int -> ST s (Buffer s) newBuffer Int chunkSize forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b >>=Builder -> forall s. (Buffer s -> ST s [Text]) -> Buffer s -> ST s [Text] runBuilder (Builder m Builder -> Builder -> Builder `append` Builder flush )(forall a b. a -> b -> a const(forall (m :: * -> *) a. Monad m => a -> m a 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 flush =(forall s. (Buffer s -> ST s [Text]) -> Buffer s -> ST s [Text]) -> Builder Builder forall a b. (a -> b) -> a -> b $\Buffer s -> ST s [Text] k buf :: Buffer s buf @(Buffer MArray s p Int o Int u Int l )->ifInt u forall a. Eq a => a -> a -> Bool ==Int 0thenBuffer s -> ST s [Text] k Buffer s buf elsedoArray arr <-forall s. MArray s -> ST s Array A.unsafeFreeze MArray s p let!b :: Buffer s b =forall s. MArray s -> Int -> Int -> Int -> Buffer s Buffer MArray s p (Int o forall a. Num a => a -> a -> a +Int u )Int 0Int l !t :: Text t =Array -> Int -> Int -> Text Text Array arr Int o Int u [Text] ts <-forall s a. ST s a -> ST s a inlineInterleaveST (Buffer s -> ST s [Text] k Buffer s b )forall (m :: * -> *) a. Monad m => a -> m a returnforall a b. (a -> b) -> a -> b $!Text t forall a. a -> [a] -> [a] :[Text] ts {-# INLINE[1]flush #-}-- defer inlining so that flush/flush rule may fire.-------------------------------------------------------------------------- | Sequence an ST operation on the bufferwithBuffer ::(foralls .Buffer s ->STs (Buffer s ))->Builder withBuffer :: (forall s. Buffer s -> ST s (Buffer s)) -> Builder withBuffer forall s. Buffer s -> ST s (Buffer s) f =(forall s. (Buffer s -> ST s [Text]) -> Buffer s -> ST s [Text]) -> Builder Builder forall a b. (a -> b) -> a -> b $\Buffer s -> ST s [Text] k Buffer s buf ->forall s. Buffer s -> ST s (Buffer s) f Buffer s buf forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b >>=Buffer s -> ST s [Text] k {-# INLINEwithBuffer #-}-- | Get the size of the bufferwithSize ::(Int->Builder )->Builder withSize :: (Int -> Builder) -> Builder withSize Int -> Builder f =(forall s. (Buffer s -> ST s [Text]) -> Buffer s -> ST s [Text]) -> Builder Builder forall a b. (a -> b) -> a -> b $\Buffer s -> ST s [Text] k buf :: Buffer s buf @(Buffer MArray s _Int _Int _Int l )->Builder -> forall s. (Buffer s -> ST s [Text]) -> Buffer s -> ST s [Text] runBuilder (Int -> Builder f Int l )Buffer s -> ST s [Text] k Buffer s buf {-# INLINEwithSize #-}-- | Map the resulting list of texts.mapBuilder ::([S.Text ]->[S.Text ])->Builder mapBuilder :: ([Text] -> [Text]) -> Builder mapBuilder [Text] -> [Text] f =(forall s. (Buffer s -> ST s [Text]) -> Buffer s -> ST s [Text]) -> Builder Builder (forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b fmap[Text] -> [Text] f forall b c a. (b -> c) -> (a -> b) -> a -> c .)-------------------------------------------------------------------------- | Ensure that there are at least @n@ many elements available.ensureFree ::Int->Builder ensureFree :: Int -> Builder ensureFree !Int n =(Int -> Builder) -> Builder withSize forall a b. (a -> b) -> a -> b $\Int l ->ifInt n forall a. Ord a => a -> a -> Bool <=Int l thenBuilder empty elseBuilder flush Builder -> Builder -> Builder `append'` (forall s. Buffer s -> ST s (Buffer s)) -> Builder withBuffer (forall a b. a -> b -> a const(forall s. Int -> ST s (Buffer s) newBuffer (forall a. Ord a => a -> a -> a maxInt n Int smallChunkSize ))){-# INLINE[0]ensureFree #-}writeAtMost ::Int->(foralls .A.MArray s ->Int->STs Int)->Builder writeAtMost :: Int -> (forall s. MArray s -> Int -> ST s Int) -> Builder writeAtMost Int n forall s. MArray s -> Int -> ST s Int f =Int -> Builder ensureFree Int n Builder -> Builder -> Builder `append'` (forall s. Buffer s -> ST s (Buffer s)) -> Builder withBuffer (forall s. (MArray s -> Int -> ST s Int) -> Buffer s -> ST s (Buffer s) writeBuffer forall s. MArray s -> Int -> ST s Int f ){-# INLINE[0]writeAtMost #-}-- | Ensure that @n@ many elements are available, and then use @f@ to-- write some elements into the memory.writeN ::Int->(foralls .A.MArray s ->Int->STs ())->Builder writeN :: Int -> (forall s. MArray s -> Int -> ST s ()) -> Builder writeN Int n forall s. MArray s -> Int -> ST s () f =Int -> (forall s. MArray s -> Int -> ST s Int) -> Builder writeAtMost Int n (\MArray s p Int o ->forall s. MArray s -> Int -> ST s () f MArray s p Int o forall (m :: * -> *) a b. Monad m => m a -> m b -> m b >>forall (m :: * -> *) a. Monad m => a -> m a returnInt n ){-# INLINEwriteN #-}writeBuffer ::(A.MArray s ->Int->STs Int)->Buffer s ->STs (Buffer s )writeBuffer :: forall s. (MArray s -> Int -> ST s Int) -> Buffer s -> ST s (Buffer s) writeBuffer MArray s -> Int -> ST s Int f (Buffer MArray s p Int o Int u Int l )=doInt n <-MArray s -> Int -> ST s Int f MArray s p (Int o forall a. Num a => a -> a -> a +Int u )forall (m :: * -> *) a. Monad m => a -> m a returnforall a b. (a -> b) -> a -> b $!forall s. MArray s -> Int -> Int -> Int -> Buffer s Buffer MArray s p Int o (Int u forall a. Num a => a -> a -> a +Int n )(Int l forall a. Num a => a -> a -> a -Int n ){-# INLINEwriteBuffer #-}newBuffer ::Int->STs (Buffer s )newBuffer :: forall s. Int -> ST s (Buffer s) newBuffer Int size =doMArray s arr <-forall s. Int -> ST s (MArray s) A.new Int size forall (m :: * -> *) a. Monad m => a -> m a returnforall a b. (a -> b) -> a -> b $!forall s. MArray s -> Int -> Int -> Int -> Buffer s Buffer MArray s arr Int 0Int 0Int size {-# INLINEnewBuffer #-}-------------------------------------------------------------------------- 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 -> Builder -> Builder append' (Builder forall s. (Buffer s -> ST s [Text]) -> Buffer s -> ST s [Text] f )(Builder forall s. (Buffer s -> ST s [Text]) -> Buffer s -> ST s [Text] g )=(forall s. (Buffer s -> ST s [Text]) -> Buffer s -> ST s [Text]) -> Builder Builder (forall s. (Buffer s -> ST s [Text]) -> Buffer s -> ST s [Text] f forall b c a. (b -> c) -> (a -> b) -> a -> c .forall s. (Buffer s -> ST s [Text]) -> Buffer s -> ST s [Text] g ){-# INLINEappend' #-}{-# RULES"append/writeAtMost"foralla b (f ::foralls .A.MArray s ->Int->STs Int)(g ::foralls .A.MArray s ->Int->STs 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 ->lets =n +m ins `seq`returns ))ws "writeAtMost/writeAtMost"foralla b (f ::foralls .A.MArray s ->Int->STs Int)(g ::foralls .A.MArray s ->Int->STs Int).append (writeAtMost a f )(writeAtMost b g )=writeAtMost (a +b )(\marr o ->f marr o >>=\n ->g marr (o +n )>>=\m ->lets =n +m ins `seq`returns )"ensureFree/ensureFree"foralla b .append (ensureFree a )(ensureFree b )=ensureFree (maxa b )"flush/flush"append flush flush =flush #-}