{-# LANGUAGE BangPatterns, CPP, ForeignFunctionInterface, MagicHash, Rank2Types,
RecordWildCards, UnboxedTuples, UnliftedFFITypes #-}
{-# OPTIONS_GHC -fno-warn-unused-matches #-}
module Data.Text.Array
(
Array(Array, aBA)
, MArray(MArray, maBA)
, copyM
, copyI
, copyToPtr
, copyFromPtr
, empty
, equal
, cmp
#if defined(ASSERTS)
, length
#endif
, run
, run2
, toList
, unsafeFreeze
, unsafeIndex
, unsafeIndex32
, unsafeIndex64
, new
, unsafeWrite
, unsafeWrite32
, unsafeWrite64
) where
#if defined(ASSERTS)
# define CHECK_BOUNDS(_func_,_len_,_k_) \
if (_k_) < 0 || (_k_) >= (_len_) then error ("Data.Text.Array." ++ (_func_) ++ ": bounds error, offset " ++ show (_k_) ++ ", length " ++ show (_len_)) else
#else
# define CHECK_BOUNDS(_func_,_len_,_k_)
#endif
#include "MachDeps.h"
#if defined(ASSERTS)
import Control.Exception (assert)
#endif
import Data.Bits ((.&.), xor)
import Data.Text.Internal.Unsafe.Shift (shiftR)
import Foreign.Ptr (Ptr)
#if __GLASGOW_HASKELL__ >= 804
import GHC.Exts (compareByteArrays#)
#elif __GLASGOW_HASKELL__ >= 703
import Data.Text.Internal.Unsafe (inlinePerformIO)
import Foreign.C.Types (CInt(CInt), CSize(CSize))
#else
import Data.Text.Internal.Unsafe (inlinePerformIO)
import Foreign.C.Types (CInt, CSize)
#endif
import GHC.Base (IO(..), ByteArray#, MutableByteArray#, Int(..), (-#),
indexWord8Array#, indexWord32Array#, indexWord64Array#, newByteArray#, plusAddr#,
unsafeFreezeByteArray#, writeWord8Array#, writeWord32Array#, writeWord64Array#,
copyByteArray#, copyMutableByteArray#, copyByteArrayToAddr#,
copyAddrToByteArray#)
import GHC.Exts (Ptr(..))
import GHC.ST (ST(..), runST)
import GHC.Word (Word8(..), Word32(..), Word64(..))
import Prelude hiding (length, read)
data Array = Array {
aBA :: ByteArray#
#if defined(ASSERTS)
, aLen :: {-# UNPACK #-} !Int
#endif
}
data MArray s = MArray {
maBA :: MutableByteArray# s
#if defined(ASSERTS)
, maLen :: {-# UNPACK #-} !Int
#endif
}
#if defined(ASSERTS)
class IArray a where
length :: a -> Int
instance IArray Array where
length = aLen
{-# INLINE length #-}
instance IArray (MArray s) where
length = maLen
{-# INLINE length #-}
#endif
new :: forall s. Int -> ST s (MArray s)
new n
| n < 0 || n .&. highBit /= 0 = array_size_error
| otherwise = ST $ \s1# ->
case newByteArray# len# s1# of
(# s2#, marr# #) -> (# s2#, MArray marr#
#if defined(ASSERTS)
n
#endif
#)
where !(I# len#) = bytesInArray n
highBit = maxBound `xor` (maxBound `shiftR` 1)
{-# INLINE new #-}
array_size_error :: a
array_size_error = error "Data.Text.Array.new: size overflow"
unsafeFreeze :: MArray s -> ST s Array
unsafeFreeze MArray{..} = ST $ \s1# ->
case unsafeFreezeByteArray# maBA s1# of
(# s2#, ba# #) -> (# s2#, Array ba#
#if defined(ASSERTS)
maLen
#endif
#)
{-# INLINE unsafeFreeze #-}
bytesInArray :: Int -> Int
bytesInArray n = n
{-# INLINE bytesInArray #-}
unsafeIndex :: Array -> Int -> Word8
unsafeIndex Array{..} i@(I# i#) =
CHECK_BOUNDS("unsafeIndex",aLen,i)
case indexWord8Array# aBA i# of r# -> (W8# r#)
{-# INLINE unsafeIndex #-}
unsafeIndex32 :: Array -> Int -> Word32
unsafeIndex32 Array{..} i@(I# i#) =
CHECK_BOUNDS("unsafeIndex32",aLen `quot` 4,i)
case indexWord32Array# aBA i# of r# -> (W32# r#)
{-# INLINE unsafeIndex32 #-}
unsafeIndex64 :: Array -> Int -> Word64
unsafeIndex64 Array{..} i@(I# i#) =
CHECK_BOUNDS("unsafeIndex64",aLen `quot` 8,i)
case indexWord64Array# aBA i# of r# -> (W64# r#)
{-# INLINE unsafeIndex64 #-}
unsafeWrite :: MArray s -> Int -> Word8 -> ST s ()
unsafeWrite MArray{..} i@(I# i#) (W8# e#) = ST $ \s1# ->
CHECK_BOUNDS("unsafeWrite",maLen,i)
case writeWord8Array# maBA i# e# s1# of
s2# -> (# s2#, () #)
{-# INLINE unsafeWrite #-}
unsafeWrite32 :: MArray s -> Int -> Word32 -> ST s ()
unsafeWrite32 MArray{..} i@(I# i#) (W32# e#) = ST $ \s1# ->
CHECK_BOUNDS("unsafeWrite32",maLen `quot` 4,i)
case writeWord32Array# maBA i# e# s1# of
s2# -> (# s2#, () #)
{-# INLINE unsafeWrite32 #-}
unsafeWrite64 :: MArray s -> Int -> Word64 -> ST s ()
unsafeWrite64 MArray{..} i@(I# i#) (W64# e#) = ST $ \s1# ->
CHECK_BOUNDS("unsafeWrite64",maLen `quot` 8,i)
case writeWord64Array# maBA i# e# s1# of
s2# -> (# s2#, () #)
{-# INLINE unsafeWrite64 #-}
toList :: Array -> Int -> Int -> [Word8]
toList ary off len = loop 0
where loop i | i < len = unsafeIndex ary (off+i) : loop (i+1)
| otherwise = []
empty :: Array
empty = runST (new 0 >>= unsafeFreeze)
run :: (forall s. ST s (MArray s)) -> Array
run k = runST (k >>= unsafeFreeze)
run2 :: (forall s. ST s (MArray s, a)) -> (Array, a)
run2 k = runST (do
(marr,b) <- k
arr <- unsafeFreeze marr
return (arr,b))
{-# INLINE run2 #-}
copyM :: MArray s
-> Int
-> MArray s
-> Int
-> Int
-> ST s ()
copyM dest didx@(I# didx#) src sidx@(I# sidx#) count@(I# count#)
| count <= 0 = return ()
| otherwise =
#if defined(ASSERTS)
assert (sidx + count <= length src) .
assert (didx + count <= length dest) .
#endif
ST $ \s ->
case copyMutableByteArray# (maBA src) sidx# (maBA dest) didx# count# s of
s' -> (# s', () #)
{-# INLINE copyM #-}
copyI :: MArray s
-> Int
-> Array
-> Int
-> Int
-> ST s ()
copyI dest i0@(I# i0#) src _j0@(I# j0#) top@(I# top#)
| i0 >= top = return ()
| otherwise = ST $ \s ->
case copyByteArray# (aBA src) j0# (maBA dest) i0# (top# -# i0#) s of
s' -> (# s', () #)
{-# INLINE copyI #-}
equal :: Array
-> Int
-> Array
-> Int
-> Int
-> Bool
equal arrA offA arrB offB count = cmp arrA offA arrB offB count == EQ
{-# INLINE equal #-}
cmp :: Array
-> Int
-> Array
-> Int
-> Int
-> Ordering
#if __GLASGOW_HASKELL__ >= 804
cmp arrA (I# offA) arrB (I# offB) (I# count) =
compare (I# (compareByteArrays# (aBA arrA) offA (aBA arrB) offB count)) 0
#else
cmp arrA offA arrB offB count = inlinePerformIO $ do
i <- memcmp (aBA arrA) (fromIntegral offA)
(aBA arrB) (fromIntegral offB) (fromIntegral count)
return $ compare i 0
{-# INLINE cmp #-}
foreign import ccall unsafe "_hs_text_utf_8_memcmp" memcmp
:: ByteArray# -> CSize -> ByteArray# -> CSize -> CSize -> IO CInt
#endif
copyToPtr :: Ptr Word8
-> Int
-> Array
-> Int
-> Int
-> IO ()
copyToPtr dest@(Ptr dest#) i0@(I# i0#) src j0@(I# j0#) top@(I# top#)
| i0 >= top = return ()
| otherwise =
IO $ \s -> case copyByteArrayToAddr# (aBA src) j0# (plusAddr# dest# i0#) (top# -# i0#) s of
s' -> (# s', () #)
{-# INLINE copyToPtr #-}
copyFromPtr :: MArray s
-> Int
-> Ptr Word8
-> Int
-> Int
-> ST s ()
copyFromPtr dest i0@(I# i0#) src@(Ptr src#) j0@(I# j0#) count@(I# count#)
| count <= 0 = return ()
| otherwise =
ST $ \s -> case copyAddrToByteArray# (plusAddr# src# i0#) (maBA dest) j0# count# s of
s' -> (# s', () #)
{-# INLINE copyFromPtr #-}