xref: /third_party/benchmark/src/re.h (revision a8c51b3f)
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(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
57namespace benchmark {
58
59// A wrapper around the POSIX regular expression API that provides automatic
60// cleanup
61class 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
90inline 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}
100catch (const std::regex_error& e) {
101  if (error) {
102    *error = e.what();
103  }
104}
105#endif
106return init_;
107}
108
109inline Regex::~Regex() {}
110
111inline 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
119inline 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      BM_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
142inline Regex::~Regex() {
143  if (init_) {
144    regfree(&re_);
145  }
146}
147
148inline 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