src/detail/grammar.hpp

100.0% Lines (91/91) 100.0% List of functions (15/15) 98.5% Branches (67/68)
grammar.hpp
f(x) Functions (15)
Function Calls Lines Branches Blocks
unsigned long boost::burl::detail::distance<unsigned long>(char const*, char const*) :30 591000x 100.0% 100.0% unsigned short boost::burl::detail::distance<unsigned short>(char const*, char const*) :30 44338x 100.0% 100.0% boost::burl::detail::trim_front(char const*, char const*) :39 17711x 100.0% 100.0% 100.0% boost::burl::detail::trim_back(char const*, char const*) :53 17711x 100.0% 83.3% 100.0% boost::burl::detail::is_token_char(char) :68 274719x 100.0% 100.0% boost::burl::detail::is_print(char) :100 360695x 100.0% 100.0% boost::burl::detail::is_target_char(char) :107 117880x 100.0% 100.0% 100.0% boost::burl::detail::is_digit(char) :115 1755x 100.0% 100.0% 100.0% boost::burl::detail::parse_token_to_eol(char const*, char const*, char const*&, boost::system::error_code&) :122 31532x 100.0% 100.0% 100.0% boost::burl::detail::parse_field(char const*&, char const*, std::basic_string_view<char, std::char_traits<char> >&, std::basic_string_view<char, std::char_traits<char> >&, boost::system::error_code&) :173 36524x 100.0% 100.0% 100.0% void boost::burl::detail::parse_limited<boost::burl::head_parser::parse(unsigned long, boost::system::error_code&)::{lambda(auto:1&, auto:2, auto:3&)#1}>(boost::burl::head_parser::parse(unsigned long, boost::system::error_code&)::{lambda(auto:1&, auto:2, auto:3&)#1}&&, char const*&, char const*, unsigned long, boost::http::error, boost::system::error_code&) :250 80679x 100.0% 100.0% 100.0% void boost::burl::detail::parse_limited<boost::burl::head_parser::parse(unsigned long, boost::system::error_code&)::{lambda(auto:1&, auto:2, auto:3&)#2}>(boost::burl::head_parser::parse(unsigned long, boost::system::error_code&)::{lambda(auto:1&, auto:2, auto:3&)#2}&&, char const*&, char const*, unsigned long, boost::http::error, boost::system::error_code&) :250 38353x 100.0% 100.0% 100.0% void boost::burl::detail::parse_limited<boost::burl::head_parser::parse_fields_(char const*&, char const*, boost::system::error_code&)::{lambda(auto:1&, auto:2, auto:3&)#1}>(boost::burl::head_parser::parse_fields_(char const*&, char const*, boost::system::error_code&)::{lambda(auto:1&, auto:2, auto:3&)#1}&&, char const*&, char const*, unsigned long, boost::http::error, boost::system::error_code&) :250 36370x 100.0% 100.0% 100.0% void boost::burl::detail::parse_limited<boost::burl::parser::parse_trailer(boost::burl::fields_base&, boost::system::error_code&)::{lambda(auto:1&, auto:2, auto:3&)#1}>(boost::burl::parser::parse_trailer(boost::burl::fields_base&, boost::system::error_code&)::{lambda(auto:1&, auto:2, auto:3&)#1}&&, char const*&, char const*, unsigned long, boost::http::error, boost::system::error_code&) :250 154x 100.0% 100.0% boost::burl::detail::parse_limited<boost::burl::parser::parse_trailer(boost::burl::fields_base&, boost::system::error_code&)::{lambda(auto:1&, auto:2, auto:3&)#1}>(boost::burl::parser::parse_trailer(boost::burl::fields_base&, boost::system::error_code&)::{lambda(auto:1&, auto:2, auto:3&)#1}&&, char const*&, char const*, unsigned long, boost::http::error, boost::system::error_code&)::{lambda()#1}::operator()() const :258 154x 100.0% 100.0% 100.0%
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2021 Vinnie Falco ([email protected])
3 // Copyright (c) 2026 Mohammad Nejati
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Official repository: https://github.com/cppalliance/burl
9 //
10
11 #ifndef BOOST_BURL_SRC_DETAIL_GRAMMAR_HPP
12 #define BOOST_BURL_SRC_DETAIL_GRAMMAR_HPP
13
14 #include <boost/config.hpp>
15 #include <boost/http/error.hpp>
16 #include <boost/system/error_code.hpp>
17
18 #include <cstddef>
19 #include <string_view>
20
21 namespace boost
22 {
23 namespace burl
24 {
25 namespace detail
26 {
27
28 template<class T = std::size_t>
29 T
30 635338x distance(
31 char const* it,
32 char const* end) noexcept
33 {
34 635338x return static_cast<T>(end - it);
35 }
36
37 inline
38 char const*
39 17711x trim_front(
40 char const* it, char const* end) noexcept
41 {
42
2/2
✓ Branch 0 taken 35420 times.
✓ Branch 1 taken 7 times.
35427x while(it != end)
43 {
44
4/4
✓ Branch 0 taken 17705 times.
✓ Branch 1 taken 17715 times.
✓ Branch 2 taken 17704 times.
✓ Branch 3 taken 1 time.
35420x if(*it != ' ' && *it != '\t')
45 17704x break;
46 17716x ++it;
47 }
48 17711x return it;
49 }
50
51 inline
52 char const*
53 17711x trim_back(
54 char const* it, char const* first) noexcept
55 {
56
2/2
✓ Branch 0 taken 17708 times.
✓ Branch 1 taken 7 times.
17715x while(it != first)
57 {
58 17708x auto const c = it[-1];
59
3/4
✓ Branch 0 taken 17704 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 17704 times.
✗ Branch 3 not taken.
17708x if(c != ' ' && c != '\t')
60 17704x break;
61 4x --it;
62 }
63 17711x return it;
64 }
65
66 inline
67 bool
68 274719x is_token_char(char c) noexcept
69 {
70 /*
71 tchar = "!" | "#" | "$" | "%" | "&" |
72 "'" | "*" | "+" | "-" | "." |
73 "^" | "_" | "`" | "|" | "~" |
74 DIGIT | ALPHA
75 */
76 static char constexpr tab[] = {
77 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
78 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
79 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32
80 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48
81 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
82 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80
83 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
84 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, // 112
85 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128
86 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 144
87 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160
88 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 176
89 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 192
90 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 208
91 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224
92 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240
93 };
94 static_assert(sizeof(tab) == 256);
95 274719x return tab[static_cast<unsigned char>(c)];
96 }
97
98 inline
99 bool
100 360695x is_print(char c) noexcept
101 {
102 360695x return static_cast<unsigned char>(c-32) < 95;
103 }
104
105 inline
106 bool
107 117880x is_target_char(char c) noexcept
108 {
109 117880x auto const u = static_cast<unsigned char>(c);
110
4/4
✓ Branch 0 taken 83508 times.
✓ Branch 1 taken 34372 times.
✓ Branch 2 taken 83506 times.
✓ Branch 3 taken 2 times.
117880x return u >= 0x21 && u <= 0x7e;
111 }
112
113 inline
114 bool
115 1755x is_digit(char c) noexcept
116 {
117
4/4
✓ Branch 0 taken 1753 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1749 times.
✓ Branch 3 taken 4 times.
1755x return c >= '0' && c <= '9';
118 }
119
120 inline
121 char const*
122 31532x parse_token_to_eol(
123 char const* it,
124 char const* end,
125 char const*& token_end,
126 system::error_code& ec) noexcept
127 {
128 338712x for(;; ++it)
129 {
130
2/2
✓ Branch 0 taken 9549 times.
✓ Branch 1 taken 360695 times.
370244x if(it >= end)
131 {
132 9549x ec = http::error::need_data;
133 9549x return it;
134 }
135
2/2
✓ Branch 1 taken 21986 times.
✓ Branch 2 taken 338709 times.
360695x if(BOOST_UNLIKELY(! is_print(*it)))
136
2/2
✓ Branch 0 taken 21984 times.
✓ Branch 1 taken 2 times.
21986x if((BOOST_LIKELY(static_cast<
137 21984x unsigned char>(*it) < '\040') &&
138
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 21981 times.
21984x BOOST_LIKELY(*it != 9)) ||
139
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5x BOOST_UNLIKELY(*it == 127))
140 21983x goto found_control;
141 }
142 21983x found_control:
143
2/2
✓ Branch 0 taken 21974 times.
✓ Branch 1 taken 9 times.
21983x if(BOOST_LIKELY(*it == '\r'))
144 {
145
2/2
✓ Branch 0 taken 1278 times.
✓ Branch 1 taken 20696 times.
21974x if(++it >= end)
146 {
147 1278x ec = http::error::need_data;
148 1278x return end;
149 }
150
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 20694 times.
20696x if(*it++ != '\n')
151 {
152 2x ec = http::error::bad_line_ending;
153 2x return end;
154 }
155 20694x token_end = it - 2;
156 }
157
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 6 times.
9x else if(*it == '\n')
158 {
159 // bare LF
160 3x ec = http::error::bad_line_ending;
161 3x return end;
162 }
163 else
164 {
165 // invalid character
166 6x return nullptr;
167 }
168 20694x return it;
169 }
170
171 inline
172 void
173 36524x parse_field(
174 char const*& it,
175 char const* end,
176 std::string_view& name,
177 std::string_view& value,
178 system::error_code& ec)
179 {
180 /* header-field = field-name ":" OWS field-value OWS
181
182 field-name = token
183 field-value = *( field-content / obs-fold )
184 field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
185 field-vchar = VCHAR / obs-text
186
187 obs-fold = CRLF 1*( SP / HTAB )
188 ; obsolete line folding
189 ; see Section 3.2.4
190
191 token = 1*<any CHAR except CTLs or separators>
192 CHAR = <any US-ASCII character (octets 0 - 127)>
193 sep = "(" | ")" | "<" | ">" | "@"
194 | "," | ";" | ":" | "\" | <">
195 | "/" | "[" | "]" | "?" | "="
196 | "{" | "}" | SP | HT
197 */
198
199 36524x auto first = it;
200
6/6
✓ Branch 0 taken 123355 times.
✓ Branch 1 taken 5572 times.
✓ Branch 3 taken 92403 times.
✓ Branch 4 taken 30952 times.
✓ Branch 5 taken 92403 times.
✓ Branch 6 taken 36524 times.
128927x while(it != end && is_token_char(*it))
201 {
202 92403x ++it;
203 }
204
2/2
✓ Branch 0 taken 5572 times.
✓ Branch 1 taken 30952 times.
36524x if(it == end)
205 {
206 5572x ec = http::error::need_data;
207 5572x return;
208 }
209
4/4
✓ Branch 0 taken 30948 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 30943 times.
30952x if(it == first || *it != ':')
210 {
211 9x ec = http::error::bad_field_name;
212 9x return;
213 }
214 30943x name = { first, it };
215 30943x ++it; // eat ':'
216 30943x first = it;
217 30943x char const* token_end = nullptr;
218 for(;;)
219 {
220 // parse to CRLF
221 30953x it = parse_token_to_eol(it, end, token_end, ec);
222
2/2
✓ Branch 1 taken 10580 times.
✓ Branch 2 taken 20373 times.
30953x if(ec)
223 10580x return;
224
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 20369 times.
20373x if(! it)
225 {
226 4x ec = http::error::bad_field_value;
227 4x return;
228 }
229 // Look 1 char past the CRLF to handle obs-fold.
230
2/2
✓ Branch 0 taken 2648 times.
✓ Branch 1 taken 17721 times.
20369x if(it == end)
231 {
232 2648x ec = http::error::need_data;
233 2648x return;
234 }
235
4/4
✓ Branch 0 taken 17713 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 17711 times.
✓ Branch 3 taken 2 times.
17721x if(*it != ' ' && *it != '\t')
236 {
237 17711x first = trim_front(first, token_end);
238 17711x value = { first, trim_back(token_end, first) };
239 17711x return;
240 }
241 // obs-fold: resolve in place, CRLF -> SP SP
242 10x auto const q = const_cast<char*>(it);
243 10x q[-2] = ' ';
244 10x q[-1] = ' ';
245 10x }
246 }
247
248 template<class Parse>
249 void
250 155556x parse_limited(
251 Parse&& parse,
252 char const*& it,
253 char const* end,
254 std::size_t limit,
255 http::error limit_err,
256 system::error_code& ec) noexcept
257 {
258 311266x bool const limited = [&]()
259 {
260
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 152 times.
154x if(distance(it, end) >= limit)
261 {
262 2x end = it + limit;
263 2x return true;
264 }
265 152x return false;
266 155556x }();
267 155556x parse(it, end, ec);
268
6/6
✓ Branch 1 taken 87826 times.
✓ Branch 2 taken 67730 times.
✓ Branch 3 taken 23357 times.
✓ Branch 4 taken 64469 times.
✓ Branch 5 taken 23357 times.
✓ Branch 6 taken 132199 times.
155556x if(ec == http::error::need_data && limited)
269 23357x ec = limit_err;
270 155556x }
271
272 } // namespace detail
273 } // namespace burl
274 } // namespace boost
275
276 #endif
277