1 // Copyright 2021 The Tint Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef FUZZERS_DATA_BUILDER_H_ 16 #define FUZZERS_DATA_BUILDER_H_ 17 18 #include <cassert> 19 #include <functional> 20 #include <string> 21 #include <unordered_map> 22 #include <vector> 23 24 #include "fuzzers/random_generator.h" 25 #include "src/writer/hlsl/generator.h" 26 #include "src/writer/msl/generator.h" 27 28 namespace tint { 29 namespace fuzzers { 30 31 /// Builder for generic pseudo-random data 32 class DataBuilder { 33 public: 34 /// @brief Initializes the internal engine using a seed value 35 /// @param seed - seed value passed to engine DataBuilder(uint64_t seed)36 explicit DataBuilder(uint64_t seed) : generator_(seed) {} 37 38 /// @brief Initializes the internal engine using seed data 39 /// @param data - data fuzzer to calculate seed from 40 /// @param size - size of data buffer DataBuilder(const uint8_t* data, size_t size)41 explicit DataBuilder(const uint8_t* data, size_t size) 42 : generator_(RandomGenerator::CalculateSeed(data, size)) { 43 assert(data != nullptr && "|data| must be !nullptr"); 44 } 45 46 ~DataBuilder() = default; 47 DataBuilder(DataBuilder&&) = default; 48 49 /// Generate pseudo-random data of a specific type 50 /// @tparam T - type of data to produce 51 /// @returns pseudo-random data of type T 52 template <typename T> build()53 T build() { 54 return BuildImpl<T>::impl(this); 55 } 56 57 /// Generate pseudo-random data of a specific type in a vector 58 /// @tparam T - data type held vector 59 /// @returns pseudo-random data of type std::vector<T> 60 template <typename T> vector()61 std::vector<T> vector() { 62 auto count = build<uint8_t>(); 63 std::vector<T> out(count); 64 for (uint8_t i = 0; i < count; i++) { 65 out[i] = build<T>(); 66 } 67 return out; 68 } 69 70 /// Generate complex pseudo-random data of a specific type in a vector 71 /// @tparam T - data type held vector 72 /// @tparam Callback - callback that takes in a DataBuilder* and returns a T 73 /// @param generate - callback for generating each instance of T 74 /// @returns pseudo-random data of type std::vector<T> 75 template <typename T, typename Callback> vector(Callback generate)76 std::vector<T> vector(Callback generate) { 77 auto count = build<uint8_t>(); 78 std::vector<T> out(count); 79 for (size_t i = 0; i < count; i++) { 80 out[i] = generate(this); 81 } 82 return out; 83 } 84 85 /// Generate an pseudo-random entry to a enum class. 86 /// Assumes enum is tightly packed starting at 0. 87 /// @tparam T - type of enum class 88 /// @param count - number of entries in enum class 89 /// @returns a random enum class entry 90 template <typename T> enum_class(uint32_t count)91 T enum_class(uint32_t count) { 92 return static_cast<T>(generator_.Get4Bytes() % count); 93 } 94 95 private: 96 RandomGenerator generator_; 97 98 // Disallow copy & assign 99 DataBuilder(const DataBuilder&) = delete; 100 DataBuilder& operator=(const DataBuilder&) = delete; 101 102 /// Get N bytes of pseudo-random data 103 /// @param out - pointer to location to save data 104 /// @param n - number of bytes to get build(void* out, size_t n)105 void build(void* out, size_t n) { 106 assert(out != nullptr && "|out| cannot be nullptr"); 107 assert(n > 0 && "|n| must be > 0"); 108 109 generator_.GetNBytes(reinterpret_cast<uint8_t*>(out), n); 110 } 111 112 /// Generate pseudo-random data of a specific type into an output var 113 /// @tparam T - type of data to produce 114 /// @param out - output var to generate into 115 template <typename T> build(T& out)116 void build(T& out) { 117 out = build<T>(); 118 } 119 120 /// Implementation of ::build<T>() 121 /// @tparam T - type of data to produce 122 template <typename T> 123 struct BuildImpl { 124 /// Generate a pseudo-random variable of type T 125 /// @param b - data builder to use 126 /// @returns a variable of type T filled with pseudo-random data impltint::fuzzers::DataBuilder::BuildImpl127 static T impl(DataBuilder* b) { 128 T out{}; 129 b->build(&out, sizeof(T)); 130 return out; 131 } 132 }; 133 134 /// Specialization for std::string 135 template <> 136 struct BuildImpl<std::string> { 137 /// Generate a pseudo-random string 138 /// @param b - data builder to use 139 /// @returns a string filled with pseudo-random data impltint::fuzzers::DataBuilder::BuildImpl140 static std::string impl(DataBuilder* b) { 141 auto count = b->build<uint8_t>(); 142 if (count == 0) { 143 return ""; 144 } 145 std::vector<uint8_t> source(count); 146 b->build(source.data(), count); 147 return {source.begin(), source.end()}; 148 } 149 }; 150 151 /// Specialization for bool 152 template <> 153 struct BuildImpl<bool> { 154 /// Generate a pseudo-random bool 155 /// @param b - data builder to use 156 /// @returns a boolean with even odds of being true or false impltint::fuzzers::DataBuilder::BuildImpl157 static bool impl(DataBuilder* b) { return b->generator_.GetBool(); } 158 }; 159 160 /// Specialization for writer::msl::Options 161 template <> 162 struct BuildImpl<writer::msl::Options> { 163 /// Generate a pseudo-random writer::msl::Options struct 164 /// @param b - data builder to use 165 /// @returns writer::msl::Options filled with pseudo-random data impltint::fuzzers::DataBuilder::BuildImpl166 static writer::msl::Options impl(DataBuilder* b) { 167 writer::msl::Options out{}; 168 b->build(out.buffer_size_ubo_index); 169 b->build(out.fixed_sample_mask); 170 b->build(out.emit_vertex_point_size); 171 b->build(out.disable_workgroup_init); 172 b->build(out.array_length_from_uniform); 173 return out; 174 } 175 }; 176 177 /// Specialization for writer::hlsl::Options 178 template <> 179 struct BuildImpl<writer::hlsl::Options> { 180 /// Generate a pseudo-random writer::hlsl::Options struct 181 /// @param b - data builder to use 182 /// @returns writer::hlsl::Options filled with pseudo-random data impltint::fuzzers::DataBuilder::BuildImpl183 static writer::hlsl::Options impl(DataBuilder* b) { 184 writer::hlsl::Options out{}; 185 b->build(out.root_constant_binding_point); 186 b->build(out.disable_workgroup_init); 187 b->build(out.array_length_from_uniform); 188 return out; 189 } 190 }; 191 192 /// Specialization for writer::ArrayLengthFromUniformOptions 193 template <> 194 struct BuildImpl<writer::ArrayLengthFromUniformOptions> { 195 /// Generate a pseudo-random writer::ArrayLengthFromUniformOptions struct 196 /// @param b - data builder to use 197 /// @returns writer::ArrayLengthFromUniformOptions filled with pseudo-random 198 /// data impltint::fuzzers::DataBuilder::BuildImpl199 static writer::ArrayLengthFromUniformOptions impl(DataBuilder* b) { 200 writer::ArrayLengthFromUniformOptions out{}; 201 b->build(out.ubo_binding); 202 b->build(out.bindpoint_to_size_index); 203 return out; 204 } 205 }; 206 207 /// Specialization for std::unordered_map<K, V> 208 template <typename K, typename V> 209 struct BuildImpl<std::unordered_map<K, V>> { 210 /// Generate a pseudo-random std::unordered_map<K, V> 211 /// @param b - data builder to use 212 /// @returns std::unordered_map<K, V> filled with 213 /// pseudo-random data impltint::fuzzers::DataBuilder::BuildImpl214 static std::unordered_map<K, V> impl(DataBuilder* b) { 215 std::unordered_map<K, V> out; 216 uint8_t count = b->build<uint8_t>(); 217 for (uint8_t i = 0; i < count; ++i) { 218 out.emplace(b->build<K>(), b->build<V>()); 219 } 220 return out; 221 } 222 }; 223 }; 224 225 } // namespace fuzzers 226 } // namespace tint 227 228 #endif // FUZZERS_DATA_BUILDER_H_ 229