- Vulkan Configuration Implementations - Fixed build errors on GCC and Clang
303 lines
8.4 KiB
C++
303 lines
8.4 KiB
C++
// =====================================================================================================================
|
|
// fennec, a free and open source game engine
|
|
// Copyright © 2025 - 2026 Medusa Slockbower
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
// =====================================================================================================================
|
|
|
|
///
|
|
/// \file fennec/format/formatter.h
|
|
/// \brief
|
|
///
|
|
///
|
|
/// \details
|
|
/// \author Medusa Slockbower
|
|
///
|
|
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
|
///
|
|
///
|
|
|
|
#ifndef FENNEC_FORMAT_FORMATTER_H
|
|
#define FENNEC_FORMAT_FORMATTER_H
|
|
|
|
#include <fennec/format/charconv.h>
|
|
#include <fennec/string/string.h>
|
|
#include <fennec/format/format_arg.h>
|
|
|
|
namespace fennec
|
|
{
|
|
|
|
// base template =======================================================================================================
|
|
|
|
///
|
|
/// \brief Formatter struct, used to turn values into formatted strings
|
|
/// \tparam T The type to format
|
|
template<typename T>
|
|
struct formatter {
|
|
///
|
|
/// \brief default format function
|
|
///
|
|
/// \details Fails an assertion, the formatter is not implemented for the provided type
|
|
/// \returns empty string
|
|
string operator()(const format_arg&, const T&) const {
|
|
static_assert(false, "Formatter not implemented for the provided type.");
|
|
return string("");
|
|
}
|
|
};
|
|
|
|
|
|
// strings =============================================================================================================
|
|
|
|
// TODO: String formatting
|
|
|
|
///
|
|
/// \brief formatter of a character array
|
|
/// \tparam N the number of characters
|
|
template<size_t N>
|
|
struct formatter<char[N]> {
|
|
///
|
|
/// \brief format function
|
|
/// \param str the string argument
|
|
/// \returns the formatted version of \emph{str}
|
|
string operator()(const format_arg&, const char (&str)[N]) const {
|
|
return string(str);
|
|
}
|
|
};
|
|
|
|
///
|
|
/// \brief formatter of a character array
|
|
/// \tparam N the number of characters
|
|
template<size_t N>
|
|
struct formatter<const char[N]> {
|
|
///
|
|
/// \brief format function
|
|
/// \param str the string argument
|
|
/// \returns the formatted version of \emph{str}
|
|
string operator()(const format_arg&, const char (&str)[N]) const {
|
|
return string(str);
|
|
}
|
|
};
|
|
|
|
///
|
|
/// \brief formatter of a C-Style string
|
|
template<>
|
|
struct formatter<cstring> {
|
|
///
|
|
/// \brief format function
|
|
/// \param str the string argument
|
|
/// \returns the formatted version of \emph{str}
|
|
string operator()(const format_arg&, const cstring& str) const {
|
|
return str;
|
|
}
|
|
};
|
|
|
|
///
|
|
/// \brief formatter of a string
|
|
template<>
|
|
struct formatter<string> {
|
|
///
|
|
/// \brief format function
|
|
/// \param str the string argument
|
|
/// \returns the formatted version of \emph{str}
|
|
string operator()(const format_arg&, const string& str) const {
|
|
return str;
|
|
}
|
|
};
|
|
|
|
|
|
// decimal types =======================================================================================================
|
|
|
|
///
|
|
/// \brief formatter of integral types
|
|
template<typename IntT> requires(is_integral_v<IntT> and not is_bool_v<IntT>)
|
|
struct formatter<IntT> {
|
|
///
|
|
/// \brief format function
|
|
/// \param fmt the format specification
|
|
/// \param x the integral argument
|
|
/// \returns the formatted version of \emph{x}
|
|
string operator()(const format_arg& fmt, IntT x) const {
|
|
char digits[128] = {};
|
|
auto chk = fennec::to_chars(digits, digits + sizeof(digits), fennec::abs(x), fmt.base);
|
|
assertf(chk != nullptr, "fennec::format error, to_chars error");
|
|
const size_t len = chk - digits;
|
|
|
|
// handle uppercase
|
|
if (fmt.upper) {
|
|
for (auto& digit : digits) {
|
|
if (digit == 0) {
|
|
break;
|
|
}
|
|
digit = toupper(digit);
|
|
}
|
|
}
|
|
|
|
const bool has_sign = (x < 0 or fmt.sign != '-');
|
|
const bool zero = fmt.fill == '0';
|
|
const size_t prefix = fmt.alt ? (fmt.type == 'd' ? 0 : 2 - (fmt.type == 'o')) : 0;
|
|
const size_t sgnlen = len + (zero ? has_sign + prefix : 0);
|
|
const size_t explen = fennec::max(sgnlen, fmt.width) + (zero ? 0 : has_sign + prefix);
|
|
const size_t fill = fmt.width > sgnlen ? fmt.width - sgnlen : 0;
|
|
size_t sign = 0;
|
|
|
|
string res = string(explen);
|
|
|
|
if (fill > 0) {
|
|
switch (fmt.align) {
|
|
case '<':
|
|
memcpy(res.data() + has_sign + prefix, digits, len);
|
|
memset(res.data() + has_sign + prefix + len, fmt.fill == '0' ? ' ' : fmt.fill, fill);
|
|
break;
|
|
case '>': case '\0':
|
|
memcpy(res.data() + explen - len, digits, len);
|
|
sign = fmt.fill == '0' ? 0 : explen - len - 1 - prefix;
|
|
memset(res.data(), fmt.fill, explen - len);
|
|
break;
|
|
case '^':
|
|
const size_t bef = fill / 2 + has_sign + prefix;
|
|
const size_t aft = explen - bef;
|
|
memcpy(res.data() + bef, digits, len);
|
|
sign = fmt.fill == '0' ? 0 : bef - 1 - prefix;
|
|
memset(res.data(), fmt.fill, bef);
|
|
memset(res.data() + bef + len, fmt.fill == '0' ? ' ' : fmt.fill, aft);
|
|
break;
|
|
|
|
|
|
}
|
|
} else {
|
|
memcpy(res.data() + has_sign + prefix, digits, len);
|
|
}
|
|
|
|
if (has_sign) {
|
|
res[sign] = (x < 0) ? '-' : fmt.sign;
|
|
}
|
|
|
|
if (prefix) {
|
|
res[sign + has_sign] = '0';
|
|
if (fmt.type != 'o') {
|
|
res[sign + has_sign + 1] = fmt.type;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
};
|
|
|
|
///
|
|
/// \brief formatter of boolean types
|
|
template<typename BoolT> requires(is_bool_v<BoolT>)
|
|
struct formatter<BoolT> {
|
|
///
|
|
/// \brief format function
|
|
/// \param fmt the format specification
|
|
/// \param x the boolean argument
|
|
/// \returns the formatted version of \emph{x}
|
|
string operator()(const format_arg& fmt, BoolT x) const {
|
|
if (fmt.type == 's' or fmt.type == '\0') {
|
|
return x ? string("true") : string("false");
|
|
}
|
|
|
|
return formatter<uint8_t>{}(fmt, static_cast<uint8_t>(x));
|
|
}
|
|
};
|
|
|
|
///
|
|
/// \brief formatter of floating point types
|
|
template<typename FloatT> requires(is_floating_point_v<FloatT>)
|
|
struct formatter<FloatT> {
|
|
///
|
|
/// \brief format function
|
|
/// \param fmt the format specification
|
|
/// \param x the float argument
|
|
/// \returns the formatted version of \emph{x}
|
|
string operator()(const format_arg& fmt, FloatT x) const {
|
|
|
|
// nan & inf cases
|
|
if (fennec::isnan(x)) {
|
|
return string("nan");
|
|
}
|
|
if (fennec::isinf(x)) {
|
|
return string("inf");
|
|
}
|
|
|
|
|
|
char digits[128] = {};
|
|
auto chk = fennec::to_chars(digits, digits + sizeof(digits), fennec::abs(x), fmt.type, fmt.precision);
|
|
assertf(chk != nullptr, "fennec::format error, to_chars error");
|
|
size_t len = chk - digits;
|
|
|
|
// handle uppercase
|
|
if (fmt.upper) {
|
|
for (auto& digit : digits) {
|
|
if (digit == 0) {
|
|
break;
|
|
}
|
|
digit = toupper(digit);
|
|
}
|
|
}
|
|
|
|
const bool has_sign = (x < 0 or fmt.sign != '-');
|
|
const bool zero = fmt.fill == '0';
|
|
const size_t prefix = fmt.alt ? 2 : 0;
|
|
const size_t sgnlen = len + (zero ? has_sign + prefix : 0);
|
|
const size_t explen = fennec::max(sgnlen, fmt.width) + (zero ? 0 : has_sign + prefix);
|
|
const size_t fill = fmt.width > sgnlen ? fmt.width - sgnlen : 0;
|
|
size_t sign = 0;
|
|
|
|
string res = string(explen);
|
|
|
|
if (fill > 0) {
|
|
switch (fmt.align) {
|
|
case '<':
|
|
memcpy(res.data() + has_sign + prefix, digits, len);
|
|
memset(res.data() + has_sign + prefix + len, fmt.fill == '0' ? ' ' : fmt.fill, fill);
|
|
break;
|
|
case '>': case '\0':
|
|
memcpy(res.data() + explen - len, digits, len);
|
|
sign = fmt.fill == '0' ? 0 : explen - len - 1 - prefix;
|
|
memset(res.data(), fmt.fill, explen - len);
|
|
break;
|
|
case '^':
|
|
const size_t bef = fill / 2 + has_sign + prefix;
|
|
const size_t aft = explen - bef;
|
|
memcpy(res.data() + bef, digits, len);
|
|
sign = fmt.fill == '0' ? 0 : bef - 1 - prefix;
|
|
memset(res.data(), fmt.fill, bef);
|
|
memset(res.data() + bef + len, fmt.fill == '0' ? ' ' : fmt.fill, aft);
|
|
break;
|
|
|
|
|
|
}
|
|
} else {
|
|
memcpy(res.data() + has_sign + prefix, digits, len);
|
|
}
|
|
|
|
if (has_sign) {
|
|
res[sign] = (x < 0) ? '-' : fmt.sign;
|
|
}
|
|
|
|
if (prefix) {
|
|
res[sign + has_sign] = '0';
|
|
res[sign + has_sign + 1] = fmt.type + ('x' - 'a');
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
};
|
|
|
|
}
|
|
|
|
#endif // FENNEC_FORMAT_FORMATTER_H
|