//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef _LIBCPP___LOCALE_DIR_SCAN_KEYWORD_H #define _LIBCPP___LOCALE_DIR_SCAN_KEYWORD_H #include <__config> #include <__memory/unique_ptr.h> #include #if _LIBCPP_HAS_LOCALIZATION # if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header # endif _LIBCPP_BEGIN_NAMESPACE_STD // __scan_keyword // Scans [__b, __e) until a match is found in the basic_strings range // [__kb, __ke) or until it can be shown that there is no match in [__kb, __ke). // __b will be incremented (visibly), consuming CharT until a match is found // or proved to not exist. A keyword may be "", in which will match anything. // If one keyword is a prefix of another, and the next CharT in the input // might match another keyword, the algorithm will attempt to find the longest // matching keyword. If the longer matching keyword ends up not matching, then // no keyword match is found. If no keyword match is found, __ke is returned // and failbit is set in __err. // Else an iterator pointing to the matching keyword is found. If more than // one keyword matches, an iterator to the first matching keyword is returned. // If on exit __b == __e, eofbit is set in __err. If __case_sensitive is false, // __ct is used to force to lower case before comparing characters. // Examples: // Keywords: "a", "abb" // If the input is "a", the first keyword matches and eofbit is set. // If the input is "abc", no match is found and "ab" are consumed. template _LIBCPP_HIDE_FROM_ABI _ForwardIterator __scan_keyword( _InputIterator& __b, _InputIterator __e, _ForwardIterator __kb, _ForwardIterator __ke, const _Ctype& __ct, ios_base::iostate& __err, bool __case_sensitive = true) { typedef typename iterator_traits<_InputIterator>::value_type _CharT; size_t __nkw = static_cast(std::distance(__kb, __ke)); const unsigned char __doesnt_match = '\0'; const unsigned char __might_match = '\1'; const unsigned char __does_match = '\2'; unsigned char __statbuf[100]; unsigned char* __status = __statbuf; unique_ptr __stat_hold(nullptr, free); if (__nkw > sizeof(__statbuf)) { __status = (unsigned char*)malloc(__nkw); if (__status == nullptr) std::__throw_bad_alloc(); __stat_hold.reset(__status); } size_t __n_might_match = __nkw; // At this point, any keyword might match size_t __n_does_match = 0; // but none of them definitely do // Initialize all statuses to __might_match, except for "" keywords are __does_match unsigned char* __st = __status; for (_ForwardIterator __ky = __kb; __ky != __ke; ++__ky, (void)++__st) { if (!__ky->empty()) *__st = __might_match; else { *__st = __does_match; --__n_might_match; ++__n_does_match; } } // While there might be a match, test keywords against the next CharT for (size_t __indx = 0; __b != __e && __n_might_match > 0; ++__indx) { // Peek at the next CharT but don't consume it _CharT __c = *__b; if (!__case_sensitive) __c = __ct.toupper(__c); bool __consume = false; // For each keyword which might match, see if the __indx character is __c // If a match if found, consume __c // If a match is found, and that is the last character in the keyword, // then that keyword matches. // If the keyword doesn't match this character, then change the keyword // to doesn't match __st = __status; for (_ForwardIterator __ky = __kb; __ky != __ke; ++__ky, (void)++__st) { if (*__st == __might_match) { _CharT __kc = (*__ky)[__indx]; if (!__case_sensitive) __kc = __ct.toupper(__kc); if (__c == __kc) { __consume = true; if (__ky->size() == __indx + 1) { *__st = __does_match; --__n_might_match; ++__n_does_match; } } else { *__st = __doesnt_match; --__n_might_match; } } } // consume if we matched a character if (__consume) { ++__b; // If we consumed a character and there might be a matched keyword that // was marked matched on a previous iteration, then such keywords // which are now marked as not matching. if (__n_might_match + __n_does_match > 1) { __st = __status; for (_ForwardIterator __ky = __kb; __ky != __ke; ++__ky, (void)++__st) { if (*__st == __does_match && __ky->size() != __indx + 1) { *__st = __doesnt_match; --__n_does_match; } } } } } // We've exited the loop because we hit eof and/or we have no more "might matches". if (__b == __e) __err |= ios_base::eofbit; // Return the first matching result for (__st = __status; __kb != __ke; ++__kb, (void)++__st) if (*__st == __does_match) break; if (__kb == __ke) __err |= ios_base::failbit; return __kb; } _LIBCPP_END_NAMESPACE_STD #endif // _LIBCPP_HAS_LOCALIZATION #endif // _LIBCPP___LOCALE_DIR_SCAN_KEYWORD_H