{-# LANGUAGE BangPatterns, CPP, ForeignFunctionInterface, MagicHash, Rank2Types, RecordWildCards, UnboxedTuples, UnliftedFFITypes #-} {-# OPTIONS_GHC -fno-warn-unused-matches #-} -- | -- Module : Data.Text.Array -- Copyright : (c) 2009, 2010, 2011 Bryan O'Sullivan -- -- License : BSD-style -- Maintainer : bos@serpentine.com -- Portability : portable -- -- Packed, unboxed, heap-resident arrays. Suitable for performance -- critical use, both in terms of large data quantities and high -- speed. -- -- This module is intended to be imported @qualified@, to avoid name -- clashes with "Prelude" functions, e.g. -- -- > import qualified Data.Text.Array as A -- -- The names in this module resemble those in the 'Data.Array' family -- of modules, but are shorter due to the assumption of qualified -- naming. module Data.Text.Array ( -- * Types Array(Array, aBA) , MArray(MArray, maBA) -- * Functions , 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) -- This fugly hack is brought by GHC's apparent reluctance to deal -- with MagicHash and UnboxedTuples when inferring types. Eek! # 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) -- | Immutable array type. -- -- The 'Array' constructor is exposed since @text-1.1.1.3@ data Array = Array { aBA :: ByteArray# #if defined(ASSERTS) , aLen :: {-# UNPACK #-} !Int -- length in bytes #endif } -- | Mutable array type, for use in the ST monad. -- -- The 'MArray' constructor is exposed since @text-1.1.1.3@ data MArray s = MArray { maBA :: MutableByteArray# s #if defined(ASSERTS) , maLen :: {-# UNPACK #-} !Int -- length in bytes #endif } #if defined(ASSERTS) -- | Operations supported by all arrays. class IArray a where -- | Return the length of an array. length :: a -> Int instance IArray Array where length = aLen {-# INLINE length #-} instance IArray (MArray s) where length = maLen {-# INLINE length #-} #endif -- | Create an uninitialized mutable array. 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" -- | Freeze a mutable array. Do not mutate the 'MArray' afterwards! 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 #-} -- | Indicate how many bytes would be used for an array of the given -- size. bytesInArray :: Int -> Int bytesInArray n = n {-# INLINE bytesInArray #-} -- | Unchecked read of an immutable array. May return garbage or -- crash on an out-of-bounds access. unsafeIndex :: Array -> Int -> Word8 unsafeIndex Array{..} i@(I# i#) = CHECK_BOUNDS("unsafeIndex",aLen,i) case indexWord8Array# aBA i# of r# -> (W8# r#) {-# INLINE unsafeIndex #-} -- | Unchecked read of an immutable array. May return garbage or -- crash on an out-of-bounds access. 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 #-} -- | Unchecked read of an immutable array. May return garbage or -- crash on an out-of-bounds access. 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 #-} -- | Unchecked write of a mutable array. May return garbage or crash -- on an out-of-bounds access. 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 #-} -- | Unchecked write of a mutable array. May return garbage or crash -- on an out-of-bounds access. 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 #-} -- | Unchecked write of a mutable array. May return garbage or crash -- on an out-of-bounds access. 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 #-} -- | Convert an immutable array to a list. toList :: Array -> Int -> Int -> [Word8] toList ary off len = loop 0 where loop i | i < len = unsafeIndex ary (off+i) : loop (i+1) | otherwise = [] -- | An empty immutable array. empty :: Array empty = runST (new 0 >>= unsafeFreeze) -- | Run an action in the ST monad and return an immutable array of -- its result. run :: (forall s. ST s (MArray s)) -> Array run k = runST (k >>= unsafeFreeze) -- | Run an action in the ST monad and return an immutable array of -- its result paired with whatever else the action returns. 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 #-} -- | Copy some elements of a mutable array. copyM :: MArray s -- ^ Destination -> Int -- ^ Destination offset -> MArray s -- ^ Source -> Int -- ^ Source offset -> Int -- ^ Count -> 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 #-} -- | Copy some elements of an immutable array. copyI :: MArray s -- ^ Destination -> Int -- ^ Destination offset -> Array -- ^ Source -> Int -- ^ Source offset -> Int -- ^ First offset in destination /not/ to -- copy (i.e. /not/ length) -> 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 #-} -- | Compare portions of two arrays for equality. No bounds checking -- is performed. equal :: Array -- ^ First -> Int -- ^ Offset into first -> Array -- ^ Second -> Int -- ^ Offset into second -> Int -- ^ Count -> Bool equal arrA offA arrB offB count = cmp arrA offA arrB offB count == EQ {-# INLINE equal #-} -- | Compare portions of two arrays for equality. No bounds checking -- is performed. cmp :: Array -- ^ First -> Int -- ^ Offset into first -> Array -- ^ Second -> Int -- ^ Offset into second -> Int -- ^ Count -> 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 -- | Copy some elements of an immutable array to a pointer copyToPtr :: Ptr Word8 -- ^ Destination -> Int -- ^ Destination offset -> Array -- ^ Source -> Int -- ^ Source offset -> Int -- ^ First offset in destination /not/ to -- copy (i.e. /not/ length) -> 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 -- ^ Destination -> Int -- ^ Destination offset -> Ptr Word8 -- ^ Source -> Int -- ^ Source offset -> Int -- ^ Count -> 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 #-}