LCOV - code coverage report
Current view: top level - http/common - syntax.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 100.0 % 43 43
Test Date: 2026-11-10 10:38:07 Functions: 100.0 % 32 32

            Line data    Source code
       1              : #ifndef THINGER_HTTP_COMMON_SYNTAX_HPP
       2              : #define THINGER_HTTP_COMMON_SYNTAX_HPP
       3              : 
       4              : #include <algorithm>
       5              : #include <cstddef>
       6              : #include <string_view>
       7              : 
       8              : // Character classes and common rules of the HTTP grammar (RFC 9110, section 5.6), for the
       9              : // parsers and for checking what is sent
      10              : namespace thinger::http::syntax {
      11              : 
      12              :     // tchar
      13      6437390 :     constexpr bool is_token_char(unsigned char c) {
      14      6437390 :         if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) return true;
      15       504313 :         switch (c) {
      16        75418 :             case '!': case '#': case '$': case '%': case '&': case '\'': case '*': case '+':
      17              :             case '-': case '.': case '^': case '_': case '`': case '|': case '~':
      18        75418 :                 return true;
      19       428895 :             default:
      20       428895 :                 return false;
      21              :         }
      22              :     }
      23              : 
      24              :     // token = 1*tchar
      25       563608 :     constexpr bool is_token(std::string_view value) {
      26      6916365 :         return !value.empty() && std::all_of(value.begin(), value.end(), [](unsigned char c) { return is_token_char(c); });
      27              :     }
      28              : 
      29              :     // SP / HTAB
      30       117328 :     constexpr bool is_whitespace(unsigned char c) {
      31       117328 :         return c == ' ' || c == '\t';
      32              :     }
      33              : 
      34              :     // Field values (and reason phrases): HTAB, SP, VCHAR and obs-text; never CR, LF, NUL or
      35              :     // another control character
      36      6553490 :     constexpr bool is_field_value_char(unsigned char c) {
      37      6553490 :         return c == '\t' || (c >= 0x20 && c != 0x7f);
      38              :     }
      39              : 
      40       485903 :     constexpr bool is_field_value(std::string_view value) {
      41      7004845 :         return std::all_of(value.begin(), value.end(), [](unsigned char c) { return is_field_value_char(c); });
      42              :     }
      43              : 
      44              :     // Without leading and trailing whitespace (OWS)
      45        28757 :     constexpr std::string_view trim(std::string_view value) {
      46        50648 :         while (!value.empty() && is_whitespace(value.front())) value.remove_prefix(1);
      47        28784 :         while (!value.empty() && is_whitespace(value.back())) value.remove_suffix(1);
      48        28757 :         return value;
      49              :     }
      50              : 
      51              :     // Whether `predicate` holds for an element of a comma-separated list (RFC 9110, 5.6.1),
      52              :     // given without whitespace around it; empty elements are skipped. Stops at the first one.
      53              :     template<typename Predicate>
      54         7121 :     constexpr bool any_element(std::string_view list, Predicate&& predicate) {
      55         7807 :         while (!list.empty()) {
      56         6365 :             auto comma = list.find(',');
      57         6365 :             auto element = trim(list.substr(0, comma));
      58         6365 :             if (!element.empty() && predicate(element)) return true;
      59         2656 :             if (comma == std::string_view::npos) break;
      60          686 :             list.remove_prefix(comma + 1);
      61              :         }
      62         3412 :         return false;
      63              :     }
      64              : 
      65              :     template<typename Function>
      66         3146 :     constexpr void for_each_element(std::string_view list, Function&& function) {
      67         4084 :         any_element(list, [&](std::string_view element) {
      68         2308 :             function(element);
      69         2308 :             return false;
      70              :         });
      71         3146 :     }
      72              : 
      73              :     // Weight (qvalue, RFC 9110, 12.4.2) in thousandths: "0" or "1" with up to three decimals;
      74              :     // -1 if invalid
      75          165 :     constexpr int parse_qvalue(std::string_view value) {
      76          165 :         if (value.empty() || value.size() > 5) return -1;
      77          162 :         if (value[0] != '0' && value[0] != '1') return -1;
      78          153 :         int quality = (value[0] - '0') * 1000;
      79          153 :         if (value.size() == 1) return quality;
      80          105 :         if (value[1] != '.') return -1;
      81          105 :         int scale = 100;
      82          216 :         for (size_t i = 2; i < value.size(); ++i, scale /= 10) {
      83          111 :             if (value[i] < '0' || value[i] > '9') return -1;
      84          111 :             quality += (value[i] - '0') * scale;
      85              :         }
      86          105 :         return quality <= 1000 ? quality : -1;
      87              :     }
      88              : 
      89              : }
      90              : 
      91              : #endif
        

Generated by: LCOV version 2.0-1