Home | History | Annotate | Download | only in src
      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