Upa URL C++ library
A WHATWG URL Standard implementation
Loading...
Searching...
No Matches
public_suffix_list.h
Go to the documentation of this file.
1// Copyright 2024-2026 Rimas Misevičius
2// Distributed under the BSD-style license that can be
3// found in the LICENSE file.
4//
5#ifndef UPA_PUBLIC_SUFFIX_LIST_H
6#define UPA_PUBLIC_SUFFIX_LIST_H
7
8#include "url.h"
9
10#ifndef UPA_MODULE
11# include <cstddef>
12# include <cstdint>
13# include <filesystem>
14# include <fstream>
15# include <ios>
16# include <istream>
17# include <memory>
18# include <string>
19# include <string_view>
20# include <unordered_map>
21# include <utility>
22#endif // UPA_MODULE
23
24namespace upa {
25
26UPA_EXPORT_BEGIN
27
36template <class StrT, enable_if_str_arg_t<StrT> = 0>
37[[nodiscard]] constexpr std::size_t get_label_pos_by_index(const StrT& str_host, std::size_t index) {
38 const auto inp = make_str_arg(str_host);
39 const auto* first = inp.begin();
40 const auto* last = inp.end();
41 const auto* ptr = first;
42 for (; index > 0; --index) {
43 while (true) {
44 if (ptr == last)
45 return static_cast<std::size_t>(ptr - first);
46 const auto c = util::to_unsigned(*ptr);
47 if (c < 0x80) {
48 ++ptr; // advance to next char
49 if (c == '.')
50 break;
51 } else {
52 const auto cp = url_utf::read_utf_char(ptr, last).value;
53 // These code points are mapped to '.' in the IDNA mapping table
54 if (cp == 0x3002 || cp == 0xFF0E || cp == 0xFF61)
55 break;
56 }
57 }
58 }
59 return static_cast<std::size_t>(ptr - first);
60}
61
92 // label_item::code values
93 static constexpr std::uint8_t DIFF_MASK = 0x03;
94 static constexpr std::uint8_t IS_RULE = 0x04;
95 static constexpr std::uint8_t IS_ICANN = 0x08;
96 static constexpr std::uint8_t IS_PRIVATE = 0x10;
97public:
100 enum class option {
103 allow_trailing_dot [[deprecated]] = 2
105 };
106
108 struct result {
110 [[nodiscard]] constexpr operator bool() const noexcept {
111 return first_label_ind != static_cast<std::size_t>(-1);
112 }
113
114 [[nodiscard]] constexpr bool is_icann() const noexcept {
115 return (code_ & IS_ICANN) != 0;
116 }
117
118 [[nodiscard]] constexpr bool is_private() const noexcept {
119 return (code_ & IS_PRIVATE) != 0;
120 }
121
122 [[nodiscard]] constexpr bool wildcard_rule() const noexcept {
123 return (code_ & DIFF_MASK) == 3;
124 }
125
126 [[nodiscard]] constexpr bool is_rules_match() const noexcept {
127 return (code_ & IS_RULE) != 0;
128 }
129
130 std::size_t first_label_ind = static_cast<std::size_t>(-1);
132 std::size_t first_label_pos = static_cast<std::size_t>(-1);
133 std::uint8_t code_ = 0;
134 };
135
143 inline bool load(const std::filesystem::path& filename) {
144 std::ifstream finp(filename, std::ios::in | std::ios::binary);
145 return finp && load(finp);
146 }
147
151 UPA_API bool load(std::istream& input_stream);
152
153 // Push interface to load Public Suffix List
154
158 std::string remaining;
159 std::uint8_t code_flags = 0;
160 std::uint8_t error = 0;
161 };
162
169 UPA_API void push_line(push_context& ctx, std::string_view line);
170
178 UPA_API void push(push_context& ctx, std::string_view buff);
179
187 UPA_API bool finalize(push_context& ctx);
188
189 // Get public suffix or registrable domain
190
202 template <class StrT, enable_if_str_arg_t<StrT> = 0>
203 [[nodiscard]] inline std::string get_suffix(const StrT& str_host,
204 option opt = option::public_suffix) const {
205 try {
206 return std::string{ get_suffix_view(
207 upa::url_host{ str_host },
208 opt) };
209 }
210 catch (const upa::url_error&) {
211 return {};
212 }
213 }
214
223 [[nodiscard]] inline result get_suffix_info(const url& url,
224 option opt = option::public_suffix) const {
226 return get_host_suffix_info(url.hostname(), opt);
227 return {};
228 }
229
238 [[nodiscard]] inline result get_suffix_info(const url_host& host,
239 option opt = option::public_suffix) const {
240 if (host.type() == HostType::Domain)
241 return get_host_suffix_info(host.name(), opt);
242 return {};
243 }
244
253 template <class StrT, enable_if_str_arg_t<StrT> = 0>
254 [[nodiscard]] inline result get_suffix_info(const StrT& str_host,
255 option opt = option::public_suffix) const {
256 try {
257 auto res = get_suffix_info(upa::url_host{ str_host }, opt);
258 res.first_label_pos = get_label_pos_by_index(str_host, res.first_label_ind);
259 return res;
260 }
261 catch (const upa::url_error&) {
262 return {};
263 }
264 }
265
281 [[nodiscard]] inline std::string_view get_suffix_view(const url& url UPA_LIFETIMEBOUND,
282 option opt = option::public_suffix) const {
284 return get_host_suffix_view(url.hostname(), opt);
285 return {};
286 }
287
303 [[nodiscard]] inline std::string_view get_suffix_view(const url_host& host UPA_LIFETIMEBOUND,
304 option opt = option::public_suffix) const {
305 if (host.type() == HostType::Domain)
306 return get_host_suffix_view(host.name(), opt);
307 return {};
308 }
309
325 template <class StrT, enable_if_str_arg_t<StrT> = 0>
326 [[nodiscard]] inline auto get_suffix_view(const StrT& str_host, option opt = option::public_suffix) const
327 -> std::basic_string_view<typename str_arg<str_arg_char_t<StrT>>::value_type> {
328 try {
329 const auto arg = make_str_arg(str_host);
330 const upa::url_host host{ arg };
331 if (host.type() == HostType::Domain) {
332 const auto res = get_host_suffix_info(host.name(), opt);
333 if (res) {
334 const auto pos = get_label_pos_by_index(arg, res.first_label_ind);
335 return { arg.data() + pos, arg.size() - pos };
336 }
337 }
338 }
339 catch (const upa::url_error&) {}
340 return {};
341 }
342
346 [[nodiscard]] UPA_API bool operator==(const public_suffix_list& other) const;
347
348 // constructors, destructor, assignment operators
353 UPA_API public_suffix_list& operator=(public_suffix_list&&) noexcept;
354 public_suffix_list& operator=(const public_suffix_list&) = delete;
355
356private:
357 UPA_API result get_host_suffix_info(std::string_view hostname, option opt) const;
358
359 inline std::string_view get_host_suffix_view(std::string_view hostname, option opt) const {
360 const auto res = get_host_suffix_info(hostname, opt);
361 if (res)
362 return hostname.substr(res.first_label_pos);
363 return {};
364 }
365
366private:
367 struct label_item {
368#ifdef __cpp_lib_generic_unordered_lookup
369 // https://en.cppreference.com/w/cpp/container/unordered_map/find
370 struct string_hash {
371 using hash_type = std::hash<std::string_view>;
372 using is_transparent = void;
373
374 inline std::size_t operator()(const char* str) const { return hash_type{}(str); }
375 inline std::size_t operator()(std::string_view str) const { return hash_type{}(str); }
376 inline std::size_t operator()(std::string const& str) const { return hash_type{}(str); }
377 };
378 using map_type = std::unordered_map<std::string, label_item, string_hash, std::equal_to<>>;
379#else
380 using map_type = std::unordered_map<std::string, label_item>;
381#endif
382
383 std::uint8_t code = 0;
384 std::unique_ptr<map_type> children;
385
386 inline bool operator==(const label_item& b) const {
387 return code == b.code && (
388 (!children && !b.children) ||
389 (children && b.children && *children == *b.children));
390 }
391 };
392
393 label_item root_;
394};
395
396namespace idna {
397
398template<>
399struct enable_bitmask_operators<upa::public_suffix_list::option>
400 : public std::true_type {};
401
402} // namespace idna
403
404UPA_EXPORT_END
405
406} // namespace upa
407
408#endif // UPA_PUBLIC_SUFFIX_LIST_H
UPA_API public_suffix_list(public_suffix_list &&) noexcept
result get_suffix_info(const StrT &str_host, option opt=option::public_suffix) const
Get public suffix or registrable domain information.
result get_suffix_info(const url_host &host, option opt=option::public_suffix) const
Get public suffix or registrable domain information.
std::string_view get_suffix_view(const url_host &host, option opt=option::public_suffix) const
Get public suffix or registrable domain as string view.
result get_suffix_info(const url &url, option opt=option::public_suffix) const
Get public suffix or registrable domain information.
UPA_API void push_line(push_context &ctx, std::string_view line)
Push a line of the Public Suffix List file.
UPA_API bool operator==(const public_suffix_list &other) const
Compares *this with other.
std::string get_suffix(const StrT &str_host, option opt=option::public_suffix) const
Get public suffix or registrable domain.
UPA_API bool finalize(push_context &ctx)
Finalizes when all chunks are processed.
option
Options for public_suffix_list::get_suffix, public_suffix_list::get_suffix_info and public_suffix_lis...
@ registrable_domain
to get registrable domain
UPA_API void push(push_context &ctx, std::string_view buff)
Push a chunk of the Public Suffix List file.
bool load(const std::filesystem::path &filename)
Load Public Suffix List from file.
auto get_suffix_view(const StrT &str_host, option opt=option::public_suffix) const -> std::basic_string_view< typename str_arg< str_arg_char_t< StrT > >::value_type >
Get public suffix or registrable domain as string view.
UPA_API bool load(std::istream &input_stream)
Load Public Suffix List from stream.
std::string_view get_suffix_view(const url &url, option opt=option::public_suffix) const
Get public suffix or registrable domain as string view.
URL exception class.
Definition url_result.h:103
HostType type() const
Definition url_host.h:109
std::string_view name() const
Definition url_host.h:116
URL class.
Definition url.h:84
bool hostname(const StrT &str)
The hostname setter.
Definition url.h:1560
HostType host_type() const noexcept
The host_type getter.
Definition url.h:1231
Definition url.h:3418
Definition url.h:50
constexpr std::size_t get_label_pos_by_index(const StrT &str_host, std::size_t index)
Get label position in hostname by it's index.
Push context for public_suffix_list::push_line, public_suffix_list::push and public_suffix_list::fina...
Return value type of the public_suffix_list::get_suffix_info function.
constexpr bool is_icann() const noexcept
std::size_t first_label_pos
First label position of public suffix or registrable domain in hostname.
constexpr bool wildcard_rule() const noexcept
constexpr bool is_private() const noexcept
constexpr bool is_rules_match() const noexcept
std::size_t first_label_ind
First label index of public suffix or registrable domain in hostname.