Skip to content

Commit 2f1130a

Browse files
authored
Add iterator support to url_search_params (#532)
1 parent 88cded9 commit 2f1130a

File tree

8 files changed

+653
-6
lines changed

8 files changed

+653
-6
lines changed

README.md

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ The Ada library passes the full range of tests from the specification,
1414
across a wide range of platforms (e.g., Windows, Linux, macOS). It fully
1515
supports the relevant [Unicode Technical Standard](https://www.unicode.org/reports/tr46/#ToUnicode).
1616

17-
A common use of a URL parser is to take a URL string and normalize it.
18-
The WHATWG URL specification has been adopted by most browsers. Other tools, such as curl and many
17+
A common use of a URL parser is to take a URL string and normalize it.
18+
The WHATWG URL specification has been adopted by most browsers. Other tools, such as curl and many
1919
standard libraries, follow the RFC 3986. The following table illustrates possible differences in practice
2020
(encoding of the host, encoding of the path):
2121

@@ -30,10 +30,10 @@ standard libraries, follow the RFC 3986. The following table illustrates possibl
3030
The project is otherwise self-contained and it has no dependency.
3131
A recent C++ compiler supporting C++17. We test GCC 9 or better, LLVM 10 or better and Microsoft Visual Studio 2022.
3232

33-
## Ada is fast.
33+
## Ada is fast.
3434

3535
On a benchmark where we need to validate and normalize [thousands URLs found
36-
on popular websites](https://github.com/ada-url/url-various-datasets/tree/main/top100),
36+
on popular websites](https://github.com/ada-url/url-various-datasets/tree/main/top100),
3737
we find that ada can be several times faster than popular competitors (system: Apple MacBook 2022
3838
with LLVM 14).
3939

@@ -201,6 +201,21 @@ url->set_hash("is-this-the-real-life");
201201
// url->get_hash() will return "#is-this-the-real-life"
202202
```
203203
For more information about command-line options, please refer to the [CLI documentation](docs/cli.md).
204+
205+
- URL search params
206+
207+
```cpp
208+
ada::url_search_params search_params("a=b&c=d&e=f");
209+
search_params.append("g=h");
210+
211+
search_params.get("g"); // will return "h"
212+
213+
auto keys = search_params.get_keys();
214+
while (keys.has_next()) {
215+
auto key = keys.next(); // "a", "c", "e", "g"
216+
}
217+
```
218+
204219
### C wrapper
205220

206221
See the file `include/ada_c.h` for our C interface. We expect ASCII or UTF-8 strings.
@@ -231,6 +246,17 @@ int main(int c, char *arg[] ) {
231246
ada_set_search(url, "new-search");
232247
ada_set_protocol(url, "wss");
233248
ada_print(ada_get_href(url)); // will print wss://changed-host:9090/new-pathname?new-search#new-hash
249+
250+
// Manipulating search params
251+
ada_string search = ada_get_search(url);
252+
ada_url_search_params search_params =
253+
ada_parse_search_params(search.data, search.length);
254+
ada_search_params_append(search_params, "a", 1, "b", 1);
255+
ada_owned_string result = ada_search_params_to_string(search_params);
256+
ada_set_search(url, result.data, result.length);
257+
ada_free_owned_string(result);
258+
ada_free_search_params(search_params);
259+
234260
ada_free(url);
235261
return EXIT_SUCCESS;
236262
}
@@ -283,6 +309,6 @@ You may amalgamate all source files into only two files (`ada.h` and `ada.cpp`)
283309
284310
### License
285311
286-
This code is made available under the Apache License 2.0 as well as the MIT license.
312+
This code is made available under the Apache License 2.0 as well as the MIT license.
287313
288314
Our tests include third-party code and data. The benchmarking code includes third-party code: it is provided for research purposes only and not part of the library.

fuzz/parse.cc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,5 +121,20 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
121121
search_params.remove(base_source, source);
122122
}
123123

124+
auto keys = search_params.get_keys();
125+
while (keys.has_next()) {
126+
keys.next();
127+
}
128+
129+
auto values = search_params.get_values();
130+
while (values.has_next()) {
131+
values.next();
132+
}
133+
134+
auto entries = search_params.get_entries();
135+
while (entries.has_next()) {
136+
entries.next();
137+
}
138+
124139
return 0;
125140
} // extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {

include/ada/url_search_params-inl.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818

1919
namespace ada {
2020

21+
// A default, empty url_search_params for use with empty iterators.
22+
template <typename T, ada::url_search_params_iter_type Type>
23+
url_search_params url_search_params_iter<T, Type>::EMPTY;
24+
2125
inline void url_search_params::initialize(std::string_view input) {
2226
if (!input.empty() && input.front() == '?') {
2327
input.remove_prefix(1);
@@ -165,6 +169,48 @@ inline void url_search_params::sort() {
165169
});
166170
}
167171

172+
inline url_search_params_keys_iter url_search_params::get_keys() {
173+
return url_search_params_keys_iter(*this);
174+
}
175+
176+
/**
177+
* @see https://url.spec.whatwg.org/#interface-urlsearchparams
178+
*/
179+
inline url_search_params_values_iter url_search_params::get_values() {
180+
return url_search_params_values_iter(*this);
181+
}
182+
183+
/**
184+
* @see https://url.spec.whatwg.org/#interface-urlsearchparams
185+
*/
186+
inline url_search_params_entries_iter url_search_params::get_entries() {
187+
return url_search_params_entries_iter(*this);
188+
}
189+
190+
template <typename T, url_search_params_iter_type Type>
191+
inline bool url_search_params_iter<T, Type>::has_next() {
192+
return pos < params.params.size();
193+
}
194+
195+
template <>
196+
inline std::optional<std::string_view> url_search_params_keys_iter::next() {
197+
if (!has_next()) return std::nullopt;
198+
return params.params[pos++].first;
199+
}
200+
201+
template <>
202+
inline std::optional<std::string_view> url_search_params_values_iter::next() {
203+
if (!has_next()) return std::nullopt;
204+
return params.params[pos++].second;
205+
}
206+
207+
template <>
208+
inline std::optional<key_value_view_pair>
209+
url_search_params_entries_iter::next() {
210+
if (!has_next()) return std::nullopt;
211+
return params.params[pos++];
212+
}
213+
168214
} // namespace ada
169215

170216
#endif // ADA_URL_SEARCH_PARAMS_INL_H

include/ada/url_search_params.h

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,26 @@
1212

1313
namespace ada {
1414

15+
enum class url_search_params_iter_type {
16+
KEYS,
17+
VALUES,
18+
ENTRIES,
19+
};
20+
21+
template <typename T, url_search_params_iter_type Type>
22+
struct url_search_params_iter;
23+
24+
typedef std::pair<std::string_view, std::string_view> key_value_view_pair;
25+
26+
using url_search_params_keys_iter =
27+
url_search_params_iter<std::string_view, url_search_params_iter_type::KEYS>;
28+
using url_search_params_values_iter =
29+
url_search_params_iter<std::string_view,
30+
url_search_params_iter_type::VALUES>;
31+
using url_search_params_entries_iter =
32+
url_search_params_iter<key_value_view_pair,
33+
url_search_params_iter_type::ENTRIES>;
34+
1535
/**
1636
* @see https://url.spec.whatwg.org/#interface-urlsearchparams
1737
*/
@@ -74,6 +94,42 @@ struct url_search_params {
7494
*/
7595
inline std::string to_string();
7696

97+
/**
98+
* Returns a simple JS-style iterator over all of the keys in this
99+
* url_search_params. The keys in the iterator are not unique. The valid
100+
* lifespan of the iterator is tied to the url_search_params. The iterator
101+
* must be freed when you're done with it.
102+
* @see https://url.spec.whatwg.org/#interface-urlsearchparams
103+
*/
104+
inline url_search_params_keys_iter get_keys();
105+
106+
/**
107+
* Returns a simple JS-style iterator over all of the values in this
108+
* url_search_params. The valid lifespan of the iterator is tied to the
109+
* url_search_params. The iterator must be freed when you're done with it.
110+
* @see https://url.spec.whatwg.org/#interface-urlsearchparams
111+
*/
112+
inline url_search_params_values_iter get_values();
113+
114+
/**
115+
* Returns a simple JS-style iterator over all of the entries in this
116+
* url_search_params. The entries are pairs of keys and corresponding values.
117+
* The valid lifespan of the iterator is tied to the url_search_params. The
118+
* iterator must be freed when you're done with it.
119+
* @see https://url.spec.whatwg.org/#interface-urlsearchparams
120+
*/
121+
inline url_search_params_entries_iter get_entries();
122+
123+
/**
124+
* C++ style conventional iterator support. const only because we
125+
* do not really want the params to be modified via the iterator.
126+
*/
127+
inline const auto begin() const { return params.begin(); }
128+
inline const auto end() const { return params.end(); }
129+
inline const auto front() const { return params.front(); }
130+
inline const auto back() const { return params.back(); }
131+
inline const auto operator[](size_t index) const { return params[index]; }
132+
77133
private:
78134
typedef std::pair<std::string, std::string> key_value_pair;
79135
std::vector<key_value_pair> params{};
@@ -82,7 +138,43 @@ struct url_search_params {
82138
* @see https://url.spec.whatwg.org/#concept-urlencoded-parser
83139
*/
84140
void initialize(std::string_view init);
141+
142+
template <typename T, url_search_params_iter_type Type>
143+
friend struct url_search_params_iter;
85144
}; // url_search_params
86145

146+
/**
147+
* Implements a non-conventional iterator pattern that is closer in style to
148+
* JavaScript's definition of an iterator.
149+
*
150+
* @see https://webidl.spec.whatwg.org/#idl-iterable
151+
*/
152+
template <typename T, url_search_params_iter_type Type>
153+
struct url_search_params_iter {
154+
inline url_search_params_iter() : params(EMPTY) {}
155+
url_search_params_iter(const url_search_params_iter &u) = default;
156+
url_search_params_iter(url_search_params_iter &&u) noexcept = default;
157+
url_search_params_iter &operator=(url_search_params_iter &&u) noexcept =
158+
default;
159+
url_search_params_iter &operator=(const url_search_params_iter &u) = default;
160+
~url_search_params_iter() = default;
161+
162+
/**
163+
* Return the next item in the iterator or std::nullopt if done.
164+
*/
165+
inline std::optional<T> next();
166+
167+
inline bool has_next();
168+
169+
private:
170+
static url_search_params EMPTY;
171+
inline url_search_params_iter(url_search_params &params_) : params(params_) {}
172+
173+
url_search_params &params;
174+
size_t pos = 0;
175+
176+
friend struct url_search_params;
177+
};
178+
87179
} // namespace ada
88180
#endif

include/ada_c.h

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,77 @@ const ada_url_components* ada_get_components(ada_url result);
109109
ada_owned_string ada_idna_to_unicode(const char* input, size_t length);
110110
ada_owned_string ada_idna_to_ascii(const char* input, size_t length);
111111

112+
// url search params
113+
typedef void* ada_url_search_params;
114+
115+
// Represents an std::vector<std::string>
116+
typedef void* ada_strings;
117+
typedef void* ada_url_search_params_keys_iter;
118+
typedef void* ada_url_search_params_values_iter;
119+
120+
typedef struct {
121+
ada_string key;
122+
ada_string value;
123+
} ada_string_pair;
124+
125+
typedef void* ada_url_search_params_entries_iter;
126+
127+
ada_url_search_params ada_parse_search_params(const char* input, size_t length);
128+
void ada_free_search_params(ada_url_search_params result);
129+
130+
size_t ada_search_params_size(ada_url_search_params result);
131+
void ada_search_params_sort(ada_url_search_params result);
132+
ada_owned_string ada_search_params_to_string(ada_url_search_params result);
133+
134+
void ada_search_params_append(ada_url_search_params result, const char* key,
135+
size_t key_length, const char* value,
136+
size_t value_length);
137+
void ada_search_params_set(ada_url_search_params result, const char* key,
138+
size_t key_length, const char* value,
139+
size_t value_length);
140+
void ada_search_params_remove(ada_url_search_params result, const char* key,
141+
size_t key_length);
142+
void ada_search_params_remove_value(ada_url_search_params result,
143+
const char* key, size_t key_length,
144+
const char* value, size_t value_length);
145+
bool ada_search_params_has(ada_url_search_params result, const char* key,
146+
size_t key_length);
147+
bool ada_search_params_has_value(ada_url_search_params result, const char* key,
148+
size_t key_length, const char* value,
149+
size_t value_length);
150+
ada_string ada_search_params_get(ada_url_search_params result, const char* key,
151+
size_t key_length);
152+
ada_strings ada_search_params_get_all(ada_url_search_params result,
153+
const char* key, size_t key_length);
154+
ada_url_search_params_keys_iter ada_search_params_get_keys(
155+
ada_url_search_params result);
156+
ada_url_search_params_values_iter ada_search_params_get_values(
157+
ada_url_search_params result);
158+
ada_url_search_params_entries_iter ada_search_params_get_entries(
159+
ada_url_search_params result);
160+
161+
void ada_free_strings(ada_strings result);
162+
size_t ada_strings_size(ada_strings result);
163+
ada_string ada_strings_get(ada_strings result, size_t index);
164+
165+
void ada_free_search_params_keys_iter(ada_url_search_params_keys_iter result);
166+
ada_string ada_search_params_keys_iter_next(
167+
ada_url_search_params_keys_iter result);
168+
bool ada_search_params_keys_iter_has_next(
169+
ada_url_search_params_keys_iter result);
170+
171+
void ada_free_search_params_values_iter(
172+
ada_url_search_params_values_iter result);
173+
ada_string ada_search_params_values_iter_next(
174+
ada_url_search_params_values_iter result);
175+
bool ada_search_params_values_iter_has_next(
176+
ada_url_search_params_values_iter result);
177+
178+
void ada_free_search_params_entries_iter(
179+
ada_url_search_params_entries_iter result);
180+
ada_string_pair ada_search_params_entries_iter_next(
181+
ada_url_search_params_entries_iter result);
182+
bool ada_search_params_entries_iter_has_next(
183+
ada_url_search_params_entries_iter result);
184+
112185
#endif // ADA_C_H

0 commit comments

Comments
 (0)