{-# LANGUAGE CPP #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeSynonymInstances #-}

-- |
-- Module      : Data.Text.Internal.Builder
-- License     : BSD-style (see LICENSE)
-- Stability   : experimental
--
-- /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!
--
-- @since 2.0.2

module Data.Text.Internal.StrictBuilder
  ( StrictTextBuilder(..)
  , StrictBuilder
  , toText
  , fromChar
  , fromText

    -- * Unsafe
    -- $unsafe
  , unsafeFromByteString
  , unsafeFromWord8
  ) where

import Control.Monad.ST (ST, runST)
import Control.Monad.ST.Unsafe (unsafeIOToST, unsafeSTToIO)
import Data.Functor (void)
import Data.Word (Word8)
import Data.ByteString (ByteString)
#if !MIN_VERSION_base(4,11,0)
import Data.Semigroup (Semigroup(..))
#endif
import Data.Text.Internal (Text(..), empty, safe)
import Data.Text.Internal.ByteStringCompat (withBS)
import Data.Text.Internal.Encoding.Utf8 (utf8Length)
import Data.Text.Internal.Unsafe (unsafeWithForeignPtr)
import qualified Data.ByteString as B
import qualified Data.Text.Array as A
import qualified Data.Text.Internal.Unsafe.Char as Char

-- | A delayed representation of strict 'Text'.
--
-- @since 2.1.2
data StrictTextBuilder = StrictTextBuilder
  { StrictTextBuilder -> Int
sbLength :: {-# UNPACK #-} !Int
  , StrictTextBuilder -> forall s. MArray s -> Int -> ST s ()
sbWrite :: forall s. A.MArray s -> Int -> ST s ()
  }

-- | A delayed representation of strict 'Text'.
--
-- @since 2.0.2
{-# DEPRECATED StrictBuilder "Use StrictTextBuilder instead" #-}
type StrictBuilder = StrictTextBuilder

-- | Use 'StrictBuilder' to build 'Text'.
--
-- @since 2.0.2
toText :: StrictTextBuilder -> Text
toText :: StrictTextBuilder -> Text
toText (StrictTextBuilder Int
0 forall s. MArray s -> Int -> ST s ()
_) = Text
empty
toText (StrictTextBuilder Int
n forall s. MArray s -> Int -> ST s ()
write) = (forall s. ST s Text) -> Text
forall a. (forall s. ST s a) -> a
runST (do
  MArray s
dst <- Int -> ST s (MArray s)
forall s. Int -> ST s (MArray s)
A.new Int
n
  MArray s -> Int -> ST s ()
forall s. MArray s -> Int -> ST s ()
write MArray s
dst Int
0
  Array
arr <- MArray s -> ST s Array
forall s. MArray s -> ST s Array
A.unsafeFreeze MArray s
dst
  Text -> ST s Text
forall a. a -> ST s a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Array -> Int -> Int -> Text
Text Array
arr Int
0 Int
n))

-- | Concatenation of 'StrictBuilder' is right-biased:
-- the right builder will be run first. This allows a builder to
-- run tail-recursively when it was accumulated left-to-right.
instance Semigroup StrictTextBuilder where
  <> :: StrictTextBuilder -> StrictTextBuilder -> StrictTextBuilder
(<>) = StrictTextBuilder -> StrictTextBuilder -> StrictTextBuilder
appendRStrictBuilder

instance Monoid StrictTextBuilder where
  mempty :: StrictTextBuilder
mempty = StrictTextBuilder
emptyStrictBuilder
  mappend :: StrictTextBuilder -> StrictTextBuilder -> StrictTextBuilder
mappend = StrictTextBuilder -> StrictTextBuilder -> StrictTextBuilder
forall a. Semigroup a => a -> a -> a
(<>)

emptyStrictBuilder :: StrictTextBuilder
emptyStrictBuilder :: StrictTextBuilder
emptyStrictBuilder = Int -> (forall s. MArray s -> Int -> ST s ()) -> StrictTextBuilder
StrictTextBuilder Int
0 (\MArray s
_ Int
_ -> () -> ST s ()
forall a. a -> ST s a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ())

appendRStrictBuilder :: StrictTextBuilder -> StrictTextBuilder -> StrictTextBuilder
appendRStrictBuilder :: StrictTextBuilder -> StrictTextBuilder -> StrictTextBuilder
appendRStrictBuilder (StrictTextBuilder Int
0 forall s. MArray s -> Int -> ST s ()
_) StrictTextBuilder
b2 = StrictTextBuilder
b2
appendRStrictBuilder StrictTextBuilder
b1 (StrictTextBuilder Int
0 forall s. MArray s -> Int -> ST s ()
_) = StrictTextBuilder
b1
appendRStrictBuilder (StrictTextBuilder Int
n1 forall s. MArray s -> Int -> ST s ()
write1) (StrictTextBuilder Int
n2 forall s. MArray s -> Int -> ST s ()
write2) =
  Int -> (forall s. MArray s -> Int -> ST s ()) -> StrictTextBuilder
StrictTextBuilder (Int
n1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
n2) (\MArray s
dst Int
ofs -> do
    MArray s -> Int -> ST s ()
forall s. MArray s -> Int -> ST s ()
write2 MArray s
dst (Int
ofs Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
n1)
    MArray s -> Int -> ST s ()
forall s. MArray s -> Int -> ST s ()
write1 MArray s
dst Int
ofs)

copyFromByteString :: A.MArray s -> Int -> ByteString -> ST s ()
copyFromByteString :: forall s. MArray s -> Int -> ByteString -> ST s ()
copyFromByteString MArray s
dst Int
ofs ByteString
src = ByteString -> (ForeignPtr Word8 -> Int -> ST s ()) -> ST s ()
forall r. ByteString -> (ForeignPtr Word8 -> Int -> r) -> r
withBS ByteString
src ((ForeignPtr Word8 -> Int -> ST s ()) -> ST s ())
-> (ForeignPtr Word8 -> Int -> ST s ()) -> ST s ()
forall a b. (a -> b) -> a -> b
$ \ ForeignPtr Word8
srcFPtr Int
len ->
  IO () -> ST s ()
forall a s. IO a -> ST s a
unsafeIOToST (IO () -> ST s ()) -> IO () -> ST s ()
forall a b. (a -> b) -> a -> b
$ ForeignPtr Word8 -> (Ptr Word8 -> IO ()) -> IO ()
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
unsafeWithForeignPtr ForeignPtr Word8
srcFPtr ((Ptr Word8 -> IO ()) -> IO ()) -> (Ptr Word8 -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ Ptr Word8
srcPtr -> do
    ST s () -> IO ()
forall s a. ST s a -> IO a
unsafeSTToIO (ST s () -> IO ()) -> ST s () -> IO ()
forall a b. (a -> b) -> a -> b
$ MArray s -> Int -> Ptr Word8 -> Int -> ST s ()
forall s. MArray s -> Int -> Ptr Word8 -> Int -> ST s ()
A.copyFromPointer MArray s
dst Int
ofs Ptr Word8
srcPtr Int
len

-- | Copy a 'ByteString'.
--
-- Unsafe: This may not be valid UTF-8 text.
--
-- @since 2.0.2
unsafeFromByteString :: ByteString -> StrictTextBuilder
unsafeFromByteString :: ByteString -> StrictTextBuilder
unsafeFromByteString ByteString
bs =
  Int -> (forall s. MArray s -> Int -> ST s ()) -> StrictTextBuilder
StrictTextBuilder (ByteString -> Int
B.length ByteString
bs) (\MArray s
dst Int
ofs -> MArray s -> Int -> ByteString -> ST s ()
forall s. MArray s -> Int -> ByteString -> ST s ()
copyFromByteString MArray s
dst Int
ofs ByteString
bs)

-- |
-- @since 2.0.2
{-# INLINE fromChar #-}
fromChar :: Char -> StrictTextBuilder
fromChar :: Char -> StrictTextBuilder
fromChar Char
c =
  Int -> (forall s. MArray s -> Int -> ST s ()) -> StrictTextBuilder
StrictTextBuilder (Char -> Int
utf8Length Char
c) (\MArray s
dst Int
ofs -> ST s Int -> ST s ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (MArray s -> Int -> Char -> ST s Int
forall s. MArray s -> Int -> Char -> ST s Int
Char.unsafeWrite MArray s
dst Int
ofs (Char -> Char
safe Char
c)))

-- $unsafe
-- For internal purposes, we abuse 'StrictBuilder' as a delayed 'Array' rather
-- than 'Text': it may not actually be valid 'Text'.

-- | Unsafe: This may not be valid UTF-8 text.
--
-- @since 2.0.2
unsafeFromWord8 :: Word8 -> StrictTextBuilder
unsafeFromWord8 :: Word8 -> StrictTextBuilder
unsafeFromWord8 !Word8
w =
  Int -> (forall s. MArray s -> Int -> ST s ()) -> StrictTextBuilder
StrictTextBuilder Int
1 (\MArray s
dst Int
ofs -> MArray s -> Int -> Word8 -> ST s ()
forall s. MArray s -> Int -> Word8 -> ST s ()
A.unsafeWrite MArray s
dst Int
ofs Word8
w)

-- | Copy 'Text' in a 'StrictBuilder'
--
-- @since 2.0.2
fromText :: Text -> StrictTextBuilder
fromText :: Text -> StrictTextBuilder
fromText (Text Array
src Int
srcOfs Int
n) = Int -> (forall s. MArray s -> Int -> ST s ()) -> StrictTextBuilder
StrictTextBuilder Int
n (\MArray s
dst Int
dstOfs ->
  Int -> MArray s -> Int -> Array -> Int -> ST s ()
forall s. Int -> MArray s -> Int -> Array -> Int -> ST s ()
A.copyI Int
n MArray s
dst Int
dstOfs Array
src Int
srcOfs)