{-# LANGUAGE CPP, MagicHash #-}

-- |
-- Module      : Data.Text.Internal.Unsafe.Char
-- Copyright   : (c) 2008, 2009 Tom Harper,
--               (c) 2009, 2010 Bryan O'Sullivan,
--               (c) 2009 Duncan Coutts
--
-- License     : BSD-style
-- Maintainer  : bos@serpentine.com
-- Stability   : experimental
-- Portability : 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!
--
-- Fast character manipulation functions.
module Data.Text.Internal.Unsafe.Char
    (
      ord
    , unsafeChr16
    , unsafeChr8
    , unsafeChr32
    , unsafeWrite
    ) where

import Control.Monad.ST (ST)
import Data.Text.Internal.Encoding.Utf8
import GHC.Exts (Char(..), Int(..), chr#, ord#, word2Int#)
import GHC.Word (Word8(..), Word16(..), Word32(..))
import qualified Data.Text.Array as A
import Data.Text.Internal.PrimCompat ( word8ToWord#, word16ToWord#, word32ToWord# )
#if defined(ASSERTS)
import GHC.Stack (HasCallStack)
#endif

ord :: Char -> Int
ord (C# c#) = I# (ord# c#)
{-# INLINE ord #-}

-- | @since 2.0
unsafeChr16 :: Word16 -> Char
unsafeChr16 (W16# w#) = C# (chr# (word2Int# (word16ToWord# w#)))
{-# INLINE unsafeChr16 #-}

unsafeChr8 :: Word8 -> Char
unsafeChr8 (W8# w#) = C# (chr# (word2Int# (word8ToWord# w#)))
{-# INLINE unsafeChr8 #-}

unsafeChr32 :: Word32 -> Char
unsafeChr32 (W32# w#) = C# (chr# (word2Int# (word32ToWord# w#)))
{-# INLINE unsafeChr32 #-}

-- | Write a character into the array at the given offset.  Returns
-- the number of 'Word8's written.
unsafeWrite ::
#if defined(ASSERTS)
    HasCallStack =>
#endif
    A.MArray s -> Int -> Char -> ST s Int
unsafeWrite marr i c = case utf8Length c of
    1 -> do
        let n0 = intToWord8 (ord c)
        A.unsafeWrite marr i n0
        return 1
    2 -> do
        let (n0, n1) = ord2 c
        A.unsafeWrite marr i     n0
        A.unsafeWrite marr (i+1) n1
        return 2
    3 -> do
        let (n0, n1, n2) = ord3 c
        A.unsafeWrite marr i     n0
        A.unsafeWrite marr (i+1) n1
        A.unsafeWrite marr (i+2) n2
        return 3
    _ -> do
        let (n0, n1, n2, n3) = ord4 c
        A.unsafeWrite marr i     n0
        A.unsafeWrite marr (i+1) n1
        A.unsafeWrite marr (i+2) n2
        A.unsafeWrite marr (i+3) n3
        return 4
{-# INLINE unsafeWrite #-}

intToWord8 :: Int -> Word8
intToWord8 = fromIntegral