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