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