diff --git a/cscore/src/main/native/cpp/HttpCameraImpl.cpp b/cscore/src/main/native/cpp/HttpCameraImpl.cpp
index 78750603987..da60d77f291 100644
--- a/cscore/src/main/native/cpp/HttpCameraImpl.cpp
+++ b/cscore/src/main/native/cpp/HttpCameraImpl.cpp
@@ -193,7 +193,8 @@ wpi::HttpConnection* HttpCameraImpl::DeviceStreamConnect(
auto [mediaType, contentType] = wpi::split(conn->contentType.str(), ';');
mediaType = wpi::trim(mediaType);
if (mediaType != "multipart/x-mixed-replace") {
- SWARNING("\"{}\": unrecognized Content-Type \"{}\"", req.host, mediaType);
+ SWARNING("\"{}\": unrecognized Content-Type \"{}\"", req.host.str(),
+ mediaType);
std::scoped_lock lock(m_mutex);
m_streamConn = nullptr;
return nullptr;
@@ -216,7 +217,8 @@ wpi::HttpConnection* HttpCameraImpl::DeviceStreamConnect(
}
if (boundary.empty()) {
- SWARNING("\"{}\": empty multi-part boundary or no Content-Type", req.host);
+ SWARNING("\"{}\": empty multi-part boundary or no Content-Type",
+ req.host.str());
std::scoped_lock lock(m_mutex);
m_streamConn = nullptr;
return nullptr;
@@ -281,8 +283,8 @@ bool HttpCameraImpl::DeviceStreamFrame(wpi::raw_istream& is,
// Check the content type (if present)
if (!contentTypeBuf.str().empty() &&
!wpi::starts_with(contentTypeBuf, "image/jpeg")) {
- auto errMsg =
- fmt::format("received unknown Content-Type \"{}\"", contentTypeBuf);
+ auto errMsg = fmt::format("received unknown Content-Type \"{}\"",
+ contentTypeBuf.str());
SWARNING("{}", errMsg);
PutError(errMsg, wpi::Now());
return false;
diff --git a/cscore/src/main/native/cpp/MjpegServerImpl.cpp b/cscore/src/main/native/cpp/MjpegServerImpl.cpp
index ea9ebe11341..82273e598da 100644
--- a/cscore/src/main/native/cpp/MjpegServerImpl.cpp
+++ b/cscore/src/main/native/cpp/MjpegServerImpl.cpp
@@ -400,12 +400,12 @@ void MjpegServerImpl::ConnThread::SendHTML(wpi::raw_ostream& os,
fmt::print(os,
"\n", name, j,
- ch_name);
+ ch_name.str());
}
break;
}
@@ -543,7 +543,7 @@ void MjpegServerImpl::ConnThread::SendJSON(wpi::raw_ostream& os,
for (char ch : *choice) {
ch_name.push_back(std::isprint(ch) ? ch : ' ');
}
- fmt::print(os, "\"{}\": \"{}\"", j, ch_name);
+ fmt::print(os, "\"{}\": \"{}\"", j, ch_name.str());
}
os << "}\n";
}
diff --git a/cscore/src/main/native/linux/UsbCameraImpl.cpp b/cscore/src/main/native/linux/UsbCameraImpl.cpp
index 270905b0e86..7dcc24a3634 100644
--- a/cscore/src/main/native/linux/UsbCameraImpl.cpp
+++ b/cscore/src/main/native/linux/UsbCameraImpl.cpp
@@ -486,7 +486,7 @@ void UsbCameraImpl::CameraThreadMain() {
// If the name is what we expect...
std::string_view name{raw_name.c_str()};
SDEBUG4("got event on '{}' ({}) compare to '{}' ({}) mask {}", name,
- name.size(), base, base.size(), event.mask);
+ name.size(), base.str(), base.size(), event.mask);
if (name == base) {
if ((event.mask & IN_DELETE) != 0) {
wasStreaming = m_streaming;
diff --git a/hal/src/main/native/sim/AddressableLED.cpp b/hal/src/main/native/sim/AddressableLED.cpp
index d9f34aad57d..bce3f1ddfde 100644
--- a/hal/src/main/native/sim/AddressableLED.cpp
+++ b/hal/src/main/native/sim/AddressableLED.cpp
@@ -139,7 +139,7 @@ void HAL_WriteAddressableLEDData(HAL_AddressableLEDHandle handle,
status,
fmt::format(
"Data length must be less than or equal to {}. {} was requested",
- SimAddressableLEDData[led->index].length, length));
+ SimAddressableLEDData[led->index].length.Get(), length));
return;
}
SimAddressableLEDData[led->index].SetData(data, length);
diff --git a/ntcore/src/main/native/cpp/LocalStorage.cpp b/ntcore/src/main/native/cpp/LocalStorage.cpp
index 4d3ea71e15a..da87b705825 100644
--- a/ntcore/src/main/native/cpp/LocalStorage.cpp
+++ b/ntcore/src/main/native/cpp/LocalStorage.cpp
@@ -410,7 +410,8 @@ void LocalStorage::Impl::NetworkAnnounce(TopicData* topic,
}
void LocalStorage::Impl::RemoveNetworkPublisher(TopicData* topic) {
- DEBUG4("LS RemoveNetworkPublisher({}, {})", topic->handle, topic->name);
+ DEBUG4("LS RemoveNetworkPublisher({}, {})", topic->handle.GetHandle(),
+ topic->name);
// this acts as an unpublish
bool didExist = topic->Exists();
topic->onNetwork = false;
diff --git a/roborioteamnumbersetter/src/main/native/cpp/DeploySession.cpp b/roborioteamnumbersetter/src/main/native/cpp/DeploySession.cpp
index b6fced44b54..3fd63cd7129 100644
--- a/roborioteamnumbersetter/src/main/native/cpp/DeploySession.cpp
+++ b/roborioteamnumbersetter/src/main/native/cpp/DeploySession.cpp
@@ -73,11 +73,11 @@ bool DeploySession::ChangeTeamNumber(const std::string& macAddress,
in_addr addr;
addr.s_addr = ipAddress;
wpi::uv::AddrToName(addr, &ip);
- DEBUG("Trying to establish SSH connection to {}.", ip);
+ DEBUG("Trying to establish SSH connection to {}.", ip.str());
try {
SshSession session{ip.str(), kPort, kUsername, kPassword, m_logger};
session.Open();
- DEBUG("SSH connection to {} was successful.", ip);
+ DEBUG("SSH connection to {} was successful.", ip.str());
SUCCESS("roboRIO Connected!");
@@ -93,7 +93,7 @@ bool DeploySession::ChangeTeamNumber(const std::string& macAddress,
throw e;
}
} catch (const SshSession::SshException& e) {
- DEBUG("SSH connection to {} failed with {}.", ip, e.what());
+ DEBUG("SSH connection to {} failed with {}.", ip.str(), e.what());
throw e;
}
return 0;
@@ -117,11 +117,11 @@ bool DeploySession::Reboot(const std::string& macAddress,
in_addr addr;
addr.s_addr = ipAddress;
wpi::uv::AddrToName(addr, &ip);
- DEBUG("Trying to establish SSH connection to {}.", ip);
+ DEBUG("Trying to establish SSH connection to {}.", ip.str());
try {
SshSession session{ip.str(), kPort, kUsername, kPassword, m_logger};
session.Open();
- DEBUG("SSH connection to {} was successful.", ip);
+ DEBUG("SSH connection to {} was successful.", ip.str());
SUCCESS("roboRIO Connected!");
@@ -132,7 +132,7 @@ bool DeploySession::Reboot(const std::string& macAddress,
throw e;
}
} catch (const SshSession::SshException& e) {
- DEBUG("SSH connection to {} failed with {}.", ip, e.what());
+ DEBUG("SSH connection to {} failed with {}.", ip.str(), e.what());
throw e;
}
return 0;
@@ -156,11 +156,11 @@ bool DeploySession::Blink(const std::string& macAddress,
in_addr addr;
addr.s_addr = ipAddress;
wpi::uv::AddrToName(addr, &ip);
- DEBUG("Trying to establish SSH connection to {}.", ip);
+ DEBUG("Trying to establish SSH connection to {}.", ip.str());
try {
SshSession session{ip.str(), kPort, kUsername, kPassword, m_logger};
session.Open();
- DEBUG("SSH connection to {} was successful.", ip);
+ DEBUG("SSH connection to {} was successful.", ip.str());
SUCCESS("roboRIO Connected!");
@@ -175,7 +175,7 @@ bool DeploySession::Blink(const std::string& macAddress,
throw e;
}
} catch (const SshSession::SshException& e) {
- DEBUG("SSH connection to {} failed with {}.", ip, e.what());
+ DEBUG("SSH connection to {} failed with {}.", ip.str(), e.what());
throw e;
}
return 0;
diff --git a/upstream_utils/fmt_patches/0001-Don-t-throw-on-write-failure.patch b/upstream_utils/fmt_patches/0001-Don-t-throw-on-write-failure.patch
index c9773bd4753..5b05dbf66fc 100644
--- a/upstream_utils/fmt_patches/0001-Don-t-throw-on-write-failure.patch
+++ b/upstream_utils/fmt_patches/0001-Don-t-throw-on-write-failure.patch
@@ -1,19 +1,19 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tyler Veness
Date: Wed, 18 May 2022 10:21:49 -0700
-Subject: [PATCH 1/3] Don't throw on write failure
+Subject: [PATCH 1/2] Don't throw on write failure
---
include/fmt/format-inl.h | 4 +---
include/fmt/xchar.h | 3 +--
- src/os.cc | 3 +--
- 3 files changed, 3 insertions(+), 7 deletions(-)
+ src/os.cc | 4 +---
+ 3 files changed, 3 insertions(+), 8 deletions(-)
diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h
-index 22b1ec8df0eb14b3f7f21797a19586b50b8423fd..abe4ff13db6287f9c5229df6c4b46beeed020bce 100644
+index dac2d437a41ab7b0b4e72895212b5a972ada73a9..af6ba74d618f29c77339e8a82906cccd26a2efa4 100644
--- a/include/fmt/format-inl.h
+++ b/include/fmt/format-inl.h
-@@ -79,9 +79,7 @@ FMT_FUNC void report_error(format_func func, int error_code,
+@@ -75,9 +75,7 @@ FMT_FUNC void report_error(format_func func, int error_code,
// A wrapper around fwrite that throws on error.
inline void fwrite_fully(const void* ptr, size_t size, size_t count,
FILE* stream) {
@@ -25,30 +25,31 @@ index 22b1ec8df0eb14b3f7f21797a19586b50b8423fd..abe4ff13db6287f9c5229df6c4b46bee
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
diff --git a/include/fmt/xchar.h b/include/fmt/xchar.h
-index 3b5bc15ca0a1d92d721611ddc70e80f098fb79ae..fc3c67f21c2294b8e73d6ea53f25c877f733082c 100644
+index 625ec36922e9bcc44a76b3c40792cb08ede63813..0f79c1720a4c855bb7088381e93af08eae56d66c 100644
--- a/include/fmt/xchar.h
+++ b/include/fmt/xchar.h
-@@ -200,8 +200,7 @@ inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) {
- wmemory_buffer buffer;
- detail::vformat_to(buffer, fmt, args);
- buffer.push_back(L'\0');
-- if (std::fputws(buffer.data(), f) == -1)
+@@ -220,8 +220,7 @@ inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) {
+ auto buf = wmemory_buffer();
+ detail::vformat_to(buf, fmt, args);
+ buf.push_back(L'\0');
+- if (std::fputws(buf.data(), f) == -1)
- FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
-+ std::fputws(buffer.data(), f);
++ std::fputws(buf.data(), f);
}
inline void vprint(wstring_view fmt, wformat_args args) {
diff --git a/src/os.cc b/src/os.cc
-index f388ead0191b61ae0e5c24692c1f523aae3c9fbb..2c499512b5396830d7f1eb8fb5874586898802dd 100644
+index bca410e945e0347d349e06179906a43b38b56a5c..d7ded50f9870a885d1ce1835fecc4f740858127a 100644
--- a/src/os.cc
+++ b/src/os.cc
-@@ -277,8 +277,7 @@ std::size_t file::read(void* buffer, std::size_t count) {
- std::size_t file::write(const void* buffer, std::size_t count) {
+@@ -258,9 +258,7 @@ long long file::size() const {
+ std::size_t file::read(void* buffer, std::size_t count) {
rwresult result = 0;
- FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
-- if (result < 0) FMT_THROW(system_error(errno, "cannot write to file"));
+ FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
+- if (result < 0)
+- FMT_THROW(system_error(errno, FMT_STRING("cannot read from file")));
- return detail::to_unsigned(result);
+ return count;
}
- file file::dup(int fd) {
+ std::size_t file::write(const void* buffer, std::size_t count) {
diff --git a/upstream_utils/fmt_patches/0002-Suppress-C-20-clang-tidy-warning-false-positive.patch b/upstream_utils/fmt_patches/0002-Suppress-C-20-clang-tidy-warning-false-positive.patch
deleted file mode 100644
index c239a68839f..00000000000
--- a/upstream_utils/fmt_patches/0002-Suppress-C-20-clang-tidy-warning-false-positive.patch
+++ /dev/null
@@ -1,22 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Tyler Veness
-Date: Fri, 2 Sep 2022 15:12:54 -0700
-Subject: [PATCH 2/3] Suppress C++20 clang-tidy warning false positive
-
----
- include/fmt/core.h | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/include/fmt/core.h b/include/fmt/core.h
-index f6a37af9e35d3d1c623803d39f73c9a85f5aff10..5c210bcb8428dba6419a055b2dd47bd717f5dbab 100644
---- a/include/fmt/core.h
-+++ b/include/fmt/core.h
-@@ -2952,7 +2952,7 @@ class format_string_checker {
- basic_string_view format_str, ErrorHandler eh)
- : context_(format_str, num_args, types_, eh),
- parse_funcs_{&parse_format_specs...},
-- types_{
-+ types_{ // NOLINT(clang-analyzer-optin.cplusplus.UninitializedObject)
- mapped_type_constant>::value...} {
- }
diff --git a/upstream_utils/fmt_patches/0002-Suppress-warnings-we-can-t-fix.patch b/upstream_utils/fmt_patches/0002-Suppress-warnings-we-can-t-fix.patch
new file mode 100644
index 00000000000..a3866e22990
--- /dev/null
+++ b/upstream_utils/fmt_patches/0002-Suppress-warnings-we-can-t-fix.patch
@@ -0,0 +1,28 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Tyler Veness
+Date: Tue, 16 May 2023 13:49:18 -0700
+Subject: [PATCH 2/2] Suppress warnings we can't fix
+
+---
+ include/fmt/format.h | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/include/fmt/format.h b/include/fmt/format.h
+index e5bd8b110efe49e12a12b004ea246a4dba671a6f..f11be0d6d58f3d992d7d06adb3d9576f81ecfe11 100644
+--- a/include/fmt/format.h
++++ b/include/fmt/format.h
+@@ -1324,7 +1324,14 @@ inline auto equal2(const char* lhs, const char* rhs) -> bool {
+ template
+ FMT_CONSTEXPR20 FMT_INLINE void copy2(Char* dst, const char* src) {
+ if (!is_constant_evaluated() && sizeof(Char) == sizeof(char)) {
++#if FMT_GCC_VERSION && FMT_GCC_VERSION >= 1000
++# pragma GCC diagnostic push
++# pragma GCC diagnostic ignored "-Wstringop-overflow"
++#endif
+ memcpy(dst, src, 2);
++#if FMT_GCC_VERSION && FMT_GCC_VERSION >= 1000
++# pragma GCC diagnostic pop
++#endif
+ return;
+ }
+ *dst++ = static_cast(*src++);
diff --git a/upstream_utils/fmt_patches/0003-Suppress-warnings-we-can-t-fix.patch b/upstream_utils/fmt_patches/0003-Suppress-warnings-we-can-t-fix.patch
deleted file mode 100644
index 81b2435e0af..00000000000
--- a/upstream_utils/fmt_patches/0003-Suppress-warnings-we-can-t-fix.patch
+++ /dev/null
@@ -1,65 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Tyler Veness
-Date: Sat, 13 May 2023 15:18:52 -0700
-Subject: [PATCH 3/3] Suppress warnings we can't fix
-
----
- include/fmt/core.h | 7 +++++++
- include/fmt/format.h | 15 +++++++++++++++
- 2 files changed, 22 insertions(+)
-
-diff --git a/include/fmt/core.h b/include/fmt/core.h
-index 5c210bcb8428dba6419a055b2dd47bd717f5dbab..962115af6db9904fc5ae8f4b2c094ada7de7a8a1 100644
---- a/include/fmt/core.h
-+++ b/include/fmt/core.h
-@@ -1732,7 +1732,14 @@ constexpr auto encode_types() -> unsigned long long {
-
- template
- FMT_CONSTEXPR FMT_INLINE auto make_value(T&& val) -> value {
-+#if FMT_GCC_VERSION && FMT_GCC_VERSION >= 1300
-+# pragma GCC diagnostic push
-+# pragma GCC diagnostic ignored "-Wdangling-reference"
-+#endif
- const auto& arg = arg_mapper().map(FMT_FORWARD(val));
-+#if FMT_GCC_VERSION && FMT_GCC_VERSION >= 1300
-+# pragma GCC diagnostic pop
-+#endif
-
- constexpr bool formattable_char =
- !std::is_same::value;
-diff --git a/include/fmt/format.h b/include/fmt/format.h
-index 7c607dbd30421b5bc57aaafc1edabeafdf2a3ea0..60b806d5e65441f9aed8a410f0f88ceac4f4fb32 100644
---- a/include/fmt/format.h
-+++ b/include/fmt/format.h
-@@ -922,8 +922,16 @@ FMT_CONSTEXPR20 void basic_memory_buffer::grow(
- T* new_data =
- std::allocator_traits::allocate(alloc_, new_capacity);
- // The following code doesn't throw, so the raw pointer above doesn't leak.
-+#if FMT_GCC_VERSION && FMT_GCC_VERSION >= 1300
-+# pragma GCC diagnostic push
-+# pragma GCC diagnostic ignored "-Warray-bounds"
-+# pragma GCC diagnostic ignored "-Wstringop-overflow"
-+#endif
- std::uninitialized_copy(old_data, old_data + this->size(),
- detail::make_checked(new_data, new_capacity));
-+#if FMT_GCC_VERSION && FMT_GCC_VERSION >= 1300
-+# pragma GCC diagnostic pop
-+#endif
- this->set(new_data, new_capacity);
- // deallocate must not throw according to the standard, but even if it does,
- // the buffer already uses the new storage and will deallocate it in
-@@ -2804,7 +2812,14 @@ class bigint {
- auto size = other.bigits_.size();
- bigits_.resize(size);
- auto data = other.bigits_.data();
-+#if FMT_GCC_VERSION && FMT_GCC_VERSION >= 1300
-+# pragma GCC diagnostic push
-+# pragma GCC diagnostic ignored "-Warray-bounds"
-+#endif
- std::copy(data, data + size, make_checked(bigits_.data(), size));
-+#if FMT_GCC_VERSION && FMT_GCC_VERSION >= 1300
-+# pragma GCC diagnostic pop
-+#endif
- exp_ = other.exp_;
- }
-
diff --git a/upstream_utils/update_fmt.py b/upstream_utils/update_fmt.py
index 554fda70a84..92c58c81dba 100755
--- a/upstream_utils/update_fmt.py
+++ b/upstream_utils/update_fmt.py
@@ -13,7 +13,7 @@
def main():
- upstream_root = clone_repo("https://github.com/fmtlib/fmt", "9.1.0")
+ upstream_root = clone_repo("https://github.com/fmtlib/fmt", "10.1.0")
wpilib_root = get_repo_root()
wpiutil = os.path.join(wpilib_root, "wpiutil")
@@ -21,8 +21,7 @@ def main():
os.chdir(upstream_root)
for f in [
"0001-Don-t-throw-on-write-failure.patch",
- "0002-Suppress-C-20-clang-tidy-warning-false-positive.patch",
- "0003-Suppress-warnings-we-can-t-fix.patch",
+ "0002-Suppress-warnings-we-can-t-fix.patch",
]:
git_am(os.path.join(wpilib_root, "upstream_utils/fmt_patches", f))
diff --git a/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp b/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp
index 0befdda5085..eb55fd0124e 100644
--- a/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp
+++ b/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp
@@ -79,7 +79,7 @@ JNIEXPORT void JNICALL
Java_edu_wpi_first_util_WPIUtilJNI_writeStderr
(JNIEnv* env, jclass, jstring str)
{
- fmt::print(stderr, "{}", JStringRef{env, str});
+ fmt::print(stderr, "{}", JStringRef{env, str}.str());
}
/*
diff --git a/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/args.h b/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/args.h
index a3966d14071..2d684e7cc11 100644
--- a/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/args.h
+++ b/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/args.h
@@ -1,4 +1,4 @@
-// Formatting library for C++ - dynamic format arguments
+// Formatting library for C++ - dynamic argument lists
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
diff --git a/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/chrono.h b/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/chrono.h
index b112f76e991..ff3e1445b96 100644
--- a/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/chrono.h
+++ b/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/chrono.h
@@ -22,6 +22,24 @@
FMT_BEGIN_NAMESPACE
+// Check if std::chrono::local_t is available.
+#ifndef FMT_USE_LOCAL_TIME
+# ifdef __cpp_lib_chrono
+# define FMT_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L)
+# else
+# define FMT_USE_LOCAL_TIME 0
+# endif
+#endif
+
+// Check if std::chrono::utc_timestamp is available.
+#ifndef FMT_USE_UTC_TIME
+# ifdef __cpp_lib_chrono
+# define FMT_USE_UTC_TIME (__cpp_lib_chrono >= 201907L)
+# else
+# define FMT_USE_UTC_TIME 0
+# endif
+#endif
+
// Enable tzset.
#ifndef FMT_USE_TZSET
// UWP doesn't provide _tzset.
@@ -203,7 +221,8 @@ To safe_duration_cast(std::chrono::duration from,
}
const auto min1 =
(std::numeric_limits::min)() / Factor::num;
- if (!std::is_unsigned::value && count < min1) {
+ if (detail::const_check(!std::is_unsigned::value) &&
+ count < min1) {
ec = 1;
return {};
}
@@ -358,37 +377,11 @@ auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc)
unit_t unit;
write_codecvt(unit, in, loc);
// In UTF-8 is used one to four one-byte code units.
- auto&& buf = basic_memory_buffer();
- for (code_unit* p = unit.buf; p != unit.end; ++p) {
- uint32_t c = static_cast(*p);
- if (sizeof(code_unit) == 2 && c >= 0xd800 && c <= 0xdfff) {
- // surrogate pair
- ++p;
- if (p == unit.end || (c & 0xfc00) != 0xd800 ||
- (*p & 0xfc00) != 0xdc00) {
- FMT_THROW(format_error("failed to format time"));
- }
- c = (c << 10) + static_cast(*p) - 0x35fdc00;
- }
- if (c < 0x80) {
- buf.push_back(static_cast(c));
- } else if (c < 0x800) {
- buf.push_back(static_cast(0xc0 | (c >> 6)));
- buf.push_back(static_cast(0x80 | (c & 0x3f)));
- } else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) {
- buf.push_back(static_cast(0xe0 | (c >> 12)));
- buf.push_back(static_cast(0x80 | ((c & 0xfff) >> 6)));
- buf.push_back(static_cast(0x80 | (c & 0x3f)));
- } else if (c >= 0x10000 && c <= 0x10ffff) {
- buf.push_back(static_cast(0xf0 | (c >> 18)));
- buf.push_back(static_cast(0x80 | ((c & 0x3ffff) >> 12)));
- buf.push_back(static_cast(0x80 | ((c & 0xfff) >> 6)));
- buf.push_back(static_cast(0x80 | (c & 0x3f)));
- } else {
- FMT_THROW(format_error("failed to format time"));
- }
- }
- return copy_str(buf.data(), buf.data() + buf.size(), out);
+ 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_str(u.c_str(), u.c_str() + u.size(), out);
}
return copy_str(in.data(), in.data() + in.size(), out);
}
@@ -427,7 +420,7 @@ 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 buf.out();
+ return get_iterator(buf, out);
}
template time_point) {
- return localtime(std::chrono::system_clock::to_time_t(time_point));
+#if FMT_USE_LOCAL_TIME
+template
+inline auto localtime(std::chrono::local_time time) -> std::tm {
+ return localtime(std::chrono::system_clock::to_time_t(
+ std::chrono::current_zone()->to_sys(time)));
}
+#endif
/**
Converts given time since epoch as ``std::time_t`` value into calendar time,
@@ -523,7 +519,7 @@ inline std::tm gmtime(std::time_t time) {
}
#endif
};
- dispatcher gt(time);
+ 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_;
@@ -534,7 +530,7 @@ inline std::tm gmtime(
return gmtime(std::chrono::system_clock::to_time_t(time_point));
}
-FMT_BEGIN_DETAIL_NAMESPACE
+namespace detail {
// Writes two-digit numbers a, b and c separated by sep to buf.
// The method by Pavel Novikov based on
@@ -599,12 +595,39 @@ enum class numeric_system {
alternative
};
+// Glibc extensions for formatting numeric values.
+enum class pad_type {
+ unspecified,
+ // Do not pad a numeric result string.
+ none,
+ // Pad a numeric result string with zeros even if the conversion specifier
+ // character uses space-padding by default.
+ zero,
+ // 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 std::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 const Char* parse_chrono_format(const Char* begin,
const Char* end,
Handler&& handler) {
+ if (begin == end || *begin == '}') return begin;
+ if (*begin != '%') FMT_THROW(format_error("invalid format"));
auto ptr = begin;
+ pad_type pad = pad_type::unspecified;
while (ptr != end) {
auto c = *ptr;
if (c == '}') break;
@@ -615,6 +638,22 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
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;
+ case '0':
+ pad = pad_type::zero;
+ ++ptr;
+ break;
+ }
+ if (ptr == end) FMT_THROW(format_error("invalid format"));
c = *ptr++;
switch (c) {
case '%':
@@ -691,16 +730,16 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
break;
// Hour, minute, second:
case 'H':
- handler.on_24_hour(numeric_system::standard);
+ handler.on_24_hour(numeric_system::standard, pad);
break;
case 'I':
- handler.on_12_hour(numeric_system::standard);
+ handler.on_12_hour(numeric_system::standard, pad);
break;
case 'M':
- handler.on_minute(numeric_system::standard);
+ handler.on_minute(numeric_system::standard, pad);
break;
case 'S':
- handler.on_second(numeric_system::standard);
+ handler.on_second(numeric_system::standard, pad);
break;
// Other:
case 'c':
@@ -737,7 +776,7 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
handler.on_duration_unit();
break;
case 'z':
- handler.on_utc_offset();
+ handler.on_utc_offset(numeric_system::standard);
break;
case 'Z':
handler.on_tz_name();
@@ -765,6 +804,9 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
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"));
}
@@ -802,16 +844,19 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
handler.on_dec1_weekday(numeric_system::alternative);
break;
case 'H':
- handler.on_24_hour(numeric_system::alternative);
+ handler.on_24_hour(numeric_system::alternative, pad);
break;
case 'I':
- handler.on_12_hour(numeric_system::alternative);
+ handler.on_12_hour(numeric_system::alternative, pad);
break;
case 'M':
- handler.on_minute(numeric_system::alternative);
+ handler.on_minute(numeric_system::alternative, pad);
break;
case 'S':
- handler.on_second(numeric_system::alternative);
+ 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"));
@@ -864,7 +909,7 @@ template struct null_chrono_spec_handler {
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() { unsupported(); }
+ FMT_CONSTEXPR void on_utc_offset(numeric_system) { unsupported(); }
FMT_CONSTEXPR void on_tz_name() { unsupported(); }
};
@@ -892,10 +937,10 @@ struct tm_format_checker : null_chrono_spec_handler {
FMT_CONSTEXPR void on_day_of_year() {}
FMT_CONSTEXPR void on_day_of_month(numeric_system) {}
FMT_CONSTEXPR void on_day_of_month_space(numeric_system) {}
- FMT_CONSTEXPR void on_24_hour(numeric_system) {}
- FMT_CONSTEXPR void on_12_hour(numeric_system) {}
- FMT_CONSTEXPR void on_minute(numeric_system) {}
- FMT_CONSTEXPR void on_second(numeric_system) {}
+ 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) {}
@@ -905,7 +950,7 @@ struct tm_format_checker : null_chrono_spec_handler {
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() {}
+ FMT_CONSTEXPR void on_utc_offset(numeric_system) {}
FMT_CONSTEXPR void on_tz_name() {}
};
@@ -957,13 +1002,130 @@ inline void tzset_once() {
}
#endif
-template class tm_writer {
+// Converts value to Int and checks that it's in the range [0, upper).
+template ::value)>
+inline Int to_nonnegative_int(T value, Int upper) {
+ FMT_ASSERT(std::is_unsigned::value ||
+ (value >= 0 && to_unsigned(value) <= to_unsigned(upper)),
+ "invalid value");
+ (void)upper;
+ return static_cast(value);
+}
+template ::value)>
+inline Int to_nonnegative_int(T value, Int upper) {
+ if (value < 0 || value > static_cast(upper))
+ FMT_THROW(format_error("invalid value"));
+ return static_cast(value);
+}
+
+constexpr long long pow10(std::uint32_t n) {
+ 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, detail::pow10(num_fractional_digits)>>;
+
+ const auto fractional =
+ d - std::chrono::duration_cast(d);
+ const auto subseconds =
+ std::chrono::treat_as_floating_point<
+ typename subsecond_precision::rep>::value
+ ? fractional.count()
+ : std::chrono::duration_cast(fractional).count();
+ auto n = static_cast>(subseconds);
+ const int num_digits = detail::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 = std::fill_n(out, leading_zeroes, '0');
+ out = format_decimal(out, n, num_digits).end;
+ }
+ } else {
+ *out++ = '.';
+ leading_zeroes = (std::min)(leading_zeroes, precision);
+ out = std::fill_n(out, leading_zeroes, '0');
+ int remaining = precision - leading_zeroes;
+ if (remaining != 0 && remaining < num_digits) {
+ n /= to_unsigned(detail::pow10(to_unsigned(num_digits - remaining)));
+ out = format_decimal(out, n, remaining).end;
+ return;
+ }
+ out = format_decimal(out, n, num_digits).end;
+ remaining -= num_digits;
+ out = std::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;
+ }
+
+ 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 {
@@ -1051,6 +1213,17 @@ template class tm_writer {
*out_++ = *d++;
*out_++ = *d;
}
+ void write2(int value, pad_type pad) {
+ unsigned int v = to_unsigned(value) % 100;
+ if (v >= 10) {
+ const char* d = digits2(v);
+ *out_++ = *d++;
+ *out_++ = *d;
+ } else {
+ out_ = detail::write_padding(out_, pad);
+ *out_++ = static_cast('0' + v);
+ }
+ }
void write_year_extended(long long year) {
// At least 4 characters.
@@ -1074,7 +1247,7 @@ template class tm_writer {
}
}
- void write_utc_offset(long offset) {
+ void write_utc_offset(long offset, numeric_system ns) {
if (offset < 0) {
*out_++ = '-';
offset = -offset;
@@ -1083,14 +1256,15 @@ template class tm_writer {
}
offset /= 60;
write2(static_cast(offset / 60));
+ if (ns != numeric_system::standard) *out_++ = ':';
write2(static_cast(offset % 60));
}
template ::value)>
- void format_utc_offset_impl(const T& tm) {
- write_utc_offset(tm.tm_gmtoff);
+ void format_utc_offset_impl(const T& tm, numeric_system ns) {
+ write_utc_offset(tm.tm_gmtoff, ns);
}
template ::value)>
- void format_utc_offset_impl(const T& tm) {
+ void format_utc_offset_impl(const T& tm, numeric_system ns) {
#if defined(_WIN32) && defined(_UCRT)
# if FMT_USE_TZSET
tzset_once();
@@ -1102,10 +1276,17 @@ template class tm_writer {
_get_dstbias(&dstbias);
offset += dstbias;
}
- write_utc_offset(-offset);
+ write_utc_offset(-offset, ns);
#else
- ignore_unused(tm);
- format_localized('z');
+ if (ns == numeric_system::standard) return format_localized('z');
+
+ // Extract timezone offset from timezone conversion functions.
+ std::tm gtm = tm;
+ std::time_t gt = std::mktime(>m);
+ std::tm ltm = gmtime(gt);
+ std::time_t lt = std::mktime(<m);
+ long offset = gt - lt;
+ write_utc_offset(offset, ns);
#endif
}
@@ -1126,10 +1307,12 @@ template class tm_writer {
}
public:
- tm_writer(const std::locale& loc, OutputIt out, const std::tm& tm)
+ tm_writer(const std::locale& loc, OutputIt out, const std::tm& tm,
+ const Duration* subsecs = nullptr)
: loc_(loc),
is_classic_(loc_ == get_classic_locale()),
out_(out),
+ subsecs_(subsecs),
tm_(tm) {}
OutputIt out() const { return out_; }
@@ -1227,7 +1410,7 @@ template class tm_writer {
out_ = copy_str(std::begin(buf) + offset, std::end(buf), out_);
}
- void on_utc_offset() { format_utc_offset_impl(tm_); }
+ void on_utc_offset(numeric_system ns) { format_utc_offset_impl(tm_, ns); }
void on_tz_name() { format_tz_name_impl(tm_); }
void on_year(numeric_system ns) {
@@ -1315,22 +1498,41 @@ template class tm_writer {
}
}
- void on_24_hour(numeric_system ns) {
- if (is_classic_ || ns == numeric_system::standard) return write2(tm_hour());
+ void on_24_hour(numeric_system ns, pad_type pad) {
+ if (is_classic_ || ns == numeric_system::standard)
+ return write2(tm_hour(), pad);
format_localized('H', 'O');
}
- void on_12_hour(numeric_system ns) {
+ void on_12_hour(numeric_system ns, pad_type pad) {
if (is_classic_ || ns == numeric_system::standard)
- return write2(tm_hour12());
+ return write2(tm_hour12(), pad);
format_localized('I', 'O');
}
- void on_minute(numeric_system ns) {
- if (is_classic_ || ns == numeric_system::standard) return write2(tm_min());
+ void on_minute(numeric_system ns, pad_type pad) {
+ if (is_classic_ || ns == numeric_system::standard)
+ return write2(tm_min(), pad);
format_localized('M', 'O');
}
- void on_second(numeric_system ns) {
- if (is_classic_ || ns == numeric_system::standard) return write2(tm_sec());
- format_localized('S', 'O');
+
+ void on_second(numeric_system ns, pad_type pad) {
+ if (is_classic_ || ns == numeric_system::standard) {
+ write2(tm_sec(), pad);
+ if (subsecs_) {
+ if (std::is_floating_point::value) {
+ auto buf = memory_buffer();
+ write_floating_seconds(buf, *subsecs_);
+ if (buf.size() > 1) {
+ // Remove the leading "0", write something like ".123".
+ out_ = std::copy(buf.begin() + 1, buf.end(), out_);
+ }
+ } else {
+ write_fractional_seconds(out_, *subsecs_);
+ }
+ }
+ } else {
+ // Currently no formatting of subseconds when a locale is set.
+ format_localized('S', 'O');
+ }
}
void on_12_hour_time() {
@@ -1351,10 +1553,9 @@ template class tm_writer {
write2(tm_min());
}
void on_iso_time() {
- char buf[8];
- write_digit2_separated(buf, to_unsigned(tm_hour()), to_unsigned(tm_min()),
- to_unsigned(tm_sec()), ':');
- out_ = copy_str(std::begin(buf), std::end(buf), out_);
+ on_24_hour_time();
+ *out_++ = ':';
+ on_second(numeric_system::standard, pad_type::unspecified);
}
void on_am_pm() {
@@ -1372,43 +1573,34 @@ template class tm_writer {
};
struct chrono_format_checker : null_chrono_spec_handler {
+ bool has_precision_integral = false;
+
FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); }
template
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
- FMT_CONSTEXPR void on_24_hour(numeric_system) {}
- FMT_CONSTEXPR void on_12_hour(numeric_system) {}
- FMT_CONSTEXPR void on_minute(numeric_system) {}
- FMT_CONSTEXPR void on_second(numeric_system) {}
+ 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_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_duration_value() {}
+ FMT_CONSTEXPR void on_duration_value() const {
+ if (has_precision_integral) {
+ FMT_THROW(format_error("precision not allowed for this argument type"));
+ }
+ }
FMT_CONSTEXPR void on_duration_unit() {}
};
-template ::value)>
+template ::value&& has_isfinite::value)>
inline bool isfinite(T) {
return true;
}
-// Converts value to Int and checks that it's in the range [0, upper).
-template ::value)>
-inline Int to_nonnegative_int(T value, Int upper) {
- FMT_ASSERT(std::is_unsigned::value ||
- (value >= 0 && to_unsigned(value) <= to_unsigned(upper)),
- "invalid value");
- (void)upper;
- return static_cast(value);
-}
-template ::value)>
-inline Int to_nonnegative_int(T value, Int upper) {
- if (value < 0 || value > static_cast(upper))
- FMT_THROW(format_error("invalid value"));
- return static_cast(value);
-}
-
template ::value)>
inline T mod(T x, int y) {
return x % static_cast(y);
@@ -1463,47 +1655,6 @@ inline std::chrono::duration get_milliseconds(
#endif
}
-// 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;
-};
-
-constexpr long long pow10(std::uint32_t n) {
- return n == 0 ? 1 : 10 * pow10(n - 1);
-}
-
-template ::is_signed)>
-constexpr std::chrono::duration abs(
- std::chrono::duration d) {
- // We need to compare the duration using the count() method directly
- // due to a compiler bug in clang-11 regarding the spaceship operator,
- // when -Wzero-as-null-pointer-constant is enabled.
- // In clang-12 the bug has been fixed. See
- // https://bugs.llvm.org/show_bug.cgi?id=46235 and the reproducible example:
- // https://www.godbolt.org/z/Knbb5joYx.
- return d.count() >= d.zero().count() ? d : -d;
-}
-
-template ::is_signed)>
-constexpr std::chrono::duration abs(
- std::chrono::duration d) {
- return d;
-}
-
template ::value)>
OutputIt format_duration_value(OutputIt out, Rep val, int) {
@@ -1513,7 +1664,7 @@ OutputIt format_duration_value(OutputIt out, Rep val, int) {
template ::value)>
OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
- auto specs = basic_format_specs();
+ auto specs = format_specs();
specs.precision = precision;
specs.type = precision >= 0 ? presentation_type::fixed_lower
: presentation_type::general_lower;
@@ -1654,44 +1805,16 @@ struct chrono_formatter {
}
}
- void write(Rep value, int width) {
+ void write(Rep value, int width, pad_type pad = pad_type::unspecified) {
write_sign();
if (isnan(value)) return write_nan();
uint32_or_64_or_128_t n =
to_unsigned(to_nonnegative_int(value, max_value()));
int num_digits = detail::count_digits(n);
- if (width > num_digits) out = std::fill_n(out, width - num_digits, '0');
- out = format_decimal(out, n, num_digits).end;
- }
-
- template void write_fractional_seconds(Duration d) {
- FMT_ASSERT(!std::is_floating_point::value, "");
- constexpr auto num_fractional_digits =
- count_fractional_digits::value;
-
- using subsecond_precision = std::chrono::duration<
- typename std::common_type::type,
- std::ratio<1, detail::pow10(num_fractional_digits)>>;
- if (std::ratio_less::value) {
- *out++ = '.';
- auto fractional =
- detail::abs(d) - std::chrono::duration_cast(d);
- auto subseconds =
- std::chrono::treat_as_floating_point<
- typename subsecond_precision::rep>::value
- ? fractional.count()
- : std::chrono::duration_cast(fractional)
- .count();
- uint32_or_64_or_128_t n =
- to_unsigned(to_nonnegative_int(subseconds, max_value()));
- int num_digits = detail::count_digits(n);
- if (num_fractional_digits > num_digits)
- out = std::fill_n(out, num_fractional_digits - num_digits, '0');
- out = format_decimal(out, n, num_digits).end;
+ if (width > num_digits) {
+ out = detail::write_padding(out, pad, width - num_digits);
}
+ out = format_decimal(out, n, num_digits).end;
}
void write_nan() { std::copy_n("nan", 3, out); }
@@ -1723,7 +1846,7 @@ struct chrono_formatter {
void on_loc_time(numeric_system) {}
void on_us_date() {}
void on_iso_date() {}
- void on_utc_offset() {}
+ void on_utc_offset(numeric_system) {}
void on_tz_name() {}
void on_year(numeric_system) {}
void on_short_year(numeric_system) {}
@@ -1739,58 +1862,56 @@ struct chrono_formatter {
void on_day_of_month(numeric_system) {}
void on_day_of_month_space(numeric_system) {}
- void on_24_hour(numeric_system ns) {
+ void on_24_hour(numeric_system ns, pad_type pad) {
if (handle_nan_inf()) return;
- if (ns == numeric_system::standard) return write(hour(), 2);
+ if (ns == numeric_system::standard) return write(hour(), 2, pad);
auto time = tm();
time.tm_hour = to_nonnegative_int(hour(), 24);
- format_tm(time, &tm_writer_type::on_24_hour, ns);
+ format_tm(time, &tm_writer_type::on_24_hour, ns, pad);
}
- void on_12_hour(numeric_system ns) {
+ void on_12_hour(numeric_system ns, pad_type pad) {
if (handle_nan_inf()) return;
- if (ns == numeric_system::standard) return write(hour12(), 2);
+ if (ns == numeric_system::standard) return write(hour12(), 2, pad);
auto time = tm();
time.tm_hour = to_nonnegative_int(hour12(), 12);
- format_tm(time, &tm_writer_type::on_12_hour, ns);
+ format_tm(time, &tm_writer_type::on_12_hour, ns, pad);
}
- void on_minute(numeric_system ns) {
+ void on_minute(numeric_system ns, pad_type pad) {
if (handle_nan_inf()) return;
- if (ns == numeric_system::standard) return write(minute(), 2);
+ if (ns == numeric_system::standard) return write(minute(), 2, pad);
auto time = tm();
time.tm_min = to_nonnegative_int(minute(), 60);
- format_tm(time, &tm_writer_type::on_minute, ns);
+ format_tm(time, &tm_writer_type::on_minute, ns, pad);
}
- void on_second(numeric_system ns) {
+ void on_second(numeric_system ns, pad_type pad) {
if (handle_nan_inf()) return;
if (ns == numeric_system::standard) {
if (std::is_floating_point::value) {
- constexpr auto num_fractional_digits =
- count_fractional_digits::value;
auto buf = memory_buffer();
- format_to(std::back_inserter(buf), runtime("{:.{}f}"),
- std::fmod(val * static_cast(Period::num) /
- static_cast(Period::den),
- static_cast(60)),
- num_fractional_digits);
+ write_floating_seconds(buf, std::chrono::duration(val),
+ precision);
if (negative) *out++ = '-';
- if (buf.size() < 2 || buf[1] == '.') *out++ = '0';
+ if (buf.size() < 2 || buf[1] == '.') {
+ out = detail::write_padding(out, pad);
+ }
out = std::copy(buf.begin(), buf.end(), out);
} else {
- write(second(), 2);
- write_fractional_seconds(std::chrono::duration(val));
+ write(second(), 2, pad);
+ write_fractional_seconds(
+ out, std::chrono::duration(val), precision);
}
return;
}
auto time = tm();
time.tm_sec = to_nonnegative_int(second(), 60);
- format_tm(time, &tm_writer_type::on_second, ns);
+ format_tm(time, &tm_writer_type::on_second, ns, pad);
}
void on_12_hour_time() {
@@ -1814,7 +1935,7 @@ struct chrono_formatter {
on_24_hour_time();
*out++ = ':';
if (handle_nan_inf()) return;
- on_second(numeric_system::standard);
+ on_second(numeric_system::standard, pad_type::unspecified);
}
void on_am_pm() {
@@ -1833,7 +1954,7 @@ struct chrono_formatter {
}
};
-FMT_END_DETAIL_NAMESPACE
+} // namespace detail
#if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907
using weekday = std::chrono::weekday;
@@ -1883,118 +2004,67 @@ template struct formatter {
template
struct formatter, Char> {
private:
- basic_format_specs specs;
- int precision = -1;
- using arg_ref_type = detail::arg_ref;
- arg_ref_type width_ref;
- arg_ref_type precision_ref;
- bool localized = false;
- basic_string_view format_str;
- using duration = std::chrono::duration;
-
- struct spec_handler {
- formatter& f;
- basic_format_parse_context& context;
- basic_string_view format_str;
-
- template FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
- context.check_arg_id(arg_id);
- return arg_ref_type(arg_id);
- }
-
- FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view arg_id) {
- context.check_arg_id(arg_id);
- return arg_ref_type(arg_id);
- }
-
- FMT_CONSTEXPR arg_ref_type make_arg_ref(detail::auto_id) {
- return arg_ref_type(context.next_arg_id());
- }
+ format_specs specs_;
+ detail::arg_ref width_ref_;
+ detail::arg_ref precision_ref_;
+ bool localized_ = false;
+ basic_string_view format_str_;
- void on_error(const char* msg) { FMT_THROW(format_error(msg)); }
- FMT_CONSTEXPR void on_fill(basic_string_view fill) {
- f.specs.fill = fill;
- }
- FMT_CONSTEXPR void on_align(align_t align) { f.specs.align = align; }
- FMT_CONSTEXPR void on_width(int width) { f.specs.width = width; }
- FMT_CONSTEXPR void on_precision(int _precision) {
- f.precision = _precision;
- }
- FMT_CONSTEXPR void end_precision() {}
+ public:
+ FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx)
+ -> decltype(ctx.begin()) {
+ auto it = ctx.begin(), end = ctx.end();
+ if (it == end || *it == '}') return it;
- template FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
- f.width_ref = make_arg_ref(arg_id);
- }
+ it = detail::parse_align(it, end, specs_);
+ if (it == end) return it;
- template FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
- f.precision_ref = make_arg_ref(arg_id);
- }
- };
+ it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
+ if (it == end) return it;
- using iterator = typename basic_format_parse_context::iterator;
- struct parse_range {
- iterator begin;
- iterator end;
- };
-
- FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context& ctx) {
- auto begin = ctx.begin(), end = ctx.end();
- if (begin == end || *begin == '}') return {begin, begin};
- spec_handler handler{*this, ctx, format_str};
- begin = detail::parse_align(begin, end, handler);
- if (begin == end) return {begin, begin};
- begin = detail::parse_width(begin, end, handler);
- if (begin == end) return {begin, begin};
- if (*begin == '.') {
- if (std::is_floating_point::value)
- begin = detail::parse_precision(begin, end, handler);
- else
- handler.on_error("precision not allowed for this argument type");
+ auto checker = detail::chrono_format_checker();
+ if (*it == '.') {
+ checker.has_precision_integral = !std::is_floating_point::value;
+ it = detail::parse_precision(it, end, specs_.precision, precision_ref_,
+ ctx);
}
- if (begin != end && *begin == 'L') {
- ++begin;
- localized = true;
+ if (it != end && *it == 'L') {
+ localized_ = true;
+ ++it;
}
- end = detail::parse_chrono_format(begin, end,
- detail::chrono_format_checker());
- return {begin, end};
- }
-
- public:
- FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx)
- -> decltype(ctx.begin()) {
- auto range = do_parse(ctx);
- format_str = basic_string_view(
- &*range.begin, detail::to_unsigned(range.end - range.begin));
- return range.end;
+ end = detail::parse_chrono_format(it, end, checker);
+ format_str_ = {it, detail::to_unsigned(end - it)};
+ return end;
}
template
- auto format(const duration& d, FormatContext& ctx) const
+ auto format(std::chrono::duration d, FormatContext& ctx) const
-> decltype(ctx.out()) {
- auto specs_copy = specs;
- auto precision_copy = precision;
- auto begin = format_str.begin(), end = format_str.end();
+ auto specs = specs_;
+ auto precision = specs.precision;
+ specs.precision = -1;
+ auto begin = format_str_.begin(), end = format_str_.end();
// As a possible future optimization, we could avoid extra copying if width
// is not specified.
- basic_memory_buffer buf;
+ auto buf = basic_memory_buffer();
auto out = std::back_inserter(buf);
- detail::handle_dynamic_spec(specs_copy.width,
- width_ref, ctx);
- detail::handle_dynamic_spec(precision_copy,
- precision_ref, ctx);
+ detail::handle_dynamic_spec(specs.width, width_ref_,
+ ctx);
+ detail::handle_dynamic_spec(precision,
+ precision_ref_, ctx);
if (begin == end || *begin == '}') {
- out = detail::format_duration_value(out, d.count(), precision_copy);
+ out = detail::format_duration_value(out, d.count(), precision);
detail::format_duration_unit(out);
} else {
- detail::chrono_formatter f(
- ctx, out, d);
- f.precision = precision_copy;
- f.localized = localized;
+ using chrono_formatter =
+ detail::chrono_formatter;
+ auto f = chrono_formatter(ctx, out, d);
+ f.precision = precision;
+ f.localized = localized_;
detail::parse_chrono_format(begin, end, f);
}
return detail::write(
- ctx.out(), basic_string_view(buf.data(), buf.size()), specs_copy);
+ ctx.out(), basic_string_view(buf.data(), buf.size()), specs);
}
};
@@ -2002,68 +2072,137 @@ template
struct formatter,
Char> : formatter {
FMT_CONSTEXPR formatter() {
- basic_string_view default_specs =
- detail::string_literal{};
- this->do_parse(default_specs.begin(), default_specs.end());
+ this->format_str_ = detail::string_literal{};
}
template
- auto format(std::chrono::time_point val,
+ auto format(std::chrono::time_point val,
FormatContext& ctx) const -> decltype(ctx.out()) {
- return formatter::format(localtime(val), ctx);
+ using period = typename Duration::period;
+ if (detail::const_check(
+ period::num != 1 || period::den != 1 ||
+ std::is_floating_point::value)) {
+ const auto epoch = val.time_since_epoch();
+ auto subsecs = std::chrono::duration_cast(
+ epoch - std::chrono::duration_cast(epoch));
+
+ if (subsecs.count() < 0) {
+ auto second =
+ std::chrono::duration_cast(std::chrono::seconds(1));
+ if (epoch.count() < ((Duration::min)() + second).count())
+ FMT_THROW(format_error("duration is too small"));
+ subsecs += second;
+ val -= second;
+ }
+
+ return formatter::do_format(
+ gmtime(std::chrono::time_point_cast(val)), ctx,
+ &subsecs);
+ }
+
+ return formatter::format(
+ gmtime(std::chrono::time_point_cast(val)), ctx);
}
};
+#if FMT_USE_LOCAL_TIME
+template
+struct formatter, Char>
+ : formatter {
+ FMT_CONSTEXPR formatter() {
+ this->format_str_ = detail::string_literal{};
+ }
+
+ template
+ auto format(std::chrono::local_time val, FormatContext& ctx) const
+ -> decltype(ctx.out()) {
+ using period = typename Duration::period;
+ if (period::num != 1 || period::den != 1 ||
+ std::is_floating_point::value) {
+ const auto epoch = val.time_since_epoch();
+ const auto subsecs = std::chrono::duration_cast(
+ epoch - std::chrono::duration_cast(epoch));
+
+ return formatter::do_format(
+ localtime(std::chrono::time_point_cast(val)),
+ ctx, &subsecs);
+ }
+
+ return formatter::format(
+ localtime(std::chrono::time_point_cast(val)),
+ ctx);
+ }
+};
+#endif
+
+#if FMT_USE_UTC_TIME
+template
+struct formatter,
+ Char>
+ : formatter,
+ Char> {
+ template
+ auto format(std::chrono::time_point val,
+ FormatContext& ctx) const -> decltype(ctx.out()) {
+ return formatter<
+ std::chrono::time_point,
+ Char>::format(std::chrono::utc_clock::to_sys(val), ctx);
+ }
+};
+#endif
+
template struct formatter {
private:
- enum class spec {
- unknown,
- year_month_day,
- hh_mm_ss,
- };
- spec spec_ = spec::unknown;
- basic_string_view specs;
+ format_specs specs_;
+ detail::arg_ref width_ref_;
protected:
- template FMT_CONSTEXPR auto do_parse(It begin, It end) -> It {
- if (begin != end && *begin == ':') ++begin;
- end = detail::parse_chrono_format(begin, end, detail::tm_format_checker());
- // Replace default spec only if the new spec is not empty.
- if (end != begin) specs = {begin, detail::to_unsigned(end - begin)};
- return end;
+ basic_string_view format_str_;
+
+ template
+ auto do_format(const std::tm& tm, FormatContext& ctx,
+ const Duration* subsecs) const -> decltype(ctx.out()) {
+ auto specs = specs_;
+ auto buf = basic_memory_buffer();
+ auto out = std::back_inserter(buf);
+ detail::handle_dynamic_spec(specs.width, width_ref_,
+ ctx);
+
+ auto loc_ref = ctx.locale();
+ detail::get_locale loc(static_cast(loc_ref), loc_ref);
+ auto w =
+ detail::tm_writer(loc, out, tm, subsecs);
+ detail::parse_chrono_format(format_str_.begin(), format_str_.end(), w);
+ return detail::write(
+ ctx.out(), basic_string_view(buf.data(), buf.size()), specs);
}
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx)
-> decltype(ctx.begin()) {
- auto end = this->do_parse(ctx.begin(), ctx.end());
- // basic_string_view<>::compare isn't constexpr before C++17.
- if (specs.size() == 2 && specs[0] == Char('%')) {
- if (specs[1] == Char('F'))
- spec_ = spec::year_month_day;
- else if (specs[1] == Char('T'))
- spec_ = spec::hh_mm_ss;
- }
+ auto it = ctx.begin(), end = ctx.end();
+ if (it == end || *it == '}') return it;
+
+ it = detail::parse_align(it, end, specs_);
+ if (it == end) return it;
+
+ it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
+ if (it == end) return it;
+
+ end = detail::parse_chrono_format(it, end, detail::tm_format_checker());
+ // Replace the default format_str only if the new spec is not empty.
+ if (end != it) format_str_ = {it, detail::to_unsigned(end - it)};
return end;
}
template
auto format(const std::tm& tm, FormatContext& ctx) const
-> decltype(ctx.out()) {
- const auto loc_ref = ctx.locale();
- detail::get_locale loc(static_cast(loc_ref), loc_ref);
- auto w = detail::tm_writer(loc, ctx.out(), tm);
- if (spec_ == spec::year_month_day)
- w.on_iso_date();
- else if (spec_ == spec::hh_mm_ss)
- w.on_iso_time();
- else
- detail::parse_chrono_format(specs.begin(), specs.end(), w);
- return w.out();
+ return do_format(tm, ctx, nullptr);
}
};
-FMT_MODULE_EXPORT_END
+FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_CHRONO_H_
diff --git a/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/color.h b/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/color.h
index 4c163277ef1..8697e1ca0ba 100644
--- a/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/color.h
+++ b/wpiutil/src/main/native/thirdparty/fmtlib/include/fmt/color.h
@@ -11,7 +11,7 @@
#include "format.h"
FMT_BEGIN_NAMESPACE
-FMT_MODULE_EXPORT_BEGIN
+FMT_BEGIN_EXPORT
enum class color : uint32_t {
alice_blue = 0xF0F8FF, // rgb(240,248,255)
@@ -203,7 +203,7 @@ struct rgb {
uint8_t b;
};
-FMT_BEGIN_DETAIL_NAMESPACE
+namespace detail {
// color is a struct of either a rgb color or a terminal color.
struct color_type {
@@ -225,8 +225,7 @@ struct color_type {
uint32_t rgb_color;
} value;
};
-
-FMT_END_DETAIL_NAMESPACE
+} // namespace detail
/** A text style consisting of foreground and background colors and emphasis. */
class text_style {
@@ -323,7 +322,7 @@ FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept {
return text_style(lhs) | rhs;
}
-FMT_BEGIN_DETAIL_NAMESPACE
+namespace detail {
template struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
@@ -423,26 +422,6 @@ FMT_CONSTEXPR ansi_color_escape make_emphasis(emphasis em) noexcept {
return ansi_color_escape(em);
}
-template inline void fputs(const Char* chars, FILE* stream) {
- int result = std::fputs(chars, stream);
- if (result < 0)
- FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
-}
-
-template <> inline void fputs(const wchar_t* chars, FILE* stream) {
- int result = std::fputws(chars, stream);
- if (result < 0)
- FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
-}
-
-template inline void reset_color(FILE* stream) {
- fputs("\x1b[0m", stream);
-}
-
-template <> inline void reset_color(FILE* stream) {
- fputs(L"\x1b[0m", stream);
-}
-
template inline void reset_color(buffer& buffer) {
auto reset_color = string_view("\x1b[0m");
buffer.append(reset_color.begin(), reset_color.end());
@@ -477,19 +456,21 @@ void vformat_to(buffer& buf, const text_style& ts,
if (has_style) detail::reset_color(buf);
}
-FMT_END_DETAIL_NAMESPACE
+} // namespace detail
-template >
-void vprint(std::FILE* f, const text_style& ts, const S& format,
- basic_format_args