RESTinio
Loading...
Searching...
No Matches
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
17namespace restinio
18{
19
20namespace router
21{
22
23namespace pcre_details
24{
25
26
27//
28// match_results_t
29//
30
31//! A wrapper class for working with pcre match results.
32template < typename Traits >
33struct match_results_t final
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
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.
54 return matched_item_descriptor_t{
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 };
66 std::array< int, 3 * Traits::max_capture_groups > m_submatches;
67};
68
69//
70// regex_t
71//
72
73//! A wrapper for using pcre regexes in express_router.
74class 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 *
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(
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.
150template < std::size_t Max_Capture_Groups = 20, int Compile_Options = 0, int Match_Options = 0 >
152{
153 static constexpr std::size_t max_capture_groups = Max_Capture_Groups;
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.
163template < typename Traits = pcre_traits_t<> >
165{
167 using match_results_t = pcre_details::match_results_t< Traits >;
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
237 {
238 return static_cast< std::size_t >( m.m_begin );
239 }
240
241 //! Get the end of a submatch.
242 static auto
244 {
245 return static_cast< std::size_t >( m.m_end );
246 }
247};
248
249} /* namespace router */
250
251} /* namespace restinio */
Exception class for all exceptions thrown by RESTinio.
Definition exception.hpp:26
exception_t(const char *err)
Definition exception.hpp:29
regex_t & operator=(const regex_t &)=delete
void compile(string_view_t r, int options)
regex_t(string_view_t r, int options)
regex_t & operator=(regex_t &&rw) noexcept
#define RESTINIO_FMT_FORMAT_STRING(s)
matched_item_descriptor_t operator[](std::size_t i) const
std::array< int, 3 *Traits::max_capture_groups > m_submatches
Regex engine implementation for PCRE.
pcre_details::match_results_t< Traits > match_results_t
static constexpr std::size_t max_capture_groups()
static auto compile_regex(string_view_t r, bool is_case_sensative)
Create compiled regex object for a given route.
typename match_results_t::matched_item_descriptor_t matched_item_descriptor_t
static auto submatch_begin_pos(const matched_item_descriptor_t &m)
Get the beginning of a submatch.
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.
static auto submatch_end_pos(const matched_item_descriptor_t &m)
Get the end of a submatch.
static constexpr std::size_t max_capture_groups