master
  1//===----------------------------------------------------------------------===//
  2//
  3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4// See https://llvm.org/LICENSE.txt for license information.
  5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6//
  7//===----------------------------------------------------------------------===//
  8
  9#ifndef _LIBCPP___LOCALE_DIR_SCAN_KEYWORD_H
 10#define _LIBCPP___LOCALE_DIR_SCAN_KEYWORD_H
 11
 12#include <__config>
 13#include <__memory/unique_ptr.h>
 14#include <ios>
 15
 16#if _LIBCPP_HAS_LOCALIZATION
 17
 18#  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 19#    pragma GCC system_header
 20#  endif
 21
 22_LIBCPP_BEGIN_NAMESPACE_STD
 23
 24// __scan_keyword
 25// Scans [__b, __e) until a match is found in the basic_strings range
 26//  [__kb, __ke) or until it can be shown that there is no match in [__kb, __ke).
 27//  __b will be incremented (visibly), consuming CharT until a match is found
 28//  or proved to not exist.  A keyword may be "", in which will match anything.
 29//  If one keyword is a prefix of another, and the next CharT in the input
 30//  might match another keyword, the algorithm will attempt to find the longest
 31//  matching keyword.  If the longer matching keyword ends up not matching, then
 32//  no keyword match is found.  If no keyword match is found, __ke is returned
 33//  and failbit is set in __err.
 34//  Else an iterator pointing to the matching keyword is found.  If more than
 35//  one keyword matches, an iterator to the first matching keyword is returned.
 36//  If on exit __b == __e, eofbit is set in __err.  If __case_sensitive is false,
 37//  __ct is used to force to lower case before comparing characters.
 38//  Examples:
 39//  Keywords:  "a", "abb"
 40//  If the input is "a", the first keyword matches and eofbit is set.
 41//  If the input is "abc", no match is found and "ab" are consumed.
 42template <class _InputIterator, class _ForwardIterator, class _Ctype>
 43_LIBCPP_HIDE_FROM_ABI _ForwardIterator __scan_keyword(
 44    _InputIterator& __b,
 45    _InputIterator __e,
 46    _ForwardIterator __kb,
 47    _ForwardIterator __ke,
 48    const _Ctype& __ct,
 49    ios_base::iostate& __err,
 50    bool __case_sensitive = true) {
 51  typedef typename iterator_traits<_InputIterator>::value_type _CharT;
 52  size_t __nkw                       = static_cast<size_t>(std::distance(__kb, __ke));
 53  const unsigned char __doesnt_match = '\0';
 54  const unsigned char __might_match  = '\1';
 55  const unsigned char __does_match   = '\2';
 56  unsigned char __statbuf[100];
 57  unsigned char* __status = __statbuf;
 58  unique_ptr<unsigned char, void (*)(void*)> __stat_hold(nullptr, free);
 59  if (__nkw > sizeof(__statbuf)) {
 60    __status = (unsigned char*)malloc(__nkw);
 61    if (__status == nullptr)
 62      std::__throw_bad_alloc();
 63    __stat_hold.reset(__status);
 64  }
 65  size_t __n_might_match = __nkw; // At this point, any keyword might match
 66  size_t __n_does_match  = 0;     // but none of them definitely do
 67  // Initialize all statuses to __might_match, except for "" keywords are __does_match
 68  unsigned char* __st = __status;
 69  for (_ForwardIterator __ky = __kb; __ky != __ke; ++__ky, (void)++__st) {
 70    if (!__ky->empty())
 71      *__st = __might_match;
 72    else {
 73      *__st = __does_match;
 74      --__n_might_match;
 75      ++__n_does_match;
 76    }
 77  }
 78  // While there might be a match, test keywords against the next CharT
 79  for (size_t __indx = 0; __b != __e && __n_might_match > 0; ++__indx) {
 80    // Peek at the next CharT but don't consume it
 81    _CharT __c = *__b;
 82    if (!__case_sensitive)
 83      __c = __ct.toupper(__c);
 84    bool __consume = false;
 85    // For each keyword which might match, see if the __indx character is __c
 86    // If a match if found, consume __c
 87    // If a match is found, and that is the last character in the keyword,
 88    //    then that keyword matches.
 89    // If the keyword doesn't match this character, then change the keyword
 90    //    to doesn't match
 91    __st = __status;
 92    for (_ForwardIterator __ky = __kb; __ky != __ke; ++__ky, (void)++__st) {
 93      if (*__st == __might_match) {
 94        _CharT __kc = (*__ky)[__indx];
 95        if (!__case_sensitive)
 96          __kc = __ct.toupper(__kc);
 97        if (__c == __kc) {
 98          __consume = true;
 99          if (__ky->size() == __indx + 1) {
100            *__st = __does_match;
101            --__n_might_match;
102            ++__n_does_match;
103          }
104        } else {
105          *__st = __doesnt_match;
106          --__n_might_match;
107        }
108      }
109    }
110    // consume if we matched a character
111    if (__consume) {
112      ++__b;
113      // If we consumed a character and there might be a matched keyword that
114      //   was marked matched on a previous iteration, then such keywords
115      //   which are now marked as not matching.
116      if (__n_might_match + __n_does_match > 1) {
117        __st = __status;
118        for (_ForwardIterator __ky = __kb; __ky != __ke; ++__ky, (void)++__st) {
119          if (*__st == __does_match && __ky->size() != __indx + 1) {
120            *__st = __doesnt_match;
121            --__n_does_match;
122          }
123        }
124      }
125    }
126  }
127  // We've exited the loop because we hit eof and/or we have no more "might matches".
128  if (__b == __e)
129    __err |= ios_base::eofbit;
130  // Return the first matching result
131  for (__st = __status; __kb != __ke; ++__kb, (void)++__st)
132    if (*__st == __does_match)
133      break;
134  if (__kb == __ke)
135    __err |= ios_base::failbit;
136  return __kb;
137}
138
139_LIBCPP_END_NAMESPACE_STD
140
141#endif // _LIBCPP_HAS_LOCALIZATION
142
143#endif // _LIBCPP___LOCALE_DIR_SCAN_KEYWORD_H