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#if !defined(HAVE_STD_REGEX) && \ 21 !defined(HAVE_GNU_POSIX_REGEX) && \ 22 !defined(HAVE_POSIX_REGEX) 23 // No explicit regex selection; detect based on builtin hints. 24 #if defined(BENCHMARK_OS_LINUX) || defined(BENCHMARK_OS_APPLE) 25 #define HAVE_POSIX_REGEX 1 26 #elif __cplusplus >= 199711L 27 #define HAVE_STD_REGEX 1 28 #endif 29#endif 30 31// Prefer C regex libraries when compiling w/o exceptions so that we can 32// correctly report errors. 33#if defined(BENCHMARK_HAS_NO_EXCEPTIONS) && \ 34 defined(BENCHMARK_HAVE_STD_REGEX) && \ 35 (defined(HAVE_GNU_POSIX_REGEX) || defined(HAVE_POSIX_REGEX)) 36 #undef HAVE_STD_REGEX 37#endif 38 39#if defined(HAVE_STD_REGEX) 40 #include <regex> 41#elif defined(HAVE_GNU_POSIX_REGEX) 42 #include <gnuregex.h> 43#elif defined(HAVE_POSIX_REGEX) 44 #include <regex.h> 45#else 46#error No regular expression backend was found! 47#endif 48#include <string> 49 50#include "check.h" 51 52namespace benchmark { 53 54// A wrapper around the POSIX regular expression API that provides automatic 55// cleanup 56class Regex { 57 public: 58 Regex() : init_(false) {} 59 60 ~Regex(); 61 62 // Compile a regular expression matcher from spec. Returns true on success. 63 // 64 // On failure (and if error is not nullptr), error is populated with a human 65 // readable error message if an error occurs. 66 bool Init(const std::string& spec, std::string* error); 67 68 // Returns whether str matches the compiled regular expression. 69 bool Match(const std::string& str); 70 71 private: 72 bool init_; 73// Underlying regular expression object 74#if defined(HAVE_STD_REGEX) 75 std::regex re_; 76#elif defined(HAVE_POSIX_REGEX) || defined(HAVE_GNU_POSIX_REGEX) 77 regex_t re_; 78#else 79 #error No regular expression backend implementation available 80#endif 81}; 82 83#if defined(HAVE_STD_REGEX) 84 85inline bool Regex::Init(const std::string& spec, std::string* error) { 86#ifdef BENCHMARK_HAS_NO_EXCEPTIONS 87 ((void)error); // suppress unused warning 88#else 89 try { 90#endif 91 re_ = std::regex(spec, std::regex_constants::extended); 92 init_ = true; 93#ifndef BENCHMARK_HAS_NO_EXCEPTIONS 94 } catch (const std::regex_error& e) { 95 if (error) { 96 *error = e.what(); 97 } 98 } 99#endif 100 return init_; 101} 102 103inline Regex::~Regex() {} 104 105inline bool Regex::Match(const std::string& str) { 106 if (!init_) { 107 return false; 108 } 109 return std::regex_search(str, re_); 110} 111 112#else 113inline bool Regex::Init(const std::string& spec, std::string* error) { 114 int ec = regcomp(&re_, spec.c_str(), REG_EXTENDED | REG_NOSUB); 115 if (ec != 0) { 116 if (error) { 117 size_t needed = regerror(ec, &re_, nullptr, 0); 118 char* errbuf = new char[needed]; 119 regerror(ec, &re_, errbuf, needed); 120 121 // regerror returns the number of bytes necessary to null terminate 122 // the string, so we move that when assigning to error. 123 CHECK_NE(needed, 0); 124 error->assign(errbuf, needed - 1); 125 126 delete[] errbuf; 127 } 128 129 return false; 130 } 131 132 init_ = true; 133 return true; 134} 135 136inline Regex::~Regex() { 137 if (init_) { 138 regfree(&re_); 139 } 140} 141 142inline bool Regex::Match(const std::string& str) { 143 if (!init_) { 144 return false; 145 } 146 return regexec(&re_, str.c_str(), 0, nullptr, 0) == 0; 147} 148#endif 149 150} // end namespace benchmark 151 152#endif // BENCHMARK_RE_H_ 153