{-# LANGUAGE CPP             #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE BangPatterns #-}

module Streamly.Compat.Text
  ( toArray
  , unsafeFromArray

  , reader

  -- , unsafeCreateOf
  , unsafeCreate
  )
where

import Control.Monad.IO.Class (MonadIO)
import Data.Word (Word8)
import GHC.Exts (unsafeCoerce#)
import Streamly.Data.Fold (Fold)
import Streamly.Data.Unfold (Unfold, lmap)

import qualified Data.Text as T
import qualified Data.Text.Array as TArr

-- Internal imports
import Data.Text.Internal (Text(..))
import Streamly.Internal.Data.Array (Array(..))
import Streamly.Internal.Data.MutByteArray (MutByteArray(..))

import qualified Streamly.Data.Array as Array
import qualified Streamly.Internal.Data.MutByteArray as MBArr

import Prelude hiding (read)

-- This module currently only supports text >= 2.
-- For text < 2 we need to consider utf16 encoding.

#if MIN_VERSION_streamly_core(0,2,2)
#define EMPTY MBArr.empty
#else
#define EMPTY MBArr.nil
#endif

#if MIN_VERSION_streamly_core(0,3,0)
#define CREATE_OF Array.createOf
#define CREATE Array.create
#else
#define CREATE_OF Array.writeN
#define CREATE Array.write
#endif

-- | Convert a 'Text' to an array of 'Word8'. It can be done in constant time.
{-# INLINE toArray #-}
toArray :: Text -> Array Word8
toArray :: Text -> Array Word8
toArray (Text (TArr.ByteArray ByteArray#
_) Int
_ Int
len)
    | Int
len Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 = MutByteArray -> Int -> Int -> Array Word8
forall a. MutByteArray -> Int -> Int -> Array a
Array EMPTY 0 0
toArray (Text (TArr.ByteArray ByteArray#
barr#) Int
off8 Int
len8) =
    MutByteArray -> Int -> Int -> Array Word8
forall a. MutByteArray -> Int -> Int -> Array a
Array (MutableByteArray# RealWorld -> MutByteArray
MutByteArray (ByteArray# -> MutableByteArray# RealWorld
forall a b. a -> b
unsafeCoerce# ByteArray#
barr#)) Int
off8 (Int
off8 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
len8)

-- | Treat an an array of 'Word8' as 'Text'.
--
-- This function is unsafe: the caller must ensure that the 'Array' 'Word8' is a
-- valid UTF-8 encoding.
--
-- This function unwraps the 'Array' and wraps it with 'Text' constructors and
-- hence the operation is performed in constant time.
{-# INLINE unsafeFromArray #-}
unsafeFromArray :: Array Word8 -> Text
unsafeFromArray :: Array Word8 -> Text
unsafeFromArray Array {Int
MutByteArray
arrContents :: MutByteArray
arrStart :: Int
arrEnd :: Int
arrEnd :: forall a. Array a -> Int
arrStart :: forall a. Array a -> Int
arrContents :: forall a. Array a -> MutByteArray
..}
    | Int
len8 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 = Text
T.empty
    | Bool
otherwise = ByteArray -> Int -> Int -> Text
Text (ByteArray# -> ByteArray
TArr.ByteArray (MutableByteArray# RealWorld -> ByteArray#
forall a b. a -> b
unsafeCoerce# MutableByteArray# RealWorld
marr#)) Int
off8 Int
len8

    where

    len8 :: Int
len8 = Int
arrEnd Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
arrStart
    off8 :: Int
off8 = Int
arrStart
    !(MutByteArray MutableByteArray# RealWorld
marr#) = MutByteArray
arrContents

-- | Unfold a 'Text' to a stream of Word8.
{-# INLINE reader #-}
reader :: Monad m => Unfold m Text Word8
reader :: forall (m :: * -> *). Monad m => Unfold m Text Word8
reader = (Text -> Array Word8)
-> Unfold m (Array Word8) Word8 -> Unfold m Text Word8
forall a c (m :: * -> *) b.
(a -> c) -> Unfold m c b -> Unfold m a b
lmap Text -> Array Word8
toArray Unfold m (Array Word8) Word8
forall (m :: * -> *) a. (Monad m, Unbox a) => Unfold m (Array a) a
Array.reader

-- | Fold a stream of Word8 to a 'Text' of given size in bytes.
{-# INLINE _unsafeCreateOf #-}
_unsafeCreateOf :: MonadIO m => Int -> Fold m Word8 Text
_unsafeCreateOf :: forall (m :: * -> *). MonadIO m => Int -> Fold m Word8 Text
_unsafeCreateOf Int
i = Array Word8 -> Text
unsafeFromArray (Array Word8 -> Text)
-> Fold m Word8 (Array Word8) -> Fold m Word8 Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CREATE_OF i

-- | Fold a stream of Word8 to a 'Text' of appropriate size.
{-# INLINE unsafeCreate #-}
unsafeCreate :: MonadIO m => Fold m Word8 Text
unsafeCreate :: forall (m :: * -> *). MonadIO m => Fold m Word8 Text
unsafeCreate = Array Word8 -> Text
unsafeFromArray (Array Word8 -> Text)
-> Fold m Word8 (Array Word8) -> Fold m Word8 Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CREATE