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