From 18c3dfe10386ed0e32f1853683bc61ec485dcf96 Mon Sep 17 00:00:00 2001 From: Charlie Vigue Date: Mon, 22 Jul 2024 17:36:18 +0000 Subject: [PATCH] Respect data encapsulation --- openvpn/buffer/buffer.hpp | 415 ++++++++++++++++++-------------------- 1 file changed, 193 insertions(+), 222 deletions(-) diff --git a/openvpn/buffer/buffer.hpp b/openvpn/buffer/buffer.hpp index be5a8ace..aa6a451d 100644 --- a/openvpn/buffer/buffer.hpp +++ b/openvpn/buffer/buffer.hpp @@ -77,8 +77,10 @@ #endif namespace openvpn { +// =============================================================================================== +// special-purpose exception class for Buffer classes +// =============================================================================================== -// special-purpose exception class for Buffer classes class BufferException : public std::exception { public: @@ -161,23 +163,36 @@ class BufferException : public std::exception std::string msg_; }; +// =============================================================================================== +// =============================================================================================== + template class BufferAllocatedType; template class BufferType; +// =============================================================================================== +// class ConstBufferType +// =============================================================================================== +/** + @brief Buffer with double ended access and adjustable free space at both ends. + + Data layout: + @verbatim + + data_ ->|--------------|------------------ Buffered Data --------------------|--------------| + ^-- offset_ ---^ ^ ^ + ^ ^----- size_ -----------------------------------------^ ^ + ^ ^ + ^-- capacity_ ----------------------------------------------------------------------^ + + @endverbatim +*/ + template class ConstBufferType { - private: - // allow access to other.data_ - template - friend class BufferAllocatedType; - - template - friend class BufferType; - public: typedef T value_type; typedef T *type; @@ -255,7 +270,6 @@ class ConstBufferType // clamped version of const_buffer() openvpn_io::const_buffer const_buffer_clamp() const; - openvpn_io::const_buffer const_buffer_limit(const size_t limit) const; #endif @@ -285,22 +299,52 @@ class ConstBufferType bool operator==(const ConstBufferType &other) const; bool operator!=(const ConstBufferType &other) const; - protected: + protected: // mutable iplementations are only available to derived classes void reserve(const size_t n); + + // return a mutable pointer to start of array T *data(); + + // return a mutable pointer to end of array T *data_end(); + + // return a mutable pointer to start of raw data T *data_raw(); + + // return the number of additional T objects that can be added before capacity is reached (without considering resize) size_t remaining(const size_t tailroom = 0) const; + + // like max_size, but take tailroom into account size_t max_size_tailroom(const size_t tailroom) const; + + // append a T object to array, with possible resize void push_back(const T &value); + + // append a T object to array, with possible resize void push_front(const T &value); + + // Place a T object after the last object in the + // array, with possible resize to contain it, + // however don't actually change the size of the + // array to reflect the added object. Useful + // for maintaining null-terminated strings. void set_trailer(const T &value); + void null_terminate(); + + // mutable index into array T *index(const size_t index); #ifndef OPENVPN_NO_IO + // return a openvpn_io::mutable_buffer object used by + // asio read methods, starting from data() openvpn_io::mutable_buffer mutable_buffer(const size_t tailroom = 0); + + // return a openvpn_io::mutable_buffer object used by + // asio read methods, starting from data_end() openvpn_io::mutable_buffer mutable_buffer_append(const size_t tailroom = 0); + + // clamped versions of mutable_buffer(), mutable_buffer_append(), openvpn_io::mutable_buffer mutable_buffer_clamp(const size_t tailroom = 0); openvpn_io::mutable_buffer mutable_buffer_append_clamp(const size_t tailroom = 0); #endif @@ -317,10 +361,19 @@ class ConstBufferType template void append(const B &other); - virtual void reset_impl(const size_t min_capacity, const unsigned int flags); - virtual void resize(const size_t new_capacity); + template + void swap(ConstBufferType &); + void buffer_full_error(const size_t newcap, const bool allocated) const; + protected: // Virtual Functions + // Called when reset method needs to expand the buffer size + virtual void reset_impl(const size_t min_capacity, const unsigned int flags); + + // Derived classes can implement buffer growing semantics + // by overloading this method. In the default implementation, + // buffers are non-growable, so we throw an exception. + virtual void resize(const size_t new_capacity); protected: ConstBufferType(T *data, const size_t offset, const size_t size, const size_t capacity); @@ -330,6 +383,7 @@ class ConstBufferType typename std::enable_if::value, int>::type = 0> ConstBufferType(const U *data, const size_t offset, const size_t size, const size_t capacity); + private: // Even though *data_ is declared as non-const, within ConstBufferType // we MUST always treat it as const. But derived classes may treat it // as non-const as long as they passed in non-const data to begin with. @@ -339,14 +393,14 @@ class ConstBufferType size_t capacity_; // maximum number of array objects of type T for which memory is allocated, starting at data_ }; +// =============================================================================================== +// class BufferType +// =============================================================================================== + template class BufferType : public ConstBufferType { private: - // allow access to other.data_ - template - friend class BufferAllocatedType; - template friend class ConstBufferType; @@ -385,28 +439,31 @@ class BufferType : public ConstBufferType using ConstBufferType::resize; using ConstBufferType::buffer_full_error; + BufferType(){}; + + BufferType(void *data, const size_t size, const bool filled) + : ConstBufferType(data, size, filled){}; - BufferType(); - BufferType(void *data, const size_t size, const bool filled); - BufferType(T *data, const size_t size, const bool filled); + BufferType(T *data, const size_t size, const bool filled) + : ConstBufferType(data, size, filled){}; protected: - BufferType(T *data, const size_t offset, const size_t size, const size_t capacity); + BufferType(T *data, const size_t offset, const size_t size, const size_t capacity) + : ConstBufferType(data, offset, size, capacity){}; }; +// =============================================================================================== +// class BufferAllocatedType +// =============================================================================================== + template class BufferAllocatedType : public BufferType, public RC { private: - // allow access to other.data_ + // Friend to all specializations of this template allows access to other.data_ template friend class BufferAllocatedType; - using BufferType::data_; - using BufferType::offset_; - using BufferType::size_; - using BufferType::capacity_; - public: using BufferType::init_headroom; using BufferType::buffer_full_error; @@ -417,7 +474,7 @@ class BufferAllocatedType : public BufferType, public RC using BufferType::c_data_raw; using BufferType::data; using BufferType::c_data; - using BufferType::operator[]; + using BufferType::swap; enum { @@ -442,7 +499,8 @@ class BufferAllocatedType : public BufferType, public RC template void move(BufferAllocatedType &other); RCPtr> move_to_ptr(); - void swap(BufferAllocatedType &other); + template + void swap(BufferAllocatedType &other); template BufferAllocatedType(BufferAllocatedType &&other) noexcept; BufferAllocatedType &operator=(BufferAllocatedType &&other) noexcept; @@ -451,56 +509,53 @@ class BufferAllocatedType : public BufferType, public RC void and_flags(const unsigned int flags); ~BufferAllocatedType(); - protected: - virtual void reset_impl(const size_t min_capacity, const unsigned int flags); - virtual void resize(const size_t new_capacity); + private: + BufferAllocatedType(const size_t offset, + const size_t size, + const size_t capacity, + const unsigned int flags); + virtual void reset_impl(const size_t min_capacity, const unsigned int flags) override; + virtual void resize(const size_t new_capacity) override; void realloc_(const size_t newcap); - template - void move_(BufferAllocatedType &other); - void erase_(); - void delete_(); + void free_data(); + private: unsigned int flags_; }; -// Member function definitions +// =============================================================================================== +// ConstBufferType member function definitions +// =============================================================================================== template ConstBufferType::ConstBufferType() { - static_assert(std::is_nothrow_move_constructible::value, "class ConstBufferType not noexcept move constructable"); + static_assert(std::is_nothrow_move_constructible::value, + "class ConstBufferType not noexcept move constructable"); data_ = nullptr; offset_ = size_ = capacity_ = 0; } template ConstBufferType::ConstBufferType(void *data, const size_t size, const bool filled) - : ConstBufferType((T *)data, size, filled) -{ -} + : ConstBufferType((T *)data, size, filled){}; template template ::value, int>::type> ConstBufferType::ConstBufferType(const void *data, const size_t size, const bool filled) - : ConstBufferType(const_cast(data), size, filled) -{ -} + : ConstBufferType(const_cast(data), size, filled){}; template ConstBufferType::ConstBufferType(T *data, const size_t size, const bool filled) -{ - data_ = data; - offset_ = 0; - capacity_ = size; - size_ = filled ? size : 0; -} + : data_(data), + offset_(0), + size_(filled ? size : 0), + capacity_(size){}; template template ::value, int>::type> ConstBufferType::ConstBufferType(const U *data, const size_t size, const bool filled) - : ConstBufferType(const_cast(data), size, filled) -{ -} + : ConstBufferType(const_cast(data), size, filled){}; template auto &ConstBufferType::operator[](const size_t index) const @@ -681,6 +736,7 @@ bool ConstBufferType::is_zeroed() const } #ifndef OPENVPN_NO_IO + template openvpn_io::const_buffer ConstBufferType::const_buffer() const { @@ -788,6 +844,16 @@ const T *ConstBufferType::c_index(const size_t index) const return &c_data()[index]; } +template +template +void ConstBufferType::swap(ConstBufferType &other) +{ + std::swap(other.data_, data_); + std::swap(other.offset_, offset_); + std::swap(other.size_, size_); + std::swap(other.capacity_, capacity_); +} + template bool ConstBufferType::operator==(const ConstBufferType &other) const { @@ -802,20 +868,6 @@ bool ConstBufferType::operator!=(const ConstBufferType &other) const return !(*this == other); } -template -ConstBufferType::ConstBufferType(T *data, const size_t offset, const size_t size, const size_t capacity) - : data_(data), offset_(offset), size_(size), capacity_(capacity) -{ -} - -template -template ::value, int>::type> -ConstBufferType::ConstBufferType(const U *data, const size_t offset, const size_t size, const size_t capacity) - : ConstBufferType(const_cast(data), offset, size, capacity) -{ -} - - template void ConstBufferType::reserve(const size_t n) { @@ -896,7 +948,9 @@ T *ConstBufferType::index(const size_t index) return &data()[index]; } + #ifndef OPENVPN_NO_IO + template openvpn_io::mutable_buffer ConstBufferType::mutable_buffer(const size_t tailroom) { @@ -1026,92 +1080,62 @@ void ConstBufferType::buffer_full_error(const size_t newcap, const bool alloc } template -BufferType::BufferType() -{ -} +ConstBufferType::ConstBufferType(T *data, const size_t offset, const size_t size, const size_t capacity) + : data_(data), offset_(offset), size_(size), capacity_(capacity){}; template -BufferType::BufferType(void *data, const size_t size, const bool filled) - : ConstBufferType(data, size, filled) -{ -} +template ::value, int>::type> +ConstBufferType::ConstBufferType(const U *data, const size_t offset, const size_t size, const size_t capacity) + : ConstBufferType(const_cast(data), offset, size, capacity){}; -template -BufferType::BufferType(T *data, const size_t size, const bool filled) - : ConstBufferType(data, size, filled) -{ -} +// =============================================================================================== +// BufferAllocatedType member function definitions +// =============================================================================================== -template -BufferType::BufferType(T *data, const size_t offset, const size_t size, const size_t capacity) - : ConstBufferType(data, offset, size, capacity) +template +BufferAllocatedType::BufferAllocatedType(const size_t offset, const size_t size, const size_t capacity, const unsigned int flags) + : BufferType(capacity ? new T[capacity] : nullptr, offset, size, capacity), flags_(flags) { + if (flags & CONSTRUCT_ZERO) + std::memset(data_raw(), 0, capacity * sizeof(T)); } template BufferAllocatedType::BufferAllocatedType() + : BufferAllocatedType(0, 0, 0, 0) { - static_assert(std::is_nothrow_move_constructible::value, "class BufferAllocatedType not noexcept move constructable"); - flags_ = 0; + static_assert(std::is_nothrow_move_constructible::value, + "class BufferAllocatedType not noexcept move constructable"); } template BufferAllocatedType::BufferAllocatedType(const size_t capacity, const unsigned int flags) -{ - flags_ = flags; - capacity_ = capacity; - if (capacity) - { - data_ = new T[capacity]; - if (flags & CONSTRUCT_ZERO) - std::memset(data_, 0, capacity * sizeof(T)); - if (flags & ARRAY) - size_ = capacity; - } -} + : BufferAllocatedType(0, flags & ARRAY ? capacity : 0, capacity, flags){}; template BufferAllocatedType::BufferAllocatedType(const T *data, const size_t size, const unsigned int flags) + : BufferAllocatedType(0, size, size, flags) { - flags_ = flags; - size_ = capacity_ = size; if (size) - { - data_ = new T[size]; - std::memcpy(data_, data, size * sizeof(T)); - } + std::memcpy(data_raw(), data, size * sizeof(T)); } template BufferAllocatedType::BufferAllocatedType(const BufferAllocatedType &other) + : BufferAllocatedType(other.offset(), other.size(), other.capacity(), other.flags_) { - offset_ = other.offset_; - size_ = other.size_; - capacity_ = other.capacity_; - flags_ = other.flags_; - if (capacity_) - { - data_ = new T[capacity_]; - if (size_) - std::memcpy(data_ + offset_, other.data_ + offset_, size_ * sizeof(T)); - } + if (size()) + std::memcpy(data(), other.c_data(), size() * sizeof(T)); } template template BufferAllocatedType::BufferAllocatedType(const BufferType &other, const unsigned int flags) + : BufferAllocatedType(other.offset(), other.size(), other.capacity(), flags) { static_assert(sizeof(T) == sizeof(T_), "size inconsistency"); - offset_ = other.offset_; - size_ = other.size_; - capacity_ = other.capacity_; - flags_ = flags; - if (capacity_) - { - data_ = new T[capacity_]; - if (size_) - std::memcpy(data_ + offset_, other.data_ + offset_, size_ * sizeof(T)); - } + if (size()) + std::memcpy(data(), other.c_data(), size() * sizeof(T)); } template @@ -1119,69 +1143,38 @@ void BufferAllocatedType::operator=(const BufferAllocatedType &other) { if (this != &other) { - offset_ = size_ = 0; - if (capacity_ != other.capacity_) - { - erase_(); - if (other.capacity_) - data_ = new T[other.capacity_]; - capacity_ = other.capacity_; - } - offset_ = other.offset_; - size_ = other.size_; - flags_ = other.flags_; - if (size_) - std::memcpy(data_ + offset_, other.data_ + offset_, size_ * sizeof(T)); + auto tempBuffer = BufferAllocatedType(other.offset(), other.size(), other.capacity(), other.flags_); + if (other.size()) + std::memcpy(tempBuffer.data(), other.c_data(), tempBuffer.size() * sizeof(T)); + swap(tempBuffer); } } template void BufferAllocatedType::init(const size_t capacity, const unsigned int flags) { - offset_ = size_ = 0; - flags_ = flags; - if (capacity_ != capacity) - { - erase_(); - if (capacity) - { - data_ = new T[capacity]; - } - capacity_ = capacity; - } - if ((flags & CONSTRUCT_ZERO) && capacity) - std::memset(data_, 0, capacity * sizeof(T)); - if (flags & ARRAY) - size_ = capacity; + auto tempBuffer = BufferAllocatedType(capacity, flags); + swap(tempBuffer); } template void BufferAllocatedType::init(const T *data, const size_t size, const unsigned int flags) { - offset_ = size_ = 0; - flags_ = flags; - if (size != capacity_) - { - erase_(); - if (size) - data_ = new T[size]; - capacity_ = size; - } - size_ = size; - std::memcpy(data_, data, size * sizeof(T)); + auto tempBuffer = BufferAllocatedType(data, size, flags); + swap(tempBuffer); } template void BufferAllocatedType::realloc(const size_t newcap) { - if (newcap > capacity_) + if (newcap > capacity()) realloc_(newcap); } template void BufferAllocatedType::reset(const size_t min_capacity, const unsigned int flags) { - if (min_capacity > capacity_) + if (min_capacity > capacity()) init(min_capacity, flags); } @@ -1196,49 +1189,48 @@ template template void BufferAllocatedType::move(BufferAllocatedType &other) { - if (data_) - delete_(); - move_(other); + auto temp = BufferAllocatedType(); + swap(other); + other.swap(temp); } template RCPtr> BufferAllocatedType::move_to_ptr() { - RCPtr> bp = new BufferAllocatedType(); - bp->move(*this); - return bp; + return RCPtr>(new BufferAllocatedType(std::move(*this))); } template -void BufferAllocatedType::swap(BufferAllocatedType &other) +template +void BufferAllocatedType::swap(BufferAllocatedType &other) { - std::swap(data_, other.data_); - std::swap(offset_, other.offset_); - std::swap(size_, other.size_); - std::swap(capacity_, other.capacity_); + BufferType::swap(other); std::swap(flags_, other.flags_); } template template BufferAllocatedType::BufferAllocatedType(BufferAllocatedType &&other) noexcept + : BufferAllocatedType() { - move_(other); + move(other); } template BufferAllocatedType &BufferAllocatedType::operator=(BufferAllocatedType &&other) noexcept { - move(other); + if (this != &other) + { + move(other); + } return *this; } template void BufferAllocatedType::clear() { - erase_(); - flags_ = 0; - size_ = offset_ = 0; + auto tempBuffer = BufferAllocatedType(0, 0, 0, 0); + swap(tempBuffer); } template @@ -1256,8 +1248,8 @@ void BufferAllocatedType::and_flags(const unsigned int flags) template BufferAllocatedType::~BufferAllocatedType() { - if (data_) - delete_(); + if (data_raw()) + free_data(); } template @@ -1269,8 +1261,8 @@ void BufferAllocatedType::reset_impl(const size_t min_capacity, const unsi template void BufferAllocatedType::resize(const size_t new_capacity) { - const size_t newcap = std::max(new_capacity, capacity_ * 2); - if (newcap > capacity_) + const size_t newcap = std::max(new_capacity, capacity() * 2); + if (newcap > capacity()) { if (flags_ & GROW) realloc_(newcap); @@ -1282,60 +1274,39 @@ void BufferAllocatedType::resize(const size_t new_capacity) template void BufferAllocatedType::realloc_(const size_t newcap) { - T *data = new T[newcap]; - if (size_) - std::memcpy(data + offset_, data_ + offset_, size_ * sizeof(T)); - delete_(); - data_ = data; - // std::cout << "*** RESIZE " << capacity_ << " -> " << newcap << std::endl; // fixme - capacity_ = newcap; -} - -template -template -void BufferAllocatedType::move_(BufferAllocatedType &other) -{ - data_ = other.data_; - offset_ = other.offset_; - size_ = other.size_; - capacity_ = other.capacity_; - flags_ = other.flags_; - - other.data_ = nullptr; - other.offset_ = other.size_ = other.capacity_ = 0; -} - -template -void BufferAllocatedType::erase_() -{ - if (data_) - { - delete_(); - data_ = nullptr; - } - capacity_ = 0; + auto tempBuffer = BufferAllocatedType(offset(), size(), newcap, flags_); + if (size()) + std::memcpy(tempBuffer.data(), c_data(), size() * sizeof(T)); + swap(tempBuffer); } template -void BufferAllocatedType::delete_() +void BufferAllocatedType::free_data() { - if (size_ && (flags_ & DESTRUCT_ZERO)) - std::memset(data_, 0, capacity_ * sizeof(T)); - delete[] data_; + if (size() && (flags_ & DESTRUCT_ZERO)) + std::memset(data_raw(), 0, capacity() * sizeof(T)); + delete[] data_raw(); } +// =============================================================================================== +// specializations of BufferType for unsigned char +// =============================================================================================== -// specializations of BufferType for unsigned char typedef BufferType Buffer; typedef ConstBufferType ConstBuffer; typedef BufferAllocatedType BufferAllocated; typedef RCPtr BufferPtr; -// BufferAllocated with thread-safe refcount +// =============================================================================================== +// BufferAllocated with thread-safe refcount +// =============================================================================================== + typedef BufferAllocatedType BufferAllocatedTS; typedef RCPtr BufferPtrTS; -// cast BufferType to ConstBufferType +// =============================================================================================== +// cast BufferType to ConstBufferType +// =============================================================================================== template inline ConstBufferType &const_buffer_ref(BufferType &src)