{-# LANGUAGE CPP, MagicHash, UnboxedTuples #-}
-- |
-- Module      : Data.Text.Unsafe
-- Copyright   : (c) 2009, 2010, 2011 Bryan O'Sullivan
-- License     : BSD-style
-- Maintainer  : bos@serpentine.com
-- Portability : portable
--
-- A module containing unsafe 'Text' operations, for very very careful
-- use in heavily tested code.
module Data.Text.Unsafe
    (
      inlineInterleaveST
    , inlinePerformIO
    , unsafeDupablePerformIO
    , Iter(..)
    , iter
    , iter_
    , reverseIter
    , reverseIter_
    , unsafeHead
    , unsafeTail
    , lengthWord16
    , takeWord16
    , dropWord16
    ) where

#if defined(ASSERTS)
import Control.Exception (assert)
import GHC.Stack (HasCallStack)
#endif
import Data.Text.Internal.Encoding.Utf16 (chr2)
import Data.Text.Internal (Text(..))
import Data.Text.Internal.Unsafe (inlineInterleaveST, inlinePerformIO)
import Data.Text.Internal.Unsafe.Char (unsafeChr)
import qualified Data.Text.Array as A
import GHC.IO (unsafeDupablePerformIO)

-- | /O(1)/ A variant of 'head' for non-empty 'Text'. 'unsafeHead'
-- omits the check for the empty case, so there is an obligation on
-- the programmer to provide a proof that the 'Text' is non-empty.
unsafeHead :: Text -> Char
unsafeHead :: Text -> Char
unsafeHead (Text Array
arr Int
off Int
_len)
    | Word16
m forall a. Ord a => a -> a -> Bool
< Word16
0xD800 Bool -> Bool -> Bool
|| Word16
m forall a. Ord a => a -> a -> Bool
> Word16
0xDBFF = Word16 -> Char
unsafeChr Word16
m
    | Bool
otherwise                = Word16 -> Word16 -> Char
chr2 Word16
m Word16
n
    where m :: Word16
m = Array -> Int -> Word16
A.unsafeIndex Array
arr Int
off
          n :: Word16
n = Array -> Int -> Word16
A.unsafeIndex Array
arr (Int
offforall a. Num a => a -> a -> a
+Int
1)
{-# INLINE unsafeHead #-}

-- | /O(1)/ A variant of 'tail' for non-empty 'Text'. 'unsafeTail'
-- omits the check for the empty case, so there is an obligation on
-- the programmer to provide a proof that the 'Text' is non-empty.
unsafeTail :: Text -> Text
unsafeTail :: Text -> Text
unsafeTail t :: Text
t@(Text Array
arr Int
off Int
len) =
#if defined(ASSERTS)
    assert (d <= len) $
#endif
    Array -> Int -> Int -> Text
Text Array
arr (Int
offforall a. Num a => a -> a -> a
+Int
d) (Int
lenforall a. Num a => a -> a -> a
-Int
d)
  where d :: Int
d = Text -> Int -> Int
iter_ Text
t Int
0
{-# INLINE unsafeTail #-}

data Iter = Iter {-# UNPACK #-} !Char {-# UNPACK #-} !Int

-- | /O(1)/ Iterate (unsafely) one step forwards through a UTF-16
-- array, returning the current character and the delta to add to give
-- the next offset to iterate at.
iter ::
#if defined(ASSERTS)
  HasCallStack =>
#endif
  Text -> Int -> Iter
iter :: Text -> Int -> Iter
iter (Text Array
arr Int
off Int
_len) Int
i
    | Word16
m forall a. Ord a => a -> a -> Bool
< Word16
0xD800 Bool -> Bool -> Bool
|| Word16
m forall a. Ord a => a -> a -> Bool
> Word16
0xDBFF = Char -> Int -> Iter
Iter (Word16 -> Char
unsafeChr Word16
m) Int
1
    | Bool
otherwise                = Char -> Int -> Iter
Iter (Word16 -> Word16 -> Char
chr2 Word16
m Word16
n) Int
2
  where m :: Word16
m = Array -> Int -> Word16
A.unsafeIndex Array
arr Int
j
        n :: Word16
n = Array -> Int -> Word16
A.unsafeIndex Array
arr Int
k
        j :: Int
j = Int
off forall a. Num a => a -> a -> a
+ Int
i
        k :: Int
k = Int
j forall a. Num a => a -> a -> a
+ Int
1
{-# INLINE iter #-}

-- | /O(1)/ Iterate one step through a UTF-16 array, returning the
-- delta to add to give the next offset to iterate at.
iter_ :: Text -> Int -> Int
iter_ :: Text -> Int -> Int
iter_ (Text Array
arr Int
off Int
_len) Int
i | Word16
m forall a. Ord a => a -> a -> Bool
< Word16
0xD800 Bool -> Bool -> Bool
|| Word16
m forall a. Ord a => a -> a -> Bool
> Word16
0xDBFF = Int
1
                            | Bool
otherwise                = Int
2
  where m :: Word16
m = Array -> Int -> Word16
A.unsafeIndex Array
arr (Int
offforall a. Num a => a -> a -> a
+Int
i)
{-# INLINE iter_ #-}

-- | /O(1)/ Iterate one step backwards through a UTF-16 array,
-- returning the current character and the delta to add (i.e. a
-- negative number) to give the next offset to iterate at.
reverseIter :: Text -> Int -> (Char,Int)
reverseIter :: Text -> Int -> (Char, Int)
reverseIter (Text Array
arr Int
off Int
_len) Int
i
    | Word16
m forall a. Ord a => a -> a -> Bool
< Word16
0xDC00 Bool -> Bool -> Bool
|| Word16
m forall a. Ord a => a -> a -> Bool
> Word16
0xDFFF = (Word16 -> Char
unsafeChr Word16
m, -Int
1)
    | Bool
otherwise                = (Word16 -> Word16 -> Char
chr2 Word16
n Word16
m,    -Int
2)
  where m :: Word16
m = Array -> Int -> Word16
A.unsafeIndex Array
arr Int
j
        n :: Word16
n = Array -> Int -> Word16
A.unsafeIndex Array
arr Int
k
        j :: Int
j = Int
off forall a. Num a => a -> a -> a
+ Int
i
        k :: Int
k = Int
j forall a. Num a => a -> a -> a
- Int
1
{-# INLINE reverseIter #-}

-- | /O(1)/ Iterate one step backwards through a UTF-16 array,
-- returning the delta to add (i.e. a negative number) to give the
-- next offset to iterate at.
--
-- @since 1.1.1.0
reverseIter_ :: Text -> Int -> Int
reverseIter_ :: Text -> Int -> Int
reverseIter_ (Text Array
arr Int
off Int
_len) Int
i
    | Word16
m forall a. Ord a => a -> a -> Bool
< Word16
0xDC00 Bool -> Bool -> Bool
|| Word16
m forall a. Ord a => a -> a -> Bool
> Word16
0xDFFF = -Int
1
    | Bool
otherwise                = -Int
2
  where m :: Word16
m = Array -> Int -> Word16
A.unsafeIndex Array
arr (Int
offforall a. Num a => a -> a -> a
+Int
i)
{-# INLINE reverseIter_ #-}

-- | /O(1)/ Return the length of a 'Text' in units of 'Word16'.  This
-- is useful for sizing a target array appropriately before using
-- 'unsafeCopyToPtr'.
lengthWord16 :: Text -> Int
lengthWord16 :: Text -> Int
lengthWord16 (Text Array
_arr Int
_off Int
len) = Int
len
{-# INLINE lengthWord16 #-}

-- | /O(1)/ Unchecked take of 'k' 'Word16's from the front of a 'Text'.
takeWord16 :: Int -> Text -> Text
takeWord16 :: Int -> Text -> Text
takeWord16 Int
k (Text Array
arr Int
off Int
_len) = Array -> Int -> Int -> Text
Text Array
arr Int
off Int
k
{-# INLINE takeWord16 #-}

-- | /O(1)/ Unchecked drop of 'k' 'Word16's from the front of a 'Text'.
dropWord16 :: Int -> Text -> Text
dropWord16 :: Int -> Text -> Text
dropWord16 Int
k (Text Array
arr Int
off Int
len) = Array -> Int -> Int -> Text
Text Array
arr (Int
offforall a. Num a => a -> a -> a
+Int
k) (Int
lenforall a. Num a => a -> a -> a
-Int
k)
{-# INLINE dropWord16 #-}