packages feed

randomgen-0.1: Main.hs

{-# LANGUAGE ForeignFunctionInterface #-}

import System.Random.Mersenne.Pure64
import Control.Concurrent
import Control.Monad
import Data.Binary.Put
import qualified Data.ByteString.Lazy as BS
import qualified Data.ByteString      as S
import Data.Word
import Foreign.Marshal.Alloc
import Foreign.Ptr
import Foreign.Storable
import System.Environment
import GHC.Conc
import Foreign.C.Types
import Debug.Trace
import System.IO


--
-- Parallel unfold and reduce.
--
-- -lcrypto
--

main :: IO ()
main = do
  args <- getArgs
  case args of
    [file, size]       -> checkReasonableAndGo (Just (show numCapabilities)) file size
    [cpus, file, size] -> checkReasonableAndGo (Just cpus) file size
    _                  -> dumpUsageAndQuit

dumpUsageAndQuit :: IO ()
dumpUsageAndQuit = mapM_ putStrLn
    ["USAGE: genblock [cpus] filename size"
    ,"where 'size' is given in gigabytes"
    ,"and cpus defaults to the value of +RTS -N"]

checkReasonableAndGo :: Maybe String -> String -> String -> IO ()
checkReasonableAndGo mcpus file size =
  case (maybe [(numCapabilities, "")] reads mcpus, reads size) of
    ([(cpus, "")], [(size', "")]) -> go cpus file size'
    _                             -> dumpUsageAndQuit

go :: Int -> String -> Int -> IO ()
go cpus filename size_in_gb = do
  chan <- newChan
  forM_ [1..cpus] $ \n -> forkIO {-n-} $ do
    baseMT <- pureMT `fmap` getOpenSSLRand
    writeList2Chan chan $
            BS.toChunks $ runPut $ mersennePut baseMT

  ls <- getChanContents chan    -- TODO need a LBS chan
  BS.writeFile filename . BS.take size . BS.fromChunks $ ls

 where
  size = fromIntegral size_in_gb * 1024 * 1024 * 1024


-- unroll the loop
mersennePut :: PureMT -> Put
mersennePut mt = do

  let (k, mt1) = randomWord64 mt
  putWord64host k
  let (k, mt2) = randomWord64 mt1
  putWord64host k
  let (k, mt3) = randomWord64 mt2
  putWord64host k
  let (k, mt4) = randomWord64 mt3
  putWord64host k
  let (k, mt5) = randomWord64 mt4
  putWord64host k
  let (k, mt6) = randomWord64 mt5
  putWord64host k
  let (k, mt7) = randomWord64 mt6
  putWord64host k
  let (k, mt8) = randomWord64 mt7
  putWord64host k
  let (k, mt9) = randomWord64 mt8
  putWord64host k
  let (k, mt10) = randomWord64 mt9
  putWord64host k
  let (k, mt11) = randomWord64 mt10
  putWord64host k
  let (k, mt12) = randomWord64 mt11
  putWord64host k
  let (k, mt13) = randomWord64 mt12
  putWord64host k
  let (k, mt14) = randomWord64 mt13
  putWord64host k
  let (k, mt15) = randomWord64 mt14
  putWord64host k
  let (k, mt16) = randomWord64 mt15
  putWord64host k
  mersennePut mt16

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

--
-- Get a decent random seed
--
getOpenSSLRand :: IO Word64
getOpenSSLRand = allocaBytes n $ \ ptr -> do
  fill_RAND_bytes ptr (fromIntegral n)
  peek $ castPtr ptr
 where
  n = sizeOf (undefined :: Word64)

foreign import ccall unsafe "openssl/rand.h RAND_bytes"
  fill_RAND_bytes :: Ptr Word8 -> CInt -> IO ()