diff --git a/intl/components/gtest/TestNumberFormat.cpp b/intl/components/gtest/TestNumberFormat.cpp index def636998647..8785c1843bfe 100644 --- a/intl/components/gtest/TestNumberFormat.cpp +++ b/intl/components/gtest/TestNumberFormat.cpp @@ -189,11 +189,19 @@ TEST(IntlNumberFormat, FormatToParts) ASSERT_TRUE(res != nullptr); ASSERT_EQ(std::u16string_view(res), u"123.456,789"); ASSERT_EQ(parts.length(), 5U); - ASSERT_EQ(parts[0], (NumberPart{NumberPartType::Integer, 3})); - ASSERT_EQ(parts[1], (NumberPart{NumberPartType::Group, 4})); - ASSERT_EQ(parts[2], (NumberPart{NumberPartType::Integer, 7})); - ASSERT_EQ(parts[3], (NumberPart{NumberPartType::Decimal, 8})); - ASSERT_EQ(parts[4], (NumberPart{NumberPartType::Fraction, 11})); + + // NumberFormat only ever produces number parts with NumberPartSource::Shared. + + ASSERT_EQ(parts[0], + (NumberPart{NumberPartType::Integer, NumberPartSource::Shared, 3})); + ASSERT_EQ(parts[1], + (NumberPart{NumberPartType::Group, NumberPartSource::Shared, 4})); + ASSERT_EQ(parts[2], + (NumberPart{NumberPartType::Integer, NumberPartSource::Shared, 7})); + ASSERT_EQ(parts[3], + (NumberPart{NumberPartType::Decimal, NumberPartSource::Shared, 8})); + ASSERT_EQ(parts[4], (NumberPart{NumberPartType::Fraction, + NumberPartSource::Shared, 11})); } } // namespace intl diff --git a/intl/components/src/NumberFormat.h b/intl/components/src/NumberFormat.h index 34c65a4177f4..a4558a4dc83f 100644 --- a/intl/components/src/NumberFormat.h +++ b/intl/components/src/NumberFormat.h @@ -185,7 +185,7 @@ struct MOZ_STACK_CLASS NumberFormatOptions { } mRoundingPriority = RoundingPriority::Auto; }; -enum class NumberPartType { +enum class NumberPartType : int16_t { Compact, Currency, Decimal, @@ -204,15 +204,18 @@ enum class NumberPartType { Unit, }; +enum class NumberPartSource : int16_t { Shared, Start, End }; + // Because parts fully partition the formatted string, we only track the // index of the end of each part -- the beginning is implicitly the last // part's end. struct NumberPart { NumberPartType type; + NumberPartSource source; size_t endIndex; bool operator==(const NumberPart& rhs) const { - return type == rhs.type && endIndex == rhs.endIndex; + return type == rhs.type && source == rhs.source && endIndex == rhs.endIndex; } bool operator!=(const NumberPart& rhs) const { return !(*this == rhs); } }; diff --git a/intl/components/src/NumberFormatFields.cpp b/intl/components/src/NumberFormatFields.cpp index 92eb111fb18f..7b1d615509ce 100644 --- a/intl/components/src/NumberFormatFields.cpp +++ b/intl/components/src/NumberFormatFields.cpp @@ -16,6 +16,7 @@ bool NumberFormatFields::append(NumberPartType type, int32_t begin, } bool NumberFormatFields::toPartsVector(size_t overallLength, + const NumberPartSourceMap& sourceMap, NumberPartVector& parts) { std::sort(fields_.begin(), fields_.end(), [](const NumberFormatField& left, const NumberFormatField& right) { @@ -87,6 +88,8 @@ bool NumberFormatFields::toPartsVector(size_t overallLength, // The length of the overall formatted string. const uint32_t limit = 0; + NumberPartSourceMap sourceMap; + Vector enclosingFields; void popEnclosingFieldsEndingAt(uint32_t end) { @@ -109,13 +112,13 @@ bool NumberFormatFields::toPartsVector(size_t overallLength, if (index == len) { if (enclosingFields.length() > 0) { const auto& enclosing = fields[enclosingFields.popCopy()]; - *part = {enclosing.type, enclosing.end}; + *part = {enclosing.type, sourceMap.source(enclosing), enclosing.end}; // If additional enclosing fields end where this part ends, // pop them as well. popEnclosingFieldsEndingAt(part->endIndex); } else { - *part = {NumberPartType::Literal, limit}; + *part = {NumberPartType::Literal, sourceMap.source(limit), limit}; } return true; @@ -134,11 +137,13 @@ bool NumberFormatFields::toPartsVector(size_t overallLength, // field or the end of the enclosing field, whichever is // earlier. const auto& enclosing = fields[enclosingFields.back()]; - *part = {enclosing.type, std::min(enclosing.end, current->begin)}; + *part = {enclosing.type, sourceMap.source(enclosing), + std::min(enclosing.end, current->begin)}; popEnclosingFieldsEndingAt(part->endIndex); } else { // If there's no enclosing field, the space is a literal. - *part = {NumberPartType::Literal, current->begin}; + *part = {NumberPartType::Literal, sourceMap.source(current->begin), + current->begin}; } return true; @@ -152,7 +157,7 @@ bool NumberFormatFields::toPartsVector(size_t overallLength, // If the current field is last, the part extends to its end. if (++index == len) { - *part = {current->type, current->end}; + *part = {current->type, sourceMap.source(*current), current->end}; return true; } @@ -175,20 +180,21 @@ bool NumberFormatFields::toPartsVector(size_t overallLength, if (current->end <= next->begin) { // The next field begins after the current field ends. Therefore // the current part ends at the end of the current field. - *part = {current->type, current->end}; + *part = {current->type, sourceMap.source(*current), current->end}; popEnclosingFieldsEndingAt(part->endIndex); } else { // The current field encloses the next one. The current part // ends where the next field/part will start. - *part = {current->type, next->begin}; + *part = {current->type, sourceMap.source(*current), next->begin}; } return true; } public: - PartGenerator(const FieldsVector& vec, uint32_t limit) - : fields(vec), limit(limit), enclosingFields() {} + PartGenerator(const FieldsVector& vec, uint32_t limit, + const NumberPartSourceMap& sourceMap) + : fields(vec), limit(limit), sourceMap(sourceMap), enclosingFields() {} bool nextPart(bool* hasPart, NumberPart* part) { // There are no parts left if we've partitioned the entire string. @@ -211,7 +217,7 @@ bool NumberFormatFields::toPartsVector(size_t overallLength, // Finally, generate the result array. size_t lastEndIndex = 0; - PartGenerator gen(fields_, overallLength); + PartGenerator gen(fields_, overallLength, sourceMap); do { bool hasPart; NumberPart part; diff --git a/intl/components/src/NumberFormatFields.h b/intl/components/src/NumberFormatFields.h index 2af0acff070b..64c0e324bb09 100644 --- a/intl/components/src/NumberFormatFields.h +++ b/intl/components/src/NumberFormatFields.h @@ -24,6 +24,33 @@ struct NumberFormatField { : begin(begin), end(end), type(type) {} }; +struct NumberPartSourceMap { + struct Range { + uint32_t begin = 0; + uint32_t end = 0; + }; + + // Begin and end position of the start range. + Range start; + + // Begin and end position of the end range. + Range end; + + NumberPartSource source(uint32_t endIndex) { + if (start.begin < endIndex && endIndex <= start.end) { + return NumberPartSource::Start; + } + if (end.begin < endIndex && endIndex <= end.end) { + return NumberPartSource::End; + } + return NumberPartSource::Shared; + } + + NumberPartSource source(const NumberFormatField& field) { + return source(field.end); + } +}; + class NumberFormatFields { using FieldsVector = Vector; @@ -33,6 +60,12 @@ class NumberFormatFields { [[nodiscard]] bool append(NumberPartType type, int32_t begin, int32_t end); [[nodiscard]] bool toPartsVector(size_t overallLength, + NumberPartVector& parts) { + return toPartsVector(overallLength, {}, parts); + } + + [[nodiscard]] bool toPartsVector(size_t overallLength, + const NumberPartSourceMap& sourceMap, NumberPartVector& parts); };