Bug 1746711 Part 2: Ensure the enqueued surface has a color space. r=gfx-reviewers...
[gecko.git] / intl / components / src / ListFormat.h
blob4952512f97e1209e9223c121f81256775f114a6d
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 #ifndef intl_components_ListFormat_h_
5 #define intl_components_ListFormat_h_
7 #include "mozilla/CheckedInt.h"
8 #include "mozilla/intl/ICU4CGlue.h"
9 #include "mozilla/PodOperations.h"
10 #include "mozilla/Result.h"
11 #include "mozilla/Vector.h"
12 #include "unicode/ulistformatter.h"
14 struct UListFormatter;
16 namespace mozilla::intl {
18 static constexpr size_t DEFAULT_LIST_LENGTH = 8;
20 /**
21 * This component is a Mozilla-focused API for the list formatting provided by
22 * ICU. It implements the API provided by the ECMA-402 Intl.ListFormat object.
24 * https://tc39.es/ecma402/#listformat-objects
26 class ListFormat final {
27 public:
28 /**
29 * The [[Type]] and [[Style]] properties of ListFormat instances.
31 * https://tc39.es/ecma402/#sec-properties-of-intl-listformat-instances
33 // [[Type]]
34 enum class Type { Conjunction, Disjunction, Unit };
35 // [[Style]]
36 enum class Style { Long, Short, Narrow };
38 /**
39 * The 'options' object to create Intl.ListFormat instance.
41 * https://tc39.es/ecma402/#sec-Intl.ListFormat
43 struct Options {
44 // "conjunction" is the default fallback value.
45 Type mType = Type::Conjunction;
47 // "long" is the default fallback value.
48 Style mStyle = Style::Long;
51 /**
52 * Create a ListFormat object for the provided locale and options.
54 * https://tc39.es/ecma402/#sec-Intl.ListFormat
56 static Result<UniquePtr<ListFormat>, ICUError> TryCreate(
57 mozilla::Span<const char> aLocale, const Options& aOptions);
59 ~ListFormat();
61 /**
62 * The list of String values for FormatList and FormatListToParts.
64 * https://tc39.es/ecma402/#sec-formatlist
65 * https://tc39.es/ecma402/#sec-formatlisttoparts
67 using StringList =
68 mozilla::Vector<mozilla::Span<const char16_t>, DEFAULT_LIST_LENGTH>;
70 /**
71 * Format the list according and write the result in buffer.
73 * https://tc39.es/ecma402/#sec-Intl.ListFormat.prototype.format
74 * https://tc39.es/ecma402/#sec-formatlist
76 template <typename Buffer>
77 ICUResult Format(const StringList& list, Buffer& buffer) const {
78 static_assert(std::is_same_v<typename Buffer::CharType, char16_t>,
79 "Currently only UTF-16 buffers are supported.");
81 mozilla::Vector<const char16_t*, DEFAULT_LIST_LENGTH> u16strings;
82 mozilla::Vector<int32_t, DEFAULT_LIST_LENGTH> u16stringLens;
83 MOZ_TRY(ConvertStringListToVectors(list, u16strings, u16stringLens));
85 int32_t u16stringCount = mozilla::AssertedCast<int32_t>(list.length());
86 MOZ_TRY(FillBufferWithICUCall(
87 buffer, [this, &u16strings, &u16stringLens, u16stringCount](
88 char16_t* chars, int32_t size, UErrorCode* status) {
89 return ulistfmt_format(mListFormatter.GetConst(), u16strings.begin(),
90 u16stringLens.begin(), u16stringCount, chars,
91 size, status);
92 }));
94 return Ok{};
97 /**
98 * The corresponding list of parts according to the effective locale and the
99 * formatting options of ListFormat.
100 * Each part has a [[Type]] field, which must be "element" or "literal", and a
101 * [[Value]] field.
103 * To store Part more efficiently, it doesn't store the ||Value|| of type
104 * string in this struct. Instead, it stores the end index of the string in
105 * the buffer(which is passed to ListFormat::FormatToParts()). The begin index
106 * of the ||Value|| is the index of the previous part.
108 * Buffer
109 * 0 i j
110 * +---------------+---------------+---------------+
111 * | Part[0].Value | Part[1].Value | Part[2].Value | ....
112 * +---------------+---------------+---------------+
114 * Part[0].index is i. Part[0].Value is stored in the Buffer[0..i].
115 * Part[1].index is j. Part[1].Value is stored in the Buffer[i..j].
117 * See https://tc39.es/ecma402/#sec-createpartsfromlist
119 enum class PartType {
120 Element,
121 Literal,
123 // The 2nd field is the end index to the buffer as mentioned above.
124 using Part = std::pair<PartType, size_t>;
125 using PartVector = mozilla::Vector<Part, DEFAULT_LIST_LENGTH>;
128 * Format the list to a list of parts, and store the formatted result of
129 * UTF-16 string into buffer, and formatted parts into the vector 'parts'.
131 * See:
132 * https://tc39.es/ecma402/#sec-Intl.ListFormat.prototype.formatToParts
133 * https://tc39.es/ecma402/#sec-formatlisttoparts
135 template <typename Buffer>
136 ICUResult FormatToParts(const StringList& list, Buffer& buffer,
137 PartVector& parts) {
138 static_assert(std::is_same_v<typename Buffer::CharType, char16_t>,
139 "Currently only UTF-16 buffers are supported.");
141 mozilla::Vector<const char16_t*, DEFAULT_LIST_LENGTH> u16strings;
142 mozilla::Vector<int32_t, DEFAULT_LIST_LENGTH> u16stringLens;
143 MOZ_TRY(ConvertStringListToVectors(list, u16strings, u16stringLens));
145 AutoFormattedList formatted;
146 UErrorCode status = U_ZERO_ERROR;
147 ulistfmt_formatStringsToResult(
148 mListFormatter.GetConst(), u16strings.begin(), u16stringLens.begin(),
149 int32_t(list.length()), formatted.GetFormatted(), &status);
150 if (U_FAILURE(status)) {
151 return Err(ToICUError(status));
154 auto spanResult = formatted.ToSpan();
155 if (spanResult.isErr()) {
156 return spanResult.propagateErr();
158 auto formattedSpan = spanResult.unwrap();
159 if (!FillBuffer(formattedSpan, buffer)) {
160 return Err(ICUError::OutOfMemory);
163 const UFormattedValue* value = formatted.Value();
164 if (!value) {
165 return Err(ICUError::InternalError);
167 return FormattedToParts(value, buffer.length(), parts);
170 private:
171 ListFormat() = delete;
172 explicit ListFormat(UListFormatter* fmt) : mListFormatter(fmt) {}
173 ListFormat(const ListFormat&) = delete;
174 ListFormat& operator=(const ListFormat&) = delete;
176 ICUPointer<UListFormatter> mListFormatter =
177 ICUPointer<UListFormatter>(nullptr);
179 // Convert StringList to an array of type 'const char16_t*' and an array of
180 // int32 for ICU-API.
181 ICUResult ConvertStringListToVectors(
182 const StringList& list,
183 mozilla::Vector<const char16_t*, DEFAULT_LIST_LENGTH>& u16strings,
184 mozilla::Vector<int32_t, DEFAULT_LIST_LENGTH>& u16stringLens) const {
185 // Keep a conservative running count of overall length.
186 mozilla::CheckedInt<int32_t> stringLengthTotal(0);
187 for (const auto& string : list) {
188 if (!u16strings.append(string.data())) {
189 return Err(ICUError::InternalError);
192 int32_t len = mozilla::AssertedCast<int32_t>(string.size());
193 if (!u16stringLens.append(len)) {
194 return Err(ICUError::InternalError);
197 stringLengthTotal += len;
200 // Add space for N unrealistically large conjunctions.
201 constexpr int32_t MaxConjunctionLen = 100;
202 stringLengthTotal += CheckedInt<int32_t>(list.length()) * MaxConjunctionLen;
203 // If the overestimate exceeds ICU length limits, don't try to format.
204 if (!stringLengthTotal.isValid()) {
205 return Err(ICUError::OverflowError);
208 return Ok{};
211 using AutoFormattedList =
212 AutoFormattedResult<UFormattedList, ulistfmt_openResult,
213 ulistfmt_resultAsValue, ulistfmt_closeResult>;
215 ICUResult FormattedToParts(const UFormattedValue* formattedValue,
216 size_t formattedSize, PartVector& parts);
218 static UListFormatterType ToUListFormatterType(Type type);
219 static UListFormatterWidth ToUListFormatterWidth(Style style);
222 } // namespace mozilla::intl
223 #endif // intl_components_ListFormat_h_