LCOV - code coverage report
Current view: top level - src - string_utils.cpp (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 85.9 % 78 67
Test Date: 2026-06-13 09:18:46 Functions: 100.0 % 20 20

            Line data    Source code
       1              : /*
       2              :  *  Copyright (C) 2004-2026 Savoir-faire Linux Inc.
       3              :  *
       4              :  *  This program is free software: you can redistribute it and/or modify
       5              :  *  it under the terms of the GNU General Public License as published by
       6              :  *  the Free Software Foundation, either version 3 of the License, or
       7              :  *  (at your option) any later version.
       8              :  *
       9              :  *  This program is distributed in the hope that it will be useful,
      10              :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      11              :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      12              :  *  GNU General Public License for more details.
      13              :  *
      14              :  *  You should have received a copy of the GNU General Public License
      15              :  *  along with this program. If not, see <https://www.gnu.org/licenses/>.
      16              :  */
      17              : #ifdef HAVE_CONFIG_H
      18              : #include "config.h"
      19              : #endif
      20              : 
      21              : #include "string_utils.h"
      22              : 
      23              : #include <fmt/core.h>
      24              : #include <fmt/ranges.h>
      25              : #include <fmt/compile.h>
      26              : 
      27              : #include <sstream>
      28              : #include <cctype>
      29              : #include <algorithm>
      30              : #include <ostream>
      31              : #include <iomanip>
      32              : #include <stdexcept>
      33              : #include <ios>
      34              : #include <charconv>
      35              : #include <string_view>
      36              : #ifdef _WIN32
      37              : #include <windows.h>
      38              : #include <oleauto.h>
      39              : #endif
      40              : 
      41              : #include "json_utils.h"
      42              : 
      43              : namespace jami {
      44              : 
      45              : namespace json {
      46              : static Json::CharReaderBuilder
      47           41 : getJsonReaderBuilder()
      48              : {
      49           41 :     Json::CharReaderBuilder builder;
      50          123 :     builder["collectComments"] = false;
      51           41 :     return builder;
      52            0 : }
      53              : 
      54              : static Json::StreamWriterBuilder
      55           41 : getJsonWriterBuilder()
      56              : {
      57           41 :     Json::StreamWriterBuilder builder;
      58          123 :     builder["commentStyle"] = "None";
      59          123 :     builder["indentation"] = "";
      60           41 :     return builder;
      61            0 : }
      62              : 
      63              : const Json::CharReaderBuilder rbuilder = getJsonReaderBuilder();
      64              : const Json::StreamWriterBuilder wbuilder = getJsonWriterBuilder();
      65              : } // namespace json
      66              : 
      67              : const std::string&
      68          694 : userAgent()
      69              : {
      70          717 :     static const std::string USER_AGENT = fmt::format("{:s} ({:s}/{:s})", PACKAGE_NAME, platform(), arch());
      71          694 :     return USER_AGENT;
      72              : }
      73              : 
      74              : #ifdef _WIN32
      75              : std::wstring
      76              : to_wstring(const std::string& str, int codePage)
      77              : {
      78              :     int srcLength = (int) str.length();
      79              :     int requiredSize = MultiByteToWideChar(codePage, 0, str.c_str(), srcLength, nullptr, 0);
      80              :     if (!requiredSize) {
      81              :         throw std::runtime_error("Unable to convert string to wstring");
      82              :     }
      83              :     std::wstring result((size_t) requiredSize, 0);
      84              :     if (!MultiByteToWideChar(codePage, 0, str.c_str(), srcLength, &(*result.begin()), requiredSize)) {
      85              :         throw std::runtime_error("Unable to convert string to wstring");
      86              :     }
      87              :     return result;
      88              : }
      89              : 
      90              : std::string
      91              : to_string(const std::wstring& wstr, int codePage)
      92              : {
      93              :     int srcLength = (int) wstr.length();
      94              :     int requiredSize = WideCharToMultiByte(codePage, 0, wstr.c_str(), srcLength, nullptr, 0, 0, 0);
      95              :     if (!requiredSize) {
      96              :         throw std::runtime_error("Unable to convert wstring to string");
      97              :     }
      98              :     std::string result((size_t) requiredSize, 0);
      99              :     if (!WideCharToMultiByte(codePage, 0, wstr.c_str(), srcLength, &(*result.begin()), requiredSize, 0, 0)) {
     100              :         throw std::runtime_error("Unable to convert wstring to string");
     101              :     }
     102              :     return result;
     103              : }
     104              : #endif
     105              : 
     106              : std::string
     107           67 : to_string(double value)
     108              : {
     109          268 :     return fmt::format(FMT_COMPILE("{:.16G}"), value);
     110              : }
     111              : 
     112              : std::string
     113          828 : to_hex_string(uint64_t id)
     114              : {
     115         3312 :     return fmt::format(FMT_COMPILE("{:016x}"), id);
     116              : }
     117              : 
     118              : uint64_t
     119            1 : from_hex_string(const std::string& str)
     120              : {
     121              :     uint64_t id;
     122            1 :     if (auto [p, ec] = std::from_chars(str.data(), str.data() + str.size(), id, 16); ec != std::errc()) {
     123            0 :         throw std::invalid_argument("Unable to parse id: " + str);
     124              :     }
     125            1 :     return id;
     126              : }
     127              : 
     128              : std::string_view
     129          111 : trim(std::string_view s)
     130              : {
     131              :     // NOLINTNEXTLINE(llvm-qualified-auto): MSVC can't convert to const char* from const_iterator
     132          222 :     auto wsfront = std::find_if_not(s.cbegin(), s.cend(), [](int c) { return std::isspace(c); });
     133          111 :     if (wsfront == s.cend())
     134            0 :         return std::string_view {};
     135          222 :     return std::string_view(&*wsfront,
     136          111 :                             std::find_if_not(s.rbegin(),
     137          111 :                                              std::string_view::const_reverse_iterator(wsfront),
     138          111 :                                              [](int c) { return std::isspace(c); })
     139          111 :                                     .base()
     140          111 :                                 - wsfront);
     141              : }
     142              : 
     143              : std::vector<unsigned>
     144          442 : split_string_to_unsigned(std::string_view str, char delim)
     145              : {
     146          442 :     std::vector<unsigned> output;
     147         1736 :     for (auto first = str.data(), second = str.data(), last = first + str.size(); second != last && first != last;
     148         1294 :          first = second + 1) {
     149         1294 :         second = std::find(first, last, delim);
     150         1294 :         if (first != second) {
     151              :             unsigned result;
     152         1292 :             auto [p, ec] = std::from_chars(first, second, result);
     153         1292 :             if (ec == std::errc())
     154         1292 :                 output.emplace_back(result);
     155              :         }
     156              :     }
     157          442 :     return output;
     158            0 : }
     159              : 
     160              : void
     161          478 : string_replace(std::string& str, const std::string& from, const std::string& to)
     162              : {
     163          478 :     size_t start_pos = 0;
     164          590 :     while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
     165          112 :         str.replace(start_pos, from.length(), to);
     166          112 :         start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
     167              :     }
     168          478 : }
     169              : 
     170              : std::string_view
     171         1004 : string_remove_suffix(std::string_view str, char separator)
     172              : {
     173         1004 :     auto it = str.find(separator);
     174         1004 :     if (it != std::string_view::npos)
     175          186 :         str = str.substr(0, it);
     176         1004 :     return str;
     177              : }
     178              : 
     179              : std::string
     180          655 : string_join(const std::set<std::string>& set, std::string_view separator)
     181              : {
     182         1310 :     return fmt::format("{}", fmt::join(set, separator));
     183              : }
     184              : 
     185              : std::set<std::string>
     186         1592 : string_split_set(std::string& str, std::string_view separator)
     187              : {
     188         1592 :     std::set<std::string> output;
     189         1592 :     for (auto first = str.data(), second = str.data(), last = first + str.size(); second != last && first != last;
     190            0 :          first = second + 1) {
     191            0 :         second = std::find_first_of(first, last, std::cbegin(separator), std::cend(separator));
     192            0 :         if (first != second)
     193            0 :             output.emplace(first, second - first);
     194              :     }
     195         1592 :     return output;
     196            0 : }
     197              : 
     198              : std::string
     199            4 : urlEncode(std::string_view input)
     200              : {
     201            4 :     if (input.empty()) {
     202            0 :         return {};
     203              :     }
     204              : 
     205           20 :     constexpr auto isAsciiAlnum = [](unsigned char c) {
     206           20 :         return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
     207              :     };
     208              : 
     209            4 :     std::ostringstream encoded;
     210              :     // Use uppercase for hex digits
     211            4 :     encoded << std::uppercase << std::hex;
     212              : 
     213           24 :     for (unsigned char c : input) {
     214              :         // If character is unreserved per RFC 3986, keep it as-is
     215           20 :         if (isAsciiAlnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
     216           16 :             encoded << c;
     217              :         } else {
     218              :             // Otherwise, percent-encode
     219            4 :             encoded << '%' << std::setw(2) << std::setfill('0') << static_cast<int>(c);
     220              :         }
     221              :     }
     222              : 
     223            4 :     return encoded.str();
     224            4 : }
     225              : 
     226              : } // namespace jami
        

Generated by: LCOV version 2.0-1