From f5d8dd201b2650e510169f8996c77196aa09aa51 Mon Sep 17 00:00:00 2001 From: Fred Nicolson Date: Wed, 22 Aug 2018 16:18:11 +0100 Subject: [PATCH] Add additional fr::Packet utility functions. Fixed some build warnings. Fixed some sign comparison warnings. Added utility functions to fr::Packet's for: Getting read cursor position. Relative read cursor seeking. Asserting that data remains in the packet Getting the amount of data remaining Getting the size of the packet --- include/frnetlib/Packet.h | 114 +++++++++++++++++++++++++++---------- include/frnetlib/version.h | 6 +- src/Base64.cpp | 2 +- src/HttpResponse.cpp | 2 +- tests/PacketTest.cpp | 89 ++++++++++++++++++++++++++++- 5 files changed, 176 insertions(+), 37 deletions(-) diff --git a/include/frnetlib/Packet.h b/include/frnetlib/Packet.h index 8ca6601..d07285f 100644 --- a/include/frnetlib/Packet.h +++ b/include/frnetlib/Packet.h @@ -99,7 +99,7 @@ namespace fr */ inline void extract_raw(char *data, size_t datasz) { - assert_data_remaining(datasz); + assert_bytes_remaining(datasz); memcpy(data, &buffer[buffer_read_index], datasz); buffer_read_index += datasz; @@ -261,7 +261,7 @@ namespace fr */ inline Packet &operator>>(bool &var) { - assert_data_remaining(sizeof(var)); + assert_bytes_remaining(sizeof(var)); memcpy(&var, &buffer[buffer_read_index], sizeof(var)); buffer_read_index += sizeof(var); @@ -282,7 +282,7 @@ namespace fr */ inline Packet &operator>>(uint8_t &var) { - assert_data_remaining(sizeof(var)); + assert_bytes_remaining(sizeof(var)); memcpy(&var, &buffer[buffer_read_index], sizeof(var)); buffer_read_index += sizeof(var); @@ -304,7 +304,7 @@ namespace fr */ inline Packet &operator>>(uint16_t &var) { - assert_data_remaining(sizeof(var)); + assert_bytes_remaining(sizeof(var)); memcpy(&var, &buffer[buffer_read_index], sizeof(var)); buffer_read_index += sizeof(var); @@ -327,7 +327,7 @@ namespace fr */ inline Packet &operator>>(uint32_t &var) { - assert_data_remaining(sizeof(var)); + assert_bytes_remaining(sizeof(var)); memcpy(&var, &buffer[buffer_read_index], sizeof(var)); buffer_read_index += sizeof(var); var = ntohl(var); @@ -349,7 +349,7 @@ namespace fr */ inline Packet &operator>>(uint64_t &var) { - assert_data_remaining(sizeof(var)); + assert_bytes_remaining(sizeof(var)); memcpy(&var, &buffer[buffer_read_index], sizeof(var)); buffer_read_index += sizeof(var); @@ -372,7 +372,7 @@ namespace fr */ inline Packet &operator>>(int16_t &var) { - assert_data_remaining(sizeof(var)); + assert_bytes_remaining(sizeof(var)); memcpy(&var, &buffer[buffer_read_index], sizeof(var)); buffer_read_index += sizeof(var); @@ -395,7 +395,7 @@ namespace fr */ inline Packet &operator>>(int32_t &var) { - assert_data_remaining(sizeof(var)); + assert_bytes_remaining(sizeof(var)); memcpy(&var, &buffer[buffer_read_index], sizeof(var)); buffer_read_index += sizeof(var); @@ -418,7 +418,7 @@ namespace fr */ inline Packet &operator>>(int64_t &var) { - assert_data_remaining(sizeof(var)); + assert_bytes_remaining(sizeof(var)); memcpy(&var, &buffer[buffer_read_index], sizeof(var)); buffer_read_index += sizeof(var); @@ -474,7 +474,7 @@ namespace fr */ inline Packet &operator>>(float &var) { - assert_data_remaining(sizeof(var)); + assert_bytes_remaining(sizeof(var)); memcpy(&var, &buffer[buffer_read_index], sizeof(var)); buffer_read_index += sizeof(var); @@ -497,7 +497,7 @@ namespace fr */ inline Packet &operator>>(double &var) { - assert_data_remaining(sizeof(var)); + assert_bytes_remaining(sizeof(var)); memcpy(&var, &buffer[buffer_read_index], sizeof(var)); buffer_read_index += sizeof(var);; @@ -585,19 +585,47 @@ namespace fr { //Leave enough for the header buffer.clear(); - for(size_t a = 0; a < PACKET_HEADER_LENGTH; ++a) - buffer.push_back('0'); + buffer.append(PACKET_HEADER_LENGTH, '\0'); buffer_read_index = PACKET_HEADER_LENGTH; } /*! - * Resets the read cursor back to 0, or a specified position. + * Sets the read cursor back to 0, or a specific position. * + * @note THIS IS AN *ABSOLUTE* SEEK * @param pos The buffer index to continue reading from. */ - inline void reset_read_cursor(size_t pos = 0) + inline void set_cursor(size_t pos = 0) { buffer_read_index = PACKET_HEADER_LENGTH + pos; + if(buffer_read_index > buffer.size()) buffer_read_index = buffer.size(); + } + + /*! + * Gets the current read cursor position within the packet + * + * @return Current read position + */ + inline size_t get_cursor() const + { + return buffer_read_index - PACKET_HEADER_LENGTH; + } + + /*! + * Relative seek of the read cursor, to seek to a specific position within the packet. + * If the seek is out of bounds, then the offset will be set to the nearest valid value. + * So seeking to -10, would seek to 0. Seeking to 110/100 would seek to 100. + * + * @param pos The number of bytes to seek in either direction + */ + inline void seek_cursor(ssize_t pos) + { + ssize_t new_offset = buffer_read_index + pos; + if(new_offset < (ssize_t)PACKET_HEADER_LENGTH) + new_offset = PACKET_HEADER_LENGTH; + if(new_offset > (ssize_t)buffer.size()) + new_offset = buffer.size(); + buffer_read_index = static_cast(new_offset); } /*! @@ -612,6 +640,45 @@ namespace fr buffer.reserve(PACKET_HEADER_LENGTH + bytes); } + /*! + * Checks that there's enough data in the buffer to extract + * a given number of bytes to prevent buffer overflows. + * + * @throws an std::out_of_range exception if there is not enough space. + * @param required_space The number of bytes needed + */ + inline void assert_bytes_remaining(size_t required_space) const + { + if(buffer_read_index + required_space > buffer.size()) + { + throw std::out_of_range("Not enough bytes remaining in packet to extract requested data"); + } + } + + /*! + * Gets the number of bytes available until the end of the packet + * + * @return The number of bytes available until the end of the packet + */ + inline size_t get_bytes_remaining() const + { + return buffer.size() - buffer_read_index; + } + + /*! + * Gets the size of the packet in bytes, this does not take into + * account the cursor position, use get_bytes_remaining() if you want to + * see how many more bytes can be extracted. + * + * @note This does not include the packet header length + * + * @return The size in bytes of the packet + */ + inline size_t size() const + { + return buffer.size() <= PACKET_HEADER_LENGTH ? 0 : buffer.size() - PACKET_HEADER_LENGTH; + } + private: friend class Socket; @@ -623,7 +690,7 @@ namespace fr * @param socket The socket to send through * @return Status indicating if the send succeeded or not. */ - virtual Socket::Status send(Socket *socket) const override + Socket::Status send(Socket *socket) const override { uint32_t length = htonl((uint32_t)buffer.size() - PACKET_HEADER_LENGTH); memcpy(&buffer[0], &length, sizeof(uint32_t)); @@ -638,7 +705,7 @@ namespace fr * @param socket The socket to send through * @return Status indicating if the send succeeded or not. */ - virtual Socket::Status receive(Socket *socket) override + Socket::Status receive(Socket *socket) override { Socket::Status status; @@ -663,19 +730,6 @@ namespace fr return Socket::Status::Success; } - /*! - * Checks that there's enough data in the buffer to extract - * a given number of bytes to prevent buffer overflows. - * Throws an exception if there is not enough space. - * - * @param required_space The number of bytes needed - */ - inline void assert_data_remaining(size_t required_space) - { - if(buffer_read_index + required_space > buffer.size()) - throw std::out_of_range("Not enough bytes remaining in packet to extract requested"); - } - mutable std::string buffer; //Packet data buffer size_t buffer_read_index; //Current read position diff --git a/include/frnetlib/version.h b/include/frnetlib/version.h index 16de973..2dcaa9f 100644 --- a/include/frnetlib/version.h +++ b/include/frnetlib/version.h @@ -9,10 +9,10 @@ #define FRNETLIB_VERSION_MAJOR 1 #define FRNETLIB_VERSION_MINOR 1 -#define FRNETLIB_VERSION_PATCH 0 +#define FRNETLIB_VERSION_PATCH 1 #define FRNETLIB_VERSION_NUMBER (FRNETLIB_VERSION_MAJOR * 100*100 + FRNETLIB_VERSION_MINOR * 100 + FRNETLIB_VERSION_PATCH) -#define FRNETLIB_VERSION_STRING "1.1.0" -#define FRNETLIB_VERSION_STRING_FULL "frnetlib 1.0.0" +#define FRNETLIB_VERSION_STRING "1.1.1" +#define FRNETLIB_VERSION_STRING_FULL "frnetlib 1.1.1" #endif //FRNETLIB_VERSION_H diff --git a/src/Base64.cpp b/src/Base64.cpp index 12736ad..6ff83ae 100644 --- a/src/Base64.cpp +++ b/src/Base64.cpp @@ -12,7 +12,7 @@ namespace fr std::string out; out.reserve(input.size() + (input.size() / 3) + 1); - int a; + size_t a; //Do as many sets of 3 bytes as we can for(a = 0; a < input.size() - 2; a += 3) diff --git a/src/HttpResponse.cpp b/src/HttpResponse.cpp index 70d3af2..35e5f04 100644 --- a/src/HttpResponse.cpp +++ b/src/HttpResponse.cpp @@ -29,7 +29,7 @@ namespace fr header_ended = header_end != std::string::npos; //Ensure that the header doesn't exceed max length - if(!header_ended && body.size() > MAX_HTTP_HEADER_SIZE || header_ended && header_end > MAX_HTTP_HEADER_SIZE) + if((!header_ended && body.size() > MAX_HTTP_HEADER_SIZE) || (header_ended && header_end > MAX_HTTP_HEADER_SIZE)) { return fr::Socket::HttpHeaderTooBig; } diff --git a/tests/PacketTest.cpp b/tests/PacketTest.cpp index 10b70db..848f6d7 100644 --- a/tests/PacketTest.cpp +++ b/tests/PacketTest.cpp @@ -147,12 +147,12 @@ TEST(PacketTest, read_cursor) ASSERT_EQ(a1, a2); ASSERT_EQ(b1, b2); - packet.reset_read_cursor(); + packet.set_cursor(); packet >> a2 >> b2; ASSERT_EQ(a1, a2); ASSERT_EQ(b1, b2); - packet.reset_read_cursor((sizeof(a1))); + packet.set_cursor((sizeof(a1))); packet >> b2; ASSERT_EQ(b1, b2); } @@ -169,4 +169,89 @@ TEST(PacketTest, clear) packet << a; packet >> b; ASSERT_EQ(b, 20); +} + +TEST(PacketTest, test_assert_bytes_remaining) +{ + fr::Packet packet; + ASSERT_THROW(packet.assert_bytes_remaining(1), std::out_of_range); + packet << (uint32_t)1; + ASSERT_NO_THROW(packet.assert_bytes_remaining(4)); + uint32_t out; + packet >> out; + ASSERT_THROW(packet.assert_bytes_remaining(1), std::out_of_range); +} + +TEST(PacketTest, test_size) +{ + uint32_t val = 30; + fr::Packet packet; + ASSERT_EQ(packet.size(), 0); + packet << val; + ASSERT_EQ(packet.size(), 4); + packet >> val; + ASSERT_EQ(packet.size(), 4); +} + +TEST(PacketTest, test_get_bytes_remaining) +{ + uint32_t val = 30; + fr::Packet packet; + ASSERT_EQ(packet.get_bytes_remaining(), 0); + packet << val; + ASSERT_EQ(packet.get_bytes_remaining(), 4); + + uint16_t out; + packet >> out; + ASSERT_EQ(packet.get_bytes_remaining(), 2); + + packet.clear(); + ASSERT_EQ(packet.get_bytes_remaining(), 0); +} + +TEST(PacketTest, test_get_cursor) +{ + uint32_t val = 0; + fr::Packet packet; + ASSERT_EQ(packet.get_cursor(), 0); + packet << val << val; + ASSERT_EQ(packet.get_cursor(), 0); + packet >> val; + ASSERT_EQ(packet.get_cursor(), 4); + packet >> val; + ASSERT_EQ(packet.get_cursor(), 8); + packet.set_cursor(4); + ASSERT_EQ(packet.get_cursor(), 4); + packet.clear(); + ASSERT_EQ(packet.get_cursor(), 0); +} + +TEST(PacketTest, test_set_cursor) +{ + uint32_t val = 0; + fr::Packet packet; + packet.set_cursor(5); + ASSERT_EQ(packet.get_cursor(), 0); + packet << val; + packet.set_cursor(3); + ASSERT_EQ(packet.get_cursor(), 3); +} + +TEST(PacketTest, test_seek_cursor) +{ + uint32_t val = 0; + fr::Packet packet; + packet.seek_cursor(-20); + ASSERT_EQ(packet.get_cursor(), 0); + packet.seek_cursor(20); + ASSERT_EQ(packet.get_cursor(), 0); + packet << val; + packet.seek_cursor(3); + ASSERT_EQ(packet.get_cursor(), 3); + packet.seek_cursor(-1); + ASSERT_EQ(packet.get_cursor(), 2); + packet.seek_cursor(-10); + ASSERT_EQ(packet.get_cursor(), 0); + packet.seek_cursor(10); + ASSERT_EQ(packet.get_cursor(), 4); } \ No newline at end of file