| /* | |
| * Copyright 2021 WebAssembly Community Group participants | |
| * | |
| * 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 wasm_tools_fuzzing_random_h | |
| #define wasm_tools_fuzzing_random_h | |
| #include <cstdint> | |
| #include <map> | |
| #include <vector> | |
| #include "wasm-features.h" | |
| #include "wasm-type.h" | |
| namespace wasm { | |
| class Random { | |
| // The input seed bytes. | |
| std::vector<char> bytes; | |
| // The current position in `bytes`. | |
| size_t pos = 0; | |
| // Whether we already cycled through all the input (which might mean we should | |
| // try to finish things off). | |
| bool finishedInput = false; | |
| // After we finish the input, we start going through it again, but xoring | |
| // so it's not identical. | |
| unsigned int xorFactor = 0; | |
| // Features used for picking among FeatureOptions. | |
| FeatureSet features; | |
| public: | |
| Random(std::vector<char>&& bytes, FeatureSet features); | |
| Random(std::vector<char>&& bytes) | |
| : Random(std::move(bytes), FeatureSet::All) {} | |
| // Methods for getting random data. | |
| int8_t get(); | |
| int16_t get16(); | |
| int32_t get32(); | |
| int64_t get64(); | |
| float getFloat(); | |
| double getDouble(); | |
| // Choose an integer value in [0, x). This doesn't use a perfectly uniform | |
| // distribution, but it's fast and reasonable. upTo(0) is defined as 0. | |
| uint32_t upTo(uint32_t x); | |
| // Returns true with probability 1 in x. oneIn(0) is defined as true. | |
| bool oneIn(uint32_t x) { return upTo(x) == 0; } | |
| // Apply upTo twice, generating a skewed distribution towards | |
| // low values. | |
| uint32_t upToSquared(uint32_t x) { return upTo(upTo(x)); } | |
| bool finished() { return finishedInput; } | |
| // How many bytes of data remain to be used. | |
| size_t remaining() { | |
| if (finishedInput) { | |
| // We finished it and are cycling through it again (using xorFactor to try | |
| // to improve the entropy). | |
| return 0; | |
| } | |
| return bytes.size() - pos; | |
| } | |
| // Pick from a vector-like container | |
| template<typename T> const typename T::value_type& pick(const T& vec) { | |
| assert(!vec.empty()); | |
| auto index = upTo(vec.size()); | |
| return vec[index]; | |
| } | |
| // Pick from a fixed list | |
| template<typename T, typename... Args> T pick(T first, Args... args) { | |
| auto num = sizeof...(Args) + 1; | |
| auto temp = upTo(num); | |
| return pickGivenNum<T>(temp, first, args...); | |
| } | |
| template<typename T> T pickGivenNum(size_t num, T first) { | |
| assert(num == 0); | |
| return first; | |
| } | |
| // Trick to avoid a bug in GCC 7.x. | |
| // Upstream bug report: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82800 | |
| #define GCC_VERSION \ | |
| (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) | |
| #if GCC_VERSION > 70000 && GCC_VERSION < 70300 | |
| #pragma GCC diagnostic push | |
| #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" | |
| #endif | |
| template<typename T, typename... Args> | |
| T pickGivenNum(size_t num, T first, Args... args) { | |
| if (num == 0) { | |
| return first; | |
| } | |
| return pickGivenNum<T>(num - 1, args...); | |
| } | |
| #if GCC_VERSION > 70000 && GCC_VERSION < 70300 | |
| #pragma GCC diagnostic pop | |
| #endif | |
| template<typename T> struct FeatureOptions { | |
| FeatureOptions<T>& add(HeapType::BasicHeapType option) { | |
| // Using FeatureOptions with BasicHeapTypes is risky as BasicHeapType | |
| // is an enum, which can convert into FeatureSet implicitly (which would | |
| // then be ambiguous with add(FeatureSet) below). Use HeapType instead. | |
| // | |
| // Use a weird static assert on something other than |false| because of | |
| // https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2593r1.html | |
| // (older compilers may error without this workaround). | |
| static_assert(sizeof(T) == 0); | |
| } | |
| // An option without a feature is applied in all cases. | |
| FeatureOptions<T>& add(T option) { | |
| options[FeatureSet::MVP].push_back(option); | |
| return *this; | |
| } | |
| template<typename... Ts> | |
| FeatureOptions<T>& add(FeatureSet feature, T option, Ts... rest) { | |
| options[feature].push_back(option); | |
| return add(feature, rest...); | |
| } | |
| struct WeightedOption { | |
| T option; | |
| size_t weight; | |
| }; | |
| template<typename... Ts> | |
| FeatureOptions<T>& | |
| add(FeatureSet feature, WeightedOption weightedOption, Ts... rest) { | |
| for (size_t i = 0; i < weightedOption.weight; i++) { | |
| options[feature].push_back(weightedOption.option); | |
| } | |
| return add(feature, rest...); | |
| } | |
| FeatureOptions<T>& add(FeatureSet feature) { return *this; } | |
| std::map<FeatureSet, std::vector<T>> options; | |
| }; | |
| template<typename T> std::vector<T> items(FeatureOptions<T>& picker) { | |
| std::vector<T> matches; | |
| for (const auto& item : picker.options) { | |
| if (features.has(item.first)) { | |
| matches.reserve(matches.size() + item.second.size()); | |
| matches.insert(matches.end(), item.second.begin(), item.second.end()); | |
| } | |
| } | |
| return matches; | |
| } | |
| template<typename T> const T pick(FeatureOptions<T>& picker) { | |
| return pick(items(picker)); | |
| } | |
| }; | |
| } // namespace wasm | |
| #endif // wasm_tools_fuzzing_random_h |