packages feed

crypto-rng-0.3.0.0: src/Crypto/RNG/Unsafe.hs

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE UndecidableInstances #-}
-- | Support for generation of __non cryptographically secure__ random numbers
-- for testing purposes.
module Crypto.RNG.Unsafe
  ( -- * CryptoRNG class
    module Crypto.RNG.Class
    -- * Monad transformer for carrying rng state
  , RNGT
  , mapRNGT
  , runRNGT
  , withRNGState
    -- * Instantiation of the initial RNG state
  , RNGState
  , newRNGState
    -- ** Low-level utils
  , withRNG
  ) where

import Control.Applicative
import Control.Concurrent
import Control.Monad
import Control.Monad.Base
import Control.Monad.Catch
import Control.Monad.Except
import Control.Monad.Reader
import Control.Monad.Trans.Control
import qualified System.Random as R

import Crypto.RNG.Class

-- | The random number generator state.
newtype RNGState = RNGState (MVar R.StdGen)

-- | Create a new 'RNGState' with a given seed.
newRNGState :: MonadIO m => Int -> m RNGState
newRNGState seed = liftIO $ do
  RNGState <$> newMVar (R.mkStdGen seed)

----------------------------------------

-- | Monad transformer with RNG state.
newtype RNGT m a = RNGT { unRNGT :: ReaderT RNGState m a }
  deriving ( Alternative, Applicative, Functor, Monad, MonadFail, MonadPlus
           , MonadError e, MonadIO, MonadBase b, MonadBaseControl b
           , MonadThrow, MonadCatch, MonadMask
           , MonadTrans, MonadTransControl
           )

mapRNGT :: (m a -> n b) -> RNGT m a -> RNGT n b
mapRNGT f m = withRNGState $ \rng -> f (runRNGT rng m)

runRNGT :: RNGState -> RNGT m a -> m a
runRNGT rng m = runReaderT (unRNGT m) rng

withRNGState :: (RNGState -> m a) -> RNGT m a
withRNGState = RNGT . ReaderT

instance MonadIO m => CryptoRNG (RNGT m) where
  randomBytes n  = RNGT ask >>= (`withRNG` \g -> R.genByteString n g)
  random         = RNGT ask >>= (`withRNG` \g -> R.uniform g)
  randomR bounds = RNGT ask >>= (`withRNG` \g -> R.uniformR bounds g)

withRNG :: MonadIO m => RNGState -> (R.StdGen -> (a, R.StdGen)) -> m a
withRNG (RNGState rng) f = liftIO . modifyMVar rng $ \g -> do
  (a, newG) <- pure $ f g
  newG `seq` pure (newG, a)