dawn-cmake/fuzzers/data_builder.h
Ryan Harrison dcb24cea41 Provide build override for SPIRV options
Without this, the fuzzing framework will fall back to the default and
copies in random bits without consideration of range restrictions on
specific fields.

BUG=chromium:1287344

Change-Id: Ifbde471074a2f68e1d9fd8215174814d2f465f93
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/76880
Auto-Submit: Ryan Harrison <rharrison@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Ryan Harrison <rharrison@chromium.org>
2022-01-18 15:37:56 +00:00

243 lines
7.7 KiB
C++

// Copyright 2021 The Tint Authors.
//
// 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 FUZZERS_DATA_BUILDER_H_
#define FUZZERS_DATA_BUILDER_H_
#include <cassert>
#include <functional>
#include <string>
#include <unordered_map>
#include <vector>
#include "fuzzers/random_generator.h"
#include "src/writer/hlsl/generator.h"
#include "src/writer/msl/generator.h"
namespace tint {
namespace fuzzers {
/// Builder for generic pseudo-random data
class DataBuilder {
public:
/// @brief Initializes the internal engine using a seed value
/// @param seed - seed value passed to engine
explicit DataBuilder(uint64_t seed) : generator_(seed) {}
/// @brief Initializes the internal engine using seed data
/// @param data - data fuzzer to calculate seed from
/// @param size - size of data buffer
explicit DataBuilder(const uint8_t* data, size_t size)
: generator_(RandomGenerator::CalculateSeed(data, size)) {
assert(data != nullptr && "|data| must be !nullptr");
}
~DataBuilder() = default;
DataBuilder(DataBuilder&&) = default;
/// Generate pseudo-random data of a specific type
/// @tparam T - type of data to produce
/// @returns pseudo-random data of type T
template <typename T>
T build() {
return BuildImpl<T>::impl(this);
}
/// Generate pseudo-random data of a specific type in a vector
/// @tparam T - data type held vector
/// @returns pseudo-random data of type std::vector<T>
template <typename T>
std::vector<T> vector() {
auto count = build<uint8_t>();
std::vector<T> out(count);
for (uint8_t i = 0; i < count; i++) {
out[i] = build<T>();
}
return out;
}
/// Generate complex pseudo-random data of a specific type in a vector
/// @tparam T - data type held vector
/// @tparam Callback - callback that takes in a DataBuilder* and returns a T
/// @param generate - callback for generating each instance of T
/// @returns pseudo-random data of type std::vector<T>
template <typename T, typename Callback>
std::vector<T> vector(Callback generate) {
auto count = build<uint8_t>();
std::vector<T> out(count);
for (size_t i = 0; i < count; i++) {
out[i] = generate(this);
}
return out;
}
/// Generate an pseudo-random entry to a enum class.
/// Assumes enum is tightly packed starting at 0.
/// @tparam T - type of enum class
/// @param count - number of entries in enum class
/// @returns a random enum class entry
template <typename T>
T enum_class(uint32_t count) {
return static_cast<T>(generator_.Get4Bytes() % count);
}
private:
RandomGenerator generator_;
// Disallow copy & assign
DataBuilder(const DataBuilder&) = delete;
DataBuilder& operator=(const DataBuilder&) = delete;
/// Get N bytes of pseudo-random data
/// @param out - pointer to location to save data
/// @param n - number of bytes to get
void build(void* out, size_t n) {
assert(out != nullptr && "|out| cannot be nullptr");
assert(n > 0 && "|n| must be > 0");
generator_.GetNBytes(reinterpret_cast<uint8_t*>(out), n);
}
/// Generate pseudo-random data of a specific type into an output var
/// @tparam T - type of data to produce
/// @param out - output var to generate into
template <typename T>
void build(T& out) {
out = build<T>();
}
/// Implementation of ::build<T>()
/// @tparam T - type of data to produce
template <typename T>
struct BuildImpl {
/// Generate a pseudo-random variable of type T
/// @param b - data builder to use
/// @returns a variable of type T filled with pseudo-random data
static T impl(DataBuilder* b) {
T out{};
b->build(&out, sizeof(T));
return out;
}
};
/// Specialization for std::string
template <>
struct BuildImpl<std::string> {
/// Generate a pseudo-random string
/// @param b - data builder to use
/// @returns a string filled with pseudo-random data
static std::string impl(DataBuilder* b) {
auto count = b->build<uint8_t>();
if (count == 0) {
return "";
}
std::vector<uint8_t> source(count);
b->build(source.data(), count);
return {source.begin(), source.end()};
}
};
/// Specialization for bool
template <>
struct BuildImpl<bool> {
/// Generate a pseudo-random bool
/// @param b - data builder to use
/// @returns a boolean with even odds of being true or false
static bool impl(DataBuilder* b) { return b->generator_.GetBool(); }
};
/// Specialization for writer::msl::Options
template <>
struct BuildImpl<writer::msl::Options> {
/// Generate a pseudo-random writer::msl::Options struct
/// @param b - data builder to use
/// @returns writer::msl::Options filled with pseudo-random data
static writer::msl::Options impl(DataBuilder* b) {
writer::msl::Options out{};
b->build(out.buffer_size_ubo_index);
b->build(out.fixed_sample_mask);
b->build(out.emit_vertex_point_size);
b->build(out.disable_workgroup_init);
b->build(out.array_length_from_uniform);
return out;
}
};
/// Specialization for writer::hlsl::Options
template <>
struct BuildImpl<writer::hlsl::Options> {
/// Generate a pseudo-random writer::hlsl::Options struct
/// @param b - data builder to use
/// @returns writer::hlsl::Options filled with pseudo-random data
static writer::hlsl::Options impl(DataBuilder* b) {
writer::hlsl::Options out{};
b->build(out.root_constant_binding_point);
b->build(out.disable_workgroup_init);
b->build(out.array_length_from_uniform);
return out;
}
};
/// Specialization for writer::spirv::Options
template <>
struct BuildImpl<writer::spirv::Options> {
/// Generate a pseudo-random writer::spirv::Options struct
/// @param b - data builder to use
/// @returns writer::spirv::Options filled with pseudo-random data
static writer::spirv::Options impl(DataBuilder* b) {
writer::spirv::Options out{};
b->build(out.emit_vertex_point_size);
b->build(out.disable_workgroup_init);
return out;
}
};
/// Specialization for writer::ArrayLengthFromUniformOptions
template <>
struct BuildImpl<writer::ArrayLengthFromUniformOptions> {
/// Generate a pseudo-random writer::ArrayLengthFromUniformOptions struct
/// @param b - data builder to use
/// @returns writer::ArrayLengthFromUniformOptions filled with pseudo-random
/// data
static writer::ArrayLengthFromUniformOptions impl(DataBuilder* b) {
writer::ArrayLengthFromUniformOptions out{};
b->build(out.ubo_binding);
b->build(out.bindpoint_to_size_index);
return out;
}
};
/// Specialization for std::unordered_map<K, V>
template <typename K, typename V>
struct BuildImpl<std::unordered_map<K, V>> {
/// Generate a pseudo-random std::unordered_map<K, V>
/// @param b - data builder to use
/// @returns std::unordered_map<K, V> filled with
/// pseudo-random data
static std::unordered_map<K, V> impl(DataBuilder* b) {
std::unordered_map<K, V> out;
uint8_t count = b->build<uint8_t>();
for (uint8_t i = 0; i < count; ++i) {
out.emplace(b->build<K>(), b->build<V>());
}
return out;
}
};
};
} // namespace fuzzers
} // namespace tint
#endif // FUZZERS_DATA_BUILDER_H_