{-# 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
    , unsafeChr
    , unsafeChr8
    , unsafeChr32
    , unsafeWrite
    -- , unsafeWriteRev
    ) where

#ifdef ASSERTS
import Control.Exception (assert)
#endif
import Control.Monad.ST (ST)
import Data.Bits ((.&.))
import Data.Text.Internal.Unsafe.Shift (shiftR)
import GHC.Exts (Char(..), Int(..), Word#, chr#, ord#, word2Int#)
import GHC.Word (Word8(..), Word16(..), Word32(..))
import qualified Data.Text.Array as A

#if MIN_VERSION_base(4,16,0)
import GHC.Exts (Word8#, Word16#, Word32#, word8ToWord#, word16ToWord#, word32ToWord#)
#endif

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

unsafeChr :: Word16 -> Char
unsafeChr :: Word16 -> Char
unsafeChr (W16# Word#
w#) = Char# -> Char
C# (Int# -> Char#
chr# (Word# -> Int#
word2Int# (Word# -> Word#
word16ToWordCompat# Word#
w#)))
{-# INLINE unsafeChr #-}

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

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

-- | Write a character into the array at the given offset.  Returns
-- the number of 'Word16's written.
unsafeWrite :: A.MArray s -> Int -> Char -> ST s Int
unsafeWrite :: MArray s -> Int -> Char -> ST s Int
unsafeWrite MArray s
marr Int
i Char
c
    | Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0x10000 = do
#if defined(ASSERTS)
        assert (i >= 0) . assert (i < A.length marr) $ return ()
#endif
        MArray s -> Int -> Word16 -> ST s ()
forall s. MArray s -> Int -> Word16 -> ST s ()
A.unsafeWrite MArray s
marr Int
i (Int -> Word16
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
n)
        Int -> ST s Int
forall (m :: * -> *) a. Monad m => a -> m a
return Int
1
    | Bool
otherwise = do
#if defined(ASSERTS)
        assert (i >= 0) . assert (i < A.length marr - 1) $ return ()
#endif
        MArray s -> Int -> Word16 -> ST s ()
forall s. MArray s -> Int -> Word16 -> ST s ()
A.unsafeWrite MArray s
marr Int
i Word16
lo
        MArray s -> Int -> Word16 -> ST s ()
forall s. MArray s -> Int -> Word16 -> ST s ()
A.unsafeWrite MArray s
marr (Int
iInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1) Word16
hi
        Int -> ST s Int
forall (m :: * -> *) a. Monad m => a -> m a
return Int
2
    where n :: Int
n = Char -> Int
ord Char
c
          m :: Int
m = Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
0x10000
          lo :: Word16
lo = Int -> Word16
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Word16) -> Int -> Word16
forall a b. (a -> b) -> a -> b
$ (Int
m Int -> Int -> Int
forall a. UnsafeShift a => a -> Int -> a
`shiftR` Int
10) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
0xD800
          hi :: Word16
hi = Int -> Word16
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Word16) -> Int -> Word16
forall a b. (a -> b) -> a -> b
$ (Int
m Int -> Int -> Int
forall a. Bits a => a -> a -> a
.&. Int
0x3FF) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
0xDC00
{-# INLINE unsafeWrite #-}

{-
unsafeWriteRev :: A.MArray s Word16 -> Int -> Char -> ST s Int
unsafeWriteRev marr i c
    | n < 0x10000 = do
        assert (i >= 0) . assert (i < A.length marr) $
          A.unsafeWrite marr i (fromIntegral n)
        return (i-1)
    | otherwise = do
        assert (i >= 1) . assert (i < A.length marr) $
          A.unsafeWrite marr (i-1) lo
        A.unsafeWrite marr i hi
        return (i-2)
    where n = ord c
          m = n - 0x10000
          lo = fromIntegral $ (m `shiftR` 10) + 0xD800
          hi = fromIntegral $ (m .&. 0x3FF) + 0xDC00
{-# INLINE unsafeWriteRev #-}
-}

#if MIN_VERSION_base(4,16,0)
word8ToWordCompat# :: Word8# -> Word#
word8ToWordCompat# = word8ToWord#

word16ToWordCompat# :: Word16# -> Word#
word16ToWordCompat# = word16ToWord#

word32ToWordCompat# :: Word32# -> Word#
word32ToWordCompat# = word32ToWord#
#else
word8ToWordCompat# :: Word# -> Word#
word8ToWordCompat# :: Word# -> Word#
word8ToWordCompat# Word#
x = Word#
x

word16ToWordCompat# :: Word# -> Word#
word16ToWordCompat# :: Word# -> Word#
word16ToWordCompat# Word#
x = Word#
x

word32ToWordCompat# :: Word# -> Word#
word32ToWordCompat# :: Word# -> Word#
word32ToWordCompat# Word#
x = Word#
x
#endif