random_generator.cpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. // Copyright (c) 2021 Google Inc.
  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. #include "test/fuzzers/random_generator.h"
  15. #include <algorithm>
  16. #include <array>
  17. #include <cassert>
  18. namespace spvtools {
  19. namespace fuzzers {
  20. namespace {
  21. /// Generate integer from uniform distribution
  22. /// @tparam I - integer type
  23. /// @param engine - random number engine to use
  24. /// @param lower - Lower bound of integer generated
  25. /// @param upper - Upper bound of integer generated
  26. /// @returns i, where lower <= i < upper
  27. template <typename I>
  28. I RandomUInt(std::mt19937_64* engine, I lower, I upper) {
  29. assert(lower < upper && "|lower| must be stictly less than |upper|");
  30. return std::uniform_int_distribution<I>(lower, upper - 1)(*engine);
  31. }
  32. /// Helper for obtaining a seed bias value for HashCombine with a bit-width
  33. /// dependent on the size of size_t.
  34. template <int SIZE_OF_SIZE_T>
  35. struct HashCombineOffset {};
  36. /// Specialization of HashCombineOffset for size_t == 4.
  37. template <>
  38. struct HashCombineOffset<4> {
  39. /// @returns the seed bias value for HashCombine()
  40. static constexpr inline uint32_t value() {
  41. return 0x9e3779b9; // Fractional portion of Golden Ratio, suggested by
  42. // Linux Kernel and Knuth's Art of Computer Programming
  43. }
  44. };
  45. /// Specialization of HashCombineOffset for size_t == 8.
  46. template <>
  47. struct HashCombineOffset<8> {
  48. /// @returns the seed bias value for HashCombine()
  49. static constexpr inline uint64_t value() {
  50. return 0x9e3779b97f4a7c16; // Fractional portion of Golden Ratio, suggested
  51. // by Linux Kernel and Knuth's Art of Computer
  52. // Programming
  53. }
  54. };
  55. /// HashCombine "hashes" together an existing hash and hashable values.
  56. template <typename T>
  57. void HashCombine(size_t* hash, const T& value) {
  58. constexpr size_t offset = HashCombineOffset<sizeof(size_t)>::value();
  59. *hash ^= std::hash<T>()(value) + offset + (*hash << 6) + (*hash >> 2);
  60. }
  61. /// Calculate the hash for the contents of a C-style data buffer
  62. /// @param data - pointer to buffer to be hashed
  63. /// @param size - number of elements in buffer
  64. /// @returns hash of the data in the buffer
  65. size_t HashBuffer(const uint8_t* data, const size_t size) {
  66. size_t hash =
  67. static_cast<size_t>(0xCA8945571519E991); // seed with an arbitrary prime
  68. HashCombine(&hash, size);
  69. for (size_t i = 0; i < size; i++) {
  70. HashCombine(&hash, data[i]);
  71. }
  72. return hash;
  73. }
  74. } // namespace
  75. RandomGenerator::RandomGenerator(uint64_t seed) : engine_(seed) {}
  76. RandomGenerator::RandomGenerator(const uint8_t* data, size_t size) {
  77. RandomGenerator(RandomGenerator::CalculateSeed(data, size));
  78. }
  79. spv_target_env RandomGenerator::GetTargetEnv() {
  80. spv_target_env result;
  81. // Need to check that the generated value isn't for a deprecated target env.
  82. do {
  83. result = static_cast<spv_target_env>(
  84. RandomUInt(&engine_, 0u, static_cast<unsigned int>(SPV_ENV_MAX)));
  85. } while (!spvIsValidEnv(result));
  86. return result;
  87. }
  88. uint32_t RandomGenerator::GetUInt32(uint32_t lower, uint32_t upper) {
  89. return RandomUInt(&engine_, lower, upper);
  90. }
  91. uint32_t RandomGenerator::GetUInt32(uint32_t bound) {
  92. assert(bound > 0 && "|bound| must be greater than 0");
  93. return RandomUInt(&engine_, 0u, bound);
  94. }
  95. uint64_t RandomGenerator::CalculateSeed(const uint8_t* data, size_t size) {
  96. assert(data != nullptr && "|data| must be !nullptr");
  97. // Number of bytes we want to skip at the start of data for the hash.
  98. // Fewer bytes may be skipped when `size` is small.
  99. // Has lower precedence than kHashDesiredMinBytes.
  100. static const int64_t kHashDesiredLeadingSkipBytes = 5;
  101. // Minimum number of bytes we want to use in the hash.
  102. // Used for short buffers.
  103. static const int64_t kHashDesiredMinBytes = 4;
  104. // Maximum number of bytes we want to use in the hash.
  105. static const int64_t kHashDesiredMaxBytes = 32;
  106. int64_t size_i64 = static_cast<int64_t>(size);
  107. int64_t hash_begin_i64 =
  108. std::min(kHashDesiredLeadingSkipBytes,
  109. std::max<int64_t>(size_i64 - kHashDesiredMinBytes, 0));
  110. int64_t hash_end_i64 =
  111. std::min(hash_begin_i64 + kHashDesiredMaxBytes, size_i64);
  112. size_t hash_begin = static_cast<size_t>(hash_begin_i64);
  113. size_t hash_size = static_cast<size_t>(hash_end_i64) - hash_begin;
  114. return HashBuffer(data + hash_begin, hash_size);
  115. }
  116. } // namespace fuzzers
  117. } // namespace spvtools