Don't use ?:
[folly.git] / folly / Random.h
index d10f2d645472d24ae0dc2c7c6ad4449f15c2b85b..0962d07e15445c6ae47385585073d680ea87ceed 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Facebook, Inc.
+ * Copyright 2016 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * limitations under the License.
  */
 
-#ifndef FOLLY_BASE_RANDOM_H_
-#define FOLLY_BASE_RANDOM_H_
+#pragma once
+#define FOLLY_RANDOM_H_
 
+#include <type_traits>
+#include <random>
 #include <stdint.h>
-#include "folly/ThreadLocal.h"
+#include <folly/Portability.h>
 
-namespace folly {
-
-/*
- * Return a good seed for a random number generator.
- */
-uint32_t randomNumberSeed();
+#if FOLLY_HAVE_EXTRANDOM_SFMT19937
+#include <ext/random>
+#endif
 
-class Random;
+namespace folly {
 
 /**
  * A PRNG with one instance per thread. This PRNG uses a mersenne twister random
@@ -64,49 +63,103 @@ class ThreadLocalPRNG {
   }
   friend class Random;
 
-  ThreadLocalPRNG() {
-    local_ = localInstance.get();
-    if (!local_) {
-      local_ = initLocal();
-    }
-  }
+  ThreadLocalPRNG();
 
  private:
   class LocalInstancePRNG;
-  static LocalInstancePRNG* initLocal();
-  static folly::ThreadLocalPtr<ThreadLocalPRNG::LocalInstancePRNG>
-    localInstance;
 
   static result_type getImpl(LocalInstancePRNG* local);
   LocalInstancePRNG* local_;
 };
 
 
-
 class Random {
 
  private:
-  template<class RNG>
+  template <class RNG>
   using ValidRNG = typename std::enable_if<
-   std::is_unsigned<typename std::result_of<RNG&()>::type>::value,
-   RNG>::type;
+      std::is_unsigned<typename std::result_of<RNG&()>::type>::value,
+      RNG>::type;
 
  public:
+  // Default generator type.
+#if FOLLY_HAVE_EXTRANDOM_SFMT19937
+  typedef __gnu_cxx::sfmt19937 DefaultGenerator;
+#else
+  typedef std::mt19937 DefaultGenerator;
+#endif
+
+  /**
+   * Get secure random bytes. (On Linux and OSX, this means /dev/urandom).
+   */
+  static void secureRandom(void* data, size_t len);
+
+  /**
+   * Shortcut to get a secure random value of integral type.
+   */
+  template <class T>
+  static typename std::enable_if<
+    std::is_integral<T>::value && !std::is_same<T,bool>::value,
+    T>::type
+  secureRandom() {
+    T val;
+    secureRandom(&val, sizeof(val));
+    return val;
+  }
+
+  /**
+   * (Re-)Seed an existing RNG with a good seed.
+   *
+   * Note that you should usually use ThreadLocalPRNG unless you need
+   * reproducibility (such as during a test), in which case you'd want
+   * to create a RNG with a good seed in production, and seed it yourself
+   * in test.
+   */
+  template <class RNG = DefaultGenerator, class /* EnableIf */ = ValidRNG<RNG>>
+  static void seed(RNG& rng);
+
+  /**
+   * Create a new RNG, seeded with a good seed.
+   *
+   * Note that you should usually use ThreadLocalPRNG unless you need
+   * reproducibility (such as during a test), in which case you'd want
+   * to create a RNG with a good seed in production, and seed it yourself
+   * in test.
+   */
+  template <class RNG = DefaultGenerator, class /* EnableIf */ = ValidRNG<RNG>>
+  static RNG create();
 
   /**
    * Returns a random uint32_t
    */
-  template<class RNG = ThreadLocalPRNG>
-  static uint32_t rand32(ValidRNG<RNG>  rrng = RNG()) {
-    uint32_t r = rrng.operator()();
+  static uint32_t rand32() {
+    ThreadLocalPRNG prng;
+    return rand32(prng);
+  }
+
+  /**
+   * Returns a random uint32_t given a specific RNG
+   */
+  template <class RNG, class /* EnableIf */ = ValidRNG<RNG>>
+  static uint32_t rand32(RNG rng) {
+    uint32_t r = rng.operator()();
     return r;
   }
 
   /**
    * Returns a random uint32_t in [0, max). If max == 0, returns 0.
    */
-  template<class RNG = ThreadLocalPRNG>
-  static uint32_t rand32(uint32_t max, ValidRNG<RNG> rng = RNG()) {
+  static uint32_t rand32(uint32_t max) {
+    ThreadLocalPRNG prng;
+    return rand32(max, prng);
+  }
+
+  /**
+   * Returns a random uint32_t in [0, max) given a specific RNG.
+   * If max == 0, returns 0.
+   */
+  template <class RNG = ThreadLocalPRNG, class /* EnableIf */ = ValidRNG<RNG>>
+  static uint32_t rand32(uint32_t max, RNG rng = RNG()) {
     if (max == 0) {
       return 0;
     }
@@ -117,10 +170,8 @@ class Random {
   /**
    * Returns a random uint32_t in [min, max). If min == max, returns 0.
    */
-  template<class RNG = ThreadLocalPRNG>
-  static uint32_t rand32(uint32_t min,
-                         uint32_t max,
-                         ValidRNG<RNG> rng = RNG()) {
+  template <class RNG = ThreadLocalPRNG, class /* EnableIf */ = ValidRNG<RNG>>
+  static uint32_t rand32(uint32_t min, uint32_t max, RNG rng = RNG()) {
     if (min == max) {
       return 0;
     }
@@ -131,16 +182,16 @@ class Random {
   /**
    * Returns a random uint64_t
    */
-  template<class RNG = ThreadLocalPRNG>
-  static uint64_t rand64(ValidRNG<RNG> rng = RNG()) {
+  template <class RNG = ThreadLocalPRNG, class /* EnableIf */ = ValidRNG<RNG>>
+  static uint64_t rand64(RNG rng = RNG()) {
     return ((uint64_t) rng() << 32) | rng();
   }
 
   /**
    * Returns a random uint64_t in [0, max). If max == 0, returns 0.
    */
-  template<class RNG = ThreadLocalPRNG>
-  static uint64_t rand64(uint64_t max, ValidRNG<RNG> rng = RNG()) {
+  template <class RNG = ThreadLocalPRNG, class /* EnableIf */ = ValidRNG<RNG>>
+  static uint64_t rand64(uint64_t max, RNG rng = RNG()) {
     if (max == 0) {
       return 0;
     }
@@ -151,10 +202,8 @@ class Random {
   /**
    * Returns a random uint64_t in [min, max). If min == max, returns 0.
    */
-  template<class RNG = ThreadLocalPRNG>
-  static uint64_t rand64(uint64_t min,
-                         uint64_t max,
-                         ValidRNG<RNG> rng = RNG()) {
+  template <class RNG = ThreadLocalPRNG, class /* EnableIf */ = ValidRNG<RNG>>
+  static uint64_t rand64(uint64_t min, uint64_t max, RNG rng = RNG()) {
     if (min == max) {
       return 0;
     }
@@ -165,7 +214,7 @@ class Random {
   /**
    * Returns true 1/n of the time. If n == 0, always returns false
    */
-  template<class RNG = ThreadLocalPRNG>
+  template <class RNG = ThreadLocalPRNG, class /* EnableIf */ = ValidRNG<RNG>>
   static bool oneIn(uint32_t n, ValidRNG<RNG> rng = RNG()) {
     if (n == 0) {
       return false;
@@ -177,8 +226,8 @@ class Random {
   /**
    * Returns a double in [0, 1)
    */
-  template<class RNG = ThreadLocalPRNG>
-  static double randDouble01(ValidRNG<RNG> rng = RNG()) {
+  template <class RNG = ThreadLocalPRNG, class /* EnableIf */ = ValidRNG<RNG>>
+  static double randDouble01(RNG rng = RNG()) {
     return std::generate_canonical<double, std::numeric_limits<double>::digits>
       (rng);
   }
@@ -186,8 +235,8 @@ class Random {
   /**
     * Returns a double in [min, max), if min == max, returns 0.
     */
-  template<class RNG = ThreadLocalPRNG>
-  static double randDouble(double min, double max, ValidRNG<RNG> rng = RNG()) {
+  template <class RNG = ThreadLocalPRNG, class /* EnableIf */ = ValidRNG<RNG>>
+  static double randDouble(double min, double max, RNG rng = RNG()) {
     if (std::fabs(max - min) < std::numeric_limits<double>::epsilon()) {
       return 0;
     }
@@ -196,6 +245,16 @@ class Random {
 
 };
 
+/*
+ * Return a good seed for a random number generator.
+ * Note that this is a legacy function, as it returns a 32-bit value, which
+ * is too small to be useful as a "real" RNG seed. Use the functions in class
+ * Random instead.
+ */
+inline uint32_t randomNumberSeed() {
+  return Random::rand32();
 }
 
-#endif
+}
+
+#include <folly/Random-inl.h>