src/detail/grammar.hpp
100.0% Lines (91/91)
100.0% List of functions (15/15)
98.5% Branches (67/68)
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 |