Ring Daemon 16.0.0
Loading...
Searching...
No Matches
string_utils.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2004-2025 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
26#include <sstream>
27#include <cctype>
28#include <algorithm>
29#include <ostream>
30#include <iomanip>
31#include <stdexcept>
32#include <ios>
33#include <charconv>
34#include <string_view>
35#ifdef _WIN32
36#include <windows.h>
37#include <oleauto.h>
38#endif
39
40#include <ciso646> // fix windows compiler bug
41
42#include "json_utils.h"
43
44namespace jami {
45
46namespace json {
47Json::CharReaderBuilder getJsonReaderBuilder()
48{
49 Json::CharReaderBuilder builder;
50 builder["collectComments"] = false;
51 return builder;
52}
53
54Json::StreamWriterBuilder getJsonWriterBuilder()
55{
56 Json::StreamWriterBuilder builder;
57 builder["commentStyle"] = "None";
58 builder["indentation"] = "";
59 return builder;
60}
61
62const Json::CharReaderBuilder rbuilder = getJsonReaderBuilder();
63const Json::StreamWriterBuilder wbuilder = getJsonWriterBuilder();
64}
65
66const std::string&
68{
69 static const std::string USER_AGENT = fmt::format("{:s} ({:s}/{:s})", PACKAGE_NAME, platform(), arch());
70 return USER_AGENT;
71}
72
73#ifdef _WIN32
74std::wstring
75to_wstring(const std::string& str, int codePage)
76{
77 int srcLength = (int) str.length();
78 int requiredSize = MultiByteToWideChar(codePage, 0, str.c_str(), srcLength, nullptr, 0);
79 if (!requiredSize) {
80 throw std::runtime_error("Unable to convert string to wstring");
81 }
82 std::wstring result((size_t) requiredSize, 0);
83 if (!MultiByteToWideChar(codePage, 0, str.c_str(), srcLength, &(*result.begin()), requiredSize)) {
84 throw std::runtime_error("Unable to convert string to wstring");
85 }
86 return result;
87}
88
89std::string
90to_string(const std::wstring& wstr, int codePage)
91{
92 int srcLength = (int) wstr.length();
93 int requiredSize = WideCharToMultiByte(codePage, 0, wstr.c_str(), srcLength, nullptr, 0, 0, 0);
94 if (!requiredSize) {
95 throw std::runtime_error("Unable to convert wstring to string");
96 }
97 std::string result((size_t) requiredSize, 0);
99 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
108{
109 char buf[64];
110 int len = snprintf(buf, sizeof(buf), "%-.*G", 16, value);
111 if (len <= 0)
112 throw std::invalid_argument {"Unable to parse double"};
113 return {buf, (size_t) len};
114}
115
116std::string
118{
119 return fmt::format("{:016x}", id);
120}
121
123from_hex_string(const std::string& str)
124{
125 uint64_t id;
126 if (auto [p, ec] = std::from_chars(str.data(), str.data()+str.size(), id, 16); ec != std::errc()) {
127 throw std::invalid_argument("Unable to parse id: " + str);
128 }
129 return id;
130}
131
132std::string_view
133trim(std::string_view s)
134{
135 auto wsfront = std::find_if_not(s.cbegin(), s.cend(), [](int c) { return std::isspace(c); });
136 return std::string_view(&*wsfront, std::find_if_not(s.rbegin(),
137 std::string_view::const_reverse_iterator(wsfront),
138 [](int c) { return std::isspace(c); })
139 .base() - wsfront);
140}
141
142std::vector<unsigned>
143split_string_to_unsigned(std::string_view str, char delim)
144{
145 std::vector<unsigned> output;
146 for (auto first = str.data(), second = str.data(), last = first + str.size(); second != last && first != last; first = second + 1) {
147 second = std::find(first, last, delim);
148 if (first != second) {
149 unsigned result;
150 auto [p, ec] = std::from_chars(first, second, result);
151 if (ec == std::errc())
152 output.emplace_back(result);
153 }
154 }
155 return output;
156}
157
158void
159string_replace(std::string& str, const std::string& from, const std::string& to)
160{
161 size_t start_pos = 0;
162 while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
163 str.replace(start_pos, from.length(), to);
164 start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
165 }
166}
167
168std::string_view
169string_remove_suffix(std::string_view str, char separator)
170{
171 auto it = str.find(separator);
172 if (it != std::string_view::npos)
173 str = str.substr(0, it);
174 return str;
175}
176
177std::string
178string_join(const std::set<std::string>& set, std::string_view separator)
179{
180 return fmt::format("{}", fmt::join(set, separator));
181}
182
183std::set<std::string>
184string_split_set(std::string& str, std::string_view separator)
185{
186 std::set<std::string> output;
187 for (auto first = str.data(), second = str.data(), last = first + str.size(); second != last && first != last; first = second + 1) {
188 second = std::find_first_of(first, last, std::cbegin(separator), std::cend(separator));
189 if (first != second)
190 output.emplace(first, second - first);
191 }
192 return output;
193}
194
195std::string urlEncode(std::string_view input)
196{
197 if (input.empty()) {
198 return {};
199 }
200
201 auto isAsciiAlnum = [](unsigned char c) {
202 return (c >= '0' && c <= '9')
203 || (c >= 'A' && c <= 'Z')
204 || (c >= 'a' && c <= 'z');
205 };
206
207 std::ostringstream encoded;
208 // Use uppercase for hex digits
209 encoded << std::uppercase << std::hex;
210
211 for (unsigned char c : input) {
212 // If character is unreserved per RFC 3986, keep it as-is
213 if (isAsciiAlnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
214 encoded << c;
215 } else {
216 // Otherwise, percent-encode
218 }
219 }
220
221 return encoded.str();
222}
223
224} // namespace jami
Json::StreamWriterBuilder getJsonWriterBuilder()
const Json::StreamWriterBuilder wbuilder
const Json::CharReaderBuilder rbuilder
Json::CharReaderBuilder getJsonReaderBuilder()
constexpr std::string_view platform()
void emitSignal(Args... args)
Definition ring_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()