blob: ca5430a14f33fd6b9cc61b2b855e8de011f6fcc9 [file] [log] [blame]
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef FUZZTEST_FUZZTEST_INTERNAL_FUZZING_MOCK_STREAM_H_
#define FUZZTEST_FUZZTEST_INTERNAL_FUZZING_MOCK_STREAM_H_
#include <cstddef>
#include <cstdint>
#include <limits>
#include "absl/functional/function_ref.h"
#include "absl/numeric/bits.h"
#include "absl/types/span.h"
namespace fuzztest::internal {
class FuzzingMockStream {
public:
// DataStreamFn copies up to n bytes from the data stream to the buffer
// pointer.
using DataStreamFn = absl::FunctionRef<void(void*, size_t)>;
// Instruction is used to select an implementation-specific random number
// variate when invoking the fuzztest mock.
enum class Instruction : uint8_t {
// Use the data stream to generate the random number.
kDataStreamVariate = 0,
// Use the LCG algorithm to generate the random number.
kLCGVariate = 1,
kMin = 2, // Return the minimum value of the distribution.
kMax = 3, // Return the maximum value of the distribution.
kMean = 4, // Return the mean of the distribution.
// Return an alternate value of the distribution. This is typically an edge
// case value that is unlikely to be returned by the normal distribution.
kAlternateVariate = 5,
};
FuzzingMockStream(DataStreamFn data_stream_fn, Instruction instruction)
: data_stream_fn_(data_stream_fn), instruction_(instruction) {}
static Instruction GetNextInstruction(
absl::Span<const uint8_t>& control_stream) {
if (control_stream.empty()) {
return Instruction::kDataStreamVariate;
}
uint8_t v = control_stream[0];
control_stream.remove_prefix(1);
return static_cast<Instruction>(v % 6);
}
Instruction instruction() { return instruction_; }
void get_bytes(void* result, size_t result_size) {
data_stream_fn_(result, result_size);
}
template <typename T>
T get_int_value() {
T x = 0;
data_stream_fn_(&x, sizeof(x));
return x;
}
// Consumes bytes from the data stream to generate a random integer in the
// range [0, range].
template <typename T>
T get_int_value_in_range(uint64_t range) {
if (range == 0) {
return 0;
}
uint64_t x = 0;
if (range <= (std::numeric_limits<uint8_t>::max)()) {
x = get_int_value<uint8_t>();
} else if (range <= (std::numeric_limits<uint16_t>::max)()) {
x = get_int_value<uint16_t>();
} else if (range <= (std::numeric_limits<uint32_t>::max)()) {
x = get_int_value<uint32_t>();
} else {
x = get_int_value<uint64_t>();
}
if (range == std::numeric_limits<uint64_t>::max() ||
absl::has_single_bit(range + 1)) {
return static_cast<T>(x & range); // range is a mask of 2^N-1
} else {
return static_cast<T>(x % (range + 1));
}
}
// URBG interface.
using result_type = uint64_t;
static constexpr result_type(min)() {
return (std::numeric_limits<result_type>::min)();
}
static constexpr result_type(max)() {
return (std::numeric_limits<result_type>::max)();
}
void reset() {}
uint64_t operator()() { return get_int_value<uint64_t>(); }
private:
DataStreamFn data_stream_fn_;
Instruction instruction_;
};
// TypeErasedFuzzFunctionT(fuzzing_mock_stream, args_tuple, result)
// is a type erased function pointer for use with absl::MockingBitGen and
// fuzztest mocking.
using TypeErasedFuzzFunctionT = void (*)(FuzzingMockStream, void*, void*);
} // namespace fuzztest::internal
#endif // FUZZTEST_FUZZTEST_INTERNAL_FUZZING_MOCK_STREAM_H_