Line data Source code
1 : #pragma once 2 : 3 : #include <AH/STL/cstddef> // std::nullptr, size_t 4 : #include <AH/STL/utility> // std::forward 5 : #include <stddef.h> // size_t 6 : 7 : #include <SLIPStream/SLIP.hpp> 8 : 9 : #include <boost/integer.hpp> 10 : 11 : /// @addtogroup SLIP 12 : /// @{ 13 : 14 : /** 15 : * @brief Class for parsing SLIP packets. 16 : */ 17 : class SLIPParser { 18 : public: 19 : /** 20 : * @brief Constructor. 21 : * 22 : * @param buffer 23 : * The byte buffer to store the parsed packets. 24 : */ 25 : template <size_t N> 26 : SLIPParser(uint8_t (&buffer)[N]) : SLIPParser(buffer, N) {} 27 : 28 : /** 29 : * @brief Default constructor for a parser without a buffer. 30 : */ 31 : SLIPParser() : SLIPParser(nullptr) {} 32 : 33 : /** 34 : * @brief Constructor for a parser without a buffer. 35 : */ 36 : SLIPParser(std::nullptr_t) : SLIPParser(nullptr, 0) {} 37 : 38 : /** 39 : * @brief Constructor. 40 : * 41 : * @param buffer 42 : * The byte buffer to store the parsed packets. 43 : * @param bufferSize 44 : * The size of the buffer. 45 : */ 46 14 : SLIPParser(uint8_t *buffer, size_t bufferSize) 47 28 : : buffer(buffer), bufferSize(bufferSize) { 48 14 : reset(); 49 14 : } 50 : 51 : /** 52 : * @brief Parse the given byte, and call the callback for each data byte. 53 : * 54 : * @tparam Callback 55 : * The type of callback function. Should be a callable object that 56 : * takes a data byte and the index of that byte in the packet: 57 : * `void callback(uint8_t databyte, size_t index)` 58 : * 59 : * @param c 60 : * The byte to parse. 61 : * @param callback 62 : * The callback function to call for each data byte. 63 : * @retval 0 64 : * The packet is not finished yet. 65 : * @retval >0 66 : * The packet was received in its entirety, and the return value is 67 : * the size of the packet in the buffer. 68 : * If the packet is not larger than the buffer, this will be the 69 : * same as the size of the packet. If the packet was larger than 70 : * the buffer, the return value will be the size of the buffer, and 71 : * @ref wasTruncated will return true. 72 : */ 73 : template <class Callback> 74 : size_t parse(uint8_t c, Callback callback); 75 : 76 : /** 77 : * @brief Parse the given byte without using a callback function. 78 : * 79 : * @param c 80 : * The byte to parse. 81 : * @retval 0 82 : * The packet is not finished yet. 83 : * @retval >0 84 : * The packet was received in its entirety, and the return value is 85 : * the size of the packet in the buffer. 86 : * If the packet is not larger than the buffer, this will be the 87 : * same as the size of the packet. If the packet was larger than 88 : * the buffer, the return value will be the size of the buffer, and 89 : * @ref wasTruncated will return true. 90 : */ 91 97 : size_t parse(uint8_t c) { 92 170 : auto cb = [](uint8_t, size_t) {}; 93 194 : return parse(c, cb); 94 97 : } 95 : 96 : /** 97 : * @brief Check if the previous packet was truncated. 98 : * 99 : * @retval true 100 : * The previous packet didn't fit the buffer. The size returned by 101 : * @ref SLIPParser::parse was smaller than the actual size of the 102 : * packet. 103 : * @retval false 104 : * The buffer was large enough to store the previous packet. 105 : */ 106 4 : bool wasTruncated() const { return truncated > 0; } 107 : 108 : /** 109 : * @brief Get the number of bytes that were truncated due to the previous 110 : * packet being too large for the buffer. 111 : */ 112 40 : size_t numTruncated() const { return truncated; } 113 : 114 : private: 115 44 : void reset() { write = buffer; } 116 : 117 : private: 118 : uint8_t *buffer = nullptr; 119 14 : uint8_t *write = nullptr; 120 : size_t bufferSize = 0; 121 14 : size_t truncated = 0; 122 14 : bool escape = false; 123 : }; 124 : 125 : /// @} 126 : 127 : /// @addtogroup CRC 128 : /// @{ 129 : 130 : /** 131 : * @brief Class for parsing SLIP packets with a CRC checksum to check the 132 : * integrity of the packets. 133 : * 134 : * @tparam CRC 135 : * The CRC type to use. 136 : */ 137 : template <class CRC> 138 : class SLIPParserCRC { 139 : public: 140 : SLIPParserCRC(const SLIPParser &parser) : parser(parser) {} 141 10 : SLIPParserCRC(const SLIPParser &parser, CRC &&crc) 142 10 : : parser(parser), crc(std::forward<CRC>(crc)) {} 143 : 144 : /// The integer type of the checksum. 145 : using checksum_t = typename boost::uint_t<CRC::bit_count>::least; 146 : 147 : /** 148 : * @brief Parse the given byte. 149 : * 150 : * @param c 151 : * The byte to parse. 152 : * 153 : * @retval 0 154 : * The packet is not finished yet. 155 : * @retval >0 156 : * The packet was received in its entirety, and the return value is 157 : * the size of the packet in the buffer. 158 : * If the packet is not larger than the buffer, this will be the 159 : * same as the size of the packet. If the packet was larger than 160 : * the buffer, the return value will be the size of the buffer, and 161 : * @ref wasTruncated will return true. 162 : */ 163 187 : size_t parse(uint8_t c) { 164 : // callback that resets the CRC when necessary, and that feeds the 165 : // parsed byte to the CRC 166 330 : auto cb = [this](uint8_t c, size_t index) { 167 143 : if (index == 0) 168 11 : crc.reset(); 169 143 : crc(c); 170 143 : }; 171 : // use the standard SLIPParser 172 187 : size_t size = parser.parse(c, cb); 173 : 174 : // Correct the size of the packet for the size of the checksum 175 187 : if (size <= sizeof(checksum_t)) 176 176 : return 0; 177 11 : if (parser.numTruncated() < sizeof(checksum_t)) 178 8 : return size + parser.numTruncated() - sizeof(checksum_t); 179 3 : return size; 180 187 : } 181 : 182 : /** 183 : * @brief Check if the previous packet was truncated. 184 : * 185 : * @retval true 186 : * The previous packet didn't fit the buffer. The size returned by 187 : * @ref SLIPParser::parse was smaller than the actual size of the 188 : * packet. 189 : * @retval false 190 : * The buffer was large enough to store the previous packet. 191 : */ 192 10 : bool wasTruncated() const { return numTruncated() > 0; } 193 : 194 : /** 195 : * @brief Get the number of bytes that were truncated due to the previous 196 : * packet being too large for the buffer. 197 : */ 198 15 : size_t numTruncated() const { 199 15 : return parser.numTruncated() < sizeof(checksum_t) 200 : ? 0 201 6 : : parser.numTruncated() - sizeof(checksum_t); 202 : } 203 : /** 204 : * @brief Get the checksum of the previous packet. A checksum of zero 205 : * indicates that the packet was received correctly. 206 : */ 207 11 : checksum_t checksum() const { return crc.checksum(); } 208 : 209 : private: 210 : SLIPParser parser; 211 : CRC crc; 212 : }; 213 : 214 : /// @} 215 : 216 : #include "SLIPParser.ipp"