Ada 3.4.4
Fast spec-compliant URL parser
Loading...
Searching...
No Matches
url_search_params-inl.h
Go to the documentation of this file.
1
5#ifndef ADA_URL_SEARCH_PARAMS_INL_H
6#define ADA_URL_SEARCH_PARAMS_INL_H
7
9#include "ada/unicode.h"
11
12#include <algorithm>
13#include <optional>
14#include <ranges>
15#include <string>
16#include <string_view>
17#include <vector>
18
19namespace ada {
20
21// A default, empty url_search_params for use with empty iterators.
22template <typename T, ada::url_search_params_iter_type Type>
23url_search_params url_search_params_iter<T, Type>::EMPTY;
24
25inline void url_search_params::reset(std::string_view input) {
26 params.clear();
27 initialize(input);
28}
29
30inline void url_search_params::initialize(std::string_view input) {
31 if (!input.empty() && input.front() == '?') {
32 input.remove_prefix(1);
33 }
34
35 auto process_key_value = [&](const std::string_view current) {
36 auto equal = current.find('=');
37
38 if (equal == std::string_view::npos) {
39 std::string name(current);
40 std::ranges::replace(name, '+', ' ');
41 params.emplace_back(unicode::percent_decode(name, name.find('%')), "");
42 } else {
43 std::string name(current.substr(0, equal));
44 std::string value(current.substr(equal + 1));
45
46 std::ranges::replace(name, '+', ' ');
47 std::ranges::replace(value, '+', ' ');
48
49 params.emplace_back(unicode::percent_decode(name, name.find('%')),
50 unicode::percent_decode(value, value.find('%')));
51 }
52 };
53
54 while (!input.empty()) {
55 auto ampersand_index = input.find('&');
56
57 if (ampersand_index == std::string_view::npos) {
58 if (!input.empty()) {
59 process_key_value(input);
60 }
61 break;
62 } else if (ampersand_index != 0) {
63 process_key_value(input.substr(0, ampersand_index));
64 }
65
66 input.remove_prefix(ampersand_index + 1);
67 }
68}
69
70inline void url_search_params::append(const std::string_view key,
71 const std::string_view value) {
72 params.emplace_back(key, value);
73}
74
75inline size_t url_search_params::size() const noexcept { return params.size(); }
76
77inline std::optional<std::string_view> url_search_params::get(
78 const std::string_view key) {
79 auto entry = std::ranges::find_if(
80 params, [&key](const auto &param) { return param.first == key; });
81
82 if (entry == params.end()) {
83 return std::nullopt;
84 }
85
86 return entry->second;
87}
88
89inline std::vector<std::string> url_search_params::get_all(
90 const std::string_view key) {
91 std::vector<std::string> out{};
92
93 for (auto &param : params) {
94 if (param.first == key) {
95 out.emplace_back(param.second);
96 }
97 }
98
99 return out;
100}
101
102inline bool url_search_params::has(const std::string_view key) noexcept {
103 auto entry = std::ranges::find_if(
104 params, [&key](const auto &param) { return param.first == key; });
105 return entry != params.end();
106}
107
108inline bool url_search_params::has(std::string_view key,
109 std::string_view value) noexcept {
110 auto entry = std::ranges::find_if(params, [&key, &value](const auto &param) {
111 return param.first == key && param.second == value;
112 });
113 return entry != params.end();
114}
115
116inline std::string url_search_params::to_string() const {
118 std::string out{};
119 for (size_t i = 0; i < params.size(); i++) {
120 auto key = ada::unicode::percent_encode(params[i].first, character_set);
121 auto value = ada::unicode::percent_encode(params[i].second, character_set);
122
123 // Performance optimization: Move this inside percent_encode.
124 std::ranges::replace(key, ' ', '+');
125 std::ranges::replace(value, ' ', '+');
126
127 if (i != 0) {
128 out += "&";
129 }
130 out.append(key);
131 out += "=";
132 out.append(value);
133 }
134 return out;
135}
136
137inline void url_search_params::set(const std::string_view key,
138 const std::string_view value) {
139 const auto find = [&key](const auto &param) { return param.first == key; };
140
141 auto it = std::ranges::find_if(params, find);
142
143 if (it == params.end()) {
144 params.emplace_back(key, value);
145 } else {
146 it->second = value;
147 params.erase(std::remove_if(std::next(it), params.end(), find),
148 params.end());
149 }
150}
151
152inline void url_search_params::remove(const std::string_view key) {
153 std::erase_if(params,
154 [&key](const auto &param) { return param.first == key; });
155}
156
157inline void url_search_params::remove(const std::string_view key,
158 const std::string_view value) {
159 std::erase_if(params, [&key, &value](const auto &param) {
160 return param.first == key && param.second == value;
161 });
162}
163
165 // Keys are expected to be valid UTF-8, but percent_decode can produce
166 // arbitrary byte sequences. Handle truncated/invalid sequences gracefully.
167 std::ranges::stable_sort(params, [](const key_value_pair &lhs,
168 const key_value_pair &rhs) {
169 size_t i = 0, j = 0;
170 uint32_t low_surrogate1 = 0, low_surrogate2 = 0;
171 while ((i < lhs.first.size() || low_surrogate1 != 0) &&
172 (j < rhs.first.size() || low_surrogate2 != 0)) {
173 uint32_t codePoint1 = 0, codePoint2 = 0;
174
175 if (low_surrogate1 != 0) {
176 codePoint1 = low_surrogate1;
177 low_surrogate1 = 0;
178 } else {
179 uint8_t c1 = uint8_t(lhs.first[i]);
180 if (c1 > 0x7F && c1 <= 0xDF && i + 1 < lhs.first.size()) {
181 codePoint1 = ((c1 & 0x1F) << 6) | (uint8_t(lhs.first[i + 1]) & 0x3F);
182 i += 2;
183 } else if (c1 > 0xDF && c1 <= 0xEF && i + 2 < lhs.first.size()) {
184 codePoint1 = ((c1 & 0x0F) << 12) |
185 ((uint8_t(lhs.first[i + 1]) & 0x3F) << 6) |
186 (uint8_t(lhs.first[i + 2]) & 0x3F);
187 i += 3;
188 } else if (c1 > 0xEF && c1 <= 0xF7 && i + 3 < lhs.first.size()) {
189 codePoint1 = ((c1 & 0x07) << 18) |
190 ((uint8_t(lhs.first[i + 1]) & 0x3F) << 12) |
191 ((uint8_t(lhs.first[i + 2]) & 0x3F) << 6) |
192 (uint8_t(lhs.first[i + 3]) & 0x3F);
193 i += 4;
194
195 codePoint1 -= 0x10000;
196 uint16_t high_surrogate = uint16_t(0xD800 + (codePoint1 >> 10));
197 low_surrogate1 = uint16_t(0xDC00 + (codePoint1 & 0x3FF));
198 codePoint1 = high_surrogate;
199 } else {
200 // ASCII (c1 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
201 codePoint1 = c1;
202 i++;
203 }
204 }
205
206 if (low_surrogate2 != 0) {
207 codePoint2 = low_surrogate2;
208 low_surrogate2 = 0;
209 } else {
210 uint8_t c2 = uint8_t(rhs.first[j]);
211 if (c2 > 0x7F && c2 <= 0xDF && j + 1 < rhs.first.size()) {
212 codePoint2 = ((c2 & 0x1F) << 6) | (uint8_t(rhs.first[j + 1]) & 0x3F);
213 j += 2;
214 } else if (c2 > 0xDF && c2 <= 0xEF && j + 2 < rhs.first.size()) {
215 codePoint2 = ((c2 & 0x0F) << 12) |
216 ((uint8_t(rhs.first[j + 1]) & 0x3F) << 6) |
217 (uint8_t(rhs.first[j + 2]) & 0x3F);
218 j += 3;
219 } else if (c2 > 0xEF && c2 <= 0xF7 && j + 3 < rhs.first.size()) {
220 codePoint2 = ((c2 & 0x07) << 18) |
221 ((uint8_t(rhs.first[j + 1]) & 0x3F) << 12) |
222 ((uint8_t(rhs.first[j + 2]) & 0x3F) << 6) |
223 (uint8_t(rhs.first[j + 3]) & 0x3F);
224 j += 4;
225 codePoint2 -= 0x10000;
226 uint16_t high_surrogate = uint16_t(0xD800 + (codePoint2 >> 10));
227 low_surrogate2 = uint16_t(0xDC00 + (codePoint2 & 0x3FF));
228 codePoint2 = high_surrogate;
229 } else {
230 // ASCII (c2 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
231 codePoint2 = c2;
232 j++;
233 }
234 }
235
236 if (codePoint1 != codePoint2) {
237 return (codePoint1 < codePoint2);
238 }
239 }
240 return (j < rhs.first.size() || low_surrogate2 != 0);
241 });
242}
243
247
254
261
262template <typename T, url_search_params_iter_type Type>
264 return pos < params.params.size();
265}
266
267template <>
268inline std::optional<std::string_view> url_search_params_keys_iter::next() {
269 if (!has_next()) {
270 return std::nullopt;
271 }
272 return params.params[pos++].first;
273}
274
275template <>
276inline std::optional<std::string_view> url_search_params_values_iter::next() {
277 if (!has_next()) {
278 return std::nullopt;
279 }
280 return params.params[pos++].second;
281}
282
283template <>
284inline std::optional<key_value_view_pair>
286 if (!has_next()) {
287 return std::nullopt;
288 }
289 return params.params[pos++];
290}
291
292} // namespace ada
293
294#endif // ADA_URL_SEARCH_PARAMS_INL_H
Definitions of the character sets used by unicode functions.
constexpr uint8_t WWW_FORM_URLENCODED_PERCENT_ENCODE[32]
Definition ada_idna.h:13
url_search_params_iter< std::string_view, url_search_params_iter_type::VALUES > url_search_params_values_iter
url_search_params_iter< key_value_view_pair, url_search_params_iter_type::ENTRIES > url_search_params_entries_iter
url_search_params_iter< std::string_view, url_search_params_iter_type::KEYS > url_search_params_keys_iter
Class for parsing and manipulating URL query strings.
void set(std::string_view key, std::string_view value)
std::vector< std::string > get_all(std::string_view key)
void remove(std::string_view key)
url_search_params_entries_iter get_entries()
std::string to_string() const
url_search_params_keys_iter get_keys()
size_t size() const noexcept
void append(std::string_view key, std::string_view value)
url_search_params_values_iter get_values()
std::optional< std::string_view > get(std::string_view key)
bool has(std::string_view key) noexcept
Definitions for all unicode specific functions.
Declaration for the URL Search Params.