Ring Daemon
Loading...
Searching...
No Matches
string_utils.cpp
Go to the documentation of this file.
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
43namespace jami {
44
45namespace json {
46static Json::CharReaderBuilder
48{
49 Json::CharReaderBuilder builder;
50 builder["collectComments"] = false;
51 return builder;
52}
53
54static Json::StreamWriterBuilder
56{
57 Json::StreamWriterBuilder builder;
58 builder["commentStyle"] = "None";
59 builder["indentation"] = "";
60 return builder;
61}
62
63const Json::CharReaderBuilder rbuilder = getJsonReaderBuilder();
64const Json::StreamWriterBuilder wbuilder = getJsonWriterBuilder();
65} // namespace json
66
67const std::string&
69{
70 static const std::string USER_AGENT = fmt::format("{:s} ({:s}/{:s})", PACKAGE_NAME, platform(), arch());
71 return USER_AGENT;
72}
73
74#ifdef _WIN32
75std::wstring
76to_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
90std::string
91to_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
106std::string
107to_string(double value)
108{
109 return fmt::format(FMT_COMPILE("{:.16G}"), value);
110}
111
112std::string
114{
115 return fmt::format(FMT_COMPILE("{:016x}"), id);
116}
117
119from_hex_string(const std::string& str)
120{
121 uint64_t id;
122 if (auto [p, ec] = std::from_chars(str.data(), str.data() + str.size(), id, 16); ec != std::errc()) {
123 throw std::invalid_argument("Unable to parse id: " + str);
124 }
125 return id;
126}
127
128std::string_view
129trim(std::string_view s)
130{
131 // NOLINTNEXTLINE(llvm-qualified-auto): MSVC can't convert to const char* from const_iterator
132 auto wsfront = std::find_if_not(s.cbegin(), s.cend(), [](int c) { return std::isspace(c); });
133 if (wsfront == s.cend())
134 return std::string_view {};
135 return std::string_view(&*wsfront,
136 std::find_if_not(s.rbegin(),
137 std::string_view::const_reverse_iterator(wsfront),
138 [](int c) { return std::isspace(c); })
139 .base()
140 - wsfront);
141}
142
143std::vector<unsigned>
144split_string_to_unsigned(std::string_view str, char delim)
145{
146 std::vector<unsigned> output;
147 for (auto first = str.data(), second = str.data(), last = first + str.size(); second != last && first != last;
148 first = second + 1) {
149 second = std::find(first, last, delim);
150 if (first != second) {
151 unsigned result;
152 auto [p, ec] = std::from_chars(first, second, result);
153 if (ec == std::errc())
154 output.emplace_back(result);
155 }
156 }
157 return output;
158}
159
160void
161string_replace(std::string& str, const std::string& from, const std::string& to)
162{
163 size_t start_pos = 0;
164 while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
165 str.replace(start_pos, from.length(), to);
166 start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
167 }
168}
169
170std::string_view
171string_remove_suffix(std::string_view str, char separator)
172{
173 auto it = str.find(separator);
174 if (it != std::string_view::npos)
175 str = str.substr(0, it);
176 return str;
177}
178
179std::string
180string_join(const std::set<std::string>& set, std::string_view separator)
181{
182 return fmt::format("{}", fmt::join(set, separator));
183}
184
185std::set<std::string>
186string_split_set(std::string& str, std::string_view separator)
187{
188 std::set<std::string> output;
189 for (auto first = str.data(), second = str.data(), last = first + str.size(); second != last && first != last;
190 first = second + 1) {
191 second = std::find_first_of(first, last, std::cbegin(separator), std::cend(separator));
192 if (first != second)
193 output.emplace(first, second - first);
194 }
195 return output;
196}
197
198std::string
199urlEncode(std::string_view input)
200{
201 if (input.empty()) {
202 return {};
203 }
204
205 constexpr auto isAsciiAlnum = [](unsigned char c) {
206 return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
207 };
208
209 std::ostringstream encoded;
210 // Use uppercase for hex digits
211 encoded << std::uppercase << std::hex;
212
213 for (unsigned char c : input) {
214 // If character is unreserved per RFC 3986, keep it as-is
215 if (isAsciiAlnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
216 encoded << c;
217 } else {
218 // Otherwise, percent-encode
220 }
221 }
222
223 return encoded.str();
224}
225
226} // namespace jami
static Json::CharReaderBuilder getJsonReaderBuilder()
static Json::StreamWriterBuilder getJsonWriterBuilder()
const Json::StreamWriterBuilder wbuilder
const Json::CharReaderBuilder rbuilder
constexpr std::string_view platform()
void emitSignal(Args... args)
Definition jami_signal.h:64
std::string string_join(const std::set< std::string > &set, std::string_view separator)
std::string to_hex_string(uint64_t id)
std::string to_string(double value)
const std::string & userAgent()
std::vector< unsigned > split_string_to_unsigned(std::string_view str, char delim)
uint64_t from_hex_string(const std::string &str)
std::string_view string_remove_suffix(std::string_view str, char separator)
std::string_view trim(std::string_view s)
std::string urlEncode(std::string_view input)
Percent-encode a string according to RFC 3986 unreserved characters.
std::set< std::string > string_split_set(std::string &str, std::string_view separator)
void string_replace(std::string &str, const std::string &from, const std::string &to)
constexpr std::string_view arch()