text-2.0: src/Data/Text/Array.hs
{-# LANGUAGE BangPatterns, CPP, 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(..)
, MArray(..)
-- * Functions
, resizeM
, shrinkM
, copyM
, copyI
, copyFromPointer
, copyToPointer
, empty
, equal
, compare
, run
, run2
, toList
, unsafeFreeze
, unsafeIndex
, new
, newPinned
, newFilled
, unsafeWrite
, tile
, getSizeofMArray
) where
#if defined(ASSERTS)
import GHC.Stack (HasCallStack)
#endif
#if !MIN_VERSION_base(4,11,0)
import Data.Text.Internal.Unsafe (inlinePerformIO)
import Foreign.C.Types (CInt(..))
#endif
import GHC.Exts hiding (toList)
import GHC.ST (ST(..), runST)
import GHC.Word (Word8(..))
import qualified Prelude
import Prelude hiding (length, read, compare)
-- | Immutable array type.
data Array = ByteArray ByteArray#
-- | Mutable array type, for use in the ST monad.
data MArray s = MutableByteArray (MutableByteArray# s)
-- | Create an uninitialized mutable array.
new :: forall s. Int -> ST s (MArray s)
new (I# len#)
#if defined(ASSERTS)
| I# len# < 0 = error "Data.Text.Array.new: size overflow"
#endif
| otherwise = ST $ \s1# ->
case newByteArray# len# s1# of
(# s2#, marr# #) -> (# s2#, MutableByteArray marr# #)
{-# INLINE new #-}
-- | Create an uninitialized mutable pinned array.
--
-- @since 2.0
newPinned :: forall s. Int -> ST s (MArray s)
newPinned (I# len#)
#if defined(ASSERTS)
| I# len# < 0 = error "Data.Text.Array.newPinned: size overflow"
#endif
| otherwise = ST $ \s1# ->
case newPinnedByteArray# len# s1# of
(# s2#, marr# #) -> (# s2#, MutableByteArray marr# #)
{-# INLINE newPinned #-}
-- | @since 2.0
newFilled :: Int -> Int -> ST s (MArray s)
newFilled (I# len#) (I# c#) = ST $ \s1# ->
case newByteArray# len# s1# of
(# s2#, marr# #) -> case setByteArray# marr# 0# len# c# s2# of
s3# -> (# s3#, MutableByteArray marr# #)
{-# INLINE newFilled #-}
-- | @since 2.0
tile :: MArray s -> Int -> ST s ()
tile marr tileLen = do
totalLen <- getSizeofMArray marr
let go l
| 2 * l > totalLen = copyM marr l marr 0 (totalLen - l)
| otherwise = copyM marr l marr 0 l >> go (2 * l)
go tileLen
{-# INLINE tile #-}
-- | Freeze a mutable array. Do not mutate the 'MArray' afterwards!
unsafeFreeze :: MArray s -> ST s Array
unsafeFreeze (MutableByteArray marr) = ST $ \s1# ->
case unsafeFreezeByteArray# marr s1# of
(# s2#, ba# #) -> (# s2#, ByteArray ba# #)
{-# INLINE unsafeFreeze #-}
-- | Unchecked read of an immutable array. May return garbage or
-- crash on an out-of-bounds access.
unsafeIndex ::
#if defined(ASSERTS)
HasCallStack =>
#endif
Array -> Int -> Word8
unsafeIndex (ByteArray arr) i@(I# i#) =
#if defined(ASSERTS)
let word8len = I# (sizeofByteArray# arr) in
if i < 0 || i >= word8len
then error ("Data.Text.Array.unsafeIndex: bounds error, offset " ++ show i ++ ", length " ++ show word8len)
else
#endif
case indexWord8Array# arr i# of r# -> (W8# r#)
{-# INLINE unsafeIndex #-}
-- | @since 2.0
getSizeofMArray :: MArray s -> ST s Int
getSizeofMArray (MutableByteArray marr) = ST $ \s0# ->
-- Cannot simply use (deprecated) 'sizeofMutableByteArray#', because it is
-- unsafe in the presence of 'shrinkMutableByteArray#' and 'resizeMutableByteArray#'.
case getSizeofMutableByteArray# marr s0# of
(# s1#, word8len# #) -> (# s1#, I# word8len# #)
#if defined(ASSERTS)
checkBoundsM :: HasCallStack => MArray s -> Int -> Int -> ST s ()
checkBoundsM ma i elSize = do
len <- getSizeofMArray ma
if i < 0 || i + elSize > len
then error ("bounds error, offset " ++ show i ++ ", length " ++ show len)
else return ()
#endif
-- | Unchecked write of a mutable array. May return garbage or crash
-- on an out-of-bounds access.
unsafeWrite ::
#if defined(ASSERTS)
HasCallStack =>
#endif
MArray s -> Int -> Word8 -> ST s ()
unsafeWrite ma@(MutableByteArray marr) i@(I# i#) (W8# e#) =
#if defined(ASSERTS)
checkBoundsM ma i 1 >>
#endif
(ST $ \s1# -> case writeWord8Array# marr i# e# s1# of
s2# -> (# s2#, () #))
{-# INLINE unsafeWrite #-}
-- | 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 #-}
-- | @since 2.0
resizeM :: MArray s -> Int -> ST s (MArray s)
resizeM (MutableByteArray ma) i@(I# i#) = ST $ \s1# ->
case resizeMutableByteArray# ma i# s1# of
(# s2#, newArr #) -> (# s2#, MutableByteArray newArr #)
{-# INLINE resizeM #-}
-- | @since 2.0
shrinkM ::
#if defined(ASSERTS)
HasCallStack =>
#endif
MArray s -> Int -> ST s ()
shrinkM (MutableByteArray marr) i@(I# newSize) = do
#if defined(ASSERTS)
oldSize <- getSizeofMArray (MutableByteArray marr)
if I# newSize > oldSize
then error $ "shrinkM: shrink cannot grow " ++ show oldSize ++ " to " ++ show (I# newSize)
else return ()
#endif
ST $ \s1# ->
case shrinkMutableByteArray# marr newSize s1# of
s2# -> (# s2#, () #)
{-# INLINE shrinkM #-}
-- | 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 dst@(MutableByteArray dst#) dstOff@(I# dstOff#) src@(MutableByteArray src#) srcOff@(I# srcOff#) count@(I# count#)
#if defined(ASSERTS)
| count < 0 = error $
"copyM: count must be >= 0, but got " ++ show count
#endif
| otherwise = do
#if defined(ASSERTS)
srcLen <- getSizeofMArray src
dstLen <- getSizeofMArray dst
if srcOff + count > srcLen
then error "copyM: source is too short"
else return ()
if dstOff + count > dstLen
then error "copyM: destination is too short"
else return ()
#endif
ST $ \s1# -> case copyMutableByteArray# src# srcOff# dst# dstOff# count# s1# of
s2# -> (# s2#, () #)
{-# INLINE copyM #-}
-- | Copy some elements of an immutable array.
copyI :: Int -- ^ Count
-> MArray s -- ^ Destination
-> Int -- ^ Destination offset
-> Array -- ^ Source
-> Int -- ^ Source offset
-> ST s ()
copyI count@(I# count#) (MutableByteArray dst#) dstOff@(I# dstOff#) (ByteArray src#) (I# srcOff#)
#if defined(ASSERTS)
| count < 0 = error $
"copyI: count must be >= 0, but got " ++ show count
#endif
| otherwise = ST $ \s1# ->
case copyByteArray# src# srcOff# dst# dstOff# count# s1# of
s2# -> (# s2#, () #)
{-# INLINE copyI #-}
-- | Copy from pointer.
--
-- @since 2.0
copyFromPointer
:: MArray s -- ^ Destination
-> Int -- ^ Destination offset
-> Ptr Word8 -- ^ Source
-> Int -- ^ Count
-> ST s ()
copyFromPointer (MutableByteArray dst#) dstOff@(I# dstOff#) (Ptr src#) count@(I# count#)
#if defined(ASSERTS)
| count < 0 = error $
"copyFromPointer: count must be >= 0, but got " ++ show count
#endif
| otherwise = ST $ \s1# ->
case copyAddrToByteArray# src# dst# dstOff# count# s1# of
s2# -> (# s2#, () #)
{-# INLINE copyFromPointer #-}
-- | Copy to pointer.
--
-- @since 2.0
copyToPointer
:: Array -- ^ Source
-> Int -- ^ Source offset
-> Ptr Word8 -- ^ Destination
-> Int -- ^ Count
-> ST s ()
copyToPointer (ByteArray src#) srcOff@(I# srcOff#) (Ptr dst#) count@(I# count#)
#if defined(ASSERTS)
| count < 0 = error $
"copyToPointer: count must be >= 0, but got " ++ show count
#endif
| otherwise = ST $ \s1# ->
case copyByteArrayToAddr# src# srcOff# dst# count# s1# of
s2# -> (# s2#, () #)
{-# INLINE copyToPointer #-}
-- | Compare portions of two arrays for equality. No bounds checking
-- is performed.
equal :: Array -> Int -> Array -> Int -> Int -> Bool
equal src1 off1 src2 off2 count = compareInternal src1 off1 src2 off2 count == 0
{-# INLINE equal #-}
-- | Compare portions of two arrays. No bounds checking is performed.
--
-- @since 2.0
compare :: Array -> Int -> Array -> Int -> Int -> Ordering
compare src1 off1 src2 off2 count = compareInternal src1 off1 src2 off2 count `Prelude.compare` 0
{-# INLINE compare #-}
compareInternal
:: Array -- ^ First
-> Int -- ^ Offset into first
-> Array -- ^ Second
-> Int -- ^ Offset into second
-> Int -- ^ Count
-> Int
compareInternal (ByteArray src1#) (I# off1#) (ByteArray src2#) (I# off2#) (I# count#) = i
where
#if MIN_VERSION_base(4,11,0)
i = I# (compareByteArrays# src1# off1# src2# off2# count#)
#else
i = fromIntegral (inlinePerformIO (memcmp src1# off1# src2# off2# count#))
foreign import ccall unsafe "_hs_text_memcmp2" memcmp
:: ByteArray# -> Int# -> ByteArray# -> Int# -> Int# -> IO CInt
#endif
{-# INLINE compareInternal #-}