RESTinio
pcre_regex_engine.hpp
Go to the documentation of this file.
1 /*
2  restinio
3 */
4 
5 /*!
6  Regex engine for using std::regex.
7 */
8 
9 #pragma once
10 
11 #include <pcre.h>
12 
13 #include <fmt/format.h>
14 
15 #include <restinio/exception.hpp>
16 
17 namespace restinio
18 {
19 
20 namespace router
21 {
22 
23 namespace pcre_details
24 {
25 
26 
27 //
28 // match_results_t
29 //
30 
31 //! A wrapper class for working with pcre match results.
32 template < typename Traits >
34 {
35  struct matched_item_descriptor_t final
36  {
38  int begin,
39  int end )
40  : m_begin{ begin }
41  , m_end{ end }
42  {}
43 
44  int m_begin;
45  int m_end;
46  };
47 
48  matched_item_descriptor_t
49  operator [] ( std::size_t i ) const
50  {
51  if( m_submatches[ 2 * i ] >= 0 )
52  {
53  // Submatch has non-empty value.
55  m_submatches[ 2 * i ],
56  m_submatches[ 1 + 2 * i ] };
57  }
58 
59  // This submatch group is empty.
60  return matched_item_descriptor_t{ 0, 0 };
61  }
62 
63  std::size_t size() const { return m_size; }
64 
65  std::size_t m_size{ 0 };
67 };
68 
69 //
70 // regex_t
71 //
72 
73 //! A wrapper for using pcre regexes in express_router.
74 class regex_t final
75 {
76  public:
77  regex_t() = default;
78  regex_t( string_view_t r, int options )
79  {
80  compile( r, options );
81  }
82 
83  regex_t( const regex_t & ) = delete;
84  regex_t & operator = ( const regex_t & ) = delete;
85 
86  regex_t( regex_t && rw )
87  {
88  (*this) = std::move( rw );
89  }
90 
91  regex_t & operator = ( regex_t && rw )
92  {
93  if( this != &rw )
94  {
95  m_route_regex = rw.m_route_regex;
96  rw.m_route_regex = nullptr;
97  }
98 
99  return *this;
100  }
101 
103  {
104  if( nullptr != m_route_regex )
105  {
106  pcre_free( m_route_regex );
107  }
108  }
109 
110  const pcre *
111  pcre_regex() const
112  {
113  return m_route_regex;
114  }
115 
116  private:
117  pcre * m_route_regex{ nullptr };
118 
119  void
120  compile( string_view_t r, int options )
121  {
122  const char* compile_error;
123  int eoffset;
124 
125  // We need zero-terminated string.
126  const std::string route{ r.data(), r.size() };
127 
128  m_route_regex = pcre_compile( route.c_str(), options, &compile_error, &eoffset, nullptr );
129 
130  if( nullptr == m_route_regex )
131  {
132  throw exception_t{
133  fmt::format(
134  "unable to compile regex \"{}\": {}",
135  route,
136  compile_error ) };
137  }
138  }
139 };
140 
141 } /* namespace pcre_details */
142 
143 //
144 // pcre_traits_t
145 //
146 
147 //! PCRE traits.
148 template < std::size_t Max_Capture_Groups = 20, int Compile_Options = 0, int Match_Options = 0 >
150 {
152  static constexpr int compile_options = Compile_Options;
153  static constexpr int match_options = Match_Options;
154 };
155 
156 //
157 // pcre_regex_engine_t
158 //
159 
160 //! Regex engine implementation for PCRE.
161 template < typename Traits = pcre_traits_t<> >
163 {
164  using compiled_regex_t = pcre_details::regex_t;
165  using match_results_t = pcre_details::match_results_t< Traits >;
166  using matched_item_descriptor_t = typename match_results_t::matched_item_descriptor_t;
167 
168  // Max itemes that can be captured be pcre engine.
169  static constexpr std::size_t
171  {
172  return Traits::max_capture_groups;
173  }
174 
175  //! Create compiled regex object for a given route.
176  static auto
178  //! Regular expression (the pattern).
179  string_view_t r,
180  //! Option for case sensativity.
181  bool is_case_sensative )
182  {
183  int options = Traits::compile_options;
184 
185  if( !is_case_sensative )
186  {
187  options |= PCRE_CASELESS;
188  }
189 
190  return compiled_regex_t{ r, options };
191  }
192 
193  //! Wrapper function for matching logic invokation.
194  static auto
196  string_view_t target_path,
197  const compiled_regex_t & r,
198  match_results_t & match_results )
199  {
200  const int rc =
201  pcre_exec(
202  r.pcre_regex(),
203  nullptr,
204  target_path.data(),
205  static_cast< int >( target_path.size() ),
206  0, // startoffset
207  Traits::match_options,
208  match_results.m_submatches.data(),
209  static_cast< int >( match_results.m_submatches.size() ) );
210 
211  if( rc > 0 )
212  {
213  match_results.m_size = static_cast<std::size_t>(rc);
214  return true;
215  }
216  else if( rc == 0 )
217  {
218  // This should not happen,
219  // because the number of groups is checked when creating route matcher.
220  throw exception_t{ "unexpected: not enough submatch vector size" };
221  }
222  if( PCRE_ERROR_NOMATCH != rc )
223  {
224  throw exception_t{ fmt::format("pcre error: {}", rc ) };
225  }
226  // else PCRE_ERROR_NOMATCH -- no match for this route
227 
228  return false;
229  }
230 
231  //! Get the beginning of a submatch.
232  static auto
233  submatch_begin_pos( const matched_item_descriptor_t & m )
234  {
235  return static_cast< std::size_t >( m.m_begin );
236  }
237 
238  //! Get the end of a submatch.
239  static auto
240  submatch_end_pos( const matched_item_descriptor_t & m )
241  {
242  return static_cast< std::size_t >( m.m_end );
243  }
244 };
245 
246 } /* namespace router */
247 
248 } /* namespace restinio */
matched_item_descriptor_t operator[](std::size_t i) const
static auto try_match(string_view_t target_path, const compiled_regex_t &r, match_results_t &match_results)
Wrapper function for matching logic invokation.
std::array< int, 3 *Traits::max_capture_groups > m_submatches
A wrapper class for working with pcre match results.
static auto compile_regex(string_view_t r, bool is_case_sensative)
Create compiled regex object for a given route.
Regex engine implementation for PCRE.
regex_t(string_view_t r, int options)
static auto submatch_end_pos(const matched_item_descriptor_t &m)
Get the end of a submatch.
void compile(string_view_t r, int options)
static constexpr std::size_t max_capture_groups()
regex_t & operator=(const regex_t &)=delete
static constexpr std::size_t max_capture_groups
static auto submatch_begin_pos(const matched_item_descriptor_t &m)
Get the beginning of a submatch.
std::enable_if< std::is_same< Parameter_Container, query_string_params_t >::value||std::is_same< Parameter_Container, router::route_params_t >::value, optional_t< Value_Type > >::type opt_value(const Parameter_Container &params, string_view_t key)
Gets the value of a parameter specified by key wrapped in optional_t<Value_Type> if parameter exists ...
Definition: value_or.hpp:64