[compiler-rt] Add ConsumeProbability and ConsumeFloatingPoint methods to FDP.

Summary:
Also slightly cleaned up the comments and changed the header's extension
back to `.h` as per comments on https://reviews.llvm.org/D65812.

New methods added:

* `ConsumeProbability` returns [0.0, 1.0] by consuming an unsigned integer value
   from the input data and dividing that value by the integer's max value.
* `ConsumeFloatingPointInRange` returns a floating point value in the given
   range. Relies on `ConsumeProbability` method. This method does not have the
   limitation of `std::uniform_real_distribution` that requires the given range
   to be <= the floating point type's max. If the range is too large, this
   implementation will additionally call `ConsumeBool` to decide whether the
   result will be in the first or the second half of the range.
* `ConsumeFloatingPoint` returns a floating point value in the range
  `[std::numeric_limits<T>::lowest(), std::numeric_limits<T>::min()]`.

Tested on Linux, Mac, Windows.

Reviewers: morehouse

Reviewed By: morehouse

Subscribers: kubamracek, mgorny, dberris, delcypher, #sanitizers, llvm-commits

Tags: #llvm, #sanitizers

Differential Revision: https://reviews.llvm.org/D65905

llvm-svn: 368331
GitOrigin-RevId: df3b465c9c4fed5045470d3eb5b65c14e6de71e6
diff --git a/FuzzedDataProvider.hpp b/FuzzedDataProvider.h
similarity index 79%
rename from FuzzedDataProvider.hpp
rename to FuzzedDataProvider.h
index d6e2d1f..47cc499 100644
--- a/FuzzedDataProvider.hpp
+++ b/FuzzedDataProvider.h
@@ -1,4 +1,4 @@
-//===- FuzzedDataProvider.hpp - Utility header for fuzz targets -*- C++ -* ===//
+//===- FuzzedDataProvider.h - Utility header for fuzz targets ---*- C++ -* ===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -25,6 +25,8 @@
 #include <utility>
 #include <vector>
 
+// In addition to the comments below, the API is also briefly documented at
+// https://github.com/google/fuzzing/blob/master/docs/split-inputs.md#fuzzed-data-provider
 class FuzzedDataProvider {
  public:
   // |data| is an array of length |size| that the FuzzedDataProvider wraps to
@@ -143,9 +145,9 @@
     return ConsumeBytes<T>(remaining_bytes_);
   }
 
+  // Returns a std::string containing all remaining bytes of the input data.
   // Prefer using |ConsumeRemainingBytes| unless you actually need a std::string
   // object.
-  // Returns a std::vector containing all remaining bytes of the input data.
   std::string ConsumeRemainingBytesAsString() {
     return ConsumeBytesAsString(remaining_bytes_);
   }
@@ -161,7 +163,7 @@
   // Reads one byte and returns a bool, or false when no data remains.
   bool ConsumeBool() { return 1 & ConsumeIntegral<uint8_t>(); }
 
-  // Returns a copy of a value selected from a fixed-size |array|.
+  // Returns a copy of the value selected from the given fixed-size |array|.
   template <typename T, size_t size>
   T PickValueInArray(const T (&array)[size]) {
     static_assert(size > 0, "The array must be non empty.");
@@ -170,11 +172,14 @@
 
   template <typename T>
   T PickValueInArray(std::initializer_list<const T> list) {
-    // static_assert(list.size() > 0, "The array must be non empty.");
+    // TODO(Dor1s): switch to static_assert once C++14 is allowed.
+    if (!list.size())
+      abort();
+
     return *(list.begin() + ConsumeIntegralInRange<size_t>(0, list.size() - 1));
   }
 
-  // Return an enum value. The enum must start at 0 and be contiguous. It must
+  // Returns an enum value. The enum must start at 0 and be contiguous. It must
   // also contain |kMaxValue| aliased to its largest (inclusive) value. Such as:
   // enum class Foo { SomeValue, OtherValue, kMaxValue = OtherValue };
   template <typename T> T ConsumeEnum() {
@@ -183,6 +188,56 @@
         0, static_cast<uint32_t>(T::kMaxValue)));
   }
 
+  // Returns a floating point number in the range [0.0, 1.0]. If there's no
+  // input data left, always returns 0.
+  template <typename T> T ConsumeProbability() {
+    static_assert(std::is_floating_point<T>::value,
+                  "A floating point type is required.");
+
+    // Use different integral types for different floating point types in order
+    // to provide better density of the resulting values.
+    using IntegralType =
+        typename std::conditional<sizeof(T) <= sizeof(uint32_t), uint32_t,
+                                  uint64_t>::type;
+
+    T result = static_cast<T>(ConsumeIntegral<IntegralType>());
+    result /= static_cast<T>(std::numeric_limits<IntegralType>::max());
+    return result;
+  }
+
+  // Returns a floating point value in the range [Type's lowest, Type's max] by
+  // consuming bytes from the input data. If there's no input data left, always
+  // returns approximately 0.
+  template <typename T> T ConsumeFloatingPoint() {
+    return ConsumeFloatingPointInRange<T>(std::numeric_limits<T>::lowest(),
+                                          std::numeric_limits<T>::max());
+  }
+
+  // Returns a floating point value in the given range by consuming bytes from
+  // the input data. If there's no input data left, returns |min|. Note that
+  // |min| must be less than or equal to |max|.
+  template <typename T> T ConsumeFloatingPointInRange(T min, T max) {
+    if (min > max)
+      abort();
+
+    T range = .0;
+    T result = min;
+    constexpr T zero(.0);
+    if (max > zero && min < zero && max > min + std::numeric_limits<T>::max()) {
+      // The diff |max - min| would overflow the given floating point type. Use
+      // the half of the diff as the range and consume a bool to decide whether
+      // the result is in the first of the second part of the diff.
+      range = (max / 2.0) - (min / 2.0);
+      if (ConsumeBool()) {
+        result += range;
+      }
+    } else {
+      range = max - min;
+    }
+
+    return result + range * ConsumeProbability<T>();
+  }
+
   // Reports the remaining bytes available for fuzzed input.
   size_t remaining_bytes() { return remaining_bytes_; }
 
diff --git a/tests/FuzzedDataProviderUnittest.cpp b/tests/FuzzedDataProviderUnittest.cpp
index 36f2090..2222834 100644
--- a/tests/FuzzedDataProviderUnittest.cpp
+++ b/tests/FuzzedDataProviderUnittest.cpp
@@ -6,7 +6,7 @@
 #include <cstdint>
 #include <cstdlib>
 
-#include <fuzzer/FuzzedDataProvider.hpp>
+#include <fuzzer/FuzzedDataProvider.h>
 
 // The test is intentionally extensive, as behavior of |FuzzedDataProvider| must
 // not be broken, given than many fuzz targets depend on it. Changing the
@@ -348,6 +348,57 @@
   EXPECT_EQ(size_t(0), DataProv.remaining_bytes());
 }
 
+TEST(FuzzedDataProvider, ConsumeProbability) {
+  FuzzedDataProvider DataProv(Data, sizeof(Data));
+  ASSERT_FLOAT_EQ(float(0.28969181), DataProv.ConsumeProbability<float>());
+  ASSERT_DOUBLE_EQ(double(0.086814121166605432),
+                   DataProv.ConsumeProbability<double>());
+  ASSERT_FLOAT_EQ(float(0.30104411), DataProv.ConsumeProbability<float>());
+  ASSERT_DOUBLE_EQ(double(0.96218831486039413),
+                   DataProv.ConsumeProbability<double>());
+  ASSERT_FLOAT_EQ(float(0.67005056), DataProv.ConsumeProbability<float>());
+  ASSERT_DOUBLE_EQ(double(0.69210584173832279),
+                   DataProv.ConsumeProbability<double>());
+
+  // Exhaust the buffer.
+  EXPECT_EQ(std::vector<uint8_t>(Data, Data + sizeof(Data) - 36),
+            DataProv.ConsumeRemainingBytes<uint8_t>());
+  ASSERT_FLOAT_EQ(float(0.0), DataProv.ConsumeProbability<float>());
+}
+
+TEST(FuzzedDataProvider, ConsumeFloatingPoint) {
+  FuzzedDataProvider DataProv(Data, sizeof(Data));
+  ASSERT_FLOAT_EQ(float(-2.8546307e+38),
+                  DataProv.ConsumeFloatingPoint<float>());
+  ASSERT_DOUBLE_EQ(double(8.0940194040236032e+307),
+                   DataProv.ConsumeFloatingPoint<double>());
+  ASSERT_FLOAT_EQ(float(271.49084),
+                  DataProv.ConsumeFloatingPointInRange<float>(123.0, 777.0));
+  ASSERT_DOUBLE_EQ(double(30.859126145478349),
+                   DataProv.ConsumeFloatingPointInRange<double>(13.37, 31.337));
+  ASSERT_FLOAT_EQ(
+      float(-903.47729),
+      DataProv.ConsumeFloatingPointInRange<float>(-999.9999, -777.77));
+  ASSERT_DOUBLE_EQ(
+      double(24.561393182922771),
+      DataProv.ConsumeFloatingPointInRange<double>(-13.37, 31.337));
+  ASSERT_FLOAT_EQ(float(1.0),
+                  DataProv.ConsumeFloatingPointInRange<float>(1.0, 1.0));
+  ASSERT_DOUBLE_EQ(double(-1.0),
+                   DataProv.ConsumeFloatingPointInRange<double>(-1.0, -1.0));
+
+  // Exhaust the buffer.
+  EXPECT_EQ((std::vector<uint8_t>(Data, Data + sizeof(Data) - 50)).size(),
+            DataProv.ConsumeRemainingBytes<uint8_t>().size());
+  ASSERT_FLOAT_EQ(float(0.0), DataProv.ConsumeProbability<float>());
+  ASSERT_NEAR(std::numeric_limits<double>::lowest(),
+              DataProv.ConsumeFloatingPoint<double>(), 1e-10);
+  ASSERT_FLOAT_EQ(float(123.0),
+                  DataProv.ConsumeFloatingPointInRange<float>(123.0, 777.0));
+  ASSERT_DOUBLE_EQ(double(-13.37), DataProv.ConsumeFloatingPointInRange<double>(
+                                       -13.37, 31.337));
+}
+
 int main(int argc, char **argv) {
   testing::InitGoogleTest(&argc, argv);
   return RUN_ALL_TESTS();
diff --git a/utils/FuzzedDataProvider.h b/utils/FuzzedDataProvider.h
index dd2ea20..5692060 100644
--- a/utils/FuzzedDataProvider.h
+++ b/utils/FuzzedDataProvider.h
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// This a temporary copy of compiler-rt/include/fuzzer/FuzzedDataProvider.hpp.
+// This a temporary copy of compiler-rt/include/fuzzer/FuzzedDataProvider.h.
 // TODO(mmoroz@chromium.org): delete this copy.
 // A single header library providing an utility class to break up an array of
 // bytes. Whenever run on the same input, provides the same output, as long as