module Data.ByteString.Random
( random
, randomGen
) where
import Control.Exception (bracketOnError)
import Data.ByteString (ByteString)
import Data.ByteString.Unsafe (unsafePackAddressLen)
import Data.Word (Word8, Word64)
import Foreign (mallocBytes, poke, plusPtr, free, castPtr)
import GHC.Ptr (Ptr(..))
import Numeric.Natural (Natural)
import System.Random.MWC (uniform, GenIO, create)
random ∷ Natural → IO ByteString
random n = do
gen ← create
randomGen gen n
randomGen ∷ GenIO → Natural → IO ByteString
randomGen gen n =
bracketOnError (mallocBytes len8) free $ \ptr@(Ptr !addr) → do
go ptr
unsafePackAddressLen len8 addr
where
len8, len64 ∷ Int
!len8 = fromIntegral n
!len64 = len8 `div` 8
go ∷ Ptr Word64 → IO ()
go !startPtr = loop64 startPtr
where
fin64Ptr ∷ Ptr Word64
!fin64Ptr = startPtr `plusPtr` (len64 * 8)
loop64 ∷ Ptr Word64 → IO ()
loop64 !curPtr
| curPtr < fin64Ptr = do
!b ← uniform gen ∷ IO Word64
poke curPtr b
loop64 $ curPtr `plusPtr` 8
| otherwise = loop8 $ castPtr curPtr
fin8Ptr ∷ Ptr Word8
!fin8Ptr = startPtr `plusPtr` len8
loop8 ∷ Ptr Word8 → IO ()
loop8 !curPtr
| curPtr < fin8Ptr = do
!b ← uniform gen ∷ IO Word8
poke curPtr b
loop8 $ curPtr `plusPtr` 1
| otherwise = return ()