{-# LANGUAGE
    TemplateHaskell, MultiParamTypeClasses, GADTs
  #-}

module Data.Random.Source.DevRandom 
    ( DevRandom(..)
    ) where

import Data.Random.Source

import Foreign.Marshal (allocaBytes)
import Foreign.Ptr (castPtr)
import Foreign.Storable (peek)
import System.IO (openBinaryFile, hGetBuf, Handle, IOMode(..))
import System.IO.Unsafe (unsafePerformIO)

-- |On systems that have it, \/dev\/random is a handy-dandy ready-to-use source
-- of nonsense.  Keep in mind that on some systems, Linux included, \/dev\/random
-- collects \"real\" entropy, and if you don't have a good source of it, such as
-- special hardware for the purpose or a *lot* of network traffic, it's pretty easy
-- to suck the entropy pool dry with entropy-intensive applications.  For many
-- purposes other than cryptography, \/dev\/urandom is preferable because when it
-- runs out of real entropy it'll still churn out pseudorandom data.
data DevRandom = DevRandom | DevURandom
    deriving (DevRandom -> DevRandom -> Bool
(DevRandom -> DevRandom -> Bool)
-> (DevRandom -> DevRandom -> Bool) -> Eq DevRandom
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: DevRandom -> DevRandom -> Bool
$c/= :: DevRandom -> DevRandom -> Bool
== :: DevRandom -> DevRandom -> Bool
$c== :: DevRandom -> DevRandom -> Bool
Eq, Int -> DevRandom -> ShowS
[DevRandom] -> ShowS
DevRandom -> String
(Int -> DevRandom -> ShowS)
-> (DevRandom -> String)
-> ([DevRandom] -> ShowS)
-> Show DevRandom
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [DevRandom] -> ShowS
$cshowList :: [DevRandom] -> ShowS
show :: DevRandom -> String
$cshow :: DevRandom -> String
showsPrec :: Int -> DevRandom -> ShowS
$cshowsPrec :: Int -> DevRandom -> ShowS
Show)

{-# NOINLINE devRandom  #-}
devRandom :: Handle
devRandom :: Handle
devRandom  = IO Handle -> Handle
forall a. IO a -> a
unsafePerformIO (String -> IOMode -> IO Handle
openBinaryFile String
"/dev/random"  IOMode
ReadMode)
{-# NOINLINE devURandom #-}
devURandom :: Handle
devURandom :: Handle
devURandom = IO Handle -> Handle
forall a. IO a -> a
unsafePerformIO (String -> IOMode -> IO Handle
openBinaryFile String
"/dev/urandom" IOMode
ReadMode)

dev :: DevRandom -> Handle
dev :: DevRandom -> Handle
dev DevRandom
DevRandom  = Handle
devRandom
dev DevRandom
DevURandom = Handle
devURandom

$(randomSource
    [d| instance RandomSource IO DevRandom where
            getRandomWord8From src = allocaBytes 1 $ \buf -> do
                1 <- hGetBuf (dev src) buf  1
                peek buf
            getRandomWord16From src = allocaBytes 2 $ \buf -> do
                2 <- hGetBuf (dev src) buf  2
                peek (castPtr buf)
            getRandomWord32From src = allocaBytes 4 $ \buf -> do
                4 <- hGetBuf (dev src) buf  4
                peek (castPtr buf)
            getRandomWord64From src = allocaBytes 8 $ \buf -> do
                8 <- hGetBuf (dev src) buf  8
                peek (castPtr buf)
     |])