blob: b7a3edfd9931d7b0a0dc213fda31e85efa7e4bdf (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
|
#include "uri.hh"
#include "u8.hh"
#include <cstddef>
#include <optional>
#include <span>
#include <string>
#include <string_view>
namespace uri {
namespace {
inline std::optional<uint8_t> hex(char c) {
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'A' && c <= 'F')
return 10 + (c - 'A');
if (c >= 'a' && c <= 'f')
return 10 + (c - 'a');
return std::nullopt;
}
} // namespace
std::optional<std::string_view> decode(std::string_view input,
std::string& dst) {
auto i = input.find('%');
if (i == std::string_view::npos)
return input;
dst.clear();
size_t last = 0;
bool check_utf8 = false;
while (true) {
if (input.size() - i < 3)
return std::nullopt;
auto a = hex(input[i + 1]);
auto b = hex(input[i + 2]);
if (!a.has_value() || !b.has_value())
return std::nullopt;
dst.append(input, last, i - last);
auto c = (a.value() << 4) | b.value();
if (c & 0x80)
check_utf8 = true;
dst.push_back(static_cast<char>(c));
last = i + 3;
i = input.find('%', last);
if (i == std::string::npos) {
dst.append(input, last);
break;
}
}
if (check_utf8) {
std::span<uint8_t const> data{reinterpret_cast<uint8_t const*>(dst.data()),
dst.size()};
auto it = data.begin();
while (it != data.end()) {
auto ret = u8::read(it, data.end());
if (!ret.has_value())
return std::nullopt;
}
}
return dst;
}
} // namespace uri
|