blob: 4376505cc0f8b556ccf803d30d480f4a705d03b7 [file] [log] [blame]
// 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.
#include "./fuzztest/internal/type_support.h"
#include <cassert>
#include <cctype>
#include <cmath>
#include <cstdint>
#include <limits>
#include <string>
#include "absl/strings/match.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
namespace fuzztest::internal {
namespace {
template <typename T>
void PrintFloatingPointLiteral(T v, RawSink out, absl::string_view suffix) {
// "%#.*gf" would get us a valid literal, but the result would be unreasonably
// long. Unfortunately, both the retention of a decimal point and the
// retention of trailing zeros are controlled by that flag for g, and that
// format specifier is needed to reasonably choose between decimal and
// exponential notation.
std::string num_string =
absl::StrFormat("%.*g", std::numeric_limits<T>::digits10 + 2, v);
bool needs_decimal = std::isfinite(v) &&
!absl::StrContains(num_string, '.') &&
!absl::StrContains(num_string, 'e');
bool needs_suffix = std::isfinite(v);
absl::Format(out, "%s%s%s", num_string, needs_decimal ? "." : "",
needs_suffix ? suffix : "");
}
} // namespace
void IntegralPrinter::PrintUserValue(bool v, RawSink out, PrintMode) const {
absl::Format(out, "%s", v ? "true" : "false");
}
void IntegralPrinter::PrintUserValue(uint64_t v, RawSink out, PrintMode) const {
absl::Format(out, "%d", v);
}
void IntegralPrinter::PrintUserValue(int64_t v, RawSink out, PrintMode) const {
absl::Format(out, "%d", v);
}
void IntegralPrinter::PrintUserValue(char v, RawSink out,
PrintMode mode) const {
auto unsigned_v = static_cast<uint8_t>(v);
switch (mode) {
case PrintMode::kHumanReadable:
if (std::isprint(v)) {
absl::Format(out, "'%c' (%d)", v, v);
} else {
absl::Format(out, "0x%02x (%u)", unsigned_v, unsigned_v);
}
break;
case PrintMode::kSourceCode:
if (std::isprint(v)) {
absl::Format(out, "'%c'", v);
} else {
absl::Format(out, "'\\%03o'", unsigned_v);
}
}
}
static void PrintNonFinite(double v, std::string_view nan,
std::string_view type, RawSink out) {
if (std::isnan(v)) {
absl::Format(out, "std::%s(\"\")", nan);
} else {
assert(std::isinf(v));
absl::Format(out, "%sstd::numeric_limits<%s>::infinity()", v > 0 ? "" : "-",
type);
}
}
void FloatingPrinter::PrintUserValue(float v, RawSink out,
PrintMode mode) const {
if (mode == PrintMode::kSourceCode && !std::isfinite(v)) {
return PrintNonFinite(v, "nanf", "float", out);
}
PrintFloatingPointLiteral(v, out, "f");
}
void FloatingPrinter::PrintUserValue(double v, RawSink out,
PrintMode mode) const {
if (mode == PrintMode::kSourceCode && !std::isfinite(v)) {
return PrintNonFinite(v, "nan", "double", out);
}
PrintFloatingPointLiteral(v, out, "");
}
void FloatingPrinter::PrintUserValue(long double v, RawSink out,
PrintMode mode) const {
if (mode == PrintMode::kSourceCode && !std::isfinite(v)) {
return PrintNonFinite(v, "nanl", "long double", out);
}
PrintFloatingPointLiteral(v, out, "L");
}
} // namespace fuzztest::internal