primitive-maybe-0.1.0: src/Data/Primitive/SmallArray/Maybe.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE RoleAnnotations #-}
-- | This provides an interface to working with boxed arrays
-- with elements of type @Maybe a@. That is:
--
-- > SmallMaybeArray a ≅ SmallArray (Maybe a)
--
-- However, this type provided by this module is more efficient
-- than its naive @SmallArray@ counterpart. It consumes less
-- memory and has fewer heap indirections.
module Data.Primitive.SmallArray.Maybe
( SmallMaybeArray
, SmallMutableMaybeArray
, indexSmallMaybeArray
, newSmallMaybeArray
, readSmallMaybeArray
, writeSmallMaybeArray
, sequenceSmallMaybeArray
, unsafeFreezeSmallMaybeArray
) where
import Control.Monad.Primitive
import Data.Primitive.SmallArray
import Data.Primitive.Maybe.Internal (nothingSurrogate)
import GHC.Exts (Any,reallyUnsafePtrEquality#)
import Unsafe.Coerce (unsafeCoerce)
newtype SmallMaybeArray a = SmallMaybeArray (SmallArray Any)
newtype SmallMutableMaybeArray s a = SmallMutableMaybeArray (SmallMutableArray s Any)
type role SmallMaybeArray representational
type role SmallMutableMaybeArray nominal representational
unsafeToMaybe :: Any -> Maybe a
unsafeToMaybe a =
case reallyUnsafePtrEquality# a nothingSurrogate of
0# -> Just (unsafeCoerce a)
_ -> Nothing
{-# INLINE unsafeToMaybe #-}
newSmallMaybeArray :: PrimMonad m => Int -> Maybe a -> m (SmallMutableMaybeArray (PrimState m) a)
{-# INLINE newSmallMaybeArray #-}
newSmallMaybeArray i ma = case ma of
Just a -> do
x <- newSmallArray i (unsafeCoerce a)
return (SmallMutableMaybeArray x)
Nothing -> do
x <- newSmallArray i nothingSurrogate
return (SmallMutableMaybeArray x)
indexSmallMaybeArray :: SmallMaybeArray a -> Int -> Maybe a
{-# INLINE indexSmallMaybeArray #-}
indexSmallMaybeArray (SmallMaybeArray a) ix =
let (# v #) = indexSmallArray## a ix
in unsafeToMaybe v
readSmallMaybeArray :: PrimMonad m => SmallMutableMaybeArray (PrimState m) a -> Int -> m (Maybe a)
{-# INLINE readSmallMaybeArray #-}
readSmallMaybeArray (SmallMutableMaybeArray m) ix = do
a <- readSmallArray m ix
return (unsafeToMaybe a)
writeSmallMaybeArray :: PrimMonad m => SmallMutableMaybeArray (PrimState m) a -> Int -> Maybe a -> m ()
{-# INLINE writeSmallMaybeArray #-}
writeSmallMaybeArray (SmallMutableMaybeArray marr) ix ma = case ma of
Just a -> writeSmallArray marr ix (unsafeCoerce a)
Nothing -> writeSmallArray marr ix nothingSurrogate
-- | This is like calling @sequence@ on a 'SmallArray'. However, in
-- the event that all the values are @Just@, it does not need
-- to allocate a new array since the array backing the @SmallMaybeArray@
-- can be reused.
sequenceSmallMaybeArray :: SmallMaybeArray a -> Maybe (SmallArray a)
sequenceSmallMaybeArray m@(SmallMaybeArray a) =
if hasNothing m then Nothing else Just (unsafeCoerce a)
hasNothing :: SmallMaybeArray a -> Bool
hasNothing (SmallMaybeArray a) = go 0 where
go !ix = if ix < sizeofSmallArray a
then
let (# v #) = indexSmallArray## a ix
in case reallyUnsafePtrEquality# v nothingSurrogate of
0# -> True
_ -> go (ix + 1)
else False
unsafeFreezeSmallMaybeArray :: PrimMonad m => SmallMutableMaybeArray (PrimState m) a -> m (SmallMaybeArray a)
{-# INLINE unsafeFreezeSmallMaybeArray #-}
unsafeFreezeSmallMaybeArray (SmallMutableMaybeArray ma) = do
a <- unsafeFreezeSmallArray ma
return (SmallMaybeArray a)