LCOV - code coverage report
Current view: top level - http/util - url.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 96.3 % 82 79
Test Date: 2026-02-20 15:38:22 Functions: 100.0 % 9 9

            Line data    Source code
       1              : #include "url.hpp"
       2              : 
       3              : namespace thinger::http::util::url{
       4              : 
       5              :     namespace {
       6              :         constexpr char hex_chars[] = "0123456789ABCDEF";
       7              : 
       8          248 :         inline int hex_digit(char c) {
       9          248 :             if (c >= '0' && c <= '9') return c - '0';
      10           69 :             if (c >= 'a' && c <= 'f') return c - 'a' + 10;
      11           66 :             if (c >= 'A' && c <= 'F') return c - 'A' + 10;
      12           18 :             return -1;
      13              :         }
      14              : 
      15         1158 :         inline bool is_unreserved(char c) {
      16         1158 :             return std::isalnum(static_cast<unsigned char>(c))
      17         1158 :                 || c == '-' || c == '_' || c == '.' || c == '~';
      18              :         }
      19              :     }
      20              : 
      21              :     // RFC 3986 Section 2.3: unreserved characters are not percent-encoded
      22          168 :     std::string url_encode(const std::string &value) {
      23          168 :         std::string result;
      24          168 :         result.reserve(value.size() * 1.2);
      25              : 
      26         1005 :         for (unsigned char c : value) {
      27          837 :             if (is_unreserved(c)) {
      28          726 :                 result += static_cast<char>(c);
      29              :             } else {
      30          111 :                 result += '%';
      31          111 :                 result += hex_chars[c >> 4];
      32          111 :                 result += hex_chars[c & 0x0F];
      33              :             }
      34              :         }
      35              : 
      36          168 :         return result;
      37            0 :     }
      38              : 
      39              :     // RFC 3986 path encoding: also preserves '/' and '~'
      40           36 :     std::string uri_path_encode(const std::string &value) {
      41           36 :         std::string result;
      42           36 :         result.reserve(value.size() * 1.2);
      43              : 
      44          357 :         for (unsigned char c : value) {
      45          321 :             if (is_unreserved(c) || c == '/' ) {
      46          312 :                 result += static_cast<char>(c);
      47              :             } else {
      48            9 :                 result += '%';
      49            9 :                 result += hex_chars[c >> 4];
      50            9 :                 result += hex_chars[c & 0x0F];
      51              :             }
      52              :         }
      53              : 
      54           36 :         return result;
      55            0 :     }
      56              : 
      57         3357 :     bool url_decode(const std::string &in, std::string &out) {
      58         3357 :         out.clear();
      59         3357 :         out.reserve(in.size());
      60              : 
      61        27575 :         for (size_t i = 0; i < in.size(); ++i) {
      62        24233 :             if (in[i] == '%') {
      63          130 :                 if (i + 2 >= in.size()) return false;
      64          124 :                 int hi = hex_digit(in[i + 1]);
      65          124 :                 int lo = hex_digit(in[i + 2]);
      66          124 :                 if (hi < 0 || lo < 0) return false;
      67          115 :                 out += static_cast<char>((hi << 4) | lo);
      68          115 :                 i += 2;
      69        24103 :             } else if (in[i] == '+') {
      70            8 :                 out += ' ';
      71              :             } else {
      72        24095 :                 out += in[i];
      73              :             }
      74              :         }
      75         3342 :         return true;
      76              :     }
      77              : 
      78         2410 :     std::string url_decode(const std::string &in) {
      79         2410 :         std::string out;
      80         2410 :         if (url_decode(in, out)) {
      81         2407 :             return out;
      82              :         }
      83            3 :         return {};
      84         2410 :     }
      85              : 
      86           30 :     void parse_url_encoded_data(const std::string &data, std::multimap<std::string, std::string>& store) {
      87           30 :         auto start = data.cbegin();
      88           30 :         auto end = data.cend();
      89           30 :         parse_url_encoded_data(start, end, store);
      90           30 :     }
      91              : 
      92          188 :     void parse_url_encoded_data(std::string::const_iterator& start, std::string::const_iterator& end, std::multimap<std::string, std::string>& store) {
      93          188 :         if (start == end) return;
      94              : 
      95          182 :         auto it = start;
      96          401 :         while (it != end) {
      97              :             // find the end of the current key=value pair
      98          219 :             auto pair_end = std::find(it, end, '&');
      99              : 
     100              :             // find the '=' separator within the pair
     101          219 :             auto eq_pos = std::find(it, pair_end, '=');
     102              : 
     103          219 :             std::string key(it, eq_pos);
     104          219 :             std::string value;
     105          219 :             if (eq_pos != pair_end) {
     106          216 :                 value.assign(eq_pos + 1, pair_end);
     107              :             }
     108              : 
     109          219 :             if (!key.empty()) {
     110          219 :                 store.emplace(url_decode(key), url_decode(value));
     111              :             }
     112              : 
     113          219 :             it = (pair_end != end) ? pair_end + 1 : end;
     114          219 :         }
     115              : 
     116          182 :         start = it;
     117              :     }
     118              : 
     119           36 :     std::string get_url_encoded_data(const std::multimap<std::string, std::string>& store) {
     120           36 :         std::string result;
     121           36 :         bool first = true;
     122           99 :         for (const auto& [key, value] : store) {
     123           63 :             if (!first) result += '&';
     124           63 :             result += url_encode(key);
     125           63 :             result += '=';
     126           63 :             result += url_encode(value);
     127           63 :             first = false;
     128              :         }
     129           36 :         return result;
     130            0 :     }
     131              : 
     132              : }
        

Generated by: LCOV version 2.0-1