Index: include/fuzzer/FuzzedDataProvider.hpp =================================================================== --- include/fuzzer/FuzzedDataProvider.hpp +++ include/fuzzer/FuzzedDataProvider.hpp @@ -143,9 +143,9 @@ return ConsumeBytes(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 +161,7 @@ // Reads one byte and returns a bool, or false when no data remains. bool ConsumeBool() { return 1 & ConsumeIntegral(); } - // 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 T PickValueInArray(const T (&array)[size]) { static_assert(size > 0, "The array must be non empty."); @@ -170,11 +170,14 @@ template T PickValueInArray(std::initializer_list 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(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 T ConsumeEnum() { @@ -183,6 +186,54 @@ 0, static_cast(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 T ConsumeProbability() { + static_assert(std::is_floating_point::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::type; + + T result = static_cast(ConsumeIntegral()); + result /= static_cast(std::numeric_limits::max()); + return result; + } + + // Returns a floating point value in the range [Type's min, Type's max] by + // consuming bytes from the input data. If there's no input data left, always + // returns approximately 0. + template T ConsumeFloatingPoint() { + return ConsumeFloatingPointInRange(std::numeric_limits::min(), + std::numeric_limits::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 either: + // * |min|, if |min| and |max| have the same sign, or + // * |min + max|, if |min| and |max| have different signs. + // Note that |min| must be less than or equal to |max|. + template T ConsumeFloatingPointInRange(T min, T max) { + if (min > max) + abort(); + + constexpr T zero(.0); + if ((min >= zero && max >= zero) || (min <= zero && max <= zero)) { + // Use range-based implementation, as |min| and |max| have the same + // sign and the diff |max - min| fits the given floating point type. + T range = max - min; + return min + range * ConsumeProbability(); + } + + // Consume negative and positive parts separately and return their sum. + T negative = min * ConsumeProbability(); + T positive = max * ConsumeProbability(); + return min + max; + } + // Reports the remaining bytes available for fuzzed input. size_t remaining_bytes() { return remaining_bytes_; } Index: lib/fuzzer/tests/FuzzedDataProviderUnittest.cpp =================================================================== --- lib/fuzzer/tests/FuzzedDataProviderUnittest.cpp +++ lib/fuzzer/tests/FuzzedDataProviderUnittest.cpp @@ -348,6 +348,51 @@ EXPECT_EQ(size_t(0), DataProv.remaining_bytes()); } +TEST(FuzzedDataProvider, ConsumeProbability) { + FuzzedDataProvider DataProv(Data, sizeof(Data)); + ASSERT_FLOAT_EQ(float(0.28969181), DataProv.ConsumeProbability()); + ASSERT_DOUBLE_EQ(double(0.086814121166605432), + DataProv.ConsumeProbability()); + ASSERT_FLOAT_EQ(float(0.30104411), DataProv.ConsumeProbability()); + ASSERT_DOUBLE_EQ(double(0.96218831486039413), + DataProv.ConsumeProbability()); + ASSERT_FLOAT_EQ(float(0.67005056), DataProv.ConsumeProbability()); + ASSERT_DOUBLE_EQ(double(0.69210584173832279), + DataProv.ConsumeProbability()); + + // Exhaust the buffer. + EXPECT_EQ(std::vector(Data, Data + sizeof(Data) - 36), + DataProv.ConsumeRemainingBytes()); + ASSERT_FLOAT_EQ(float(0.0), DataProv.ConsumeProbability()); +} + +TEST(FuzzedDataProvider, ConsumeFloatingPoint) { + FuzzedDataProvider DataProv(Data, sizeof(Data)); + ASSERT_FLOAT_EQ(float(9.8577003e+37), DataProv.ConsumeFloatingPoint()); + ASSERT_DOUBLE_EQ(double(1.5606514963031183e+307), + DataProv.ConsumeFloatingPoint()); + ASSERT_FLOAT_EQ(float(319.88284), + DataProv.ConsumeFloatingPointInRange(123.0, 777.0)); + ASSERT_DOUBLE_EQ(double(30.657637453096697), + DataProv.ConsumeFloatingPointInRange(13.37, 31.337)); + ASSERT_FLOAT_EQ(float(-851.0946), DataProv.ConsumeFloatingPointInRange( + -999.9999, -777.77)); + ASSERT_DOUBLE_EQ( + double(17.966999999999999), + DataProv.ConsumeFloatingPointInRange(-13.37, 31.337)); + + // Exhaust the buffer. + EXPECT_EQ(std::vector(Data, Data + sizeof(Data) - 44), + DataProv.ConsumeRemainingBytes()); + ASSERT_FLOAT_EQ(float(0.0), DataProv.ConsumeProbability()); + ASSERT_NEAR(double(0.0), DataProv.ConsumeFloatingPoint(), 1e-10); + ASSERT_FLOAT_EQ(float(123.0), + DataProv.ConsumeFloatingPointInRange(123.0, 777.0)); + ASSERT_DOUBLE_EQ( + double(31.337 - 13.37), + DataProv.ConsumeFloatingPointInRange(-13.37, 31.337)); +} + int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS();