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 <restinio/impl/include_fmtlib.hpp>
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 ) noexcept
88  {
89  rw.m_route_regex = nullptr;
90  }
91 
92  regex_t & operator = ( regex_t && rw ) noexcept
93  {
94  if( this != &rw )
95  {
96  m_route_regex = rw.m_route_regex;
97  rw.m_route_regex = nullptr;
98  }
99 
100  return *this;
101  }
102 
104  {
105  if( nullptr != m_route_regex )
106  {
107  pcre_free( m_route_regex );
108  }
109  }
110 
111  const pcre *
112  pcre_regex() const
113  {
114  return m_route_regex;
115  }
116 
117  private:
118  pcre * m_route_regex{ nullptr };
119 
120  void
121  compile( string_view_t r, int options )
122  {
123  const char* compile_error;
124  int eoffset;
125 
126  // We need zero-terminated string.
127  const std::string route{ r.data(), r.size() };
128 
129  m_route_regex = pcre_compile( route.c_str(), options, &compile_error, &eoffset, nullptr );
130 
131  if( nullptr == m_route_regex )
132  {
133  throw exception_t{
134  fmt::format(
135  RESTINIO_FMT_FORMAT_STRING(
136  "unable to compile regex \"{}\": {}" ),
137  route,
138  compile_error ) };
139  }
140  }
141 };
142 
143 } /* namespace pcre_details */
144 
145 //
146 // pcre_traits_t
147 //
148 
149 //! PCRE traits.
150 template < std::size_t Max_Capture_Groups = 20, int Compile_Options = 0, int Match_Options = 0 >
152 {
154  static constexpr int compile_options = Compile_Options;
155  static constexpr int match_options = Match_Options;
156 };
157 
158 //
159 // pcre_regex_engine_t
160 //
161 
162 //! Regex engine implementation for PCRE.
163 template < typename Traits = pcre_traits_t<> >
165 {
166  using compiled_regex_t = pcre_details::regex_t;
167  using match_results_t = pcre_details::match_results_t< Traits >;
168  using matched_item_descriptor_t = typename match_results_t::matched_item_descriptor_t;
169 
170  // Max itemes that can be captured be pcre engine.
171  static constexpr std::size_t
173  {
174  return Traits::max_capture_groups;
175  }
176 
177  //! Create compiled regex object for a given route.
178  static auto
180  //! Regular expression (the pattern).
181  string_view_t r,
182  //! Option for case sensativity.
183  bool is_case_sensative )
184  {
185  int options = Traits::compile_options;
186 
187  if( !is_case_sensative )
188  {
189  options |= PCRE_CASELESS;
190  }
191 
192  return compiled_regex_t{ r, options };
193  }
194 
195  //! Wrapper function for matching logic invokation.
196  static auto
198  string_view_t target_path,
199  const compiled_regex_t & r,
200  match_results_t & match_results )
201  {
202  const int rc =
203  pcre_exec(
204  r.pcre_regex(),
205  nullptr,
206  target_path.data(),
207  static_cast< int >( target_path.size() ),
208  0, // startoffset
209  Traits::match_options,
210  match_results.m_submatches.data(),
211  static_cast< int >( match_results.m_submatches.size() ) );
212 
213  if( rc > 0 )
214  {
215  match_results.m_size = static_cast<std::size_t>(rc);
216  return true;
217  }
218  else if( rc == 0 )
219  {
220  // This should not happen,
221  // because the number of groups is checked when creating route matcher.
222  throw exception_t{ "unexpected: not enough submatch vector size" };
223  }
224  if( PCRE_ERROR_NOMATCH != rc )
225  {
226  throw exception_t{
227  fmt::format( RESTINIO_FMT_FORMAT_STRING( "pcre error: {}" ), rc ) };
228  }
229  // else PCRE_ERROR_NOMATCH -- no match for this route
230 
231  return false;
232  }
233 
234  //! Get the beginning of a submatch.
235  static auto
236  submatch_begin_pos( const matched_item_descriptor_t & m )
237  {
238  return static_cast< std::size_t >( m.m_begin );
239  }
240 
241  //! Get the end of a submatch.
242  static auto
243  submatch_end_pos( const matched_item_descriptor_t & m )
244  {
245  return static_cast< std::size_t >( m.m_end );
246  }
247 };
248 
249 } /* namespace router */
250 
251 } /* namespace restinio */
matched_item_descriptor_t operator[](std::size_t i) const
regex_t & operator=(regex_t &&rw) noexcept
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::enable_if< std::is_same< Parameter_Container, query_string_params_t >::value||std::is_same< Parameter_Container, router::route_params_t >::value, std::optional< Value_Type > >::type opt_value(const Parameter_Container &params, string_view_t key)
Gets the value of a parameter specified by key wrapped in std::optional<Value_Type> if parameter exis...
Definition: value_or.hpp:64
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.