random-bytestring 0.1.1 → 0.1.2
raw patch · 12 files changed
+375/−114 lines, 12 filesdep +cryptonitedep +pcg-randombinary-added
Dependencies added: cryptonite, pcg-random
Files
- CHANGELOG.md +14/−0
- benchmark/Benchmarks.hs +11/−3
- benchmark/Implementations.hs +81/−18
- benchmark/benchmark-compare.hs +1/−1
- benchmark/benchmark.hs +6/−2
- benchmarks-details.png binary
- benchmarks.png binary
- random-bytestring.cabal +38/−9
- src/Data/ByteString/Random.hs +2/−81
- src/Data/ByteString/Random/Internal.hs +92/−0
- src/Data/ByteString/Random/MWC.hs +65/−0
- src/Data/ByteString/Random/PCG.hs +65/−0
CHANGELOG.md view
@@ -1,3 +1,17 @@+0.1.2+=====++* Add support for PCG as PRNG in addition to MWC.++ The module `Data.ByteString.Random.PCG` uses PCG. The module+ `Data.ByteString.Random.MWC` uses MWC. The module `Data.ByteString.Random`+ uses MWC for backward compatibility.++* Support usage of custom PRNGs through the type class `RandomWords` and the+ function `generate` in the module `Data.ByteString.Random.Internal`.++* Add some benchmark results to package description+ 0.1.1 =====
benchmark/Benchmarks.hs view
@@ -14,6 +14,7 @@ ( Impl(..) , benchmarks , largeBenchmarks+, compareBenchmarks ) where import Criterion@@ -42,7 +43,7 @@ [ singleThreaded impls 1024 , singleThreaded impls (1024 * 1024) , singleThreaded impls (1024 * 1024 * 10)- , singleThreaded impls (1024 * 1024 * 100)+ -- , singleThreaded impls (1024 * 1024 * 100) ] , bgroup "multi-threaded" [ concurrent impls 10 1024@@ -52,14 +53,21 @@ ] ] +compareBenchmarks ∷ [Impl] → [Benchmark]+compareBenchmarks impls =+ [ singleThreaded impls (1024 * 1024)+ , singleThreaded impls (1024 * 1024 * 10)+ -- , singleThreaded impls (1024 * 1024 * 100)+ ]+ concurrent ∷ [Impl] → Natural → Natural → Benchmark-concurrent impls c n = bgroup (show c <> "-" <> show n)+concurrent impls c n = bgroup (show c <> "-" <> show n <> "-bytes") [ cgo label f | (Impl label f) ← impls ] where cgo s f = bench s $ nfIO $ forConcurrently [0..(c-1)] (const $ f n) singleThreaded ∷ [Impl] → Natural → Benchmark-singleThreaded impls n = bgroup (show n)+singleThreaded impls n = bgroup (show n <> "-bytes") [ bench label $ nfIO $ f n | (Impl label f) ← impls ]
benchmark/Implementations.hs view
@@ -19,6 +19,8 @@ , random , entropy+, cryptoSystem+, cryptoChaCha , mwcUnfoldr , mwcUnfoldrIO @@ -26,11 +28,16 @@ , mwcMalloc8 , mwcMalloc32 , mwcMalloc64++--+, pcgMalloc64 ) where import Control.Exception (bracketOnError) import Control.Monad.Primitive +import Crypto.Random (getRandomBytes, drgNew, getSystemDRG, withDRG)+ import Data.ByteString (ByteString, pack, unfoldrN) import Data.ByteString.Unsafe (unsafePackAddressLen) import Data.Word (Word8, Word32, Word64)@@ -44,7 +51,8 @@ import System.Entropy (getEntropy) import System.Random (randoms, getStdGen)-import System.Random.MWC (uniform, create, Gen)+import qualified System.Random.MWC as MWC (uniform, create, Gen)+import qualified System.Random.PCG as PCG (uniform, create, Gen) import Benchmarks @@ -55,11 +63,14 @@ allImplementations = [ Impl "random" random , Impl "entropy" entropy+ , Impl "cryptonite-system" cryptoSystem+ , Impl "cryptonite-chacha" cryptoChaCha , Impl "mwc-unfoldr" mwcUnfoldr , Impl "mwc-unfoldr-io" mwcUnfoldrIO , Impl "mwc-malloc-8" mwcMalloc8 , Impl "mwc-malloc-32" mwcMalloc32 , Impl "mwc-malloc-64" mwcMalloc64+ , Impl "pcg-malloc-64" pcgMalloc64 ] -- -------------------------------------------------------------------------- --@@ -75,11 +86,24 @@ entropy n = getEntropy (fromIntegral n) -- -------------------------------------------------------------------------- --+-- cryptonite getRandomBytes++cryptoSystem ∷ Natural → IO ByteString+cryptoSystem n = do+ gen ← getSystemDRG+ return $ fst (withDRG gen $ getRandomBytes (fromIntegral n))++cryptoChaCha ∷ Natural → IO ByteString+cryptoChaCha n = do+ gen ← drgNew+ return $ fst (withDRG gen $ getRandomBytes (fromIntegral n))++-- -------------------------------------------------------------------------- -- -- Malloc8 mwcMalloc8 ∷ Natural → IO ByteString mwcMalloc8 n = do- !gen ← create+ !gen ← MWC.create bracketOnError (mallocBytes len) free $ \ptr@(Ptr !addr) → do go gen ptr unsafePackAddressLen len addr@@ -93,7 +117,7 @@ loop !i | i == pred len = return () | otherwise = do- !b ← uniform gen ∷ IO Word8+ !b ← MWC.uniform gen ∷ IO Word8 poke (ptr `plusPtr` i) b loop (succ i) @@ -102,7 +126,7 @@ mwcMalloc32 ∷ Natural → IO ByteString mwcMalloc32 n = do- !gen ← create+ !gen ← MWC.create bracketOnError (mallocBytes len8) free $ \ptr@(Ptr !addr) → do go gen ptr unsafePackAddressLen len8 addr@@ -116,7 +140,7 @@ !fin32Ptr = startPtr `plusPtr` (len32 * 4) loop32 !curPtr | curPtr < fin32Ptr = do- !b ← uniform gen ∷ IO Word32+ !b ← MWC.uniform gen ∷ IO Word32 poke curPtr b loop32 $ curPtr `plusPtr` 4 | otherwise = loop8 $ castPtr curPtr@@ -124,7 +148,7 @@ !fin8Ptr = startPtr `plusPtr` len8 loop8 !curPtr | curPtr < fin8Ptr = do- !b ← uniform gen ∷ IO Word8+ !b ← MWC.uniform gen ∷ IO Word8 poke curPtr b loop8 $ curPtr `plusPtr` 1 | otherwise = return ()@@ -135,7 +159,7 @@ mwcMalloc64 ∷ Natural → IO ByteString mwcMalloc64 n = do- !gen ← create+ !gen ← MWC.create bracketOnError (mallocBytes len8) free $ \ptr@(Ptr !addr) → do go gen ptr unsafePackAddressLen len8 addr@@ -144,7 +168,7 @@ !len8 = fromIntegral n !len64 = len8 `div` 8 - go ∷ Gen (PrimState IO) → Ptr Word64 → IO ()+ go ∷ MWC.Gen (PrimState IO) → Ptr Word64 → IO () go !gen !startPtr = loop64 startPtr where fin64Ptr ∷ Ptr Word64@@ -153,7 +177,7 @@ loop64 ∷ Ptr Word64 → IO () loop64 !curPtr | curPtr < fin64Ptr = do- !b ← uniform gen ∷ IO Word64+ !b ← MWC.uniform gen ∷ IO Word64 poke curPtr b loop64 $ curPtr `plusPtr` 8 | otherwise = loop8 $ castPtr curPtr@@ -164,7 +188,7 @@ loop8 ∷ Ptr Word8 → IO () loop8 !curPtr | curPtr < fin8Ptr = do- !b ← uniform gen ∷ IO Word8+ !b ← MWC.uniform gen ∷ IO Word8 poke curPtr b loop8 $ curPtr `plusPtr` 1 | otherwise = return ()@@ -176,11 +200,11 @@ mwcUnfoldr ∷ ∀ m . (PrimMonad m, PrimBase m) ⇒ Natural → m ByteString mwcUnfoldr n = do- !gen ← create+ !gen ← MWC.create primitive $ go gen where go- ∷ Gen (PrimState m)+ ∷ MWC.Gen (PrimState m) → State# (PrimState m) → (# State# (PrimState m), ByteString #) go !gen !st =@@ -188,11 +212,11 @@ in (# st', b #) step- ∷ Gen (PrimState m)+ ∷ MWC.Gen (PrimState m) → Box m → Maybe (Word8, Box m) step !gen (Box !s) =- let !(# !s', !b #) = internal (uniform gen ∷ m Word8) s+ let !(# !s', !b #) = internal (MWC.uniform gen ∷ m Word8) s in Just (b, Box s') -- -------------------------------------------------------------------------- --@@ -200,11 +224,11 @@ mwcUnfoldrIO ∷ Natural → IO ByteString mwcUnfoldrIO n = do- !gen ← create+ !gen ← MWC.create primitive $ go gen where go- ∷ Gen (PrimState IO)+ ∷ MWC.Gen (PrimState IO) → State# (PrimState IO) → (# State# (PrimState IO), ByteString #) go !gen !st =@@ -212,10 +236,49 @@ in (# st', b #) step- ∷ Gen (PrimState IO)+ ∷ MWC.Gen (PrimState IO) → Box IO → Maybe (Word8, Box IO) step !gen (Box !s) =- let !(# !s', !b #) = internal (uniform gen ∷ IO Word8) s+ let !(# !s', !b #) = internal (MWC.uniform gen ∷ IO Word8) s in Just (b, Box s')++-- -------------------------------------------------------------------------- --+-- PCG Malloc64++pcgMalloc64 ∷ Natural → IO ByteString+pcgMalloc64 n = do+ !gen ← PCG.create+ bracketOnError (mallocBytes len8) free $ \ptr@(Ptr !addr) → do+ go gen ptr+ unsafePackAddressLen len8 addr+ where+ len8, len64 ∷ Int+ !len8 = fromIntegral n+ !len64 = len8 `div` 8++ go ∷ PCG.Gen (PrimState IO) → Ptr Word64 → IO ()+ go !gen !startPtr = loop64 startPtr+ where+ fin64Ptr ∷ Ptr Word64+ !fin64Ptr = startPtr `plusPtr` (len64 * 8)++ loop64 ∷ Ptr Word64 → IO ()+ loop64 !curPtr+ | curPtr < fin64Ptr = do+ !b ← PCG.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 ← PCG.uniform gen ∷ IO Word8+ poke curPtr b+ loop8 $ curPtr `plusPtr` 1+ | otherwise = return ()
benchmark/benchmark-compare.hs view
@@ -19,5 +19,5 @@ import Implementations main ∷ IO () -main = defaultMain $ benchmarks allImplementations +main = defaultMain $ compareBenchmarks allImplementations
benchmark/benchmark.hs view
@@ -16,8 +16,12 @@ import Benchmarks import Criterion.Main -import Data.ByteString.Random +import qualified Data.ByteString.Random.MWC as MWC +import qualified Data.ByteString.Random.PCG as PCG main ∷ IO () -main = defaultMain $ largeBenchmarks [Impl "Data.ByteString.Random" random] +main = defaultMain $ compareBenchmarks + [ Impl "random-bytestring+mwc" MWC.random + , Impl "random-bytestring+pcg" PCG.random + ]
+ benchmarks-details.png view
binary file changed (absent → 33474 bytes)
+ benchmarks.png view
binary file changed (absent → 47057 bytes)
random-bytestring.cabal view
@@ -1,15 +1,26 @@ Name: random-bytestring-Version: 0.1.1+Version: 0.1.2 Synopsis: Efficient generation of random bytestrings Description: Efficient generation of random bytestrings. The implementation populates uninitialized memory with uniformily distributed random- 64 bit words. The words are generated using the PRNG from the- mwc-random package.-+ 64 bit words (and 8 bit words for remaining bytes at the end of+ the bytestring).+ .+ Random words are generated using the PRNG from+ the [mwc-random](https://hackage.haskell.org/package/mwc-random) package+ or the [pcg-random](https://hackage.haskell.org/package/pcg-random)+ package. It is also possible to use a custom PRNG by providing+ an instance for the 'RandomWords' type class and using the function+ 'generate' from the module "Data.ByteString.Random.Internal".+ . The generated byte strings are suitable for statistical applications. They are /not/ suitable for cryptographic applications.+ .+ + .+  Homepage: https://www.github.com/larskuhtz/random-bytestring License: MIT@@ -25,6 +36,8 @@ extra-doc-files: README.md, CHANGELOG.md+ benchmarks.png+ benchmarks-details.png source-repository head type: git@@ -36,17 +49,21 @@ exposed-modules: Data.ByteString.Random+ Data.ByteString.Random.Internal+ Data.ByteString.Random.PCG+ Data.ByteString.Random.MWC build-depends: base >= 4.7 && < 4.11, bytestring >= 0.10,- mwc-random >= 0.13+ mwc-random >= 0.13,+ pcg-random >= 0.1 if impl(ghc < 7.10) build-depends: nats >= 1.1 - ghc-options: -Wall+ ghc-options: -Wall -O2 Benchmark benchmark default-language: Haskell2010@@ -59,7 +76,14 @@ async >= 2.1, base >= 4.7 && < 4.11, bytestring >= 0.10,- criterion >= 1.1+ criterion >= 1.1,+ entropy >= 0.3,+ ghc-prim >= 0.3,+ mwc-random >= 0.13,+ pcg-random >= 0.1,+ primitive >= 0.6,+ random >= 1.1,+ cryptonite >= 0.24 if impl(ghc < 7.10) build-depends:@@ -70,6 +94,7 @@ other-modules: Benchmarks+ Implementations Benchmark benchmark-compare default-language: Haskell2010@@ -86,8 +111,10 @@ entropy >= 0.3, ghc-prim >= 0.3, mwc-random >= 0.13,+ pcg-random >= 0.1, primitive >= 0.6,- random >= 1.1+ random >= 1.1,+ cryptonite >= 0.24 if impl(ghc < 7.10) build-depends:@@ -115,8 +142,10 @@ entropy >= 0.3, ghc-prim >= 0.3, mwc-random >= 0.13,+ pcg-random >= 0.1, primitive >= 0.6,- random >= 1.1+ random >= 1.1,+ cryptonite >= 0.24 if impl(ghc < 7.10) build-depends:
src/Data/ByteString/Random.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE UnicodeSyntax #-}-{-# LANGUAGE BangPatterns #-} -- -------------------------------------------------------------------------- -- -- |@@ -8,88 +7,10 @@ -- License: MIT -- Maintainer: lakuhtz@gmail.com -- Stability: experimental--- -------------------------------------------------------------------------- -- module Data.ByteString.Random-( random-, randomGen+( module Data.ByteString.Random.MWC ) 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, withSystemRandom)---- $setup--- >>> import qualified Data.ByteString as B--- >>> import Test.QuickCheck---- | Generate a random bytestring of length n. The PRNG is seeded--- from the system randomness source.------ prop> ioProperty $ ((fromIntegral n ===) . B.length) <$> random n--- prop> n > 4 ==> ioProperty $ (/=) <$> random n <*> random n----random- ∷ Natural- -- ^ Length of the result bytestring in bytes- → IO ByteString-random n = withSystemRandom $ \gen → randomGen gen n---- | Generate a random bytestring of length n using the given--- PRNG.----randomGen- ∷ GenIO- -- ^ A PRNG that is used to generate random bytes.- → Natural- -- ^ Length of the result bytestring in bytes- → IO ByteString-randomGen gen n =- bracketOnError (mallocBytes len8) free $ \ptr@(Ptr !addr) → do- {-# SCC "go" #-} go ptr- {-# SCC "pack" #-} unsafePackAddressLen len8 addr- where- len8, len64 ∷ Int- !len8 = fromIntegral n- !len64 = len8 `div` 8-- go ∷ Ptr Word64 → IO ()- go !startPtr = loop64 startPtr- where- -- Would it help to add more manual unrolling levels?- -- How smart is the compiler about unrolling loops?-- -- Generate 64bit values- fin64Ptr ∷ Ptr Word64- !fin64Ptr = startPtr `plusPtr` (len64 * 8)-- loop64 ∷ Ptr Word64 → IO ()- loop64 !curPtr- | curPtr < fin64Ptr = {-# SCC "loop64" #-} do- !b ← uniform gen ∷ IO Word64- {-# SCC "poke64" #-} poke curPtr b- loop64 $ {-# SCC "ptr_inc" #-} curPtr `plusPtr` 8- | otherwise = loop8 $ castPtr curPtr-- -- Generate 8bit values- fin8Ptr ∷ Ptr Word8- !fin8Ptr = startPtr `plusPtr` len8-- loop8 ∷ Ptr Word8 → IO ()- loop8 !curPtr- | curPtr < fin8Ptr = {-# SCC "loop8" #-} do- !b ← uniform gen ∷ IO Word8- poke curPtr b- loop8 $ curPtr `plusPtr` 1- | otherwise = return ()+import Data.ByteString.Random.MWC
+ src/Data/ByteString/Random/Internal.hs view
@@ -0,0 +1,92 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE UnicodeSyntax #-}++-- -------------------------------------------------------------------------- --+-- |+-- Module: Data.ByteString.Random.Internal+-- Copyright: (c) Lars Kuhtz <lakuhtz@gmail.com> 2017+-- License: MIT+-- Maintainer: lakuhtz@gmail.com+-- Stability: experimental++module Data.ByteString.Random.Internal+( generate+, RandomWords(..)+) 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)++-- -------------------------------------------------------------------------- --++class RandomWords g where+ uniformW8 ∷ g → IO Word8+ -- ^ function that generates uniformily distributed random 8 bit words+ uniformW64 ∷ g → IO Word64+ -- ^ function that generates uniformily distributed random 64 bit words++-- The reason why a type class is used instead of passing the IO functions+-- directoy to generate, is that we can force GHC to inline these methods by+-- using a SPECIALIZE pragma.++-- -------------------------------------------------------------------------- --++-- | Generates uniformily distributed random bytestrings of length n using the+-- given PRNG.+--+generate+ ∷ RandomWords g+ ⇒ g+ -- ^ PRNG+ → Natural+ -- ^ Length of the result bytestring in bytes+ → IO ByteString+generate g n =+ bracketOnError (mallocBytes len8) free $ \ptr@(Ptr !addr) → do+ {-# SCC "go" #-} go ptr+ {-# SCC "pack" #-} unsafePackAddressLen len8 addr+ where+ len8, len64 ∷ Int+ !len8 = fromIntegral n+ !len64 = len8 `div` 8++ go ∷ Ptr Word64 → IO ()+ go !startPtr = loop64 startPtr+ where+ -- Would it help to add more manual unrolling levels?+ -- How smart is the compiler about unrolling loops?++ -- Generate 64bit values+ fin64Ptr ∷ Ptr Word64+ !fin64Ptr = startPtr `plusPtr` (len64 * 8)++ loop64 ∷ Ptr Word64 → IO ()+ loop64 !curPtr+ | curPtr < fin64Ptr = {-# SCC "loop64" #-} do+ !b ← uniformW64 g+ {-# SCC "poke64" #-} poke curPtr b+ loop64 $ {-# SCC "ptr_inc" #-} curPtr `plusPtr` 8+ | otherwise = loop8 $ castPtr curPtr++ -- Generate 8bit values+ fin8Ptr ∷ Ptr Word8+ !fin8Ptr = startPtr `plusPtr` len8++ loop8 ∷ Ptr Word8 → IO ()+ loop8 !curPtr+ | curPtr < fin8Ptr = {-# SCC "loop8" #-} do+ !b ← uniformW8 g+ poke curPtr b+ loop8 $ curPtr `plusPtr` 1+ | otherwise = return ()+{-# INLINEABLE generate #-}+
+ src/Data/ByteString/Random/MWC.hs view
@@ -0,0 +1,65 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UnicodeSyntax #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++-- -------------------------------------------------------------------------- --+-- |+-- Module: Data.ByteString.Random.MWC+-- Copyright: (c) Lars Kuhtz <lakuhtz@gmail.com> 2017+-- License: MIT+-- Maintainer: lakuhtz@gmail.com+-- Stability: experimental++module Data.ByteString.Random.MWC+( random+, randomGen+) where++import Data.ByteString (ByteString)++import Numeric.Natural (Natural)++import System.Random.MWC (uniform, GenIO, withSystemRandom)++-- internal imports++import Data.ByteString.Random.Internal++-- -------------------------------------------------------------------------- --++instance (g ~ GenIO) ⇒ RandomWords g where+ uniformW8 = uniform+ {-# INLINE uniformW8 #-}+ uniformW64 = uniform+ {-# INLINE uniformW64 #-}++{-# SPECIALIZE generate ∷ GenIO → Natural → IO ByteString #-}++-- -------------------------------------------------------------------------- --++randomGen+ ∷ GenIO+ -- ^ PRNG+ → Natural+ -- ^ Length of the result bytestring in bytes+ → IO ByteString+randomGen = generate+{-# INLINE randomGen #-}++-- $setup+-- >>> import qualified Data.ByteString as B+-- >>> import Test.QuickCheck++-- | Generate a random bytestring of length n. The PRNG is seeded+-- from the system randomness source.+--+-- prop> ioProperty $ ((fromIntegral n ===) . B.length) <$> random n+-- prop> n > 4 ==> ioProperty $ (/=) <$> random n <*> random n+--+random+ ∷ Natural+ -- ^ Length of the result bytestring in bytes+ → IO ByteString+random = withSystemRandom . flip randomGen+
+ src/Data/ByteString/Random/PCG.hs view
@@ -0,0 +1,65 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UnicodeSyntax #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++-- -------------------------------------------------------------------------- --+-- |+-- Module: Data.ByteString.Random.PCG+-- Copyright: (c) Lars Kuhtz <lakuhtz@gmail.com> 2017+-- License: MIT+-- Maintainer: lakuhtz@gmail.com+-- Stability: experimental++module Data.ByteString.Random.PCG+( random+, randomGen+) where++import Data.ByteString (ByteString)++import Numeric.Natural (Natural)++import System.Random.PCG (uniform, GenIO, withSystemRandom)++-- internal imports++import Data.ByteString.Random.Internal++-- -------------------------------------------------------------------------- --++instance (g ~ GenIO) ⇒ RandomWords g where+ uniformW8 = uniform+ {-# INLINE uniformW8 #-}+ uniformW64 = uniform+ {-# INLINE uniformW64 #-}++{-# SPECIALIZE generate ∷ GenIO → Natural → IO ByteString #-}++-- -------------------------------------------------------------------------- --++randomGen+ ∷ GenIO+ -- ^ PRNG+ → Natural+ -- ^ Length of the result bytestring in bytes+ → IO ByteString+randomGen = generate+{-# INLINE randomGen #-}++-- $setup+-- >>> import qualified Data.ByteString as B+-- >>> import Test.QuickCheck++-- | Generate a random bytestring of length n. The PRNG is seeded+-- from the system randomness source.+--+-- prop> ioProperty $ ((fromIntegral n ===) . B.length) <$> random n+-- prop> n > 4 ==> ioProperty $ (/=) <$> random n <*> random n+--+random+ ∷ Natural+ -- ^ Length of the result bytestring in bytes+ → IO ByteString+random n = withSystemRandom $ flip randomGen n+