1 // Copyright 2015 Google Inc. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef BENCHMARK_RE_H_ 16 #define BENCHMARK_RE_H_ 17 18 #include "internal_macros.h" 19 20 // clang-format off 21 22 #if !defined(HAVE_STD_REGEX) && \ 23 !defined(HAVE_GNU_POSIX_REGEX) && \ 24 !defined(HAVE_POSIX_REGEX) 25 // No explicit regex selection; detect based on builtin hints. 26 #if defined(BENCHMARK_OS_LINUX) || defined(BENCHMARK_OS_APPLE) 27 #define HAVE_POSIX_REGEX 1 28 #elif __cplusplus >= 199711L 29 #define HAVE_STD_REGEX 1 30 #endif 31 #endif 32 33 // Prefer C regex libraries when compiling w/o exceptions so that we can 34 // correctly report errors. 35 #if defined(BENCHMARK_HAS_NO_EXCEPTIONS) && \ 36 defined(BENCHMARK_HAVE_STD_REGEX) && \ 37 (defined(HAVE_GNU_POSIX_REGEX) || defined(HAVE_POSIX_REGEX)) 38 #undef HAVE_STD_REGEX 39 #endif 40 41 #if defined(HAVE_STD_REGEX) 42 #include <regex> 43 #elif defined(HAVE_GNU_POSIX_REGEX) 44 #include <gnuregex.h> 45 #elif defined(HAVE_POSIX_REGEX) 46 #include <regex.h> 47 #else 48 #error No regular expression backend was found! 49 #endif 50 51 // clang-format on 52 53 #include <string> 54 55 #include "check.h" 56 57 namespace benchmark { 58 59 // A wrapper around the POSIX regular expression API that provides automatic 60 // cleanup 61 class Regex { 62 public: 63 Regex() : init_(false) {} 64 65 ~Regex(); 66 67 // Compile a regular expression matcher from spec. Returns true on success. 68 // 69 // On failure (and if error is not nullptr), error is populated with a human 70 // readable error message if an error occurs. 71 bool Init(const std::string& spec, std::string* error); 72 73 // Returns whether str matches the compiled regular expression. 74 bool Match(const std::string& str); 75 76 private: 77 bool init_; 78 // Underlying regular expression object 79 #if defined(HAVE_STD_REGEX) 80 std::regex re_; 81 #elif defined(HAVE_POSIX_REGEX) || defined(HAVE_GNU_POSIX_REGEX) 82 regex_t re_; 83 #else 84 #error No regular expression backend implementation available 85 #endif 86 }; 87 88 #if defined(HAVE_STD_REGEX) 89 90 inline bool Regex::Init(const std::string& spec, std::string* error) { 91 #ifdef BENCHMARK_HAS_NO_EXCEPTIONS 92 ((void)error); // suppress unused warning 93 #else 94 try { 95 #endif 96 re_ = std::regex(spec, std::regex_constants::extended); 97 init_ = true; 98 #ifndef BENCHMARK_HAS_NO_EXCEPTIONS 99 } 100 catch (const std::regex_error& e) { 101 if (error) { 102 *error = e.what(); 103 } 104 } 105 #endif 106 return init_; 107 } 108 109 inline Regex::~Regex() {} 110 111 inline bool Regex::Match(const std::string& str) { 112 if (!init_) { 113 return false; 114 } 115 return std::regex_search(str, re_); 116 } 117 118 #else 119 inline bool Regex::Init(const std::string& spec, std::string* error) { 120 int ec = regcomp(&re_, spec.c_str(), REG_EXTENDED | REG_NOSUB); 121 if (ec != 0) { 122 if (error) { 123 size_t needed = regerror(ec, &re_, nullptr, 0); 124 char* errbuf = new char[needed]; 125 regerror(ec, &re_, errbuf, needed); 126 127 // regerror returns the number of bytes necessary to null terminate 128 // the string, so we move that when assigning to error. 129 CHECK_NE(needed, 0); 130 error->assign(errbuf, needed - 1); 131 132 delete[] errbuf; 133 } 134 135 return false; 136 } 137 138 init_ = true; 139 return true; 140 } 141 142 inline Regex::~Regex() { 143 if (init_) { 144 regfree(&re_); 145 } 146 } 147 148 inline bool Regex::Match(const std::string& str) { 149 if (!init_) { 150 return false; 151 } 152 return regexec(&re_, str.c_str(), 0, nullptr, 0) == 0; 153 } 154 #endif 155 156 } // end namespace benchmark 157 158 #endif // BENCHMARK_RE_H_ 159