cryptostore-0.6.0.0: src/Crypto/Store/Block.hs
-- |
-- Module : Crypto.Store.Block
-- License : BSD-style
-- Maintainer : Olivier Chéron <olivier.cheron@gmail.com>
-- Stability : experimental
-- Portability : unknown
--
-- Minimal port of basement @Block@ data type. Provides a typed interface on
-- top of @ByteArray#@.
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UnboxedTuples #-}
module Crypto.Store.Block
( Block, Offset(..), CountOf(..), PrimType(..)
, createWithPtr, Crypto.Store.Block.map, unsafeCast, unsafeIndex
) where
import Data.Memory.Endian
import Data.Proxy
import Data.Word
import Foreign.Ptr (castPtr)
import GHC.Exts
import GHC.IO (IO(..))
import GHC.Word
import System.IO.Unsafe
data Block a = Block ByteArray#
data MutableBlock a = MutableBlock (MutableByteArray# RealWorld)
newtype CountOf a = CountOf Int deriving (Show, Eq, Ord)
newtype Offset a = Offset Int deriving (Show, Eq, Ord, Num)
class PrimType a where
primSizeInBytes :: Proxy a -> CountOf Word8
primBaIndex :: ByteArray# -> Offset a -> a
primMbaWrite :: MutableByteArray# RealWorld -> Offset a -> a -> IO ()
instance PrimType Word8 where
primSizeInBytes _ = CountOf 1
{-# INLINE primSizeInBytes #-}
primBaIndex ba (Offset (I# n)) = W8# (indexWord8Array# ba n)
{-# INLINE primBaIndex #-}
primMbaWrite mba (Offset (I# n)) (W8# w) = IO $ \s -> (# writeWord8Array# mba n w s, () #)
{-# INLINE primMbaWrite #-}
instance PrimType Word16 where
primSizeInBytes _ = CountOf 2
{-# INLINE primSizeInBytes #-}
primBaIndex ba (Offset (I# n)) = W16# (indexWord16Array# ba n)
{-# INLINE primBaIndex #-}
primMbaWrite mba (Offset (I# n)) (W16# w) = IO $ \s -> (# writeWord16Array# mba n w s, () #)
{-# INLINE primMbaWrite #-}
instance PrimType a => PrimType (LE a) where
primSizeInBytes _ = primSizeInBytes (Proxy :: Proxy a)
{-# INLINE primSizeInBytes #-}
primBaIndex ba (Offset i) = LE $ primBaIndex ba (Offset i)
{-# INLINE primBaIndex #-}
primMbaWrite mba (Offset i) (LE x) = primMbaWrite mba (Offset i) x
{-# INLINE primMbaWrite #-}
create :: PrimType a => CountOf a -> (Offset a -> a) -> Block a
create n@(CountOf !sz) f = unsafeDupablePerformIO $ do
mb <- new n
loop mb 0
unsafeFreeze mb
where
loop !mb i
| i == sz = pure ()
| otherwise =
let off = Offset i
in unsafeWrite mb off (f off) >> loop mb (i + 1)
{-# INLINE create #-}
createWithPtr :: CountOf Word8 -> (Ptr p -> IO a) -> Block Word8
createWithPtr n f = unsafeDupablePerformIO $ do
b <- unsafeNewPinned n >>= unsafeFreeze
f (castPtr $ unsafeBlockPtr b) *> touch b
return b
{-# INLINE createWithPtr #-}
length :: forall a. PrimType a => Block a -> CountOf a
length (Block ba) = CountOf (I# (sizeofByteArray# ba) `quot` sz)
where CountOf sz = primSizeInBytes (Proxy :: Proxy a)
{-# INLINE length #-}
map :: (PrimType a, PrimType b) => (a -> b) -> Block a -> Block b
map f b = create (CountOf len) $ \(Offset i) -> f (unsafeIndex b (Offset i))
where CountOf len = Crypto.Store.Block.length b
{-# INLINE map #-}
new :: forall a. PrimType a => CountOf a -> IO (MutableBlock a)
new (CountOf n) = IO $ \s1 ->
case newByteArray# bytes s1 of
(# s2, mba #) -> (# s2, MutableBlock mba #)
where
!(I# bytes) = n * sz
CountOf sz = primSizeInBytes (Proxy :: Proxy a)
{-# INLINE new #-}
touch :: Block a -> IO ()
touch (Block ba) = IO $ \s1 -> case touch# ba s1 of { s2 -> (# s2, () #) }
unsafeBlockPtr :: Block a -> Ptr a
unsafeBlockPtr (Block ba) = Ptr (byteArrayContents# ba)
{-# INLINE unsafeBlockPtr #-}
unsafeCast :: Block a -> Block b
unsafeCast (Block ba) = Block ba
{-# INLINE unsafeCast #-}
unsafeFreeze :: MutableBlock a -> IO (Block a)
unsafeFreeze (MutableBlock mba) = IO $ \s1 ->
case unsafeFreezeByteArray# mba s1 of
(# s2, ba #) -> (# s2, Block ba #)
{-# INLINE unsafeFreeze #-}
unsafeIndex :: PrimType a => Block a -> Offset a -> a
unsafeIndex (Block ba) = primBaIndex ba
{-# INLINE unsafeIndex #-}
unsafeNewPinned :: CountOf Word8 -> IO (MutableBlock a)
unsafeNewPinned (CountOf (I# bytes)) = IO $ \s1 ->
case newAlignedPinnedByteArray# bytes 8# s1 of
(# s2, mba #) -> (# s2, MutableBlock mba #)
{-# INLINE unsafeNewPinned #-}
unsafeWrite :: PrimType a => MutableBlock a -> Offset a -> a -> IO ()
unsafeWrite (MutableBlock mba) = primMbaWrite mba
{-# INLINE unsafeWrite #-}