GCC Code Coverage Report


Directory: ./
File: include/deserializers/base_deserializer.hpp
Date: 2024-07-09 12:21:25
Exec Total Coverage
Lines: 102 113 90.3%
Functions: 13 13 100.0%
Branches: 106 178 59.6%

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