Bug 1717448 - Integrate {fmt} with nsString. r=xpcom-reviewers,nika,mccr8

Differential Revision: https://phabricator.services.mozilla.com/D217721
This commit is contained in:
Nika Layzell 2024-10-21 12:54:56 +00:00
parent 6a1d862d72
commit 661bc320d5
13 changed files with 205 additions and 0 deletions

View File

@ -13,6 +13,7 @@ EXPORTS += [
"nsCharTraits.h",
"nsDependentString.h",
"nsDependentSubstring.h",
"nsFmtString.h",
"nsLiteralString.h",
"nsPrintfCString.h",
"nsPromiseFlatString.h",

View File

@ -0,0 +1,43 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsFmtCString_h___
#define nsFmtCString_h___
#include <type_traits>
#include "fmt/format.h"
#include "fmt/xchar.h"
#include "nsString.h"
/**
* nsTFmtString lets you create a nsTString using a C++20-style format
* string. For example:
*
* NS_WARNING(nsFmtCString(FMT_STRING("Unexpected value: {}"), 13.917).get());
* NS_WARNING(nsFmtString(FMT_STRING(u"Unexpected value: {}"),
* u"weird").get());
*
* nsTFmtString has a small built-in auto-buffer. For larger strings, it
* will allocate on the heap.
*
* See also nsTSubstring::AppendFmt().
*/
template <typename T>
class nsTFmtString : public nsTAutoStringN<T, 16> {
public:
template <typename... Args>
explicit nsTFmtString(
fmt::basic_format_string<T, type_identity_t<Args>...> aFormatStr,
Args&&... aArgs) {
this->AppendFmt(aFormatStr, std::forward<Args>(aArgs)...);
}
};
template <typename Char>
struct fmt::formatter<nsTFmtString<Char>, Char>
: fmt::formatter<nsTString<Char>, Char> {};
#endif // !defined(nsFmtString_h___)

View File

@ -33,6 +33,10 @@ class nsPrintfCString : public nsAutoCStringN<16> {
}
};
template <>
struct fmt::formatter<nsPrintfCString, char> : fmt::formatter<nsCString, char> {
};
/**
*
*
@ -61,4 +65,8 @@ class nsVprintfCString : public nsAutoCStringN<16> {
}
};
template <>
struct fmt::formatter<nsVprintfCString, char>
: fmt::formatter<nsCString, char> {};
#endif // !defined(nsPrintfCString_h___)

View File

@ -58,6 +58,10 @@ class NS_LossyConvertUTF16toASCII : public nsAutoCString {
NS_LossyConvertUTF16toASCII(char) = delete;
};
template <>
struct fmt::formatter<NS_LossyConvertUTF16toASCII, char>
: fmt::formatter<nsAutoCString, char> {};
class NS_ConvertASCIItoUTF16 : public nsAutoString {
public:
explicit NS_ConvertASCIItoUTF16(const char* aCString) {
@ -81,6 +85,10 @@ class NS_ConvertASCIItoUTF16 : public nsAutoString {
NS_ConvertASCIItoUTF16(char16_t) = delete;
};
template <>
struct fmt::formatter<NS_ConvertASCIItoUTF16, char16_t>
: fmt::formatter<nsAutoString, char16_t> {};
/**
* A helper class that converts a UTF-16 string to UTF-8
*/
@ -108,6 +116,10 @@ class NS_ConvertUTF16toUTF8 : public nsAutoCString {
NS_ConvertUTF16toUTF8(char) = delete;
};
template <>
struct fmt::formatter<NS_ConvertUTF16toUTF8, char>
: fmt::formatter<nsAutoCString, char> {};
class NS_ConvertUTF8toUTF16 : public nsAutoString {
public:
explicit NS_ConvertUTF8toUTF16(const char* aCString) {
@ -131,6 +143,10 @@ class NS_ConvertUTF8toUTF16 : public nsAutoString {
NS_ConvertUTF8toUTF16(char16_t) = delete;
};
template <>
struct fmt::formatter<NS_ConvertUTF8toUTF16, char16_t>
: fmt::formatter<nsAutoString, char16_t> {};
/**
* Converts an integer (signed/unsigned, 32/64bit) to its decimal string
* representation and returns it as an nsAutoCString/nsAutoString.

View File

@ -40,6 +40,8 @@ class nsTDependentString;
template <typename T>
class nsTDependentSubstring;
template <typename T>
class nsTFmtString;
template <typename T>
class nsTPromiseFlatString;
template <typename T>
class nsTLiteralString;
@ -69,6 +71,7 @@ template <size_t N>
using nsAutoStringN = nsTAutoStringN<char16_t, N>;
using nsDependentString = nsTDependentString<char16_t>;
using nsDependentSubstring = nsTDependentSubstring<char16_t>;
using nsFmtString = nsTFmtString<char16_t>;
using nsPromiseFlatString = nsTPromiseFlatString<char16_t>;
using nsStringComparator = nsTStringComparator<char16_t>;
using nsLiteralString = nsTLiteralString<char16_t>;
@ -84,6 +87,7 @@ template <size_t N>
using nsAutoCStringN = nsTAutoStringN<char, N>;
using nsDependentCString = nsTDependentString<char>;
using nsDependentCSubstring = nsTDependentSubstring<char>;
using nsFmtCString = nsTFmtString<char>;
using nsPromiseFlatCString = nsTPromiseFlatString<char>;
using nsCStringComparator = nsTStringComparator<char>;
using nsLiteralCString = nsTLiteralString<char>;

View File

@ -123,4 +123,8 @@ class nsTDependentString : public nsTString<T> {
extern template class nsTDependentString<char>;
extern template class nsTDependentString<char16_t>;
template <typename Char>
struct fmt::formatter<nsTDependentString<Char>, Char>
: fmt::formatter<nsTString<Char>, Char> {};
#endif

View File

@ -104,6 +104,10 @@ class nsTDependentSubstring : public nsTSubstring<T> {
extern template class nsTDependentSubstring<char>;
extern template class nsTDependentSubstring<char16_t>;
template <typename Char>
struct fmt::formatter<nsTDependentSubstring<Char>, Char>
: fmt::formatter<nsTSubstring<Char>, Char> {};
template <typename T>
inline const nsTDependentSubstring<T> Substring(const nsTSubstring<T>& aStr,
size_t aStartPos,

View File

@ -113,6 +113,10 @@ class nsTLiteralString : public mozilla::detail::nsTStringRepr<T> {
extern template class nsTLiteralString<char>;
extern template class nsTLiteralString<char16_t>;
template <typename Char>
struct fmt::formatter<nsTLiteralString<Char>, Char>
: fmt::formatter<mozilla::detail::nsTStringRepr<Char>, Char> {};
namespace mozilla {
constexpr MOZ_IMPLICIT StaticString::StaticString(nsLiteralCString const& str)
: mStr(str.get()) {}

View File

@ -115,6 +115,10 @@ class MOZ_STACK_CLASS nsTPromiseFlatString : public nsTString<T> {
extern template class nsTPromiseFlatString<char>;
extern template class nsTPromiseFlatString<char16_t>;
template <typename Char>
struct fmt::formatter<nsTPromiseFlatString<Char>, Char>
: fmt::formatter<nsTString<Char>, Char> {};
// We template this so that the constructor is chosen based on the type of the
// parameter. This allows us to reject attempts to promise a flat flat string.
template <class T>

View File

@ -238,6 +238,10 @@ class nsTString : public nsTSubstring<T> {
extern template class nsTString<char>;
extern template class nsTString<char16_t>;
template <typename Char>
struct fmt::formatter<nsTString<Char>, Char>
: fmt::formatter<nsTSubstring<Char>, Char> {};
/**
* nsTAutoStringN
*
@ -368,6 +372,10 @@ class MOZ_NON_MEMMOVABLE nsTAutoStringN : public nsTString<T> {
extern template class nsTAutoStringN<char, 64>;
extern template class nsTAutoStringN<char16_t, 64>;
template <typename Char, size_t N>
struct fmt::formatter<nsTAutoStringN<Char, N>, Char>
: fmt::formatter<nsTString<Char>, Char> {};
//
// nsAutoString stores pointers into itself which are invalidated when an
// nsTArray is resized, so nsTArray must not be instantiated with nsAutoString

View File

@ -11,6 +11,9 @@
#include <string_view>
#include <type_traits> // std::enable_if
#include "fmt/format.h"
#include "fmt/xchar.h"
#include "mozilla/Char16.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/fallible.h"
@ -555,4 +558,15 @@ inline bool operator>(const mozilla::detail::nsTStringRepr<T>& aLhs,
return Compare(aLhs, aRhs) > 0;
}
template <typename Char>
struct fmt::formatter<mozilla::detail::nsTStringRepr<Char>, Char>
: fmt::formatter<basic_string_view<Char>, Char> {
template <typename FormatContext>
constexpr auto format(const mozilla::detail::nsTStringRepr<Char>& aVal,
FormatContext& aCtx) const -> decltype(aCtx.out()) {
return formatter<basic_string_view<Char>, Char>::format(
basic_string_view<Char>{aVal.BeginReading(), aVal.Length()}, aCtx);
}
};
#endif

View File

@ -5,12 +5,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "double-conversion/double-conversion.h"
#include "mozilla/Assertions.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Printf.h"
#include "mozilla/ResultExtensions.h"
#include <iterator>
#include "fmt/format.h"
#include "fmt/xchar.h"
#include "nsASCIIMask.h"
#include "nsCharTraits.h"
#include "nsISupports.h"
@ -1089,6 +1094,73 @@ void nsTSubstring<T>::StripCRLF() {
StripTaggedASCII(mozilla::ASCIIMask::MaskCRLF());
}
// An adapter type for a `nsTSubstring<T>` to provide a std::string-like
// interface for `{fmt}`.
template <typename T>
class nsTSubstringStdCollectionAdapter {
public:
using value_type = T;
explicit nsTSubstringStdCollectionAdapter(nsTSubstring<T>& aString)
: mSize(aString.Length()), mHandle(InfallibleBulkWrite(aString)) {}
~nsTSubstringStdCollectionAdapter() { mHandle.Finish(mSize, false); }
size_t size() const { return mSize; }
void resize(size_t aNewSize) {
EnsureCapacity(aNewSize);
mSize = aNewSize;
// XXX: technically resize should zero-initialize any new bytes to match
// std::string.
}
T& operator[](size_t i) {
MOZ_RELEASE_ASSERT(i < mSize);
return mHandle.Elements()[i];
}
const T& operator[](size_t i) const {
MOZ_RELEASE_ASSERT(i < mSize);
return mHandle.Elements()[i];
}
private:
void EnsureCapacity(size_t aNewCapacity) {
if (aNewCapacity > mHandle.Length()) {
auto result = mHandle.RestartBulkWrite(aNewCapacity, mSize, false);
if (result.isErr()) {
::NS_ABORT_OOM(aNewCapacity * sizeof(value_type));
}
}
}
static mozilla::BulkWriteHandle<T> InfallibleBulkWrite(
nsTSubstring<T>& aString) {
size_t length = aString.Length();
auto res = aString.BulkWrite(length, length, false);
if (res.isErr()) {
::NS_ABORT_OOM(length * sizeof(value_type));
}
return res.unwrap();
}
size_t mSize;
mozilla::BulkWriteHandle<T> mHandle;
};
// Mark the collection as contiguous for {fmt} so that the buffer will be used
// directly.
namespace fmt {
template <typename T>
struct is_contiguous<nsTSubstringStdCollectionAdapter<T>> : std::true_type {};
} // namespace fmt
template <typename T>
void nsTSubstring<T>::AppendVfmt(
fmt::basic_string_view<char_type> aFormatStr,
fmt::basic_format_args<fmt::buffered_context<char_type>> aArgs) {
nsTSubstringStdCollectionAdapter<char_type> adapter{*this};
fmt::vformat_to(std::back_inserter(adapter), aFormatStr, aArgs);
}
template <typename T>
struct MOZ_STACK_CLASS PrintfAppend : public mozilla::PrintfTarget {
explicit PrintfAppend(nsTSubstring<T>* aString) : mString(aString) {}

View File

@ -37,6 +37,12 @@ class nsTString;
template <typename T>
class nsTSubstring;
template <typename T> struct type_identity {
using type = T;
};
template <typename T> using type_identity_t = typename type_identity<T>::type;
namespace mozilla {
/**
@ -731,6 +737,19 @@ class nsTSubstring : public mozilla::detail::nsTStringRepr<T> {
[[nodiscard]] bool AppendASCII(const char* aData, size_type aLength,
const fallible_t& aFallible);
template <typename... Args>
void AppendFmt(
fmt::basic_format_string<char_type, type_identity_t<Args>...>
aFormatStr,
Args&&... aArgs) {
AppendVfmt(
aFormatStr,
fmt::make_format_args<fmt::buffered_context<char_type>>(aArgs...));
}
void AppendVfmt(
fmt::basic_string_view<char_type> aFormatStr,
fmt::basic_format_args<fmt::buffered_context<char_type>> aArgs);
// Appends a literal string ("" literal in the 8-bit case and u"" literal
// in the 16-bit case) to the string.
//
@ -1474,4 +1493,8 @@ Span(const nsTSubstring<char16_t>&) -> Span<const char16_t>;
} // namespace mozilla
template <typename Char>
struct fmt::formatter<nsTSubstring<Char>, Char>
: fmt::formatter<mozilla::detail::nsTStringRepr<Char>, Char> {};
#endif