chromium/third_party/fuzztest/src/fuzztest/internal/domains/arbitrary_impl.h

// Copyright 2022 Google LLC
//
// 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 FUZZTEST_FUZZTEST_INTERNAL_DOMAINS_ARBITRARY_IMPL_H_
#define FUZZTEST_FUZZTEST_INTERNAL_DOMAINS_ARBITRARY_IMPL_H_

#include <array>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <memory>
#include <optional>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
#include <variant>
#include <vector>

#include "absl/random/bit_gen_ref.h"
#include "absl/random/distributions.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "./fuzztest/internal/coverage.h"
#include "./fuzztest/internal/domains/absl_helpers.h"
#include "./fuzztest/internal/domains/aggregate_of_impl.h"
#include "./fuzztest/internal/domains/container_of_impl.h"
#include "./fuzztest/internal/domains/domain.h"
#include "./fuzztest/internal/domains/domain_base.h"
#include "./fuzztest/internal/domains/element_of_impl.h"
#include "./fuzztest/internal/domains/in_range_impl.h"
#include "./fuzztest/internal/domains/map_impl.h"
#include "./fuzztest/internal/domains/one_of_impl.h"
#include "./fuzztest/internal/domains/optional_of_impl.h"
#include "./fuzztest/internal/domains/smart_pointer_of_impl.h"
#include "./fuzztest/internal/domains/value_mutation_helpers.h"
#include "./fuzztest/internal/domains/variant_of_impl.h"
#include "./fuzztest/internal/meta.h"
#include "./fuzztest/internal/serialization.h"
#include "./fuzztest/internal/status.h"
#include "./fuzztest/internal/table_of_recent_compares.h"
#include "./fuzztest/internal/type_support.h"

namespace fuzztest::internal {

// Fallback for error reporting, if T is not matched in Arbitrary<T>.
template <typename T, typename = void>
class ArbitraryImpl {};

// Arbitrary for monostate.
//
// For monostate types with a default constructor, just give the single value.
ArbitraryImpl<T, std::enable_if_t<is_monostate_v<T>>>;

// Arbitrary for bool.
template <>
class ArbitraryImpl<bool>
    : public domain_implementor::DomainBase<ArbitraryImpl<bool>> {};

// Arbitrary for integers.
ArbitraryImpl<T, std::enable_if_t<!std::is_const_v<T> && std::numeric_limits<T>::is_integer>>;

// Arbitrary for std::byte.
template <>
class ArbitraryImpl<std::byte>
    : public domain_implementor::DomainBase<ArbitraryImpl<std::byte>> {};

// Arbitrary for floats.
ArbitraryImpl<T, std::enable_if_t<std::is_floating_point_v<T>>>;

// Arbitrary for containers.
ArbitraryImpl<T, std::enable_if_t<always_true<T>, decltype(T().begin() , T().end() , T().size() , T().insert(T().end(), std::declval<value_type_t<T>>()) , T().erase(T().begin()) , (void)0)>>;

// Arbitrary for std::string_view.
//
// We define a separate container for string_view, to detect out of bounds bugs
// better. See below.
ArbitraryImpl<std::basic_string_view<Char>>;

#ifndef ABSL_USES_STD_STRING_VIEW
// Arbitrary for absl::string_view when it does not alias to std::string_view.
//
// We define a separate container for string_view, to detect out of bounds bugs
// better. See below.
template <>
class ArbitraryImpl<absl::string_view>
    : public domain_implementor::DomainBase<
          ArbitraryImpl<absl::string_view>, absl::string_view,
          // We use a vector to better manage the buffer and help
          // ASan find out-of-bounds bugs.
          std::vector<char>> {
 public:
  using typename ArbitraryImpl::DomainBase::corpus_type;
  using typename ArbitraryImpl::DomainBase::value_type;

  corpus_type Init(absl::BitGenRef prng) {
    if (auto seed = this->MaybeGetRandomSeed(prng)) return *seed;
    return inner_.Init(prng);
  }

  void Mutate(corpus_type& val, absl::BitGenRef prng, bool only_shrink) {
    inner_.Mutate(val, prng, only_shrink);
  }

  void UpdateMemoryDictionary(const corpus_type& val) {
    inner_.UpdateMemoryDictionary(val);
  }

  auto GetPrinter() const { return StringPrinter{}; }

  value_type GetValue(const corpus_type& value) const {
    return value_type(value.data(), value.size());
  }

  std::optional<corpus_type> FromValue(const value_type& value) const {
    return corpus_type(value.begin(), value.end());
  }

  std::optional<corpus_type> ParseCorpus(const IRObject& obj) const {
    return obj.ToCorpus<corpus_type>();
  }

  IRObject SerializeCorpus(const corpus_type& v) const {
    return IRObject::FromCorpus(v);
  }

  absl::Status ValidateCorpusValue(const corpus_type&) const {
    return absl::OkStatus();  // Nothing to validate.
  }

 private:
  ArbitraryImpl<std::vector<char>> inner_;
};
#endif

// Arbitrary for any const T.
//
// We capture const types as a workaround in order to enable
// Arbitrary<std::map<std::string, int>>() and similar container domains that
// require Arbitrary<std::pair< _const_ std::string, int>>() to be defined,
// which in turn requires Arbitrary<const std::string>() to be defined.
ArbitraryImpl<const T>;

// Arbitrary for user-defined aggregate types (structs/classes).

template <typename T, typename... Elem>
AggregateOfImpl<T, RequireCustomCorpusType::kYes, ArbitraryImpl<Elem>...>
    DetectAggregateOfImpl2(std::tuple<Elem&...>);

// Detect the number and types of the fields.
// TODO(sbenzaquen): Verify the compiler error in case we can't detect it and
// improve if possible.
template <typename T, int N = *DetectBindableFieldCount<T>()>
decltype(DetectAggregateOfImpl2<T>(
    BindAggregate(std::declval<T&>(), std::integral_constant<int, N>{};

ArbitraryImpl<T, std::enable_if_t<std::is_class_v<T> && std::is_aggregate_v<T> && !is_monostate_v<T> && !is_array_v<T>>>;

// Arbitrary for std::pair.
ArbitraryImpl<std::pair<T, U>>;

// Arbitrary for std::tuple.
ArbitraryImpl<std::tuple<T...>, std::enable_if_t<sizeof...(T) != 0>>;

// Arbitrary for std::array.
template <typename T, size_t N>
auto AggregateOfImplForArray() {}
ArbitraryImpl<std::array<T, N>>;

// Arbitrary for std::variant.
ArbitraryImpl<std::variant<T...>>;

// Arbitrary for std::optional.
ArbitraryImpl<std::optional<T>>;

// Used by Arbitrary std::unique_ptr / std::shared_ptr.
template <typename T>
const Domain<T>& GetGlobalDomainDefaultInstance() {}

// Arbitrary for std::unique_ptr.
ArbitraryImpl<std::unique_ptr<T>>;

// Arbitrary for std::shared_ptr.
ArbitraryImpl<std::shared_ptr<T>>;

// Arbitrary for absl::Duration.
template <>
class ArbitraryImpl<absl::Duration>
    : public OneOfImpl<
          ElementOfImpl<absl::Duration>,
          ReversibleMapImpl<absl::Duration (*)(int64_t, uint32_t),
                            std::optional<std::tuple<int64_t, uint32_t>> (*)(
                                absl::Duration),
                            ArbitraryImpl<int64_t>, InRangeImpl<uint32_t>>> {};

// Arbitrary for absl::Time.
template <>
class ArbitraryImpl<absl::Time>
    : public ReversibleMapImpl<absl::Time (*)(absl::Duration),
                               std::optional<std::tuple<absl::Duration>> (*)(
                                   absl::Time),
                               ArbitraryImpl<absl::Duration>> {};

}  // namespace fuzztest::internal

#endif  // FUZZTEST_FUZZTEST_INTERNAL_DOMAINS_ARBITRARY_IMPL_H_