From 099ea5a98a391f4e31e55c85cf85b0d4b355e35f Mon Sep 17 00:00:00 2001 From: Kevin Matz Date: Sun, 2 Apr 2023 13:13:26 -0400 Subject: [PATCH] split the buffer stream out of ACN::PDU --- CMakeLists.txt | 5 +- lib/CMakeLists.txt | 1 + lib/bufferstream/CMakeLists.txt | 14 ++ lib/bufferstream/bufferstream.cpp | 353 ++++++++++++++++++++++++++++++ lib/bufferstream/bufferstream.h | 133 +++++++++++ 5 files changed, 504 insertions(+), 2 deletions(-) create mode 100644 lib/CMakeLists.txt create mode 100644 lib/bufferstream/CMakeLists.txt create mode 100644 lib/bufferstream/bufferstream.cpp create mode 100644 lib/bufferstream/bufferstream.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d6c266e..bc50bb5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,9 +8,10 @@ if (CMAKE_BUILD_TYPE STREQUAL "Debug") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_GLIBCXX_DEBUG") endif() -add_subdirectory(protocol) -add_subdirectory(platform) add_subdirectory(example) +add_subdirectory(lib) +add_subdirectory(platform) +add_subdirectory(protocol) add_subdirectory(test) #if (CMAKE_BUILD_TYPE MATCHES "^[Rr]elease") diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt new file mode 100644 index 0000000..d089682 --- /dev/null +++ b/lib/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(bufferstream) diff --git a/lib/bufferstream/CMakeLists.txt b/lib/bufferstream/CMakeLists.txt new file mode 100644 index 0000000..ae40dba --- /dev/null +++ b/lib/bufferstream/CMakeLists.txt @@ -0,0 +1,14 @@ +project(bufferstream VERSION 0.1.0 LANGUAGES CXX) + +add_library(${PROJECT_NAME} SHARED) +add_library(LCP::BufferStream ALIAS ${PROJECT_NAME}) + +target_sources(${PROJECT_NAME} + PUBLIC + bufferstream.h + PRIVATE + bufferstream.cpp + ) + +set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${PROJECT_VERSION}) +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/lib/bufferstream/bufferstream.cpp b/lib/bufferstream/bufferstream.cpp new file mode 100644 index 0000000..9c79ec4 --- /dev/null +++ b/lib/bufferstream/bufferstream.cpp @@ -0,0 +1,353 @@ +/* + bufferstream.cpp + + Copyright (c) 2023 Kevin Matz (kevin.matz@gmail.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#include "bufferstream.h" + +/** + * @brief bufferstream::bufferstream + * @param p + * @param l + */ +bufferstream::bufferstream(uint8_t * p, std::streamsize l) + : std::basic_streambuf() + , std::basic_iostream(this) +{ + setg(p, p, p + l); + setp(p, p + l); +} + + +/** + * @brief bufferstream::available + * @return + */ +uint32_t bufferstream::available() +{ + return in_avail(); +} + + +/** + * @brief bufferstream::data + * @return + */ +uint8_t * bufferstream::data() +{ + return gptr(); +} + + +/** + * @brief bufferstream::size + * @return + */ +uint32_t bufferstream::size() +{ + return pptr() - pbase(); +} + + +/** + * @brief bufferstream::base + * @return + */ +uint8_t * bufferstream::base() +{ + return pbase(); +} + + +/** + * @brief bufferstream::operator >> + * @param val + * @return + */ +bufferstream& bufferstream::operator>> (uint8_t& val) +{ + val = readType(); + return *this; +}; + + +/** + * @brief bufferstream::operator >> + * @param val + * @return + */ +bufferstream& bufferstream::operator>> (uint16_t& val) +{ + val = readType(); + return *this; +}; + + +/** + * @brief bufferstream::operator >> + * @param val + * @return + */ +bufferstream& bufferstream::operator>> (uint32_t& val) +{ + val = readType(); + return *this; +}; + + +/** + * @brief bufferstream::operator >> + * @param val + * @return + */ +bufferstream& bufferstream::operator>> (uint64_t& val) +{ + val = readType(); + return *this; +}; + + +/** + * @brief bufferstream::operator >> + * @param val + * @return + */ +bufferstream& bufferstream::operator>> (int8_t& val) +{ + val = readType(); + return *this; +} + + +/** + * @brief bufferstream::operator >> + * @param val + * @return + */ +bufferstream& bufferstream::operator>> (int16_t& val) +{ + val = readType(); + return *this; + +} + + +/** + * @brief bufferstream::operator >> + * @param val + * @return + */ +bufferstream& bufferstream::operator>> (int32_t& val) +{ + val = readType(); + return *this; +} + + +/** + * @brief bufferstream::operator >> + * @param val + * @return + */ +bufferstream& bufferstream::operator>> (int64_t& val) +{ + val = readType(); + return *this; +} + + +/** + * @brief bufferstream::operator << + * @param val + * @return + */ +bufferstream& bufferstream::operator<< (const int8_t& val) +{ + put(val); + return *this; +} + + +/** + * @brief bufferstream::operator << + * @param val + * @return + */ +bufferstream& bufferstream::operator<< (const int16_t& val) +{ + writeType(val); + return *this; +} + + +/** + * @brief bufferstream::operator << + * @param val + * @return + */ +bufferstream& bufferstream::operator<< (const int32_t& val) +{ + writeType(val); + return *this; +} + + +/** + * @brief bufferstream::operator << + * @param val + * @return + */ +bufferstream& bufferstream::operator<< (const int64_t& val) +{ + writeType(val); + return *this; +} + + +/** + * @brief bufferstream::operator << + * @param val + * @return + */ +bufferstream& bufferstream::operator<< (const uint8_t& val) +{ + writeType(val); + return *this; +} + + +/** + * @brief bufferstream::operator << + * @param val + * @return + */ +bufferstream& bufferstream::operator<< (const uint16_t& val) +{ + writeType(val); + return *this; +} + + +/** + * @brief bufferstream::operator << + * @param val + * @return + */ +bufferstream& bufferstream::operator<< (const uint32_t& val) +{ + writeType(val); + return *this; +} + + +/** + * @brief bufferstream::operator << + * @param val + * @return + */ +bufferstream& bufferstream::operator<< (const uint64_t& val) +{ + writeType(val); + return *this; +} + + +/** + * @brief bufferstream::operator >> + * @param obj + * @return + */ +bufferstream& bufferstream::operator>> (streamable& obj) +{ + obj.iStream(shared_from_this()); + return *this; +} + + +/** + * @brief bufferstream::operator << + * @param obj + * @return + */ +bufferstream& bufferstream::operator<< (const streamable& obj) +{ + obj.oStream(shared_from_this()); + return *this; +} + + +/** + * @brief bufferstream::readString + * @param str std::string to which the read string will be appended. + * @param fixed_length this many bytes will be read from the stream. If 0, all + * available bytes on the stream will be used to construct the appended string. + */ +void bufferstream::readString(std::string &str, const int fixed_length) +{ + // if fixed_length == 0, use all available data on the stream + if (!fixed_length) + { + str += std::string(reinterpret_cast(data()), available()); + return setstate(std::ios_base::eofbit); + } + + // otherwise, construct a buffer and read into that. + uint8_t buffer[fixed_length]; + read(buffer, fixed_length); + if (gcount() != fixed_length) + return setstate(std::ios_base::failbit); + str += std::string(reinterpret_cast(buffer)); +} + + +/** + * @brief bufferstream::writeString + * @param str + * @param fixed_length write this length to the stream, padding with null + * if str is shorter than fixed_length. If 0, will write the unrestricted + * contents of str. + * @param terminated If true, the last byte of fixed_length is guaranteed to + * be a 0 (null) byte. + */ +void bufferstream::writeString(const std::string &str, const size_t fixed_length, + const bool terminated) +{ + // fixed_length == 0 means dynamic length. + bool fixed = fixed_length > 0 ? true : false; + + // fixed length strings restricted to fixed_lengh characters + size_t maxlen = fixed ? fixed_length : str.size(); + + // write no more of the string than is available + size_t wrtlen = str.size() < maxlen ? str.size() : maxlen; + + // terminated fixed-length strings get a guaranteed padding byte + if (fixed && terminated && wrtlen == maxlen) + --wrtlen; + + // output the correct ammount of data from the string + write(reinterpret_cast(str.data()), wrtlen); + + // output any required padding bytes + for (size_t i = wrtlen; i < maxlen; i++) + put(0); +} diff --git a/lib/bufferstream/bufferstream.h b/lib/bufferstream/bufferstream.h new file mode 100644 index 0000000..3cc45e8 --- /dev/null +++ b/lib/bufferstream/bufferstream.h @@ -0,0 +1,133 @@ +/* + bufferstream.h + + Copyright (c) 2023 Kevin Matz (kevin.matz@gmail.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +#pragma once + +#include +#include +#include + +struct streamable; + +/** + * @brief Input/Output stream for unsigned 8-bit buffers + */ +class bufferstream + : public std::enable_shared_from_this + , private std::basic_streambuf + , public std::basic_iostream +{ +public: + bufferstream(uint8_t * p, std::streamsize l); + + // input sequence + uint32_t available(); + uint8_t * data(); + + // output sequence + uint32_t size(); + uint8_t * base(); + + // unsigned integer + bufferstream& operator>> (uint8_t& val); + bufferstream& operator>> (uint16_t& val); + bufferstream& operator>> (uint32_t& val); + bufferstream& operator>> (uint64_t& val); + bufferstream& operator<< (const uint8_t& val); + bufferstream& operator<< (const uint16_t& val); + bufferstream& operator<< (const uint32_t& val); + bufferstream& operator<< (const uint64_t& val); + + // signed integer + bufferstream& operator>> (int8_t& val); + bufferstream& operator>> (int16_t& val); + bufferstream& operator>> (int32_t& val); + bufferstream& operator>> (int64_t& val); + bufferstream& operator<< (const int8_t& val); + bufferstream& operator<< (const int16_t& val); + bufferstream& operator<< (const int32_t& val); + bufferstream& operator<< (const int64_t& val); + + // stream objects + bufferstream& operator>> (streamable& obj); + bufferstream& operator<< (const streamable& obj); + + // strings + void readString(std::string& str, const int fixed_length = 0); + void writeString(const std::string& str, const size_t fixed_length = 0, + const bool terminated = true); + + // reinterpreted i/o + /** + * @brief readType + * @return T + */ + template + T readType() + { + if (in_avail() < sizeof(T)) + setstate(std::ios_base::failbit); + if (!good()) + return 0; + T ret = 0; + auto data = reinterpret_cast(&ret); + for (int i = sizeof(T); --i >= 0; ) + data[i] = get(); + if (!in_avail()) + setstate(std::ios_base::eofbit); + return ret; + } + /** + * @brief writeType + * @param val + */ + template + void writeType (const T& val) + { + auto data = reinterpret_cast(&val); + for (int i = sizeof(T); --i >= 0; ) + put(data[i]); + } +}; + + +/** + * @brief The inheritable base of data objects that can be read from and written to a bufferstream. + */ +struct streamable +{ + virtual ~streamable() {}; + /** + * @brief streamSize + * @return length (count of octets) on the wire + */ + virtual size_t streamSize() const = 0; + /** + * @brief fill structure data from input stream + */ + virtual void iStream(std::shared_ptr) = 0; + /** + * @brief write structure data to output stream + */ + virtual void oStream(std::shared_ptr) const = 0; +};