diff --git a/icu4c/source/common/unicode/utfiterator.h b/icu4c/source/common/unicode/utfiterator.h index 9f1f247fba4..24c849c03e1 100644 --- a/icu4c/source/common/unicode/utfiterator.h +++ b/icu4c/source/common/unicode/utfiterator.h @@ -1068,6 +1068,12 @@ public: * @draft ICU 78 */ U_FORCE_INLINE explicit UTFIterator(UnitIter p) : p_(p), start_(p), limit_(p), units_(0, 0, false, p, p) {} + /** + * Default constructor. Makes a non-functional iterator. + * + * @draft ICU 78 + */ + U_FORCE_INLINE UTFIterator() : p_{}, start_{}, limit_{}, units_(0, 0, false, p_, p_) {} /** Move constructor. @draft ICU 78 */ U_FORCE_INLINE UTFIterator(UTFIterator &&src) noexcept = default; @@ -1381,6 +1387,7 @@ public: U_FORCE_INLINE explicit reverse_iterator(U_HEADER_ONLY_NAMESPACE::UTFIterator iter) : p_(iter.getLogicalPosition()), start_(iter.start_), limit_(iter.limit_), units_(0, 0, false, p_, p_) {} + U_FORCE_INLINE reverse_iterator() : p_{}, start_{}, limit_{}, units_(0, 0, false, p_, p_) {} U_FORCE_INLINE reverse_iterator(reverse_iterator &&src) noexcept = default; U_FORCE_INLINE reverse_iterator &operator=(reverse_iterator &&src) noexcept = default; @@ -1704,6 +1711,12 @@ public: * @draft ICU 78 */ U_FORCE_INLINE explicit UnsafeUTFIterator(UnitIter p) : p_(p), units_(0, 0, p, p) {} + /** + * Default constructor. Makes a non-functional iterator. + * + * @draft ICU 78 + */ + U_FORCE_INLINE UnsafeUTFIterator() : p_{}, units_(0, 0, p_, p_) {} /** Move constructor. @draft ICU 78 */ U_FORCE_INLINE UnsafeUTFIterator(UnsafeUTFIterator &&src) noexcept = default; @@ -2004,6 +2017,7 @@ public: U_FORCE_INLINE explicit reverse_iterator(U_HEADER_ONLY_NAMESPACE::UnsafeUTFIterator iter) : p_(iter.getLogicalPosition()), units_(0, 0, p_, p_) {} + U_FORCE_INLINE reverse_iterator() : p_{}, units_(0, 0, p_, p_) {} U_FORCE_INLINE reverse_iterator(reverse_iterator &&src) noexcept = default; U_FORCE_INLINE reverse_iterator &operator=(reverse_iterator &&src) noexcept = default; diff --git a/icu4c/source/test/intltest/utfiteratortest.cpp b/icu4c/source/test/intltest/utfiteratortest.cpp index 63e4637cb42..eedaadd9f6f 100644 --- a/icu4c/source/test/intltest/utfiteratortest.cpp +++ b/icu4c/source/test/intltest/utfiteratortest.cpp @@ -26,12 +26,14 @@ using U_HEADER_ONLY_NAMESPACE::UTFIterator; using U_HEADER_ONLY_NAMESPACE::utfIterator; using U_HEADER_ONLY_NAMESPACE::UTFStringCodePoints; using U_HEADER_ONLY_NAMESPACE::utfStringCodePoints; +using U_HEADER_ONLY_NAMESPACE::UnsafeUTFIterator; +using U_HEADER_ONLY_NAMESPACE::unsafeUTFIterator; +using U_HEADER_ONLY_NAMESPACE::UnsafeUTFStringCodePoints; +using U_HEADER_ONLY_NAMESPACE::unsafeUTFStringCodePoints; #if 0 // Sample code for API docs etc. Compile when changing samples or APIs. #include -using U_HEADER_ONLY_NAMESPACE::unsafeUTFIterator; -using U_HEADER_ONLY_NAMESPACE::unsafeUTFStringCodePoints; int32_t rangeLoop16(std::u16string_view s) { // We are just adding up the code points for minimal-code demonstration purposes. @@ -203,6 +205,7 @@ public: typedef std::forward_iterator_tag iterator_category; explicit FwdIter(const Unit *data) : p(data) {} + FwdIter() = default; bool operator==(const FwdIter &other) const { return p == other.p; } bool operator!=(const FwdIter &other) const { return !operator==(other); } @@ -254,6 +257,8 @@ std::string string8FromBytes(const int bytes[], size_t length) { return result; } +enum TestMode { ILL_FORMED, WELL_FORMED, UNSAFE }; + class UTFIteratorTest : public IntlTest { public: void runIndexedTest(int32_t index, UBool exec, const char *&name, char * /*par*/) override { @@ -267,6 +272,7 @@ public: TESTCASE_AUTO(testSafe16SinglePassIterGood); TESTCASE_AUTO(testSafe16SinglePassIterNegative); TESTCASE_AUTO(testSafe16FwdIter); + TESTCASE_AUTO(testUnsafe16FwdIter); TESTCASE_AUTO(testSafe8Good); TESTCASE_AUTO(testSafe8Negative); @@ -274,6 +280,7 @@ public: TESTCASE_AUTO(testSafe8SinglePassIterGood); TESTCASE_AUTO(testSafe8SinglePassIterFFFD); TESTCASE_AUTO(testSafe8FwdIter); + TESTCASE_AUTO(testUnsafe8FwdIter); TESTCASE_AUTO(testSafe32Good); TESTCASE_AUTO(testSafe32Negative); @@ -282,6 +289,7 @@ public: TESTCASE_AUTO(testSafe32SinglePassIterGood); TESTCASE_AUTO(testSafe32SinglePassIterSurrogate); TESTCASE_AUTO(testSafe32FwdIter); + TESTCASE_AUTO(testUnsafe32FwdIter); TESTCASE_AUTO_END; } @@ -304,8 +312,8 @@ public: template void testSafeSinglePassIter(StringView piped, bool isWellFormed); - template - void testSafeFwdIter(StringView piped); + template + void testFwdIter(StringView piped); static constexpr std::u16string_view good16{u"a|b|ç|カ|🚴"}; static const char *good8Chars; @@ -345,7 +353,12 @@ public: testSafeSinglePassIter(bad16, false); } void testSafe16FwdIter() { - testSafeFwdIter(good16); + testFwdIter< + UTFIterator>, + WELL_FORMED>(good16); + } + void testUnsafe16FwdIter() { + testFwdIter>, UNSAFE>(good16); } void testSafe8Good() { @@ -367,7 +380,13 @@ public: std::string_view(string8FromBytes(badChars8, std::size(badChars8))), false); } void testSafe8FwdIter() { - testSafeFwdIter(std::string_view{good8Chars}); + testFwdIter< + UTFIterator>, + WELL_FORMED>(std::string_view{good8Chars}); + } + void testUnsafe8FwdIter() { + testFwdIter>, UNSAFE>( + std::string_view{good8Chars}); } void testSafe32Good() { @@ -389,7 +408,12 @@ public: testSafeSinglePassIter(bad32, false); } void testSafe32FwdIter() { - testSafeFwdIter(good32); + testFwdIter< + UTFIterator>, + WELL_FORMED>(good32); + } + void testUnsafe32FwdIter() { + testFwdIter>, UNSAFE>(good32); } }; @@ -518,16 +542,21 @@ void UTFIteratorTest::testSafeSinglePassIter(StringView piped, bool isWellFormed assertTrue("iter == endIter", iter == rangeLimit); } -template -void UTFIteratorTest::testSafeFwdIter(StringView piped) { +template +void UTFIteratorTest::testFwdIter(StringView piped) { using Unit = typename StringView::value_type; auto parts = split(piped); auto joined = join(parts); // "abçカ🚴" FwdIter goodBegin(joined.data()); FwdIter goodLimit(joined.data() + joined.length()); - auto iter = utfIterator(goodBegin, goodLimit); - auto rangeLimit = utfIterator(goodLimit); + Iter iter; + if constexpr (mode == UNSAFE) { + iter = Iter(goodBegin); + } else { + iter = Iter(goodBegin, goodLimit); + } + auto rangeLimit = Iter(goodLimit); assertTrue( "forward_iterator_tag", std::is_same_v< @@ -539,7 +568,9 @@ void UTFIteratorTest::testSafeFwdIter(StringView piped) { auto units = *iter; assertEquals("iter[1] * codePoint", u'b', units.codePoint()); assertEquals("iter[1] * length", parts[1].length(), units.length()); - assertTrue("iter[1] * wellFormed", units.wellFormed()); + if constexpr (mode != UNSAFE) { + assertTrue("iter[1] * wellFormed", units.wellFormed()); + } // No units.stringView() when the unit iterator is not a pointer. auto unitsIter = units.begin(); for (auto c : parts[1]) { @@ -556,7 +587,9 @@ void UTFIteratorTest::testSafeFwdIter(StringView piped) { units = *iter++; assertEquals("iter[4] * codePoint", U'🚴', units.codePoint()); assertEquals("iter[4] * length", parts[4].length(), units.length()); - assertTrue("iter[4] * wellFormed", units.wellFormed()); + if constexpr (mode != UNSAFE) { + assertTrue("iter[4] * wellFormed", units.wellFormed()); + } unitsIter = units.begin(); for (auto c : parts[4]) { assertEquals("iter[back 4] * begin()[i]",