Line data Source code
1 : #pragma once
2 :
3 : #include <AH/STL/utility> // std::forward
4 : #include <AH/STL/vector> // std::vector
5 : #include <Settings/NamespaceSettings.hpp>
6 : #include <ctype.h> // isxdigit, tolower
7 :
8 : BEGIN_CS_NAMESPACE
9 :
10 : /**
11 : * @brief Class that parses hexadecimal ASCII text to bytes.
12 : *
13 : * For example, parses the string `"7f A 123456"` to the bytes
14 : * `{0x7F, 0x0A, 0x12, 0x34, 0x56}`.
15 : *
16 : * @tparam CharPuller
17 : * Class that supplies the ASCII characters to parse.
18 : *
19 : * @see @ref BufferPuller
20 : * @see @ref StreamPuller
21 : *
22 : * @ingroup MIDIParsers
23 : */
24 : template <class CharPuller>
25 : class HexPuller {
26 : public:
27 16 : HexPuller(CharPuller &&puller) : puller(std::forward<CharPuller>(puller)) {}
28 :
29 : /// Pull out a new byte. Pulls characters from the `CharPuller` until a
30 : /// hexadecimal number was found, decodes it, and returns it.
31 : ///
32 : /// @param[out] output
33 : /// A new byte (if available).
34 : /// @return True if a byte was available, false otherwise.
35 21 : bool pull(uint8_t &output) {
36 21 : uint8_t input;
37 53 : while (puller.pull(input)) {
38 : // If we receive a hexadecimal digit
39 53 : if (isxdigit(input)) {
40 40 : (char1 ? char2 : char1) = tolower(input);
41 : }
42 : // If we received two hex characters
43 53 : if (char1 && char2) {
44 19 : output = hex2int(char1) << 4 | hex2int(char2);
45 19 : char1 = '\0';
46 19 : char2 = '\0';
47 19 : return true;
48 : }
49 : // If we received one hex character followed by whitespace/other
50 34 : else if (!isxdigit(input) && char1) {
51 2 : output = hex2int(char1);
52 2 : char1 = '\0';
53 2 : return true;
54 : }
55 : }
56 0 : return false;
57 : }
58 :
59 : private:
60 : /// Convert a hexadecimal character to a 4-bit nibble.
61 40 : static uint8_t hex2int(char hex) {
62 40 : return hex < 'a' ? hex - '0' : hex - 'a' + 10;
63 : }
64 :
65 : public:
66 : CharPuller puller;
67 :
68 : private:
69 : char char1 = '\0';
70 : char char2 = '\0';
71 : };
72 :
73 : END_CS_NAMESPACE
|