diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /intl/components/src/ListFormat.h | |
parent | Initial commit. (diff) | |
download | thunderbird-upstream.tar.xz thunderbird-upstream.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'intl/components/src/ListFormat.h')
-rw-r--r-- | intl/components/src/ListFormat.h | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/intl/components/src/ListFormat.h b/intl/components/src/ListFormat.h new file mode 100644 index 0000000000..4952512f97 --- /dev/null +++ b/intl/components/src/ListFormat.h @@ -0,0 +1,223 @@ +/* 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 intl_components_ListFormat_h_ +#define intl_components_ListFormat_h_ + +#include "mozilla/CheckedInt.h" +#include "mozilla/intl/ICU4CGlue.h" +#include "mozilla/PodOperations.h" +#include "mozilla/Result.h" +#include "mozilla/Vector.h" +#include "unicode/ulistformatter.h" + +struct UListFormatter; + +namespace mozilla::intl { + +static constexpr size_t DEFAULT_LIST_LENGTH = 8; + +/** + * This component is a Mozilla-focused API for the list formatting provided by + * ICU. It implements the API provided by the ECMA-402 Intl.ListFormat object. + * + * https://tc39.es/ecma402/#listformat-objects + */ +class ListFormat final { + public: + /** + * The [[Type]] and [[Style]] properties of ListFormat instances. + * + * https://tc39.es/ecma402/#sec-properties-of-intl-listformat-instances + */ + // [[Type]] + enum class Type { Conjunction, Disjunction, Unit }; + // [[Style]] + enum class Style { Long, Short, Narrow }; + + /** + * The 'options' object to create Intl.ListFormat instance. + * + * https://tc39.es/ecma402/#sec-Intl.ListFormat + */ + struct Options { + // "conjunction" is the default fallback value. + Type mType = Type::Conjunction; + + // "long" is the default fallback value. + Style mStyle = Style::Long; + }; + + /** + * Create a ListFormat object for the provided locale and options. + * + * https://tc39.es/ecma402/#sec-Intl.ListFormat + */ + static Result<UniquePtr<ListFormat>, ICUError> TryCreate( + mozilla::Span<const char> aLocale, const Options& aOptions); + + ~ListFormat(); + + /** + * The list of String values for FormatList and FormatListToParts. + * + * https://tc39.es/ecma402/#sec-formatlist + * https://tc39.es/ecma402/#sec-formatlisttoparts + */ + using StringList = + mozilla::Vector<mozilla::Span<const char16_t>, DEFAULT_LIST_LENGTH>; + + /** + * Format the list according and write the result in buffer. + * + * https://tc39.es/ecma402/#sec-Intl.ListFormat.prototype.format + * https://tc39.es/ecma402/#sec-formatlist + */ + template <typename Buffer> + ICUResult Format(const StringList& list, Buffer& buffer) const { + static_assert(std::is_same_v<typename Buffer::CharType, char16_t>, + "Currently only UTF-16 buffers are supported."); + + mozilla::Vector<const char16_t*, DEFAULT_LIST_LENGTH> u16strings; + mozilla::Vector<int32_t, DEFAULT_LIST_LENGTH> u16stringLens; + MOZ_TRY(ConvertStringListToVectors(list, u16strings, u16stringLens)); + + int32_t u16stringCount = mozilla::AssertedCast<int32_t>(list.length()); + MOZ_TRY(FillBufferWithICUCall( + buffer, [this, &u16strings, &u16stringLens, u16stringCount]( + char16_t* chars, int32_t size, UErrorCode* status) { + return ulistfmt_format(mListFormatter.GetConst(), u16strings.begin(), + u16stringLens.begin(), u16stringCount, chars, + size, status); + })); + + return Ok{}; + } + + /** + * The corresponding list of parts according to the effective locale and the + * formatting options of ListFormat. + * Each part has a [[Type]] field, which must be "element" or "literal", and a + * [[Value]] field. + * + * To store Part more efficiently, it doesn't store the ||Value|| of type + * string in this struct. Instead, it stores the end index of the string in + * the buffer(which is passed to ListFormat::FormatToParts()). The begin index + * of the ||Value|| is the index of the previous part. + * + * Buffer + * 0 i j + * +---------------+---------------+---------------+ + * | Part[0].Value | Part[1].Value | Part[2].Value | .... + * +---------------+---------------+---------------+ + * + * Part[0].index is i. Part[0].Value is stored in the Buffer[0..i]. + * Part[1].index is j. Part[1].Value is stored in the Buffer[i..j]. + * + * See https://tc39.es/ecma402/#sec-createpartsfromlist + */ + enum class PartType { + Element, + Literal, + }; + // The 2nd field is the end index to the buffer as mentioned above. + using Part = std::pair<PartType, size_t>; + using PartVector = mozilla::Vector<Part, DEFAULT_LIST_LENGTH>; + + /** + * Format the list to a list of parts, and store the formatted result of + * UTF-16 string into buffer, and formatted parts into the vector 'parts'. + * + * See: + * https://tc39.es/ecma402/#sec-Intl.ListFormat.prototype.formatToParts + * https://tc39.es/ecma402/#sec-formatlisttoparts + */ + template <typename Buffer> + ICUResult FormatToParts(const StringList& list, Buffer& buffer, + PartVector& parts) { + static_assert(std::is_same_v<typename Buffer::CharType, char16_t>, + "Currently only UTF-16 buffers are supported."); + + mozilla::Vector<const char16_t*, DEFAULT_LIST_LENGTH> u16strings; + mozilla::Vector<int32_t, DEFAULT_LIST_LENGTH> u16stringLens; + MOZ_TRY(ConvertStringListToVectors(list, u16strings, u16stringLens)); + + AutoFormattedList formatted; + UErrorCode status = U_ZERO_ERROR; + ulistfmt_formatStringsToResult( + mListFormatter.GetConst(), u16strings.begin(), u16stringLens.begin(), + int32_t(list.length()), formatted.GetFormatted(), &status); + if (U_FAILURE(status)) { + return Err(ToICUError(status)); + } + + auto spanResult = formatted.ToSpan(); + if (spanResult.isErr()) { + return spanResult.propagateErr(); + } + auto formattedSpan = spanResult.unwrap(); + if (!FillBuffer(formattedSpan, buffer)) { + return Err(ICUError::OutOfMemory); + } + + const UFormattedValue* value = formatted.Value(); + if (!value) { + return Err(ICUError::InternalError); + } + return FormattedToParts(value, buffer.length(), parts); + } + + private: + ListFormat() = delete; + explicit ListFormat(UListFormatter* fmt) : mListFormatter(fmt) {} + ListFormat(const ListFormat&) = delete; + ListFormat& operator=(const ListFormat&) = delete; + + ICUPointer<UListFormatter> mListFormatter = + ICUPointer<UListFormatter>(nullptr); + + // Convert StringList to an array of type 'const char16_t*' and an array of + // int32 for ICU-API. + ICUResult ConvertStringListToVectors( + const StringList& list, + mozilla::Vector<const char16_t*, DEFAULT_LIST_LENGTH>& u16strings, + mozilla::Vector<int32_t, DEFAULT_LIST_LENGTH>& u16stringLens) const { + // Keep a conservative running count of overall length. + mozilla::CheckedInt<int32_t> stringLengthTotal(0); + for (const auto& string : list) { + if (!u16strings.append(string.data())) { + return Err(ICUError::InternalError); + } + + int32_t len = mozilla::AssertedCast<int32_t>(string.size()); + if (!u16stringLens.append(len)) { + return Err(ICUError::InternalError); + } + + stringLengthTotal += len; + } + + // Add space for N unrealistically large conjunctions. + constexpr int32_t MaxConjunctionLen = 100; + stringLengthTotal += CheckedInt<int32_t>(list.length()) * MaxConjunctionLen; + // If the overestimate exceeds ICU length limits, don't try to format. + if (!stringLengthTotal.isValid()) { + return Err(ICUError::OverflowError); + } + + return Ok{}; + } + + using AutoFormattedList = + AutoFormattedResult<UFormattedList, ulistfmt_openResult, + ulistfmt_resultAsValue, ulistfmt_closeResult>; + + ICUResult FormattedToParts(const UFormattedValue* formattedValue, + size_t formattedSize, PartVector& parts); + + static UListFormatterType ToUListFormatterType(Type type); + static UListFormatterWidth ToUListFormatterWidth(Style style); +}; + +} // namespace mozilla::intl +#endif // intl_components_ListFormat_h_ |