Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * Copyright (c) 2024 Muhammad Nawaz | ||
3 | * Licensed under the MIT License. See LICENSE file for more information. | ||
4 | */ | ||
5 | // [ END OF LICENSE c6bd0f49d040fca8d8a9cb05868e66aa63f0e2e0 ] | ||
6 | |||
7 | #ifndef BASE_DESERIALIZER_HPP | ||
8 | #define BASE_DESERIALIZER_HPP | ||
9 | |||
10 | #include "utils/common.hpp" | ||
11 | |||
12 | #include <array> | ||
13 | #include <cstring> | ||
14 | #include <stdexcept> | ||
15 | #include <string> | ||
16 | |||
17 | class DeserializationException: public std::exception | ||
18 | { | ||
19 | private: | ||
20 | std::string ex_msg_; | ||
21 | |||
22 | public: | ||
23 | 73 | explicit DeserializationException(const std::string& message) | |
24 |
2/4✓ Branch 2 taken 73 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 73 times.
✗ Branch 6 not taken.
|
73 | : ex_msg_(R"("description":")" + message + R"(")") |
25 | { | ||
26 | 73 | } | |
27 | |||
28 | 58 | [[nodiscard]] const char* what() const noexcept override | |
29 | { | ||
30 | 58 | return ex_msg_.c_str(); | |
31 | } | ||
32 | }; | ||
33 | |||
34 | class BaseDeserializer | ||
35 | { | ||
36 | public: | ||
37 | explicit BaseDeserializer(const std::string& param_name, char start, bool skip_name); | ||
38 | |||
39 | virtual std::string Deserialize(const char* beg, const char* const end) = 0; | ||
40 | 148 | virtual ~BaseDeserializer() = default; | |
41 | |||
42 | protected: | ||
43 | const std::string param_name_; | ||
44 | const char start_; | ||
45 | const bool skip_name_; | ||
46 | |||
47 | static const std::array<char, 256> kHexLookupTable; | ||
48 | |||
49 | // Utilities | ||
50 | |||
51 | 282 | inline void CheckNSkipStart(const char*& cursor) const | |
52 | { | ||
53 |
2/2✓ Branch 0 taken 107 times.
✓ Branch 1 taken 175 times.
|
282 | if (start_) { |
54 |
2/2✓ Branch 0 taken 102 times.
✓ Branch 1 taken 5 times.
|
107 | if (start_ == *cursor) { |
55 | 102 | ++cursor; | |
56 | } else { | ||
57 |
4/8✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 5 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 5 times.
✗ Branch 11 not taken.
|
15 | throw DeserializationException("Parameter '" + param_name_ + "' should start with '" + |
58 |
2/4✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 5 times.
✗ Branch 7 not taken.
|
20 | std::string(1, start_) + "'"); |
59 | } | ||
60 | } | ||
61 | 277 | } | |
62 | |||
63 | 356 | inline void CheckNSkipChar(const char*& cursor, const char* const end, const char c) const | |
64 | { | ||
65 |
1/2✓ Branch 0 taken 356 times.
✗ Branch 1 not taken.
|
356 | if (cursor < end) { |
66 |
2/2✓ Branch 0 taken 349 times.
✓ Branch 1 taken 7 times.
|
356 | if (c == *cursor) { |
67 | 349 | ++cursor; | |
68 | } else { | ||
69 |
3/6✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 7 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 7 times.
✗ Branch 9 not taken.
|
7 | throw DeserializationException("Invalid serialization of ' for parameter '" + param_name_ + "'"); |
70 | } | ||
71 | } | ||
72 | 349 | } | |
73 | |||
74 | 188 | inline void CheckNSkipName(const char*& cursor, const char* const end) const | |
75 | { | ||
76 |
4/4✓ Branch 2 taken 186 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 186 times.
|
374 | if (std::distance(cursor, end) < static_cast<long>(param_name_.size()) || |
77 |
1/2✗ Branch 3 not taken.
✓ Branch 4 taken 186 times.
|
186 | !std::equal(param_name_.begin(), param_name_.end(), cursor)) { |
78 |
3/6✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
|
2 | throw DeserializationException("Parameter name mismatch for the parameter '" + param_name_ + "'"); |
79 | } | ||
80 | 186 | cursor += param_name_.size(); | |
81 | 186 | } | |
82 | |||
83 | 264 | inline void CheckData(const char*& cursor, const char* const end) const | |
84 | { | ||
85 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 264 times.
|
264 | if (cursor >= end) { |
86 | ✗ | throw DeserializationException("Parameter '" + param_name_ + "' has no data"); | |
87 | } | ||
88 | 264 | } | |
89 | |||
90 | 133 | inline void DeserializeBoolean(const char*& cursor, const char* const end, std::string& ret) const | |
91 | { | ||
92 | 133 | constexpr std::size_t true_size = sizeof("true") - 1; | |
93 | 133 | constexpr std::size_t false_size = sizeof("false") - 1; | |
94 | |||
95 |
4/4✓ Branch 0 taken 128 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 68 times.
✓ Branch 3 taken 60 times.
|
133 | if ((cursor + true_size) <= end && std::strncmp(cursor, "true", true_size) == 0) { |
96 | 68 | ret.append("true"); | |
97 | 68 | cursor += true_size; | |
98 |
4/4✓ Branch 0 taken 60 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 53 times.
✓ Branch 3 taken 7 times.
|
65 | } else if ((cursor + false_size) <= end && std::strncmp(cursor, "false", false_size) == 0) { |
99 | 53 | ret.append("false"); | |
100 | 53 | cursor += false_size; | |
101 | } else { | ||
102 |
3/6✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 12 times.
✗ Branch 9 not taken.
|
12 | throw DeserializationException("Invalid `boolean` value for parameter `" + param_name_ + "`"); |
103 | } | ||
104 | 121 | } | |
105 | |||
106 | 137 | inline void DeserializeInteger(const char*& cursor, const char* const end, std::string& ret) const | |
107 | { | ||
108 | 137 | const char* start_cursor = cursor; | |
109 |
2/4✓ Branch 0 taken 137 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 137 times.
|
137 | if (cursor < end && '-' == *cursor) { |
110 | ✗ | ++cursor; | |
111 | } | ||
112 |
4/4✓ Branch 0 taken 365 times.
✓ Branch 1 taken 53 times.
✓ Branch 2 taken 281 times.
✓ Branch 3 taken 84 times.
|
418 | while (cursor < end && isdigit(*cursor)) { |
113 | 281 | ++cursor; | |
114 | } | ||
115 |
5/6✓ Branch 0 taken 126 times.
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 48 times.
✓ Branch 3 taken 78 times.
✓ Branch 4 taken 48 times.
✗ Branch 5 not taken.
|
137 | if (cursor > start_cursor && (cursor - start_cursor != 1 || *start_cursor != '-')) { |
116 | 126 | ret.append(start_cursor, cursor); | |
117 | } else { | ||
118 |
3/6✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 11 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 11 times.
✗ Branch 9 not taken.
|
11 | throw DeserializationException("Invalid 'integer' format for '" + param_name_ + "'"); |
119 | } | ||
120 | 126 | } | |
121 | |||
122 | 83 | inline void DeserializeNumber(const char*& cursor, const char* const end, std::string& ret) const | |
123 | { | ||
124 | 83 | const char* start_cursor = cursor; | |
125 | 83 | bool has_decimal_point = false; | |
126 |
2/4✓ Branch 0 taken 83 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 83 times.
|
83 | if (cursor < end && '-' == *cursor) { |
127 | ✗ | ++cursor; | |
128 | } | ||
129 |
6/6✓ Branch 0 taken 399 times.
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 271 times.
✓ Branch 3 taken 128 times.
✓ Branch 4 taken 74 times.
✓ Branch 5 taken 54 times.
|
427 | while (cursor < end && (isdigit(*cursor) || *cursor == '.')) { |
130 |
4/4✓ Branch 0 taken 74 times.
✓ Branch 1 taken 271 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 73 times.
|
345 | if (*cursor == '.' && has_decimal_point) { |
131 |
3/6✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
|
1 | throw DeserializationException("Multiple '.' in number for '" + param_name_ + "'"); |
132 | } | ||
133 | 344 | has_decimal_point |= (*cursor == '.'); | |
134 | 344 | ++cursor; | |
135 | } | ||
136 |
3/8✓ Branch 0 taken 72 times.
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 72 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
82 | if (cursor > start_cursor && (cursor - start_cursor != 1 || (*start_cursor != '-' && *start_cursor != '.')) && |
137 |
2/4✓ Branch 0 taken 72 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 72 times.
✗ Branch 3 not taken.
|
72 | (!has_decimal_point || *(cursor - 1) != '.')) { |
138 | 72 | ret.append(start_cursor, cursor); | |
139 | } else { | ||
140 |
3/6✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 10 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 10 times.
✗ Branch 9 not taken.
|
10 | throw DeserializationException("Invalid 'number' format for '" + param_name_ + "'"); |
141 | } | ||
142 | 72 | } | |
143 | |||
144 | 29 | inline void DeserializeString(const char*& cursor, const char* const end, std::string& ret) const | |
145 | { | ||
146 | 29 | ret.push_back('"'); | |
147 |
2/2✓ Branch 0 taken 151 times.
✓ Branch 1 taken 22 times.
|
173 | while (cursor < end) { |
148 | 151 | char c = *cursor++; | |
149 |
2/3✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 130 times.
|
151 | switch (c) { |
150 | 21 | case '%': { | |
151 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
|
21 | if (cursor + 1 >= end) { |
152 | ✗ | throw DeserializationException("Incomplete percent encoding for '" + param_name_ + "'"); | |
153 | } | ||
154 | 21 | const char dec1 = kHexLookupTable[static_cast<unsigned char>(*cursor++)]; | |
155 | 21 | const char dec2 = kHexLookupTable[static_cast<unsigned char>(*cursor++)]; | |
156 |
4/4✓ Branch 0 taken 20 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 14 times.
|
21 | if (dec1 < 0 || dec2 < 0) { |
157 |
3/6✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 7 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 7 times.
✗ Branch 9 not taken.
|
7 | throw DeserializationException("Invalid HEX character for '" + param_name_ + "'"); |
158 | } | ||
159 | 14 | ret.push_back(static_cast<char>((dec1 << 4) | dec2)); | |
160 | 14 | } break; | |
161 | |||
162 | ✗ | case '+': | |
163 | ✗ | ret += ' '; | |
164 | ✗ | break; | |
165 | |||
166 | 130 | default: | |
167 | 130 | ret += c; | |
168 | 130 | break; | |
169 | } | ||
170 | } | ||
171 | 22 | ret.push_back('"'); | |
172 | 22 | } | |
173 | |||
174 | 86 | inline void DeserializeString(const char*& cursor, const char* const end, const char terminator, | |
175 | std::string& ret) const | ||
176 | { | ||
177 | 86 | ret.push_back('"'); | |
178 |
4/4✓ Branch 0 taken 503 times.
✓ Branch 1 taken 48 times.
✓ Branch 2 taken 467 times.
✓ Branch 3 taken 36 times.
|
551 | while (cursor < end && *cursor != terminator) { |
179 | 467 | char c = *cursor++; | |
180 |
2/3✓ Branch 0 taken 46 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 421 times.
|
467 | switch (c) { |
181 | 46 | case '%': { | |
182 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
|
46 | if (cursor + 1 >= end) { |
183 | ✗ | throw DeserializationException("Incomplete percent encoding for '" + param_name_ + "'"); | |
184 | } | ||
185 | 46 | const char dec1 = kHexLookupTable[static_cast<unsigned char>(*cursor++)]; | |
186 | 46 | const char dec2 = kHexLookupTable[static_cast<unsigned char>(*cursor++)]; | |
187 |
4/4✓ Branch 0 taken 45 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 44 times.
|
46 | if (dec1 < 0 || dec2 < 0) { |
188 |
3/6✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
|
2 | throw DeserializationException("Invalid HEX character for '" + param_name_ + "'"); |
189 | } | ||
190 | 44 | ret.push_back(static_cast<char>((dec1 << 4) | dec2)); | |
191 | 44 | } break; | |
192 | |||
193 | ✗ | case '+': | |
194 | ✗ | ret += ' '; | |
195 | ✗ | break; | |
196 | |||
197 | 421 | default: | |
198 | 421 | ret += c; | |
199 | 421 | break; | |
200 | } | ||
201 | } | ||
202 | 84 | ret.push_back('"'); | |
203 | 84 | } | |
204 | |||
205 | 219 | inline void CheckEnd(const char*& cursor, const char* const end) const | |
206 | { | ||
207 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 209 times.
|
219 | if (cursor != end) { |
208 |
3/6✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 10 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 10 times.
✗ Branch 9 not taken.
|
10 | throw DeserializationException("Invalid serialization of parameter '" + param_name_ + "'"); |
209 | } | ||
210 | 209 | } | |
211 | }; | ||
212 | |||
213 | #endif // BASE_DESERIALIZER_HPP | ||
214 |