From 806e976b066f8958bce850eb1904d4843092d13c Mon Sep 17 00:00:00 2001
From: Sadie Powell
Date: Fri, 11 Apr 2025 21:19:35 +0100
Subject: The verbose log level should be mapped to debug not warning.
---
src/logging.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/logging.cpp b/src/logging.cpp
index 139a1b8b3..3912d78f4 100644
--- a/src/logging.cpp
+++ b/src/logging.cpp
@@ -257,7 +257,7 @@ void Log::Manager::OpenLogs(bool requiremethods)
// Deprecated v3 names.
{ "sparse", Level::CRITICAL },
- { "verbose", Level::WARNING },
+ { "verbose", Level::DEBUG },
{ "default", Level::NORMAL },
});
--
cgit v1.3.1-10-gc9f91
From 97672a80a91abfe560a43a03380fcb5fa869a47b Mon Sep 17 00:00:00 2001
From: Sadie Powell
Date: Sat, 12 Apr 2025 09:29:25 +0100
Subject: Warn when a link block has misconfigured TLS.
---
src/modules/m_spanningtree/utils.cpp | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/src/modules/m_spanningtree/utils.cpp b/src/modules/m_spanningtree/utils.cpp
index db62a8b0a..b1e5cc8da 100644
--- a/src/modules/m_spanningtree/utils.cpp
+++ b/src/modules/m_spanningtree/utils.cpp
@@ -294,6 +294,12 @@ void SpanningTreeUtilities::ReadConfiguration()
L->Bind = tag->getString("bind");
L->Hidden = tag->getBool("hidden");
+ if (!tag->getString("ssl").empty())
+ {
+ throw ModuleException((Module*)Creator, "Obsolete TLS configuration in link block at {}. See {}modules/spanningtree/#link for the correct way to configure TLS.",
+ tag->source.str(), INSPIRCD_DOCS);
+ }
+
if (L->Name.empty())
throw ModuleException((Module*)Creator, "Invalid configuration, found a link tag without a name!" + (!L->IPAddr.empty() ? " IP address: "+L->IPAddr : ""));
--
cgit v1.3.1-10-gc9f91
From 1f898006ccc4b1b2b02fde3ddc95b015cd003afc Mon Sep 17 00:00:00 2001
From: Sadie Powell
Date: Sat, 12 Apr 2025 16:46:03 +0100
Subject: Add the require_environment() build function.
---
make/directive.pm | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/make/directive.pm b/make/directive.pm
index 14d587572..aa3005750 100644
--- a/make/directive.pm
+++ b/make/directive.pm
@@ -345,6 +345,24 @@ sub __function_require_library {
return $ok;
}
+sub __function_require_environment {
+ my ($file, $name, $value) = @_;
+
+ # Check for an inverted match.
+ my ($ok, $err) = ("", undef);
+ if ($name =~ s/^!//) {
+ ($ok, $err) = ($err, $ok);
+ }
+
+ return $err unless defined $ENV{$name};
+ if (defined $value) {
+ return $err unless $ENV{$name} eq $value;
+ }
+
+ # Requirement directives don't change anything directly.
+ return $ok;
+}
+
sub __function_warning {
my ($file, @messages) = @_;
print_warning @messages;
--
cgit v1.3.1-10-gc9f91
From 3dbb636af28e09dbd294f6fa065950a883db5203 Mon Sep 17 00:00:00 2001
From: Sadie Powell
Date: Sat, 12 Apr 2025 16:49:12 +0100
Subject: Allow building the httpd module against the system http_parser lib.
---
src/modules/m_httpd.cpp | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/src/modules/m_httpd.cpp b/src/modules/m_httpd.cpp
index 249a91f83..fa8c16a47 100644
--- a/src/modules/m_httpd.cpp
+++ b/src/modules/m_httpd.cpp
@@ -26,6 +26,9 @@
* along with this program. If not, see .
*/
+/// $CompilerFlags: require_environment("SYSTEM_HTTP_PARSER" "1") -DUSE_SYSTEM_HTTP_PARSER
+/// $LinkerFlags: require_environment("SYSTEM_HTTP_PARSER" "1") -lhttp_parser
+
#include "inspircd.h"
#include "iohook.h"
@@ -41,7 +44,11 @@
# pragma GCC diagnostic ignored "-Wshadow"
#endif
-#include
+#ifdef USE_SYSTEM_HTTP_PARSER
+# include
+#else
+# include
+#endif
#ifdef __GNUC__
# pragma GCC diagnostic pop
--
cgit v1.3.1-10-gc9f91
From dac1ed6d7f61ceac6e08e111e522548edcdb0271 Mon Sep 17 00:00:00 2001
From: Sadie Powell
Date: Sat, 12 Apr 2025 16:53:23 +0100
Subject: Allow building the websocket module against the system utfcpp lib.
---
src/modules/m_websocket.cpp | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/src/modules/m_websocket.cpp b/src/modules/m_websocket.cpp
index bbdc26eec..7a8630903 100644
--- a/src/modules/m_websocket.cpp
+++ b/src/modules/m_websocket.cpp
@@ -19,6 +19,14 @@
* along with this program. If not, see .
*/
+/// $CompilerFlags: require_environment("SYSTEM_UTFCPP" "1") -DUSE_SYSTEM_UTFCPP
+
+
+#ifdef USE_SYSTEM_UTFCPP
+# include
+#else
+# include
+#endif
#include "inspircd.h"
#include "extension.h"
@@ -28,9 +36,6 @@
#include "modules/whois.h"
#include "utility/string.h"
-#define UTF_CPP_CPLUSPLUS 199711L
-#include
-
static constexpr char MagicGUID[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
static constexpr char newline[] = "\r\n";
static constexpr char whitespace[] = " \t";
--
cgit v1.3.1-10-gc9f91
From ea4ea3b1423e23e4534591fbdf842dd5328ba148 Mon Sep 17 00:00:00 2001
From: Sadie Powell
Date: Sat, 12 Apr 2025 17:02:02 +0100
Subject: Remove the {fmt} headers that we don't use.
---
vendor/fmt/LICENSE | 27 +
vendor/fmt/LICENSE.rst | 27 -
vendor/fmt/args.h | 220 -----
vendor/fmt/chrono.h | 2338 ------------------------------------------------
vendor/fmt/os.h | 427 ---------
vendor/fmt/ostream.h | 166 ----
vendor/fmt/printf.h | 633 -------------
vendor/fmt/ranges.h | 850 ------------------
vendor/fmt/std.h | 726 ---------------
vendor/fmt/xchar.h | 373 --------
vendor/update.toml | 2 +-
11 files changed, 28 insertions(+), 5761 deletions(-)
create mode 100644 vendor/fmt/LICENSE
delete mode 100644 vendor/fmt/LICENSE.rst
delete mode 100644 vendor/fmt/args.h
delete mode 100644 vendor/fmt/chrono.h
delete mode 100644 vendor/fmt/os.h
delete mode 100644 vendor/fmt/ostream.h
delete mode 100644 vendor/fmt/printf.h
delete mode 100644 vendor/fmt/ranges.h
delete mode 100644 vendor/fmt/std.h
delete mode 100644 vendor/fmt/xchar.h
diff --git a/vendor/fmt/LICENSE b/vendor/fmt/LICENSE
new file mode 100644
index 000000000..1cd1ef926
--- /dev/null
+++ b/vendor/fmt/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+--- Optional exception to the license ---
+
+As an exception, if, as a result of your compiling your source code, portions
+of this Software are embedded into a machine-executable object form of such
+source code, you may redistribute such embedded portions in such object form
+without including the above copyright and permission notices.
diff --git a/vendor/fmt/LICENSE.rst b/vendor/fmt/LICENSE.rst
deleted file mode 100644
index 1cd1ef926..000000000
--- a/vendor/fmt/LICENSE.rst
+++ /dev/null
@@ -1,27 +0,0 @@
-Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
---- Optional exception to the license ---
-
-As an exception, if, as a result of your compiling your source code, portions
-of this Software are embedded into a machine-executable object form of such
-source code, you may redistribute such embedded portions in such object form
-without including the above copyright and permission notices.
diff --git a/vendor/fmt/args.h b/vendor/fmt/args.h
deleted file mode 100644
index 3ff478807..000000000
--- a/vendor/fmt/args.h
+++ /dev/null
@@ -1,220 +0,0 @@
-// Formatting library for C++ - dynamic argument lists
-//
-// Copyright (c) 2012 - present, Victor Zverovich
-// All rights reserved.
-//
-// For the license information refer to format.h.
-
-#ifndef FMT_ARGS_H_
-#define FMT_ARGS_H_
-
-#ifndef FMT_MODULE
-# include // std::reference_wrapper
-# include // std::unique_ptr
-# include
-#endif
-
-#include "format.h" // std_string_view
-
-FMT_BEGIN_NAMESPACE
-namespace detail {
-
-template struct is_reference_wrapper : std::false_type {};
-template
-struct is_reference_wrapper> : std::true_type {};
-
-template auto unwrap(const T& v) -> const T& { return v; }
-template
-auto unwrap(const std::reference_wrapper& v) -> const T& {
- return static_cast(v);
-}
-
-// node is defined outside dynamic_arg_list to workaround a C2504 bug in MSVC
-// 2022 (v17.10.0).
-//
-// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
-// templates it doesn't complain about inability to deduce single translation
-// unit for placing vtable. So node is made a fake template.
-template struct node {
- virtual ~node() = default;
- std::unique_ptr> next;
-};
-
-class dynamic_arg_list {
- template struct typed_node : node<> {
- T value;
-
- template
- FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}
-
- template
- FMT_CONSTEXPR typed_node(const basic_string_view& arg)
- : value(arg.data(), arg.size()) {}
- };
-
- std::unique_ptr> head_;
-
- public:
- template auto push(const Arg& arg) -> const T& {
- auto new_node = std::unique_ptr>(new typed_node(arg));
- auto& value = new_node->value;
- new_node->next = std::move(head_);
- head_ = std::move(new_node);
- return value;
- }
-};
-} // namespace detail
-
-/**
- * A dynamic list of formatting arguments with storage.
- *
- * It can be implicitly converted into `fmt::basic_format_args` for passing
- * into type-erased formatting functions such as `fmt::vformat`.
- */
-template class dynamic_format_arg_store {
- private:
- using char_type = typename Context::char_type;
-
- template struct need_copy {
- static constexpr detail::type mapped_type =
- detail::mapped_type_constant::value;
-
- enum {
- value = !(detail::is_reference_wrapper::value ||
- std::is_same>::value ||
- std::is_same>::value ||
- (mapped_type != detail::type::cstring_type &&
- mapped_type != detail::type::string_type &&
- mapped_type != detail::type::custom_type))
- };
- };
-
- template
- using stored_t = conditional_t<
- std::is_convertible>::value &&
- !detail::is_reference_wrapper::value,
- std::basic_string, T>;
-
- // Storage of basic_format_arg must be contiguous.
- std::vector> data_;
- std::vector> named_info_;
-
- // Storage of arguments not fitting into basic_format_arg must grow
- // without relocation because items in data_ refer to it.
- detail::dynamic_arg_list dynamic_args_;
-
- friend class basic_format_args;
-
- auto data() const -> const basic_format_arg* {
- return named_info_.empty() ? data_.data() : data_.data() + 1;
- }
-
- template void emplace_arg(const T& arg) {
- data_.emplace_back(arg);
- }
-
- template
- void emplace_arg(const detail::named_arg& arg) {
- if (named_info_.empty())
- data_.insert(data_.begin(), basic_format_arg(nullptr, 0));
- data_.emplace_back(detail::unwrap(arg.value));
- auto pop_one = [](std::vector>* data) {
- data->pop_back();
- };
- std::unique_ptr>, decltype(pop_one)>
- guard{&data_, pop_one};
- named_info_.push_back({arg.name, static_cast(data_.size() - 2u)});
- data_[0] = {named_info_.data(), named_info_.size()};
- guard.release();
- }
-
- public:
- constexpr dynamic_format_arg_store() = default;
-
- operator basic_format_args() const {
- return basic_format_args(data(), static_cast(data_.size()),
- !named_info_.empty());
- }
-
- /**
- * Adds an argument into the dynamic store for later passing to a formatting
- * function.
- *
- * Note that custom types and string types (but not string views) are copied
- * into the store dynamically allocating memory if necessary.
- *
- * **Example**:
- *
- * fmt::dynamic_format_arg_store store;
- * store.push_back(42);
- * store.push_back("abc");
- * store.push_back(1.5f);
- * std::string result = fmt::vformat("{} and {} and {}", store);
- */
- template void push_back(const T& arg) {
- if (detail::const_check(need_copy::value))
- emplace_arg(dynamic_args_.push>(arg));
- else
- emplace_arg(detail::unwrap(arg));
- }
-
- /**
- * Adds a reference to the argument into the dynamic store for later passing
- * to a formatting function.
- *
- * **Example**:
- *
- * fmt::dynamic_format_arg_store store;
- * char band[] = "Rolling Stones";
- * store.push_back(std::cref(band));
- * band[9] = 'c'; // Changing str affects the output.
- * std::string result = fmt::vformat("{}", store);
- * // result == "Rolling Scones"
- */
- template void push_back(std::reference_wrapper arg) {
- static_assert(
- need_copy::value,
- "objects of built-in types and string views are always copied");
- emplace_arg(arg.get());
- }
-
- /**
- * Adds named argument into the dynamic store for later passing to a
- * formatting function. `std::reference_wrapper` is supported to avoid
- * copying of the argument. The name is always copied into the store.
- */
- template
- void push_back(const detail::named_arg& arg) {
- const char_type* arg_name =
- dynamic_args_.push>(arg.name).c_str();
- if (detail::const_check(need_copy::value)) {
- emplace_arg(
- fmt::arg(arg_name, dynamic_args_.push>(arg.value)));
- } else {
- emplace_arg(fmt::arg(arg_name, arg.value));
- }
- }
-
- /// Erase all elements from the store.
- void clear() {
- data_.clear();
- named_info_.clear();
- dynamic_args_ = {};
- }
-
- /// Reserves space to store at least `new_cap` arguments including
- /// `new_cap_named` named arguments.
- void reserve(size_t new_cap, size_t new_cap_named) {
- FMT_ASSERT(new_cap >= new_cap_named,
- "set of arguments includes set of named arguments");
- data_.reserve(new_cap);
- named_info_.reserve(new_cap_named);
- }
-
- /// Returns the number of elements in the store.
- size_t size() const noexcept { return data_.size(); }
-};
-
-FMT_END_NAMESPACE
-
-#endif // FMT_ARGS_H_
diff --git a/vendor/fmt/chrono.h b/vendor/fmt/chrono.h
deleted file mode 100644
index 50c777c84..000000000
--- a/vendor/fmt/chrono.h
+++ /dev/null
@@ -1,2338 +0,0 @@
-// Formatting library for C++ - chrono support
-//
-// Copyright (c) 2012 - present, Victor Zverovich
-// All rights reserved.
-//
-// For the license information refer to format.h.
-
-#ifndef FMT_CHRONO_H_
-#define FMT_CHRONO_H_
-
-#ifndef FMT_MODULE
-# include
-# include
-# include // std::isfinite
-# include // std::memcpy
-# include
-# include
-# include
-# include
-# include
-#endif
-
-#include "format.h"
-
-namespace fmt_detail {
-struct time_zone {
- template
- auto to_sys(T)
- -> std::chrono::time_point {
- return {};
- }
-};
-template inline auto current_zone(T...) -> time_zone* {
- return nullptr;
-}
-
-template inline void _tzset(T...) {}
-} // namespace fmt_detail
-
-FMT_BEGIN_NAMESPACE
-
-// Enable safe chrono durations, unless explicitly disabled.
-#ifndef FMT_SAFE_DURATION_CAST
-# define FMT_SAFE_DURATION_CAST 1
-#endif
-#if FMT_SAFE_DURATION_CAST
-
-// For conversion between std::chrono::durations without undefined
-// behaviour or erroneous results.
-// This is a stripped down version of duration_cast, for inclusion in fmt.
-// See https://github.com/pauldreik/safe_duration_cast
-//
-// Copyright Paul Dreik 2019
-namespace safe_duration_cast {
-
-template ::value &&
- std::numeric_limits::is_signed ==
- std::numeric_limits::is_signed)>
-FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec)
- -> To {
- ec = 0;
- using F = std::numeric_limits;
- using T = std::numeric_limits;
- static_assert(F::is_integer, "From must be integral");
- static_assert(T::is_integer, "To must be integral");
-
- // A and B are both signed, or both unsigned.
- if (detail::const_check(F::digits <= T::digits)) {
- // From fits in To without any problem.
- } else {
- // From does not always fit in To, resort to a dynamic check.
- if (from < (T::min)() || from > (T::max)()) {
- // outside range.
- ec = 1;
- return {};
- }
- }
- return static_cast(from);
-}
-
-/// Converts From to To, without loss. If the dynamic value of from
-/// can't be converted to To without loss, ec is set.
-template ::value &&
- std::numeric_limits::is_signed !=
- std::numeric_limits::is_signed)>
-FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec)
- -> To {
- ec = 0;
- using F = std::numeric_limits;
- using T = std::numeric_limits;
- static_assert(F::is_integer, "From must be integral");
- static_assert(T::is_integer, "To must be integral");
-
- if (detail::const_check(F::is_signed && !T::is_signed)) {
- // From may be negative, not allowed!
- if (fmt::detail::is_negative(from)) {
- ec = 1;
- return {};
- }
- // From is positive. Can it always fit in To?
- if (detail::const_check(F::digits > T::digits) &&
- from > static_cast(detail::max_value())) {
- ec = 1;
- return {};
- }
- }
-
- if (detail::const_check(!F::is_signed && T::is_signed &&
- F::digits >= T::digits) &&
- from > static_cast(detail::max_value())) {
- ec = 1;
- return {};
- }
- return static_cast(from); // Lossless conversion.
-}
-
-template ::value)>
-FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec)
- -> To {
- ec = 0;
- return from;
-} // function
-
-// clang-format off
-/**
- * converts From to To if possible, otherwise ec is set.
- *
- * input | output
- * ---------------------------------|---------------
- * NaN | NaN
- * Inf | Inf
- * normal, fits in output | converted (possibly lossy)
- * normal, does not fit in output | ec is set
- * subnormal | best effort
- * -Inf | -Inf
- */
-// clang-format on
-template ::value)>
-FMT_CONSTEXPR auto safe_float_conversion(const From from, int& ec) -> To {
- ec = 0;
- using T = std::numeric_limits;
- static_assert(std::is_floating_point::value, "From must be floating");
- static_assert(std::is_floating_point::value, "To must be floating");
-
- // catch the only happy case
- if (std::isfinite(from)) {
- if (from >= T::lowest() && from <= (T::max)()) {
- return static_cast(from);
- }
- // not within range.
- ec = 1;
- return {};
- }
-
- // nan and inf will be preserved
- return static_cast(from);
-} // function
-
-template ::value)>
-FMT_CONSTEXPR auto safe_float_conversion(const From from, int& ec) -> To {
- ec = 0;
- static_assert(std::is_floating_point::value, "From must be floating");
- return from;
-}
-
-/// Safe duration_cast between floating point durations
-template ::value),
- FMT_ENABLE_IF(std::is_floating_point::value)>
-auto safe_duration_cast(std::chrono::duration from,
- int& ec) -> To {
- using From = std::chrono::duration;
- ec = 0;
- if (std::isnan(from.count())) {
- // nan in, gives nan out. easy.
- return To{std::numeric_limits::quiet_NaN()};
- }
- // maybe we should also check if from is denormal, and decide what to do about
- // it.
-
- // +-inf should be preserved.
- if (std::isinf(from.count())) {
- return To{from.count()};
- }
-
- // the basic idea is that we need to convert from count() in the from type
- // to count() in the To type, by multiplying it with this:
- struct Factor
- : std::ratio_divide {};
-
- static_assert(Factor::num > 0, "num must be positive");
- static_assert(Factor::den > 0, "den must be positive");
-
- // the conversion is like this: multiply from.count() with Factor::num
- // /Factor::den and convert it to To::rep, all this without
- // overflow/underflow. let's start by finding a suitable type that can hold
- // both To, From and Factor::num
- using IntermediateRep =
- typename std::common_type::type;
-
- // force conversion of From::rep -> IntermediateRep to be safe,
- // even if it will never happen be narrowing in this context.
- IntermediateRep count =
- safe_float_conversion(from.count(), ec);
- if (ec) {
- return {};
- }
-
- // multiply with Factor::num without overflow or underflow
- if (detail::const_check(Factor::num != 1)) {
- constexpr auto max1 = detail::max_value() /
- static_cast(Factor::num);
- if (count > max1) {
- ec = 1;
- return {};
- }
- constexpr auto min1 = std::numeric_limits::lowest() /
- static_cast(Factor::num);
- if (count < min1) {
- ec = 1;
- return {};
- }
- count *= static_cast(Factor::num);
- }
-
- // this can't go wrong, right? den>0 is checked earlier.
- if (detail::const_check(Factor::den != 1)) {
- using common_t = typename std::common_type::type;
- count /= static_cast(Factor::den);
- }
-
- // convert to the to type, safely
- using ToRep = typename To::rep;
-
- const ToRep tocount = safe_float_conversion(count, ec);
- if (ec) {
- return {};
- }
- return To{tocount};
-}
-} // namespace safe_duration_cast
-#endif
-
-namespace detail {
-
-// Check if std::chrono::utc_time is available.
-#ifdef FMT_USE_UTC_TIME
-// Use the provided definition.
-#elif defined(__cpp_lib_chrono)
-# define FMT_USE_UTC_TIME (__cpp_lib_chrono >= 201907L)
-#else
-# define FMT_USE_UTC_TIME 0
-#endif
-#if FMT_USE_UTC_TIME
-using utc_clock = std::chrono::utc_clock;
-#else
-struct utc_clock {
- template void to_sys(T);
-};
-#endif
-
-// Check if std::chrono::local_time is available.
-#ifdef FMT_USE_LOCAL_TIME
-// Use the provided definition.
-#elif defined(__cpp_lib_chrono)
-# define FMT_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L)
-#else
-# define FMT_USE_LOCAL_TIME 0
-#endif
-#if FMT_USE_LOCAL_TIME
-using local_t = std::chrono::local_t;
-#else
-struct local_t {};
-#endif
-
-} // namespace detail
-
-template
-using sys_time = std::chrono::time_point;
-
-template
-using utc_time = std::chrono::time_point;
-
-template
-using local_time = std::chrono::time_point;
-
-namespace detail {
-
-// Prevents expansion of a preceding token as a function-style macro.
-// Usage: f FMT_NOMACRO()
-#define FMT_NOMACRO
-
-template struct null {};
-inline auto localtime_r FMT_NOMACRO(...) -> null<> { return null<>(); }
-inline auto localtime_s(...) -> null<> { return null<>(); }
-inline auto gmtime_r(...) -> null<> { return null<>(); }
-inline auto gmtime_s(...) -> null<> { return null<>(); }
-
-// It is defined here and not in ostream.h because the latter has expensive
-// includes.
-template class formatbuf : public StreamBuf {
- private:
- using char_type = typename StreamBuf::char_type;
- using streamsize = decltype(std::declval().sputn(nullptr, 0));
- using int_type = typename StreamBuf::int_type;
- using traits_type = typename StreamBuf::traits_type;
-
- buffer& buffer_;
-
- public:
- explicit formatbuf(buffer& buf) : buffer_(buf) {}
-
- protected:
- // The put area is always empty. This makes the implementation simpler and has
- // the advantage that the streambuf and the buffer are always in sync and
- // sputc never writes into uninitialized memory. A disadvantage is that each
- // call to sputc always results in a (virtual) call to overflow. There is no
- // disadvantage here for sputn since this always results in a call to xsputn.
-
- auto overflow(int_type ch) -> int_type override {
- if (!traits_type::eq_int_type(ch, traits_type::eof()))
- buffer_.push_back(static_cast(ch));
- return ch;
- }
-
- auto xsputn(const char_type* s, streamsize count) -> streamsize override {
- buffer_.append(s, s + count);
- return count;
- }
-};
-
-inline auto get_classic_locale() -> const std::locale& {
- static const auto& locale = std::locale::classic();
- return locale;
-}
-
-template struct codecvt_result {
- static constexpr const size_t max_size = 32;
- CodeUnit buf[max_size];
- CodeUnit* end;
-};
-
-template
-void write_codecvt(codecvt_result& out, string_view in,
- const std::locale& loc) {
- FMT_PRAGMA_CLANG(diagnostic push)
- FMT_PRAGMA_CLANG(diagnostic ignored "-Wdeprecated")
- auto& f = std::use_facet>(loc);
- FMT_PRAGMA_CLANG(diagnostic pop)
- auto mb = std::mbstate_t();
- const char* from_next = nullptr;
- auto result = f.in(mb, in.begin(), in.end(), from_next, std::begin(out.buf),
- std::end(out.buf), out.end);
- if (result != std::codecvt_base::ok)
- FMT_THROW(format_error("failed to format time"));
-}
-
-template
-auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc)
- -> OutputIt {
- if (const_check(detail::use_utf8) && loc != get_classic_locale()) {
- // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
- // gcc-4.
-#if FMT_MSC_VERSION != 0 || \
- (defined(__GLIBCXX__) && \
- (!defined(_GLIBCXX_USE_DUAL_ABI) || _GLIBCXX_USE_DUAL_ABI == 0))
- // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5
- // and newer.
- using code_unit = wchar_t;
-#else
- using code_unit = char32_t;
-#endif
-
- using unit_t = codecvt_result;
- unit_t unit;
- write_codecvt(unit, in, loc);
- // In UTF-8 is used one to four one-byte code units.
- auto u =
- to_utf8>();
- if (!u.convert({unit.buf, to_unsigned(unit.end - unit.buf)}))
- FMT_THROW(format_error("failed to format time"));
- return copy(u.c_str(), u.c_str() + u.size(), out);
- }
- return copy(in.data(), in.data() + in.size(), out);
-}
-
-template ::value)>
-auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc)
- -> OutputIt {
- codecvt_result unit;
- write_codecvt(unit, sv, loc);
- return copy(unit.buf, unit.end, out);
-}
-
-template ::value)>
-auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc)
- -> OutputIt {
- return write_encoded_tm_str(out, sv, loc);
-}
-
-template
-inline void do_write(buffer& buf, const std::tm& time,
- const std::locale& loc, char format, char modifier) {
- auto&& format_buf = formatbuf>(buf);
- auto&& os = std::basic_ostream(&format_buf);
- os.imbue(loc);
- const auto& facet = std::use_facet>(loc);
- auto end = facet.put(os, os, Char(' '), &time, format, modifier);
- if (end.failed()) FMT_THROW(format_error("failed to format time"));
-}
-
-template ::value)>
-auto write(OutputIt out, const std::tm& time, const std::locale& loc,
- char format, char modifier = 0) -> OutputIt {
- auto&& buf = get_buffer(out);
- do_write(buf, time, loc, format, modifier);
- return get_iterator(buf, out);
-}
-
-template ::value)>
-auto write(OutputIt out, const std::tm& time, const std::locale& loc,
- char format, char modifier = 0) -> OutputIt {
- auto&& buf = basic_memory_buffer();
- do_write(buf, time, loc, format, modifier);
- return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc);
-}
-
-template
-struct is_same_arithmetic_type
- : public std::integral_constant::value &&
- std::is_integral::value) ||
- (std::is_floating_point::value &&
- std::is_floating_point::value)> {
-};
-
-FMT_NORETURN inline void throw_duration_error() {
- FMT_THROW(format_error("cannot format duration"));
-}
-
-// Cast one integral duration to another with an overflow check.
-template ::value&&
- std::is_integral::value)>
-auto duration_cast(std::chrono::duration from) -> To {
-#if !FMT_SAFE_DURATION_CAST
- return std::chrono::duration_cast(from);
-#else
- // The conversion factor: to.count() == factor * from.count().
- using factor = std::ratio_divide;
-
- using common_rep = typename std::common_type::type;
-
- int ec = 0;
- auto count = safe_duration_cast::lossless_integral_conversion(
- from.count(), ec);
- if (ec) throw_duration_error();
-
- // Multiply from.count() by factor and check for overflow.
- if (const_check(factor::num != 1)) {
- if (count > max_value() / factor::num) throw_duration_error();
- const auto min = (std::numeric_limits::min)() / factor::num;
- if (const_check(!std::is_unsigned::value) && count < min)
- throw_duration_error();
- count *= factor::num;
- }
- if (const_check(factor::den != 1)) count /= factor::den;
- auto to =
- To(safe_duration_cast::lossless_integral_conversion(
- count, ec));
- if (ec) throw_duration_error();
- return to;
-#endif
-}
-
-template ::value&&
- std::is_floating_point::value)>
-auto duration_cast(std::chrono::duration from) -> To {
-#if FMT_SAFE_DURATION_CAST
- // Throwing version of safe_duration_cast is only available for
- // integer to integer or float to float casts.
- int ec;
- To to = safe_duration_cast::safe_duration_cast(from, ec);
- if (ec) throw_duration_error();
- return to;
-#else
- // Standard duration cast, may overflow.
- return std::chrono::duration_cast(from);
-#endif
-}
-
-template <
- typename To, typename FromRep, typename FromPeriod,
- FMT_ENABLE_IF(!is_same_arithmetic_type::value)>
-auto duration_cast(std::chrono::duration from) -> To {
- // Mixed integer <-> float cast is not supported by safe_duration_cast.
- return std::chrono::duration_cast(from);
-}
-
-template
-auto to_time_t(sys_time time_point) -> std::time_t {
- // Cannot use std::chrono::system_clock::to_time_t since this would first
- // require a cast to std::chrono::system_clock::time_point, which could
- // overflow.
- return detail::duration_cast>(
- time_point.time_since_epoch())
- .count();
-}
-
-// Workaround a bug in libstdc++ which sets __cpp_lib_chrono to 201907 without
-// providing current_zone(): https://github.com/fmtlib/fmt/issues/4160.
-template FMT_CONSTEXPR auto has_current_zone() -> bool {
- using namespace std::chrono;
- using namespace fmt_detail;
- return !std::is_same::value;
-}
-} // namespace detail
-
-FMT_BEGIN_EXPORT
-
-/**
- * Converts given time since epoch as `std::time_t` value into calendar time,
- * expressed in local time. Unlike `std::localtime`, this function is
- * thread-safe on most platforms.
- */
-inline auto localtime(std::time_t time) -> std::tm {
- struct dispatcher {
- std::time_t time_;
- std::tm tm_;
-
- inline dispatcher(std::time_t t) : time_(t) {}
-
- inline auto run() -> bool {
- using namespace fmt::detail;
- return handle(localtime_r(&time_, &tm_));
- }
-
- inline auto handle(std::tm* tm) -> bool { return tm != nullptr; }
-
- inline auto handle(detail::null<>) -> bool {
- using namespace fmt::detail;
- return fallback(localtime_s(&tm_, &time_));
- }
-
- inline auto fallback(int res) -> bool { return res == 0; }
-
-#if !FMT_MSC_VERSION
- inline auto fallback(detail::null<>) -> bool {
- using namespace fmt::detail;
- std::tm* tm = std::localtime(&time_);
- if (tm) tm_ = *tm;
- return tm != nullptr;
- }
-#endif
- };
- dispatcher lt(time);
- // Too big time values may be unsupported.
- if (!lt.run()) FMT_THROW(format_error("time_t value out of range"));
- return lt.tm_;
-}
-
-#if FMT_USE_LOCAL_TIME
-template ())>
-inline auto localtime(std::chrono::local_time time) -> std::tm {
- using namespace std::chrono;
- using namespace fmt_detail;
- return localtime(detail::to_time_t(current_zone()->to_sys(time)));
-}
-#endif
-
-/**
- * Converts given time since epoch as `std::time_t` value into calendar time,
- * expressed in Coordinated Universal Time (UTC). Unlike `std::gmtime`, this
- * function is thread-safe on most platforms.
- */
-inline auto gmtime(std::time_t time) -> std::tm {
- struct dispatcher {
- std::time_t time_;
- std::tm tm_;
-
- inline dispatcher(std::time_t t) : time_(t) {}
-
- inline auto run() -> bool {
- using namespace fmt::detail;
- return handle(gmtime_r(&time_, &tm_));
- }
-
- inline auto handle(std::tm* tm) -> bool { return tm != nullptr; }
-
- inline auto handle(detail::null<>) -> bool {
- using namespace fmt::detail;
- return fallback(gmtime_s(&tm_, &time_));
- }
-
- inline auto fallback(int res) -> bool { return res == 0; }
-
-#if !FMT_MSC_VERSION
- inline auto fallback(detail::null<>) -> bool {
- std::tm* tm = std::gmtime(&time_);
- if (tm) tm_ = *tm;
- return tm != nullptr;
- }
-#endif
- };
- auto gt = dispatcher(time);
- // Too big time values may be unsupported.
- if (!gt.run()) FMT_THROW(format_error("time_t value out of range"));
- return gt.tm_;
-}
-
-template
-inline auto gmtime(sys_time time_point) -> std::tm {
- return gmtime(detail::to_time_t(time_point));
-}
-
-namespace detail {
-
-// Writes two-digit numbers a, b and c separated by sep to buf.
-// The method by Pavel Novikov based on
-// https://johnnylee-sde.github.io/Fast-unsigned-integer-to-time-string/.
-inline void write_digit2_separated(char* buf, unsigned a, unsigned b,
- unsigned c, char sep) {
- unsigned long long digits =
- a | (b << 24) | (static_cast(c) << 48);
- // Convert each value to BCD.
- // We have x = a * 10 + b and we want to convert it to BCD y = a * 16 + b.
- // The difference is
- // y - x = a * 6
- // a can be found from x:
- // a = floor(x / 10)
- // then
- // y = x + a * 6 = x + floor(x / 10) * 6
- // floor(x / 10) is (x * 205) >> 11 (needs 16 bits).
- digits += (((digits * 205) >> 11) & 0x000f00000f00000f) * 6;
- // Put low nibbles to high bytes and high nibbles to low bytes.
- digits = ((digits & 0x00f00000f00000f0) >> 4) |
- ((digits & 0x000f00000f00000f) << 8);
- auto usep = static_cast(sep);
- // Add ASCII '0' to each digit byte and insert separators.
- digits |= 0x3030003030003030 | (usep << 16) | (usep << 40);
-
- constexpr const size_t len = 8;
- if (const_check(is_big_endian())) {
- char tmp[len];
- std::memcpy(tmp, &digits, len);
- std::reverse_copy(tmp, tmp + len, buf);
- } else {
- std::memcpy(buf, &digits, len);
- }
-}
-
-template
-FMT_CONSTEXPR inline auto get_units() -> const char* {
- if (std::is_same::value) return "as";
- if (std::is_same::value) return "fs";
- if (std::is_same::value) return "ps";
- if (std::is_same::value) return "ns";
- if (std::is_same::value)
- return detail::use_utf8 ? "µs" : "us";
- if (std::is_same::value) return "ms";
- if (std::is_same::value) return "cs";
- if (std::is_same::value) return "ds";
- if (std::is_same>::value) return "s";
- if (std::is_same::value) return "das";
- if (std::is_same::value) return "hs";
- if (std::is_same::value) return "ks";
- if (std::is_same::value) return "Ms";
- if (std::is_same::value) return "Gs";
- if (std::is_same::value) return "Ts";
- if (std::is_same::value) return "Ps";
- if (std::is_same::value) return "Es";
- if (std::is_same>::value) return "min";
- if (std::is_same>::value) return "h";
- if (std::is_same>::value) return "d";
- return nullptr;
-}
-
-enum class numeric_system {
- standard,
- // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale.
- alternative
-};
-
-// Glibc extensions for formatting numeric values.
-enum class pad_type {
- // Pad a numeric result string with zeros (the default).
- zero,
- // Do not pad a numeric result string.
- none,
- // Pad a numeric result string with spaces.
- space,
-};
-
-template
-auto write_padding(OutputIt out, pad_type pad, int width) -> OutputIt {
- if (pad == pad_type::none) return out;
- return detail::fill_n(out, width, pad == pad_type::space ? ' ' : '0');
-}
-
-template
-auto write_padding(OutputIt out, pad_type pad) -> OutputIt {
- if (pad != pad_type::none) *out++ = pad == pad_type::space ? ' ' : '0';
- return out;
-}
-
-// Parses a put_time-like format string and invokes handler actions.
-template
-FMT_CONSTEXPR auto parse_chrono_format(const Char* begin, const Char* end,
- Handler&& handler) -> const Char* {
- if (begin == end || *begin == '}') return begin;
- if (*begin != '%') FMT_THROW(format_error("invalid format"));
- auto ptr = begin;
- while (ptr != end) {
- pad_type pad = pad_type::zero;
- auto c = *ptr;
- if (c == '}') break;
- if (c != '%') {
- ++ptr;
- continue;
- }
- if (begin != ptr) handler.on_text(begin, ptr);
- ++ptr; // consume '%'
- if (ptr == end) FMT_THROW(format_error("invalid format"));
- c = *ptr;
- switch (c) {
- case '_':
- pad = pad_type::space;
- ++ptr;
- break;
- case '-':
- pad = pad_type::none;
- ++ptr;
- break;
- }
- if (ptr == end) FMT_THROW(format_error("invalid format"));
- c = *ptr++;
- switch (c) {
- case '%': handler.on_text(ptr - 1, ptr); break;
- case 'n': {
- const Char newline[] = {'\n'};
- handler.on_text(newline, newline + 1);
- break;
- }
- case 't': {
- const Char tab[] = {'\t'};
- handler.on_text(tab, tab + 1);
- break;
- }
- // Year:
- case 'Y': handler.on_year(numeric_system::standard, pad); break;
- case 'y': handler.on_short_year(numeric_system::standard); break;
- case 'C': handler.on_century(numeric_system::standard); break;
- case 'G': handler.on_iso_week_based_year(); break;
- case 'g': handler.on_iso_week_based_short_year(); break;
- // Day of the week:
- case 'a': handler.on_abbr_weekday(); break;
- case 'A': handler.on_full_weekday(); break;
- case 'w': handler.on_dec0_weekday(numeric_system::standard); break;
- case 'u': handler.on_dec1_weekday(numeric_system::standard); break;
- // Month:
- case 'b':
- case 'h': handler.on_abbr_month(); break;
- case 'B': handler.on_full_month(); break;
- case 'm': handler.on_dec_month(numeric_system::standard, pad); break;
- // Day of the year/month:
- case 'U':
- handler.on_dec0_week_of_year(numeric_system::standard, pad);
- break;
- case 'W':
- handler.on_dec1_week_of_year(numeric_system::standard, pad);
- break;
- case 'V': handler.on_iso_week_of_year(numeric_system::standard, pad); break;
- case 'j': handler.on_day_of_year(pad); break;
- case 'd': handler.on_day_of_month(numeric_system::standard, pad); break;
- case 'e':
- handler.on_day_of_month(numeric_system::standard, pad_type::space);
- break;
- // Hour, minute, second:
- case 'H': handler.on_24_hour(numeric_system::standard, pad); break;
- case 'I': handler.on_12_hour(numeric_system::standard, pad); break;
- case 'M': handler.on_minute(numeric_system::standard, pad); break;
- case 'S': handler.on_second(numeric_system::standard, pad); break;
- // Other:
- case 'c': handler.on_datetime(numeric_system::standard); break;
- case 'x': handler.on_loc_date(numeric_system::standard); break;
- case 'X': handler.on_loc_time(numeric_system::standard); break;
- case 'D': handler.on_us_date(); break;
- case 'F': handler.on_iso_date(); break;
- case 'r': handler.on_12_hour_time(); break;
- case 'R': handler.on_24_hour_time(); break;
- case 'T': handler.on_iso_time(); break;
- case 'p': handler.on_am_pm(); break;
- case 'Q': handler.on_duration_value(); break;
- case 'q': handler.on_duration_unit(); break;
- case 'z': handler.on_utc_offset(numeric_system::standard); break;
- case 'Z': handler.on_tz_name(); break;
- // Alternative representation:
- case 'E': {
- if (ptr == end) FMT_THROW(format_error("invalid format"));
- c = *ptr++;
- switch (c) {
- case 'Y': handler.on_year(numeric_system::alternative, pad); break;
- case 'y': handler.on_offset_year(); break;
- case 'C': handler.on_century(numeric_system::alternative); break;
- case 'c': handler.on_datetime(numeric_system::alternative); break;
- case 'x': handler.on_loc_date(numeric_system::alternative); break;
- case 'X': handler.on_loc_time(numeric_system::alternative); break;
- case 'z': handler.on_utc_offset(numeric_system::alternative); break;
- default: FMT_THROW(format_error("invalid format"));
- }
- break;
- }
- case 'O':
- if (ptr == end) FMT_THROW(format_error("invalid format"));
- c = *ptr++;
- switch (c) {
- case 'y': handler.on_short_year(numeric_system::alternative); break;
- case 'm': handler.on_dec_month(numeric_system::alternative, pad); break;
- case 'U':
- handler.on_dec0_week_of_year(numeric_system::alternative, pad);
- break;
- case 'W':
- handler.on_dec1_week_of_year(numeric_system::alternative, pad);
- break;
- case 'V':
- handler.on_iso_week_of_year(numeric_system::alternative, pad);
- break;
- case 'd':
- handler.on_day_of_month(numeric_system::alternative, pad);
- break;
- case 'e':
- handler.on_day_of_month(numeric_system::alternative, pad_type::space);
- break;
- case 'w': handler.on_dec0_weekday(numeric_system::alternative); break;
- case 'u': handler.on_dec1_weekday(numeric_system::alternative); break;
- case 'H': handler.on_24_hour(numeric_system::alternative, pad); break;
- case 'I': handler.on_12_hour(numeric_system::alternative, pad); break;
- case 'M': handler.on_minute(numeric_system::alternative, pad); break;
- case 'S': handler.on_second(numeric_system::alternative, pad); break;
- case 'z': handler.on_utc_offset(numeric_system::alternative); break;
- default: FMT_THROW(format_error("invalid format"));
- }
- break;
- default: FMT_THROW(format_error("invalid format"));
- }
- begin = ptr;
- }
- if (begin != ptr) handler.on_text(begin, ptr);
- return ptr;
-}
-
-template struct null_chrono_spec_handler {
- FMT_CONSTEXPR void unsupported() {
- static_cast(this)->unsupported();
- }
- FMT_CONSTEXPR void on_year(numeric_system, pad_type) { unsupported(); }
- FMT_CONSTEXPR void on_short_year(numeric_system) { unsupported(); }
- FMT_CONSTEXPR void on_offset_year() { unsupported(); }
- FMT_CONSTEXPR void on_century(numeric_system) { unsupported(); }
- FMT_CONSTEXPR void on_iso_week_based_year() { unsupported(); }
- FMT_CONSTEXPR void on_iso_week_based_short_year() { unsupported(); }
- FMT_CONSTEXPR void on_abbr_weekday() { unsupported(); }
- FMT_CONSTEXPR void on_full_weekday() { unsupported(); }
- FMT_CONSTEXPR void on_dec0_weekday(numeric_system) { unsupported(); }
- FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); }
- FMT_CONSTEXPR void on_abbr_month() { unsupported(); }
- FMT_CONSTEXPR void on_full_month() { unsupported(); }
- FMT_CONSTEXPR void on_dec_month(numeric_system, pad_type) { unsupported(); }
- FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system, pad_type) {
- unsupported();
- }
- FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system, pad_type) {
- unsupported();
- }
- FMT_CONSTEXPR void on_iso_week_of_year(numeric_system, pad_type) {
- unsupported();
- }
- FMT_CONSTEXPR void on_day_of_year(pad_type) { unsupported(); }
- FMT_CONSTEXPR void on_day_of_month(numeric_system, pad_type) {
- unsupported();
- }
- FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); }
- FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); }
- FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); }
- FMT_CONSTEXPR void on_second(numeric_system) { unsupported(); }
- FMT_CONSTEXPR void on_datetime(numeric_system) { unsupported(); }
- FMT_CONSTEXPR void on_loc_date(numeric_system) { unsupported(); }
- FMT_CONSTEXPR void on_loc_time(numeric_system) { unsupported(); }
- FMT_CONSTEXPR void on_us_date() { unsupported(); }
- FMT_CONSTEXPR void on_iso_date() { unsupported(); }
- FMT_CONSTEXPR void on_12_hour_time() { unsupported(); }
- FMT_CONSTEXPR void on_24_hour_time() { unsupported(); }
- FMT_CONSTEXPR void on_iso_time() { unsupported(); }
- FMT_CONSTEXPR void on_am_pm() { unsupported(); }
- FMT_CONSTEXPR void on_duration_value() { unsupported(); }
- FMT_CONSTEXPR void on_duration_unit() { unsupported(); }
- FMT_CONSTEXPR void on_utc_offset(numeric_system) { unsupported(); }
- FMT_CONSTEXPR void on_tz_name() { unsupported(); }
-};
-
-struct tm_format_checker : null_chrono_spec_handler {
- FMT_NORETURN inline void unsupported() {
- FMT_THROW(format_error("no format"));
- }
-
- template
- FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
- FMT_CONSTEXPR void on_year(numeric_system, pad_type) {}
- FMT_CONSTEXPR void on_short_year(numeric_system) {}
- FMT_CONSTEXPR void on_offset_year() {}
- FMT_CONSTEXPR void on_century(numeric_system) {}
- FMT_CONSTEXPR void on_iso_week_based_year() {}
- FMT_CONSTEXPR void on_iso_week_based_short_year() {}
- FMT_CONSTEXPR void on_abbr_weekday() {}
- FMT_CONSTEXPR void on_full_weekday() {}
- FMT_CONSTEXPR void on_dec0_weekday(numeric_system) {}
- FMT_CONSTEXPR void on_dec1_weekday(numeric_system) {}
- FMT_CONSTEXPR void on_abbr_month() {}
- FMT_CONSTEXPR void on_full_month() {}
- FMT_CONSTEXPR void on_dec_month(numeric_system, pad_type) {}
- FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system, pad_type) {}
- FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system, pad_type) {}
- FMT_CONSTEXPR void on_iso_week_of_year(numeric_system, pad_type) {}
- FMT_CONSTEXPR void on_day_of_year(pad_type) {}
- FMT_CONSTEXPR void on_day_of_month(numeric_system, pad_type) {}
- FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {}
- FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {}
- FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {}
- FMT_CONSTEXPR void on_second(numeric_system, pad_type) {}
- FMT_CONSTEXPR void on_datetime(numeric_system) {}
- FMT_CONSTEXPR void on_loc_date(numeric_system) {}
- FMT_CONSTEXPR void on_loc_time(numeric_system) {}
- FMT_CONSTEXPR void on_us_date() {}
- FMT_CONSTEXPR void on_iso_date() {}
- FMT_CONSTEXPR void on_12_hour_time() {}
- FMT_CONSTEXPR void on_24_hour_time() {}
- FMT_CONSTEXPR void on_iso_time() {}
- FMT_CONSTEXPR void on_am_pm() {}
- FMT_CONSTEXPR void on_utc_offset(numeric_system) {}
- FMT_CONSTEXPR void on_tz_name() {}
-};
-
-inline auto tm_wday_full_name(int wday) -> const char* {
- static constexpr const char* full_name_list[] = {
- "Sunday", "Monday", "Tuesday", "Wednesday",
- "Thursday", "Friday", "Saturday"};
- return wday >= 0 && wday <= 6 ? full_name_list[wday] : "?";
-}
-inline auto tm_wday_short_name(int wday) -> const char* {
- static constexpr const char* short_name_list[] = {"Sun", "Mon", "Tue", "Wed",
- "Thu", "Fri", "Sat"};
- return wday >= 0 && wday <= 6 ? short_name_list[wday] : "???";
-}
-
-inline auto tm_mon_full_name(int mon) -> const char* {
- static constexpr const char* full_name_list[] = {
- "January", "February", "March", "April", "May", "June",
- "July", "August", "September", "October", "November", "December"};
- return mon >= 0 && mon <= 11 ? full_name_list[mon] : "?";
-}
-inline auto tm_mon_short_name(int mon) -> const char* {
- static constexpr const char* short_name_list[] = {
- "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
- };
- return mon >= 0 && mon <= 11 ? short_name_list[mon] : "???";
-}
-
-template
-struct has_member_data_tm_gmtoff : std::false_type {};
-template
-struct has_member_data_tm_gmtoff>
- : std::true_type {};
-
-template
-struct has_member_data_tm_zone : std::false_type {};
-template
-struct has_member_data_tm_zone>
- : std::true_type {};
-
-inline void tzset_once() {
- static bool init = []() {
- using namespace fmt_detail;
- _tzset();
- return false;
- }();
- ignore_unused(init);
-}
-
-// Converts value to Int and checks that it's in the range [0, upper).
-template ::value)>
-inline auto to_nonnegative_int(T value, Int upper) -> Int {
- if (!std::is_unsigned::value &&
- (value < 0 || to_unsigned(value) > to_unsigned(upper))) {
- FMT_THROW(fmt::format_error("chrono value is out of range"));
- }
- return static_cast(value);
-}
-template ::value)>
-inline auto to_nonnegative_int(T value, Int upper) -> Int {
- auto int_value = static_cast(value);
- if (int_value < 0 || value > static_cast(upper))
- FMT_THROW(format_error("invalid value"));
- return int_value;
-}
-
-constexpr auto pow10(std::uint32_t n) -> long long {
- return n == 0 ? 1 : 10 * pow10(n - 1);
-}
-
-// Counts the number of fractional digits in the range [0, 18] according to the
-// C++20 spec. If more than 18 fractional digits are required then returns 6 for
-// microseconds precision.
-template () / 10)>
-struct count_fractional_digits {
- static constexpr int value =
- Num % Den == 0 ? N : count_fractional_digits::value;
-};
-
-// Base case that doesn't instantiate any more templates
-// in order to avoid overflow.
-template
-struct count_fractional_digits {
- static constexpr int value = (Num % Den == 0) ? N : 6;
-};
-
-// Format subseconds which are given as an integer type with an appropriate
-// number of digits.
-template
-void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) {
- constexpr auto num_fractional_digits =
- count_fractional_digits::value;
-
- using subsecond_precision = std::chrono::duration<
- typename std::common_type::type,
- std::ratio<1, pow10(num_fractional_digits)>>;
-
- const auto fractional = d - detail::duration_cast(d);
- const auto subseconds =
- std::chrono::treat_as_floating_point<
- typename subsecond_precision::rep>::value
- ? fractional.count()
- : detail::duration_cast(fractional).count();
- auto n = static_cast>(subseconds);
- const int num_digits = count_digits(n);
-
- int leading_zeroes = (std::max)(0, num_fractional_digits - num_digits);
- if (precision < 0) {
- FMT_ASSERT(!std::is_floating_point::value, "");
- if (std::ratio_less::value) {
- *out++ = '.';
- out = detail::fill_n(out, leading_zeroes, '0');
- out = format_decimal(out, n, num_digits);
- }
- } else if (precision > 0) {
- *out++ = '.';
- leading_zeroes = min_of(leading_zeroes, precision);
- int remaining = precision - leading_zeroes;
- out = detail::fill_n(out, leading_zeroes, '0');
- if (remaining < num_digits) {
- int num_truncated_digits = num_digits - remaining;
- n /= to_unsigned(pow10(to_unsigned(num_truncated_digits)));
- if (n != 0) out = format_decimal(out, n, remaining);
- return;
- }
- if (n != 0) {
- out = format_decimal(out, n, num_digits);
- remaining -= num_digits;
- }
- out = detail::fill_n(out, remaining, '0');
- }
-}
-
-// Format subseconds which are given as a floating point type with an
-// appropriate number of digits. We cannot pass the Duration here, as we
-// explicitly need to pass the Rep value in the chrono_formatter.
-template
-void write_floating_seconds(memory_buffer& buf, Duration duration,
- int num_fractional_digits = -1) {
- using rep = typename Duration::rep;
- FMT_ASSERT(std::is_floating_point::value, "");
-
- auto val = duration.count();
-
- if (num_fractional_digits < 0) {
- // For `std::round` with fallback to `round`:
- // On some toolchains `std::round` is not available (e.g. GCC 6).
- using namespace std;
- num_fractional_digits =
- count_fractional_digits::value;
- if (num_fractional_digits < 6 && static_cast(round(val)) != val)
- num_fractional_digits = 6;
- }
-
- fmt::format_to(std::back_inserter(buf), FMT_STRING("{:.{}f}"),
- std::fmod(val * static_cast(Duration::period::num) /
- static_cast(Duration::period::den),
- static_cast(60)),
- num_fractional_digits);
-}
-
-template
-class tm_writer {
- private:
- static constexpr int days_per_week = 7;
-
- const std::locale& loc_;
- const bool is_classic_;
- OutputIt out_;
- const Duration* subsecs_;
- const std::tm& tm_;
-
- auto tm_sec() const noexcept -> int {
- FMT_ASSERT(tm_.tm_sec >= 0 && tm_.tm_sec <= 61, "");
- return tm_.tm_sec;
- }
- auto tm_min() const noexcept -> int {
- FMT_ASSERT(tm_.tm_min >= 0 && tm_.tm_min <= 59, "");
- return tm_.tm_min;
- }
- auto tm_hour() const noexcept -> int {
- FMT_ASSERT(tm_.tm_hour >= 0 && tm_.tm_hour <= 23, "");
- return tm_.tm_hour;
- }
- auto tm_mday() const noexcept -> int {
- FMT_ASSERT(tm_.tm_mday >= 1 && tm_.tm_mday <= 31, "");
- return tm_.tm_mday;
- }
- auto tm_mon() const noexcept -> int {
- FMT_ASSERT(tm_.tm_mon >= 0 && tm_.tm_mon <= 11, "");
- return tm_.tm_mon;
- }
- auto tm_year() const noexcept -> long long { return 1900ll + tm_.tm_year; }
- auto tm_wday() const noexcept -> int {
- FMT_ASSERT(tm_.tm_wday >= 0 && tm_.tm_wday <= 6, "");
- return tm_.tm_wday;
- }
- auto tm_yday() const noexcept -> int {
- FMT_ASSERT(tm_.tm_yday >= 0 && tm_.tm_yday <= 365, "");
- return tm_.tm_yday;
- }
-
- auto tm_hour12() const noexcept -> int {
- const auto h = tm_hour();
- const auto z = h < 12 ? h : h - 12;
- return z == 0 ? 12 : z;
- }
-
- // POSIX and the C Standard are unclear or inconsistent about what %C and %y
- // do if the year is negative or exceeds 9999. Use the convention that %C
- // concatenated with %y yields the same output as %Y, and that %Y contains at
- // least 4 characters, with more only if necessary.
- auto split_year_lower(long long year) const noexcept -> int {
- auto l = year % 100;
- if (l < 0) l = -l; // l in [0, 99]
- return static_cast(l);
- }
-
- // Algorithm: https://en.wikipedia.org/wiki/ISO_week_date.
- auto iso_year_weeks(long long curr_year) const noexcept -> int {
- const auto prev_year = curr_year - 1;
- const auto curr_p =
- (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) %
- days_per_week;
- const auto prev_p =
- (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) %
- days_per_week;
- return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0);
- }
- auto iso_week_num(int tm_yday, int tm_wday) const noexcept -> int {
- return (tm_yday + 11 - (tm_wday == 0 ? days_per_week : tm_wday)) /
- days_per_week;
- }
- auto tm_iso_week_year() const noexcept -> long long {
- const auto year = tm_year();
- const auto w = iso_week_num(tm_yday(), tm_wday());
- if (w < 1) return year - 1;
- if (w > iso_year_weeks(year)) return year + 1;
- return year;
- }
- auto tm_iso_week_of_year() const noexcept -> int {
- const auto year = tm_year();
- const auto w = iso_week_num(tm_yday(), tm_wday());
- if (w < 1) return iso_year_weeks(year - 1);
- if (w > iso_year_weeks(year)) return 1;
- return w;
- }
-
- void write1(int value) {
- *out_++ = static_cast