DiscordCoreAPI
A Discord bot library written in C++, with custom asynchronous coroutines.
Loading...
Searching...
No Matches
Etf.hpp
Go to the documentation of this file.
1/*
2 MIT License
3
4 DiscordCoreAPI, A bot library for Discord, written in C++, and featuring explicit multithreading through the usage of custom, asynchronous C++ CoRoutines.
5
6 Copyright 2022, 2023 Chris M. (RealTimeChris)
7
8 Permission is hereby granted, free of charge, to any person obtaining a copy
9 of this software and associated documentation files (the "Software"), to deal
10 in the Software without restriction, including without limitation the rights
11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 copies of the Software, and to permit persons to whom the Software is
13 furnished to do so, subject to the following conditions:
14
15 The above copyright notice and this permission notice shall be included in all
16 copies or substantial portions of the Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 SOFTWARE.
25*/
26/// Etf.hpp - Header for the erlpacking class.
27/// Nov 8, 2021
28/// https://discordcoreapi.com
29/// \file Etf.hpp
30#pragma once
31
33
34namespace discord_core_api {
35
36 namespace discord_core_internal {
37
38 /**
39 * \addtogroup discord_core_internal
40 * @{
41 */
42 template<typename return_type> inline return_type ntohsNew(return_type value) {
43 return static_cast<return_type>(((value & 0x00FF) << 8) | ((value & 0xFF00) >> 8));
44 }
45
46 template<typename return_type> inline return_type ntohlNew(return_type value) {
47 return static_cast<return_type>(((value & 0x000000FF) << 24) | ((value & 0x0000FF00) << 8) | ((value & 0x00FF0000) >> 8) | ((value & 0xFF000000) >> 24));
48 }
49
50 template<typename return_type> inline return_type ntohllNew(return_type value) {
51 return static_cast<return_type>(((value & 0x00000000000000FFull) << 56) | ((value & 0x000000000000FF00ULL) << 40) | ((value & 0x0000000000FF0000ULL) << 24) |
52 ((value & 0x00000000FF000000ULL) << 8) | ((value & 0x000000FF00000000ULL) >> 8) | ((value & 0x0000FF0000000000ULL) >> 24) |
53 ((value & 0x00FF000000000000ULL) >> 40) | ((value & 0xFF00000000000000ULL) >> 56));
54 }
55
56 /// @brief Reverses the byte order of a value if needed, based on the endianness.
57 /// @tparam return_type the type of the value to reverse.
58 /// @param net the value to reverse.
59 /// @return the reversed value.
60 template<typename return_type> inline return_type reverseByteOrder(return_type net) {
61 if constexpr (std::endian::native == std::endian::little) {
62 switch (sizeof(return_type)) {
63 case 2: {
64 return ntohsNew(net);
65 }
66 case 4: {
67 return ntohlNew(net);
68 }
69 case 8: {
70 return ntohllNew(net);
71 }
72 default: {
73 return net;
74 }
75 }
76 } else {
77 return net;
78 }
79 }
80
81 /// @brief Stores the bits of a number into a character array.
82 /// @tparam return_type the type of the number.
83 /// @param to the character array to store the bits.
84 /// @param num the number whose bits are to be stored.
85 template<typename return_type, typename value_type> inline void storeBits(value_type* to, return_type num) {
86 const uint8_t byteSize{ 8 };
87 num = reverseByteOrder(num);
88
89 // store the bits of the number in the character array
90 for (uint64_t x = 0; x < sizeof(return_type); ++x) {
91 to[x] = static_cast<value_type>(num >> (byteSize * x));
92 }
93 }
94
95 /// @brief Exception class for etf parsing errors.
97 /// @brief Constructs an etf_parse_error instance with a message and source location.
98 /// @param message the error message.
99 /// @param location the source location where the error occurred.
100 inline explicit etf_parse_error(jsonifier::string_view message, const std::source_location& location = std::source_location::current())
101 : dca_exception{ message, location } {};
102 };
103
104 enum class etf_type : int8_t {
105 New_Float_Ext = 70,
106 Small_Integer_Ext = 97,
107 Integer_Ext = 98,
108 Atom_Ext = 100,
109 Nil_Ext = 106,
110 String_Ext = 107,
111 List_Ext = 108,
112 Binary_Ext = 109,
113 Small_Big_Ext = 110,
114 Small_Atom_Ext = 115,
115 Map_Ext = 116,
116 };
117
118 constexpr uint8_t formatVersion{ 131 };
119
120 /// @brief Class for parsing etf data into json format.
121 class DiscordCoreAPI_Dll etf_parser : public jsonifier_internal::alloc_wrapper<uint8_t> {
122 public:
123 friend class websocket_client;
124 using allocator = jsonifier_internal::alloc_wrapper<uint8_t>;
125
126 /// @brief Parse etf data to json format.
127 /// @param dataToParse the etf data to be parsed.
128 /// @return the json representation of the parsed data.
129 inline jsonifier::string_view_base<uint8_t> parseEtfToJson(jsonifier::string_view_base<uint8_t> dataToParse) {
130 dataBuffer = dataToParse.data();
131 dataSize = dataToParse.size();
132 finalString.clear();
133 currentSize = 0;
134 offSet = 0;
135 if (readBitsFromBuffer<uint8_t>() != formatVersion) {
136 throw etf_parse_error{ "etf_parser::parseEtfToJson() error: incorrect format version specified." };
137 }
138 singleValueETFToJson();
139 return { finalString.data(), currentSize };
140 }
141
142 protected:
143 jsonifier::string_base<uint8_t> finalString{};///< The final json string.
144 const uint8_t* dataBuffer{};///< Pointer to etf data buffer.
145 uint64_t currentSize{};///< c///< current size of the json string.
146 uint64_t dataSize{};///< Size of the etf data.
147 uint64_t offSet{};///< current offset in the etf data.
148
149 /// @brief Read bits from the data buffer and convert to return_type.
150 /// @tparam return_type the type to convert the read data to.
151 /// @return the converted value.
152 template<typename return_type> inline return_type readBitsFromBuffer() {
153 if (offSet + sizeof(return_type) > dataSize) {
154 throw etf_parse_error{ "etf_parser::readBitsFromBuffer() error: readBitsFromBuffer() past end of the buffer." };
155 }
156 return_type newValue{};
157 std::memcpy(&newValue, dataBuffer + offSet, sizeof(return_type));
158 offSet += sizeof(return_type);
159 newValue = reverseByteOrder(newValue);
160 return newValue;
161 }
162
163 /// @brief Write characters to the final json string.
164 /// @param data pointer to the data to be written.
165 /// @param length number of characters to write.
166 inline void writeCharacters(const char* data, uint64_t length) {
167 if (finalString.size() < currentSize + length) {
168 finalString.resize((finalString.size() + length) * 2);
169 }
170 std::memcpy(finalString.data() + currentSize, data, length);
171 currentSize += length;
172 }
173
174 /// @brief Write characters from the buffer to the final json string.
175 /// @param length number of characters to write from the buffer.
176 inline void writeCharactersFromBuffer(uint32_t length) {
177 if (!length) {
178 writeCharacters("\"\"", 2);
179 return;
180 }
181 if (offSet + static_cast<uint64_t>(length) > dataSize) {
182 throw etf_parse_error{ "erl_packer::writeCharactersFromBuffer() error: read past end of buffer." };
183 }
184 if (finalString.size() < currentSize + length) {
185 finalString.resize((finalString.size() + length) * 2);
186 }
187 const uint8_t* stringNew = dataBuffer + offSet;
188 offSet += length;
189 if (length >= 3 && length <= 5) {
190 if (length == 3 && stringNew[0] == 'n' && stringNew[1] == 'i' && stringNew[2] == 'l') {
191 writeCharacters("null", 4);
192 return;
193 } else if (length == 4 && stringNew[0] == 'n' && stringNew[1] == 'u' && stringNew[2] == 'l' && stringNew[3] == 'l') {
194 writeCharacters("null", 4);
195 return;
196 } else if (length == 4 && stringNew[0] == 't' && stringNew[1] == 'r' && stringNew[2] == 'u' && stringNew[3] == 'e') {
197 writeCharacters("true", 4);
198 return;
199 } else if (length == 5 && stringNew[0] == 'f' && stringNew[1] == 'a' && stringNew[2] == 'l' && stringNew[3] == 's' && stringNew[4] == 'e') {
200 writeCharacters("false", 5);
201 return;
202 }
203 }
204 writeCharacter<'"'>();
205 for (uint64_t x = 0; x < length; ++x) {
206 switch (stringNew[x]) {
207 case '\\': {
208 switch (stringNew[++x]) {
209 case '\"':
210 writeCharacter<'\"'>();
211 break;
212 case '\\':
213 writeCharacter<'\\'>();
214 writeCharacter<'\\'>();
215 break;
216 case 'b':
217 writeCharacter<'\b'>();
218 break;
219 case 'f':
220 writeCharacter<'\f'>();
221 break;
222 case 'n':
223 writeCharacter<'\n'>();
224 break;
225 case 'r':
226 writeCharacter<'\r'>();
227 break;
228 case 't':
229 writeCharacter<'\t'>();
230 break;
231 default: {
232 writeCharacter(stringNew[x]);
233 break;
234 }
235 }
236 break;
237 }
238 case '"': {
239 writeCharacter<'\\'>();
240 writeCharacter<'\"'>();
241 break;
242 }
243 default: {
244 writeCharacter(stringNew[x]);
245 break;
246 }
247 }
248 }
249 writeCharacter<'"'>();
250 }
251
252 /// @brief Write a character to the final json string.
253 /// @param value the character to write.
254 template<typename value_type> inline void writeCharacter(const value_type value) {
255 if (finalString.size() < currentSize + 1) {
256 finalString.resize((finalString.size() + 1) * 2);
257 }
258 allocator::construct(&finalString[currentSize++], static_cast<uint8_t>(value));
259 }
260
261 /// @brief Write a character to the final json string.
262 /// @tparam value the character to write.
263 template<const char charToWrite> inline void writeCharacter() {
264 if (finalString.size() < currentSize + 1) {
265 finalString.resize((finalString.size() + 1) * 2);
266 }
267 allocator::construct(&finalString[currentSize++], static_cast<uint8_t>(charToWrite));
268 }
269
270 /// @brief Parse a single etf value and convert to json.
272 if (offSet > dataSize) {
273 throw etf_parse_error{ "erl_packer::singleValueETFToJson() error: read past end of buffer." };
274 }
275 uint8_t type = readBitsFromBuffer<uint8_t>();
276 switch (static_cast<etf_type>(type)) {
277 case etf_type::New_Float_Ext: {
278 return parseNewFloatExt();
279 }
280 case etf_type::Small_Integer_Ext: {
281 return parseSmallIntegerExt();
282 }
283 case etf_type::Integer_Ext: {
284 return parseIntegerExt();
285 }
286 case etf_type::Atom_Ext: {
287 return parseAtomExt();
288 }
289 case etf_type::Nil_Ext: {
290 return parseNilExt();
291 }
292 case etf_type::String_Ext: {
293 return parseStringExt();
294 }
295 case etf_type::List_Ext: {
296 return parseListExt();
297 }
298 case etf_type::Binary_Ext: {
299 return parseBinaryExt();
300 }
301 case etf_type::Small_Big_Ext: {
302 return parseSmallBigExt();
303 }
304 case etf_type::Small_Atom_Ext: {
305 return parseSmallAtomExt();
306 }
307 case etf_type::Map_Ext: {
308 return parseMapExt();
309 }
310 default: {
311 throw etf_parse_error{ "etf_parser::singleValueETFToJson() error: unknown data type in etf, the type: " + jsonifier::toString(type) };
312 }
313 }
314 }
315
316 /// @brief Parse etf data representing a list and convert to json array.
317 inline void parseListExt() {
318 uint32_t length = readBitsFromBuffer<uint32_t>();
319 writeCharacter<'['>();
320 if (static_cast<uint64_t>(offSet) + length > dataSize) {
321 throw etf_parse_error{ "erl_packer::parseListExt() error: read past end of buffer." };
322 }
323 for (uint16_t x = 0; x < length; ++x) {
324 singleValueETFToJson();
325 if (x < length - 1) {
326 writeCharacter<','>();
327 }
328 }
329 readBitsFromBuffer<uint8_t>();
330 writeCharacter<']'>();
331 }
332
333 /// @brief Parse etf data representing a small integer and convert to json number.
334 inline void parseSmallIntegerExt() {
335 auto string = jsonifier::toString(readBitsFromBuffer<uint8_t>());
336 writeCharacters(string.data(), string.size());
337 }
338
339 /// @brief Parse etf data representing an integer and convert to json number.
340 inline void parseIntegerExt() {
341 auto string = jsonifier::toString(readBitsFromBuffer<uint32_t>());
342 writeCharacters(string.data(), string.size());
343 }
344
345 /// @brief Parse etf data representing a string and convert to json string.
346 inline void parseStringExt() {
347 writeCharacter<'"'>();
348 uint16_t length = readBitsFromBuffer<uint16_t>();
349 if (static_cast<uint64_t>(offSet) + length > dataSize) {
350 throw etf_parse_error{ "erl_packer::parseStringExt() error: read past end of buffer." };
351 }
352 for (uint16_t x = 0; x < length; ++x) {
353 parseSmallIntegerExt();
354 }
355 writeCharacter<'"'>();
356 }
357
358 /// @brief Parse etf data representing a new float and convert to json number.
359 inline void parseNewFloatExt() {
360 uint64_t value = readBitsFromBuffer<uint64_t>();
361 double newDouble{};
362 std::memcpy(&newDouble, &value, sizeof(double));
363 jsonifier::string valueNew = jsonifier::toString(newDouble);
364 writeCharacters(valueNew.data(), valueNew.size());
365 }
366
367 /// @brief Parse etf data representing a small big integer and convert to json number.
368 inline void parseSmallBigExt() {
369 auto digits = readBitsFromBuffer<uint8_t>();
370 uint8_t sign = readBitsFromBuffer<uint8_t>();
371
372 if (digits > 8) {
373 throw etf_parse_error{ "etf_parser::parseSmallBigExt() error: big integers larger than 8 bytes not supported." };
374 }
375
376 uint64_t value = 0;
377 uint64_t bits = 1;
378 for (uint8_t x = 0; x < digits; ++x) {
379 uint64_t digit = readBitsFromBuffer<uint8_t>();
380 value += digit * bits;
381 bits <<= 8;
382 }
383
384 if (sign == 0) {
385 auto string = jsonifier::toString(value);
386 writeCharacters(string.data(), string.size());
387 } else {
388 auto string = jsonifier::toString(-(static_cast<int64_t>(value)));
389 writeCharacters(string.data(), string.size());
390 }
391 }
392
393 /// @brief Parse etf data representing an atom and convert to json string.
394 inline void parseAtomExt() {
395 writeCharactersFromBuffer(readBitsFromBuffer<uint16_t>());
396 }
397
398 /// @brief Parse etf data representing a binary and convert to json string.
399 inline void parseBinaryExt() {
400 writeCharactersFromBuffer(readBitsFromBuffer<uint32_t>());
401 }
402
403 /// @brief Parse etf data representing a nil value and convert to json null.
404 inline void parseNilExt() {
405 writeCharacters("[]", 2);
406 }
407
408 /// @brief Parse etf data representing a small atom and convert to json string.
409 inline void parseSmallAtomExt() {
410 writeCharactersFromBuffer(readBitsFromBuffer<uint8_t>());
411 }
412
413 /// @brief Parse etf data representing a map and convert to json object.
414 inline void parseMapExt() {
415 uint32_t length = readBitsFromBuffer<uint32_t>();
416 writeCharacter<'{'>();
417 for (uint32_t x = 0; x < length; ++x) {
418 singleValueETFToJson();
419 writeCharacter<':'>();
420 singleValueETFToJson();
421 if (x < length - 1) {
422 writeCharacter<','>();
423 }
424 }
425 writeCharacter<'}'>();
426 }
427 };
428
429 /// @brief Custom exception class for etf serialization errors.
431 public:
432 /// @brief Constructor for etf_serialize_error.
433 /// @param message the error message.
434 /// @param location source location where the error occurred.
435 inline etf_serialize_error(jsonifier::string_view message, const std::source_location& location = std::source_location::current())
436 : dca_exception{ message, location } {};
437 };
438
439 /// @brief Enumeration for different json value types.
440 enum class json_type : uint8_t { null_t = 0, object_t = 1, array_t = 2, string_t = 3, float_t = 4, uint_t = 5, int_t = 6, bool_t = 7 };
441
442 /// @brief Concept for array types excluding etf_serializer.
443 template<typename value_type>
444 concept array_t = jsonifier::concepts::range<value_type> && jsonifier::concepts::has_resize<std::unwrap_ref_decay_t<value_type>> &&
445 jsonifier::concepts::has_emplace_back<std::unwrap_ref_decay_t<value_type>> && jsonifier::concepts::vector_subscriptable<std::unwrap_ref_decay_t<value_type>> &&
446 requires(value_type&& data) { typename value_type::value_type; };
447
448 /// @brief Concept for object (associative container) types excluding etf_serializer.
449 template<typename value_type>
450 concept object_t = requires(value_type data) {
451 typename value_type::mapped_type;
452 typename value_type::key_type;
453 } && jsonifier::concepts::range<value_type>;
454
455 class etf_serializer {
456 public:
457 template<typename value_type> using allocator = jsonifier_internal::alloc_wrapper<value_type>;
458 using object_type = unordered_map<jsonifier::string, etf_serializer>;
459 using array_type = jsonifier::vector<etf_serializer>;
460 using string_type = jsonifier::string;
461 using float_type = double;
462 using uint_type = uint64_t;
463 using int_type = int64_t;
464 using bool_type = bool;
465
466 inline etf_serializer() = default;
467
468 inline etf_serializer& operator=(etf_serializer&& data) noexcept {
469 destroyImpl();
470 stringReal = std::move(data.stringReal);
471 type = data.type;
472 data.type = json_type::null_t;
473 switch (type) {
474 case json_type::object_t: {
475 objectValue = data.objectValue;
476 data.objectValue = nullptr;
477 break;
478 }
479 case json_type::array_t: {
480 arrayValue = data.arrayValue;
481 data.arrayValue = nullptr;
482 break;
483 }
484 case json_type::string_t: {
485 stringValue = data.stringValue;
486 data.stringValue = nullptr;
487 break;
488 }
489 case json_type::float_t: {
490 floatValue = data.floatValue;
491 data.floatValue = nullptr;
492 break;
493 }
494 case json_type::int_t: {
495 intValue = data.intValue;
496 data.intValue = nullptr;
497 break;
498 }
499 case json_type::uint_t: {
500 uintValue = data.uintValue;
501 data.uintValue = nullptr;
502 break;
503 }
504 case json_type::bool_t: {
505 boolValue = data.boolValue;
506 data.boolValue = nullptr;
507 break;
508 }
509 case json_type::null_t: {
510 break;
511 }
512 }
513 return *this;
514 }
515
516 inline etf_serializer(etf_serializer&& data) noexcept {
517 *this = std::move(data);
518 }
519
520 inline etf_serializer& operator=(const etf_serializer& data) {
521 destroyImpl();
522 switch (data.type) {
523 case json_type::object_t: {
524 setValue<json_type::object_t>(data.getObject());
525 break;
526 }
527 case json_type::array_t: {
528 setValue<json_type::array_t>(data.getArray());
529 break;
530 }
531 case json_type::string_t: {
532 setValue<json_type::string_t>(data.getString());
533 break;
534 }
535 case json_type::float_t: {
536 setValue<json_type::float_t>(data.getFloat());
537 break;
538 }
539 case json_type::uint_t: {
540 setValue<json_type::uint_t>(data.getUint());
541 break;
542 }
543 case json_type::int_t: {
544 setValue<json_type::int_t>(data.getInt());
545 break;
546 }
547 case json_type::bool_t: {
548 setValue<json_type::bool_t>(data.getBool());
549 break;
550 }
551 case json_type::null_t: {
552 break;
553 }
554 }
555 stringReal = data.stringReal;
556 return *this;
557 }
558
559 inline etf_serializer(const etf_serializer& data) {
560 *this = data;
561 }
562
563 template<object_t value_type> inline etf_serializer& operator=(value_type&& data) noexcept {
564 setValue<json_type::object_t>(std::forward<value_type>(data));
565 return *this;
566 }
567
568 template<object_t value_type> inline etf_serializer(value_type&& data) noexcept {
569 *this = std::forward<value_type>(data);
570 }
571
572 template<array_t value_type> inline etf_serializer& operator=(value_type&& data) noexcept {
573 setValue<json_type::array_t>(std::forward<value_type>(data));
574 return *this;
575 }
576
577 template<array_t value_type> inline etf_serializer(value_type&& data) noexcept {
578 *this = std::forward<value_type>(data);
579 }
580
581 template<jsonifier::concepts::string_t value_type> inline etf_serializer& operator=(value_type&& data) noexcept {
582 setValue<json_type::string_t>(std::forward<value_type>(data));
583 return *this;
584 }
585
586 template<jsonifier::concepts::string_t value_type> inline etf_serializer(value_type&& data) noexcept {
587 *this = std::forward<value_type>(data);
588 }
589
590 template<uint_type str_length> inline etf_serializer& operator=(const char (&str)[str_length]) {
591 setValue<json_type::string_t>(str);
592 return *this;
593 }
594
595 template<uint_type str_length> inline etf_serializer(const char (&str)[str_length]) {
596 *this = str;
597 }
598
599 template<jsonifier::concepts::float_t value_type> inline etf_serializer& operator=(value_type&& data) {
600 setValue<json_type::float_t>(std::forward<value_type>(data));
601 return *this;
602 }
603
604 template<jsonifier::concepts::float_t value_type> inline etf_serializer(value_type&& data) {
605 *this = std::forward<value_type>(data);
606 }
607
608 template<jsonifier::concepts::integer_t value_type> inline etf_serializer& operator=(value_type&& data) {
609 if constexpr (jsonifier::concepts::signed_t<value_type>) {
610 setValue<json_type::int_t>(std::forward<value_type>(data));
611 } else if constexpr (jsonifier::concepts::unsigned_t<value_type>) {
612 setValue<json_type::uint_t>(std::forward<value_type>(data));
613 }
614 return *this;
615 }
616
617 template<jsonifier::concepts::integer_t value_type> inline etf_serializer(value_type&& data) {
618 *this = std::forward<value_type>(data);
619 }
620
621 template<jsonifier::concepts::bool_t value_type> inline etf_serializer& operator=(value_type&& data) {
622 setValue<json_type::bool_t>(std::forward<value_type>(data));
623 return *this;
624 }
625
626 template<jsonifier::concepts::bool_t value_type> inline etf_serializer(value_type&& data) {
627 *this = std::forward<value_type>(data);
628 }
629
630 template<jsonifier::concepts::enum_t value_type> inline etf_serializer& operator=(value_type&& data) noexcept {
631 setValue<json_type::int_t>(static_cast<int_type>(std::forward<value_type>(data)));
632 return *this;
633 }
634
635 template<jsonifier::concepts::enum_t value_type> inline etf_serializer(value_type&& data) noexcept {
636 *this = std::forward<value_type>(data);
637 }
638
639 inline etf_serializer& operator=(json_type data) {
640 switch (data) {
641 case json_type::object_t: {
642 setValue<json_type::object_t>();
643 break;
644 }
645 case json_type::array_t: {
646 setValue<json_type::array_t>();
647 break;
648 }
649 case json_type::string_t: {
650 setValue<json_type::string_t>();
651 break;
652 }
653 case json_type::float_t: {
654 setValue<json_type::float_t>();
655 break;
656 }
657 case json_type::uint_t: {
658 setValue<json_type::uint_t>();
659 break;
660 }
661 case json_type::int_t: {
662 setValue<json_type::int_t>();
663 break;
664 }
665 case json_type::bool_t: {
666 setValue<json_type::bool_t>();
667 break;
668 }
669 case json_type::null_t: {
670 setValue<json_type::null_t>();
671 break;
672 }
673 }
674 return *this;
675 }
676
677 inline etf_serializer(json_type data) {
678 *this = data;
679 }
680
681 inline json_type getType() const {
682 return type;
683 }
684
685 inline operator jsonifier::string_base<uint8_t>() {
686 stringReal.clear();
687 appendVersion();
688 serializeJsonToEtfString(*this);
689 return stringReal;
690 }
691
692 etf_serializer& operator[](typename object_type::key_type&& key) {
693 if (type == json_type::null_t) {
694 setValue<json_type::object_t>();
695 }
696
697 if (type == json_type::object_t) {
698 return getObject().operator[](std::forward<typename object_type::key_type>(key));
699 }
700 throw etf_serialize_error{ "Sorry, but this value's type is not object." };
701 }
702
703 inline etf_serializer& operator[](uint_type index) {
704 if (type == json_type::null_t) {
705 setValue<json_type::array_t>();
706 }
707
708 if (type == json_type::array_t) {
709 if (index >= getArray().size()) {
710 getArray().resize(index + 1);
711 }
712
713 return getArray().at(index);
714 }
715 throw etf_serialize_error{ "Sorry, but this value's type is not array." };
716 }
717
718 inline void emplaceBack(etf_serializer&& data) {
719 if (type == json_type::null_t) {
720 setValue<json_type::array_t>();
721 }
722
723 if (type == json_type::array_t) {
724 getArray().emplace_back(std::move(data));
725 return;
726 }
727 throw etf_serialize_error{ "Sorry, but this value's type is not array." };
728 }
729
730 inline void emplaceBack(const etf_serializer& other) {
731 if (type == json_type::null_t) {
732 setValue<json_type::array_t>();
733 }
734
735 if (type == json_type::array_t) {
736 getArray().emplace_back(other);
737 return;
738 }
739 throw etf_serialize_error{ "Sorry, but this value's type is not array." };
740 }
741
742 inline bool_type operator==(const etf_serializer& lhs) const {
743 if (lhs.type != type) {
744 return false;
745 }
746 switch (type) {
747 case json_type::object_t: {
748 if (!compareValues<json_type::object_t>(lhs)) {
749 return false;
750 }
751 break;
752 }
753 case json_type::array_t: {
754 if (!compareValues<json_type::array_t>(lhs)) {
755 return false;
756 }
757 break;
758 }
759 case json_type::string_t: {
760 if (!compareValues<json_type::string_t>(lhs)) {
761 return false;
762 }
763 break;
764 }
765 case json_type::float_t: {
766 if (!compareValues<json_type::float_t>(lhs)) {
767 return false;
768 }
769 break;
770 }
771 case json_type::uint_t: {
772 if (!compareValues<json_type::uint_t>(lhs)) {
773 return false;
774 }
775 break;
776 }
777 case json_type::int_t: {
778 if (!compareValues<json_type::int_t>(lhs)) {
779 return false;
780 }
781 break;
782 }
783 case json_type::bool_t: {
784 if (!compareValues<json_type::bool_t>(lhs)) {
785 return false;
786 }
787 break;
788 }
789 case json_type::null_t: {
790 break;
791 }
792 }
793 return true;
794 }
795
796 inline object_type& getObject() const {
797 if (type != json_type::object_t) {
798 throw etf_serialize_error{ "Sorry, but this value's type is not object!" };
799 }
800 return *objectValue;
801 }
802
803 inline array_type& getArray() const {
804 if (type != json_type::array_t) {
805 throw etf_serialize_error{ "Sorry, but this value's type is not array!" };
806 }
807 return *arrayValue;
808 }
809
810 inline string_type& getString() const {
811 if (type != json_type::string_t) {
812 throw etf_serialize_error{ "Sorry, but this value's type is not string!" };
813 }
814 return *stringValue;
815 }
816
817 inline float_type& getFloat() const {
818 if (type != json_type::float_t) {
819 throw etf_serialize_error{ "Sorry, but this value's type is not float!" };
820 }
821 return *floatValue;
822 }
823
824 inline uint_type& getUint() const {
825 if (type != json_type::uint_t) {
826 throw etf_serialize_error{ "Sorry, but this value's type is not uint!" };
827 }
828 return *uintValue;
829 }
830
831 inline int_type& getInt() const {
832 if (type != json_type::int_t) {
833 throw etf_serialize_error{ "Sorry, but this value's type is not int!" };
834 }
835 return *intValue;
836 }
837
838 inline bool_type& getBool() const {
839 if (type != json_type::bool_t) {
840 throw etf_serialize_error{ "Sorry, but this value's type is not bool!" };
841 }
842 return *boolValue;
843 }
844
845 inline ~etf_serializer() {
846 destroyImpl();
847 }
848
849 protected:
850 jsonifier::string_base<uint8_t> stringReal{};
851 json_type type{ json_type::null_t };
852 union {
853 object_type* objectValue;
854 array_type* arrayValue;
855 string_type* stringValue;
856 float_type* floatValue;
857 uint_type* uintValue;
858 int_type* intValue;
859 bool_type* boolValue;
860 };
861
862 inline void serializeJsonToEtfString(const etf_serializer& dataToParse) {
863 switch (dataToParse.type) {
864 case json_type::object_t: {
865 return writeEtfObject(dataToParse.getObject());
866 }
867 case json_type::array_t: {
868 return writeEtfArray(dataToParse.getArray());
869 }
870 case json_type::string_t: {
871 return writeEtfString(dataToParse.getString());
872 }
873 case json_type::float_t: {
874 return writeEtfFloat(dataToParse.getFloat());
875 }
876 case json_type::uint_t: {
877 return writeEtfUint(dataToParse.getUint());
878 }
879 case json_type::int_t: {
880 return writeEtfInt(dataToParse.getInt());
881 }
882 case json_type::bool_t: {
883 return writeEtfBool(dataToParse.getBool());
884 }
885 case json_type::null_t: {
886 return writeEtfNull();
887 }
888 }
889 }
890
891 inline void writeEtfObject(const object_type& jsonData) {
892 appendMapHeader(static_cast<uint32_t>(jsonData.size()));
893 for (auto& [key, valueNew]: jsonData) {
894 appendBinaryExt(key, static_cast<uint32_t>(key.size()));
895 serializeJsonToEtfString(valueNew);
896 }
897 }
898
899 inline void writeEtfArray(const array_type& jsonData) {
900 appendListHeader(static_cast<uint32_t>(jsonData.size()));
901 for (auto& valueNew: jsonData) {
902 serializeJsonToEtfString(valueNew);
903 }
904 appendNilExt();
905 }
906
907 inline void writeEtfString(const string_type& jsonData) {
908 appendBinaryExt(jsonData, static_cast<uint32_t>(jsonData.size()));
909 }
910
911 inline void writeEtfUint(const uint_type jsonData) {
912 if (jsonData <= std::numeric_limits<uint8_t>::max() && jsonData >= std::numeric_limits<uint8_t>::min()) {
913 appendUint8(static_cast<uint8_t>(jsonData));
914 } else if (jsonData <= std::numeric_limits<uint32_t>::max() && jsonData >= std::numeric_limits<uint32_t>::min()) {
915 appendUint32(static_cast<uint32_t>(jsonData));
916 } else {
917 appendUint64(jsonData);
918 }
919 }
920
921 inline void writeEtfInt(const int_type jsonData) {
922 if (jsonData <= std::numeric_limits<int8_t>::max() && jsonData >= std::numeric_limits<int8_t>::min()) {
923 appendInt8(static_cast<int8_t>(jsonData));
924 } else if (jsonData <= std::numeric_limits<int32_t>::max() && jsonData >= std::numeric_limits<int32_t>::min()) {
925 appendInt32(static_cast<int32_t>(jsonData));
926 } else {
927 appendInt64(jsonData);
928 }
929 }
930
931 inline void writeEtfFloat(const float_type jsonData) {
932 appendNewFloatExt(jsonData);
933 }
934
935 inline void writeEtfBool(const bool_type jsonData) {
936 appendBool(jsonData);
937 }
938
939 inline void writeEtfNull() {
940 appendNil();
941 }
942
943 template<typename value_type> inline void writeString(const value_type* data, uint_type length) {
944 auto oldSize = stringReal.size();
945 stringReal.resize(oldSize + length);
946 std::memcpy(stringReal.data() + oldSize, data, length);
947 }
948
949 inline void appendBinaryExt(jsonifier::string_view bytes, uint32_t sizeNew) {
950 uint8_t newBuffer[5]{ static_cast<uint8_t>(etf_type::Binary_Ext) };
951 storeBits(newBuffer + 1, sizeNew);
952 writeString(newBuffer, std::size(newBuffer));
953 writeString(bytes.data(), bytes.size());
954 }
955
956 inline void appendNewFloatExt(const float_type newFloat) {
957 uint8_t newBuffer[9]{ static_cast<uint8_t>(etf_type::New_Float_Ext) };
958 uint_type newValue{};
959 std::memcpy(&newValue, &newFloat, sizeof(newFloat));
960 storeBits(newBuffer + 1, newValue);
961 writeString(newBuffer, std::size(newBuffer));
962 }
963
964 inline void appendListHeader(const uint32_t sizeNew) {
965 uint8_t newBuffer[5]{ static_cast<uint8_t>(etf_type::List_Ext) };
966 storeBits(newBuffer + 1, sizeNew);
967 writeString(newBuffer, std::size(newBuffer));
968 }
969
970 inline void appendMapHeader(const uint32_t sizeNew) {
971 uint8_t newBuffer[5]{ static_cast<uint8_t>(etf_type::Map_Ext) };
972 storeBits(newBuffer + 1, sizeNew);
973 writeString(newBuffer, std::size(newBuffer));
974 }
975
976 inline void appendUint64(uint_type valueNew) {
977 uint8_t newBuffer[11]{ static_cast<uint8_t>(etf_type::Small_Big_Ext) };
978 uint8_t encodedBytes{};
979 while (valueNew > 0) {
980 newBuffer[3 + encodedBytes] = static_cast<uint8_t>(valueNew & 0xFF);
981 valueNew >>= 8;
982 ++encodedBytes;
983 }
984 newBuffer[1] = encodedBytes;
985 newBuffer[2] = 0;
986 writeString(newBuffer, 1ull + 2ull + static_cast<uint_type>(encodedBytes));
987 }
988
989 inline void appendInt64(int_type valueNew) {
990 uint8_t newBuffer[11]{ static_cast<uint8_t>(etf_type::Small_Big_Ext) };
991 uint8_t encodedBytes{};
992 while (valueNew > 0) {
993 newBuffer[3 + encodedBytes] = static_cast<uint8_t>(valueNew & 0xFF);
994 valueNew >>= 8;
995 ++encodedBytes;
996 }
997 newBuffer[1] = encodedBytes;
998 if (valueNew >= 0) {
999 newBuffer[2] = 0;
1000 } else {
1001 newBuffer[2] = 1;
1002 }
1003 writeString(newBuffer, 1ull + 2ull + static_cast<uint_type>(encodedBytes));
1004 }
1005
1006 inline void appendUint32(const uint32_t valueNew) {
1007 uint8_t newBuffer[5]{ static_cast<uint8_t>(etf_type::Integer_Ext) };
1008 storeBits(newBuffer + 1, valueNew);
1009 writeString(newBuffer, std::size(newBuffer));
1010 }
1011
1012 inline void appendInt32(const int32_t valueNew) {
1013 uint8_t newBuffer[5]{ static_cast<uint8_t>(etf_type::Integer_Ext) };
1014 storeBits(newBuffer + 1, valueNew);
1015 writeString(newBuffer, std::size(newBuffer));
1016 }
1017
1018 inline void appendUint8(const uint8_t valueNew) {
1019 uint8_t newBuffer[2]{ static_cast<uint8_t>(etf_type::Small_Integer_Ext), static_cast<uint8_t>(valueNew) };
1020 writeString(newBuffer, std::size(newBuffer));
1021 }
1022
1023 inline void appendInt8(const int8_t valueNew) {
1024 uint8_t newBuffer[2]{ static_cast<uint8_t>(etf_type::Small_Integer_Ext), static_cast<uint8_t>(valueNew) };
1025 writeString(newBuffer, std::size(newBuffer));
1026 }
1027
1028 inline void appendBool(bool_type data) {
1029 if (data) {
1030 uint8_t newBuffer[6]{ static_cast<uint8_t>(etf_type::Small_Atom_Ext), static_cast<uint8_t>(4), 't', 'r', 'u', 'e' };
1031 writeString(newBuffer, std::size(newBuffer));
1032
1033 } else {
1034 uint8_t newBuffer[7]{ static_cast<uint8_t>(etf_type::Small_Atom_Ext), static_cast<uint8_t>(5), 'f', 'a', 'l', 's', 'e' };
1035 writeString(newBuffer, std::size(newBuffer));
1036 }
1037 }
1038
1039 inline void appendVersion() {
1040 uint8_t newBuffer[1]{ static_cast<uint8_t>(formatVersion) };
1041 writeString(newBuffer, std::size(newBuffer));
1042 }
1043
1044 inline void appendNilExt() {
1045 uint8_t newBuffer[1]{ static_cast<uint8_t>(etf_type::Nil_Ext) };
1046 writeString(newBuffer, std::size(newBuffer));
1047 }
1048
1049 inline void appendNil() {
1050 uint8_t newBuffer[5]{ static_cast<uint8_t>(etf_type::Small_Atom_Ext), static_cast<uint8_t>(3), 'n', 'i', 'l' };
1051 writeString(newBuffer, std::size(newBuffer));
1052 }
1053
1054 template<json_type typeNew, typename... value_types> inline void setValue(value_types&&... args) {
1055 destroyImpl();
1056 type = typeNew;
1057 if constexpr (typeNew == json_type::object_t) {
1058 allocator<object_type> alloc{};
1059 objectValue = alloc.allocate(1);
1060 alloc.construct(objectValue, std::forward<value_types>(args)...);
1061 } else if constexpr (typeNew == json_type::array_t) {
1062 allocator<array_type> alloc{};
1063 arrayValue = alloc.allocate(1);
1064 alloc.construct(arrayValue, std::forward<value_types>(args)...);
1065 } else if constexpr (typeNew == json_type::string_t) {
1066 allocator<string_type> alloc{};
1067 stringValue = alloc.allocate(1);
1068 alloc.construct(stringValue, std::forward<value_types>(args)...);
1069 } else if constexpr (typeNew == json_type::float_t) {
1070 allocator<float_type> alloc{};
1071 floatValue = alloc.allocate(1);
1072 alloc.construct(floatValue, std::forward<value_types>(args)...);
1073 } else if constexpr (typeNew == json_type::uint_t) {
1074 allocator<uint_type> alloc{};
1075 uintValue = alloc.allocate(1);
1076 alloc.construct(uintValue, std::forward<value_types>(args)...);
1077 } else if constexpr (typeNew == json_type::int_t) {
1078 allocator<int_type> alloc{};
1079 intValue = alloc.allocate(1);
1080 alloc.construct(intValue, std::forward<value_types>(args)...);
1081 } else if constexpr (typeNew == json_type::bool_t) {
1082 allocator<bool_type> alloc{};
1083 boolValue = alloc.allocate(1);
1084 alloc.construct(boolValue, std::forward<value_types>(args)...);
1085 }
1086 }
1087
1088 template<json_type typeNew> inline void destroy() {
1089 if constexpr (typeNew == json_type::object_t) {
1090 allocator<object_type> alloc{};
1091 alloc.destroy(objectValue);
1092 alloc.deallocate(static_cast<object_type*>(objectValue), 1);
1093 objectValue = nullptr;
1094 } else if constexpr (typeNew == json_type::array_t) {
1095 allocator<array_type> alloc{};
1096 alloc.destroy(arrayValue);
1097 alloc.deallocate(static_cast<array_type*>(arrayValue), 1);
1098 arrayValue = nullptr;
1099 } else if constexpr (typeNew == json_type::string_t) {
1100 allocator<string_type> alloc{};
1101 alloc.destroy(stringValue);
1102 alloc.deallocate(static_cast<string_type*>(stringValue), 1);
1103 stringValue = nullptr;
1104 } else if constexpr (typeNew == json_type::float_t) {
1105 allocator<float_type> alloc{};
1106 alloc.destroy(floatValue);
1107 alloc.deallocate(static_cast<float_type*>(floatValue), 1);
1108 floatValue = nullptr;
1109 } else if constexpr (typeNew == json_type::uint_t) {
1110 allocator<uint_type> alloc{};
1111 alloc.destroy(uintValue);
1112 alloc.deallocate(static_cast<uint_type*>(uintValue), 1);
1113 uintValue = nullptr;
1114 } else if constexpr (typeNew == json_type::int_t) {
1115 allocator<int_type> alloc{};
1116 alloc.destroy(intValue);
1117 alloc.deallocate(static_cast<int_type*>(intValue), 1);
1118 intValue = nullptr;
1119 } else if constexpr (typeNew == json_type::bool_t) {
1120 allocator<bool_type> alloc{};
1121 alloc.destroy(boolValue);
1122 alloc.deallocate(static_cast<bool_type*>(boolValue), 1);
1123 boolValue = nullptr;
1124 }
1125 }
1126
1127 template<json_type typeNew> inline bool_type compareValues(const etf_serializer& other) const {
1128 if constexpr (typeNew == json_type::object_t) {
1129 return *objectValue == *other.objectValue;
1130 } else if constexpr (typeNew == json_type::array_t) {
1131 return *arrayValue == *other.arrayValue;
1132 } else if constexpr (typeNew == json_type::string_t) {
1133 return *stringValue == *other.stringValue;
1134 } else if constexpr (typeNew == json_type::float_t) {
1135 return *floatValue == *other.floatValue;
1136 } else if constexpr (typeNew == json_type::uint_t) {
1137 return *uintValue == *other.uintValue;
1138 } else if constexpr (typeNew == json_type::int_t) {
1139 return *intValue == *other.intValue;
1140 } else if constexpr (typeNew == json_type::bool_t) {
1141 return *boolValue == *other.boolValue;
1142 } else {
1143 return true;
1144 }
1145 }
1146
1147 inline void destroyImpl() {
1148 switch (type) {
1149 case json_type::object_t: {
1150 destroy<json_type::object_t>();
1151 break;
1152 }
1153 case json_type::array_t: {
1154 destroy<json_type::array_t>();
1155 break;
1156 }
1157 case json_type::string_t: {
1158 destroy<json_type::string_t>();
1159 break;
1160 }
1161 case json_type::float_t: {
1162 destroy<json_type::float_t>();
1163 break;
1164 }
1165 case json_type::uint_t: {
1166 destroy<json_type::uint_t>();
1167 break;
1168 }
1169 case json_type::int_t: {
1170 destroy<json_type::int_t>();
1171 break;
1172 }
1173 case json_type::bool_t: {
1174 destroy<json_type::bool_t>();
1175 break;
1176 }
1177 case json_type::null_t: {
1178 break;
1179 }
1180 default: {
1181 break;
1182 }
1183 }
1184 type = json_type::null_t;
1185 }
1186 };
1187
1188 /**@}*/
1189
1190 };
1191}// namespace discord_core_internal
Class for parsing etf data into json format.
Definition: Etf.hpp:121
void parseNilExt()
Parse etf data representing a nil value and convert to json null.
Definition: Etf.hpp:404
void parseStringExt()
Parse etf data representing a string and convert to json string.
Definition: Etf.hpp:346
jsonifier::string_view_base< uint8_t > parseEtfToJson(jsonifier::string_view_base< uint8_t > dataToParse)
Parse etf data to json format.
Definition: Etf.hpp:129
void parseIntegerExt()
Parse etf data representing an integer and convert to json number.
Definition: Etf.hpp:340
void parseListExt()
Parse etf data representing a list and convert to json array.
Definition: Etf.hpp:317
void writeCharactersFromBuffer(uint32_t length)
Write characters from the buffer to the final json string.
Definition: Etf.hpp:176
void writeCharacter()
Write a character to the final json string.
Definition: Etf.hpp:263
void writeCharacters(const char *data, uint64_t length)
Write characters to the final json string.
Definition: Etf.hpp:166
void parseBinaryExt()
Parse etf data representing a binary and convert to json string.
Definition: Etf.hpp:399
void singleValueETFToJson()
Parse a single etf value and convert to json.
Definition: Etf.hpp:271
void writeCharacter(const value_type value)
Write a character to the final json string.
Definition: Etf.hpp:254
void parseSmallAtomExt()
Parse etf data representing a small atom and convert to json string.
Definition: Etf.hpp:409
void parseSmallIntegerExt()
Parse etf data representing a small integer and convert to json number.
Definition: Etf.hpp:334
void parseAtomExt()
Parse etf data representing an atom and convert to json string.
Definition: Etf.hpp:394
return_type readBitsFromBuffer()
Read bits from the data buffer and convert to return_type.
Definition: Etf.hpp:152
void parseMapExt()
Parse etf data representing a map and convert to json object.
Definition: Etf.hpp:414
void parseNewFloatExt()
Parse etf data representing a new float and convert to json number.
Definition: Etf.hpp:359
void parseSmallBigExt()
Parse etf data representing a small big integer and convert to json number.
Definition: Etf.hpp:368
A websocket client, for communication via a tcp-connection.
Concept for array types excluding etf_serializer.
Definition: Etf.hpp:444
Concept for object (associative container) types excluding etf_serializer.
Definition: Etf.hpp:450
json_type
Enumeration for different json value types.
Definition: Etf.hpp:440
void storeBits(value_type *to, return_type num)
Stores the bits of a number into a character array.
Definition: Etf.hpp:85
return_type reverseByteOrder(return_type net)
Reverses the byte order of a value if needed, based on the endianness.
Definition: Etf.hpp:60
The main namespace for the forward-facing interfaces.
An exception class derived from std::runtime_error for dca-related exceptions.
Definition: Base.hpp:820
Exception class for etf parsing errors.
Definition: Etf.hpp:96
etf_parse_error(jsonifier::string_view message, const std::source_location &location=std::source_location::current())
Constructs an etf_parse_error instance with a message and source location.
Definition: Etf.hpp:100
Custom exception class for etf serialization errors.
Definition: Etf.hpp:430
etf_serialize_error(jsonifier::string_view message, const std::source_location &location=std::source_location::current())
Constructor for etf_serialize_error.
Definition: Etf.hpp:435