// 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 #include #include #include #include #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 T build() { return BuildImpl::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 template std::vector vector() { auto count = build(); std::vector out(count); for (uint8_t i = 0; i < count; i++) { out[i] = build(); } 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 template std::vector vector(Callback generate) { auto count = build(); std::vector 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 T enum_class(uint32_t count) { return static_cast(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(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 void build(T& out) { out = build(); } /// Implementation of ::build() /// @tparam T - type of data to produce template 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 { /// 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(); if (count == 0) { return ""; } std::vector source(count); b->build(source.data(), count); return {source.begin(), source.end()}; } }; /// Specialization for bool template <> struct BuildImpl { /// 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 { /// 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 { /// 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 { /// 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 { /// 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 template struct BuildImpl> { /// Generate a pseudo-random std::unordered_map /// @param b - data builder to use /// @returns std::unordered_map filled with /// pseudo-random data static std::unordered_map impl(DataBuilder* b) { std::unordered_map out; uint8_t count = b->build(); for (uint8_t i = 0; i < count; ++i) { out.emplace(b->build(), b->build()); } return out; } }; }; } // namespace fuzzers } // namespace tint #endif // FUZZERS_DATA_BUILDER_H_