diff --git a/stl/inc/regex b/stl/inc/regex index 287db3420ec..dcc678294c7 100644 --- a/stl/inc/regex +++ b/stl/inc/regex @@ -1590,6 +1590,9 @@ enum class _Rx_char_class_kind : int { // must be aligned with corresponding _No template class _Builder3 { // provides operations used by _Parser3 to build the nfa +private: + using _String_type = typename _RxTraits::string_type; + public: _Builder3(const _RxTraits& _Tr, regex_constants::syntax_option_type); void _Setlong(); @@ -1602,6 +1605,7 @@ public: void _Add_dot(); void _Add_char(_Elem _Ch); void _Add_class(); + void _Finalize_class(); void _Add_char_to_class(_Elem _Ch); void _Add_range(_Elem, _Elem); void _Add_named_class(typename _RxTraits::char_class_type, _Rx_char_class_kind); @@ -1627,14 +1631,20 @@ private: void _Add_char_to_bitmap(unsigned char _Ch); void _Add_char_to_array(_Elem _Ch); void _Add_elts(_Node_class<_Elem, _RxTraits>*, typename _RxTraits::char_class_type, bool); - void _Char_to_elts(const _Elem*, const _Elem*, _Sequence<_Elem>**); + void _Add_coll_elem_to_buf( + vector<_String_type>& _Target, unsigned int _Start_length, const _Elem* _First, const _Elem* _Last); + void _Fill_coll_sequence_from_buf( + _Sequence<_Elem>*& _Seq_start, vector<_String_type>& _Source, unsigned int _Start_length); void _Generate_rep(_Node_base* _First_inner, int _Min, int _Max, bool _Greedy); _Root_node* _Root; _Node_base* _Current; regex_constants::syntax_option_type _Flags; const _RxTraits& _Traits; - typename _RxTraits::string_type _Chars; + _String_type _Chars; + _String_type _Ranges; + vector<_String_type> _Coll_elems; + vector<_String_type> _Equivs; public: _Builder3(const _Builder3&) = delete; @@ -3374,6 +3384,37 @@ void _Builder3<_FwdIt, _Elem, _RxTraits>::_Add_class() { // add bracket expressi _Link_node(new _Node_class<_Elem, _RxTraits>); } +template +void _Builder3<_FwdIt, _Elem, _RxTraits>::_Finalize_class() { // finalize bracket expression node + const auto _Node = static_cast<_Node_class<_Elem, _RxTraits>*>(_Current); + if (_Node->_Classes != typename _RxTraits::char_class_type{}) { + _Add_elts(_Node, _Node->_Classes, false); + if constexpr (sizeof(_Elem) == 1) { // unset when all code points are less than 256 + _Node->_Classes = typename _RxTraits::char_class_type{}; + } + } + + if (!_Chars.empty()) { + _Node->_Large = new _Buf<_Elem>; + _Node->_Large->_Insert2(_Chars.data(), _Chars.data() + _Chars.size()); + _Chars.clear(); + } + + if (!_Ranges.empty()) { + _Node->_Ranges = new _Buf<_Elem>; + _Node->_Ranges->_Insert2(_Ranges.data(), _Ranges.data() + _Ranges.size()); + _Ranges.clear(); + } + + if (!_Coll_elems.empty()) { + _Fill_coll_sequence_from_buf(_Node->_Coll, _Coll_elems, 2U); + } + + if (!_Equivs.empty()) { + _Fill_coll_sequence_from_buf(_Node->_Equiv, _Equivs, 1U); + } +} + template void _Builder3<_FwdIt, _Elem, _RxTraits>::_Add_char_to_bitmap(unsigned char _Ch) { // add character to accelerator table _Node_class<_Elem, _RxTraits>* _Node = static_cast<_Node_class<_Elem, _RxTraits>*>(_Current); @@ -3387,12 +3428,7 @@ void _Builder3<_FwdIt, _Elem, _RxTraits>::_Add_char_to_bitmap(unsigned char _Ch) template void _Builder3<_FwdIt, _Elem, _RxTraits>::_Add_char_to_array(_Elem _Ch) { // append character to character array - _Node_class<_Elem, _RxTraits>* _Node = static_cast<_Node_class<_Elem, _RxTraits>*>(_Current); - if (!_Node->_Large) { - _Node->_Large = new _Buf<_Elem>; - } - - _Node->_Large->_Insert2(_Ch); + _Chars.push_back(_Ch); } template @@ -3414,7 +3450,6 @@ void _Builder3<_FwdIt, _Elem, _RxTraits>::_Add_char_to_class(_Elem _Ch) { // add template void _Builder3<_FwdIt, _Elem, _RxTraits>::_Add_range(_Elem _Arg0, const _Elem _Arg1) { // add character range to set - using _String_type = typename _RxTraits::string_type; using _Char_traits_type = typename _String_type::traits_type; _Node_class<_Elem, _RxTraits>* _Node = static_cast<_Node_class<_Elem, _RxTraits>*>(_Current); @@ -3497,12 +3532,8 @@ void _Builder3<_FwdIt, _Elem, _RxTraits>::_Add_range(_Elem _Arg0, const _Elem _A } // store remaining range as pair - if (!_Node->_Ranges) { - _Node->_Ranges = new _Buf<_Elem>; - } - - _Node->_Ranges->_Insert2(_Arg0); - _Node->_Ranges->_Insert2(_Arg1); + _Ranges.push_back(_Arg0); + _Ranges.push_back(_Arg1); } template @@ -3527,18 +3558,20 @@ void _Builder3<_FwdIt, _Elem, _RxTraits>::_Add_named_class( // add contents of named class to bracket expression using _Char_class_type = typename _RxTraits::char_class_type; _Node_class<_Elem, _RxTraits>* _Node = static_cast<_Node_class<_Elem, _RxTraits>*>(_Current); - _Add_elts(_Node, _Cl, _Kind != _Rx_char_class_kind::_Positive); - if constexpr (sizeof(_Elem) > 1U) { - if (_Kind == _Rx_char_class_kind::_Positive) { - auto _Cl_all_bits_set = static_cast<_Char_class_type>(-1); - if ((_Node->_Classes != _Cl_all_bits_set && _Cl != _Cl_all_bits_set) - || _Node->_Classes == _Char_class_type{}) { - _Node->_Classes = static_cast<_Char_class_type>(_Node->_Classes | _Cl); - } else if (_Node->_Classes != _Cl) { - _Node->_Classes = static_cast<_Char_class_type>(_Node->_Classes & _Cl); + if (_Kind == _Rx_char_class_kind::_Positive) { + const auto _Cl_all_bits_set = static_cast<_Char_class_type>(-1); + if ((_Node->_Classes != _Cl_all_bits_set && _Cl != _Cl_all_bits_set) || _Node->_Classes == _Char_class_type{}) { + _Node->_Classes = static_cast<_Char_class_type>(_Node->_Classes | _Cl); + } else if (_Node->_Classes != _Cl) { + _Add_elts(_Node, _Cl_all_bits_set, false); + _Node->_Classes = static_cast<_Char_class_type>(_Node->_Classes & _Cl); + if constexpr (sizeof(_Elem) > 1) { // set only when potentially matching code points >= 256 _Node->_Flags |= _Fl_class_cl_all_bits; } - } else { + } + } else { + _Add_elts(_Node, _Cl, true); + if constexpr (sizeof(_Elem) > 1) { // set only when potentially matching code points >= 256 auto _Node_flag = static_cast<_Node_flags>(_Kind); _Node->_Flags |= _Node_flag; _Root->_Flags |= _Node_flag; @@ -3547,20 +3580,38 @@ void _Builder3<_FwdIt, _Elem, _RxTraits>::_Add_named_class( } template -void _Builder3<_FwdIt, _Elem, _RxTraits>::_Char_to_elts(const _Elem* const _First, const _Elem* const _Last, - _Sequence<_Elem>** _Cur) { // add collation element to element sequence - auto _Diff = static_cast(_Last - _First); - while (*_Cur && _Diff < (*_Cur)->_Sz) { - _Cur = &(*_Cur)->_Next; +void _Builder3<_FwdIt, _Elem, _RxTraits>::_Add_coll_elem_to_buf( + vector<_String_type>& _Target, unsigned int _Start_length, const _Elem* _First, const _Elem* _Last) { + // add collation element to internal length-partitioned buffer + auto _Length = static_cast(_Last - _First); + if (_Length > UINT_MAX) { + _Xregex_error(regex_constants::error_space); } - if (!(*_Cur) || _Diff != (*_Cur)->_Sz) { - // add new sequence holding elements of the same length - _Sequence<_Elem>* _Node = *_Cur; - *_Cur = new _Sequence<_Elem>(_Diff); - (*_Cur)->_Next = _Node; + if (_Target.size() <= _Length - _Start_length) { + _Target.resize(_Length - _Start_length + 1U); + } + + _Target[_Length - _Start_length].append(_First, _Last); +} + +template +void _Builder3<_FwdIt, _Elem, _RxTraits>::_Fill_coll_sequence_from_buf( + _Sequence<_Elem>*& _Seq_start, vector<_String_type>& _Source, const unsigned int _Start_length) { + // generate collation element sequence from internal length-partitioned buffer + unsigned int _Length{_Start_length}; + for (auto& _Elems_of_length : _Source) { + if (!_Elems_of_length.empty()) { + auto _Seq = new _Sequence<_Elem>(_Length); + _Seq->_Next = _Seq_start; + _Seq_start = _Seq; + auto _Start = _Elems_of_length.data(); + _Seq->_Data._Insert2(_Start, _Start + _Elems_of_length.size()); + _Elems_of_length.clear(); + } + + ++_Length; } - (*_Cur)->_Data._Insert2(_First, _Last); } template @@ -3586,17 +3637,14 @@ void _Builder3<_FwdIt, _Elem, _RxTraits>::_Add_equiv(const _Elem* const _First, } if constexpr (sizeof(_Elem) > 1U) { // map range - _Sequence<_Elem>** _Cur = _STD addressof(_Node->_Equiv); - _Char_to_elts(_First, _Last, _Cur); + _Add_coll_elem_to_buf(_Equivs, 1U, _First, _Last); } } template void _Builder3<_FwdIt, _Elem, _RxTraits>::_Add_coll(const _Elem* const _First, const _Elem* const _Last) { // add collation element to bracket expression - _Node_class<_Elem, _RxTraits>* _Node = static_cast<_Node_class<_Elem, _RxTraits>*>(_Current); - _Sequence<_Elem>** _Cur = _STD addressof(_Node->_Coll); - _Char_to_elts(_First, _Last, _Cur); + _Add_coll_elem_to_buf(_Coll_elems, 2U, _First, _Last); } template @@ -5447,6 +5495,7 @@ bool _Parser3<_FwdIt, _Elem, _RxTraits>::_CharacterClassEscape(bool _Addit) { // : _Char == 'S' ? _Rx_char_class_kind::_Negated_s : _Char == 'D' ? _Rx_char_class_kind::_Negated_d : _Rx_char_class_kind::_Positive); + _Next(); if (_Addit) { _Nfa._Add_class(); @@ -5455,12 +5504,14 @@ bool _Parser3<_FwdIt, _Elem, _RxTraits>::_CharacterClassEscape(bool _Addit) { // // Since the former negation is defective, do the latter instead. if (_Kind != _Rx_char_class_kind::_Positive) { _Nfa._Negate(); - _Kind = _Rx_char_class_kind::_Positive; } + + _Nfa._Add_named_class(_Cls, _Rx_char_class_kind::_Positive); + _Nfa._Finalize_class(); + } else { + _Nfa._Add_named_class(_Cls, _Kind); } - _Nfa._Add_named_class(_Cls, _Kind); - _Next(); return true; } @@ -5575,6 +5626,7 @@ void _Parser3<_FwdIt, _Elem, _RxTraits>::_CharacterClass() { // add bracket expr } _ClassRanges(); + _Nfa._Finalize_class(); } template diff --git a/tests/std/tests/GH_005204_regex_collating_ranges/test.cpp b/tests/std/tests/GH_005204_regex_collating_ranges/test.cpp index 25c39fe1584..078990b4448 100644 --- a/tests/std/tests/GH_005204_regex_collating_ranges/test.cpp +++ b/tests/std/tests/GH_005204_regex_collating_ranges/test.cpp @@ -675,12 +675,60 @@ void test_gh_6191() { } } +void test_gh_6441() { + // GH-6441: Perform insertions into character class NFA node buffers when parsing of the character class completes + + // U+0100 LATIN CAPITAL LETTER A WITH MACRON, U+0120 LATIN CAPITAL LETTER G WITH DOT ABOVE + { + test_wregex re_two_classes_with_large_chars(&g_regexTester, L"^[\u0100][\u0120]$"); + re_two_classes_with_large_chars.should_search_match(L"\u0100\u0120", L"\u0100\u0120"); + re_two_classes_with_large_chars.should_search_fail(L"\u0100\u0100"); + re_two_classes_with_large_chars.should_search_fail(L"\u0120\u0120"); + } + + // U+011F LATIN SMALL LETTER G WITH BREVE, U+013F LATIN CAPITAL LETTER L WITH MIDDLE DOT + { + test_wregex re_two_classes_with_large_char_ranges(&g_regexTester, L"^[\u0100-\u011F][\u0120-\u013F]$"); + re_two_classes_with_large_char_ranges.should_search_match(L"\u0100\u0120", L"\u0100\u0120"); + re_two_classes_with_large_char_ranges.should_search_fail(L"\u0100\u0100"); + re_two_classes_with_large_char_ranges.should_search_fail(L"\u0120\u0120"); + } + + for (auto pattern : {LR"(^[\d[:alpha:]]$)", LR"(^[[:alpha:]\d]$)"}) { + test_wregex re_alphanumeric(&g_regexTester, pattern); + re_alphanumeric.should_search_match(L"a", L"a"); + re_alphanumeric.should_search_match(L"0", L"0"); + re_alphanumeric.should_search_fail(L" "); + + re_alphanumeric.should_search_match(L"\u0100", L"\u0100"); // U+0100 LATIN CAPITAL LETTER A WITH MACRON + re_alphanumeric.should_search_match(L"\u0662", L"\u0662"); // U+0662 ARABIC-INDIC DIGIT TWO + re_alphanumeric.should_search_fail(L"\u202F"); // U+202F NARROW NO-BREAK SPACE + } + + gh_994_verify_match("csa", "[[.cs.]][a]", true); + gh_994_verify_match("cscs", "[[.cs.]][a]", false); + gh_994_verify_match("cscs", "[a][[.cs.]]", false); + gh_994_verify_match("csdzs", "[[.cs.]][[.dzs.]]", true); + gh_994_verify_match("cscs", "[[.cs.]][[.dzs.]]", false); + gh_994_verify_match("cscs", "[[.dzs.]][[.cs.]]", false); + +#ifndef _M_CEE_PURE + g_regexTester.should_match("ab", "[[=a=]][b]"); + g_regexTester.should_not_match("aa", "[[=a=]][b]"); + g_regexTester.should_not_match("aa", "[b][[=a=]]"); + g_regexTester.should_match("ab", "[[=a=]][[=b=]]"); + g_regexTester.should_not_match("aa", "[[=a=]][[=b=]]"); + g_regexTester.should_not_match("bb", "[[=a=]][[=b=]]"); +#endif // ^^^ !defined(_M_CEE_PURE) ^^^ +} + int main() { test_collating_ranges_german(); test_gh_994(); test_gh_5435(); test_gh_5437(); test_gh_6191(); + test_gh_6441(); return g_regexTester.result(); }