From cb0cb4bb09ae3662eb86c5efc33033305990deb2 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Fri, 23 Dec 2016 12:03:56 +0200 Subject: [PATCH] Add maxscale::Buffer maxscale::Buffer is a simple C++ wrapper around GWBUF that allows the latter to be manipulated like an STL compatible collection. maxscale::Buffer provides both a const and non-const forward iterator, using which a non-contiguous GWBUF can be traversed and manipulated. The iterators can be used with any standard C++ algorithm that accepts forward iterators. --- include/maxscale/buffer.hh | 680 +++++++++++++++++++++++++++++++++++++ 1 file changed, 680 insertions(+) create mode 100644 include/maxscale/buffer.hh diff --git a/include/maxscale/buffer.hh b/include/maxscale/buffer.hh new file mode 100644 index 000000000..51f25c2dd --- /dev/null +++ b/include/maxscale/buffer.hh @@ -0,0 +1,680 @@ +#pragma once +/* + * Copyright (c) 2016 MariaDB Corporation Ab + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file and at www.mariadb.com/bsl. + * + * Change Date: 2019-07-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2 or later of the General + * Public License. + */ + +#include +#include +#include +#include +#include +#include + +namespace maxscale +{ + +/** + * @class Buffer + * + * @c Buffer is a simple wrapper around @ GWBUF that is more convenient to + * use in a C++ context. + * + * As @c Buffer is a handle class, it should be created on the stack or as a + * member of an enclosing class, *never* dynamically. + * + * @c Buffer exposed _forward_ iterators to the underlying buffer that can + * be used in conjunction with standard C++ algorithms and functions. + */ +class Buffer +{ +public: + // buf_type : The type of the buffer, either "GWBUF*" or "const GWBUF*" + // pointer_type : The type of a pointer to an element, either "uint8_t*" or "const uint8_t*". + // reference_type: The type of a reference to an element, either "uint8_t&" or "const uint8_t&". + template + class iterator_base : public std::iterator // The reference type of an elem + { + protected: + iterator_base(buf_type pBuffer = NULL) + : m_pBuffer(pBuffer) + , m_i(m_pBuffer ? GWBUF_DATA(m_pBuffer) : NULL) + , m_end(m_pBuffer ? (m_i + GWBUF_LENGTH(m_pBuffer)) : NULL) + {} + + void advance() + { + ss_dassert(m_i != m_end); + + ++m_i; + + if (m_i == m_end) + { + m_pBuffer = m_pBuffer->next; + + if (m_pBuffer) + { + m_i = GWBUF_DATA(m_pBuffer); + m_end = m_i + GWBUF_LENGTH(m_pBuffer); + } + else + { + m_i = NULL; + m_end = NULL; + } + } + } + + bool eq(const iterator_base& rhs) const + { + return m_i == rhs.m_i; + } + + bool neq(const iterator_base& rhs) const + { + return !eq(rhs); + } + + protected: + buf_type m_pBuffer; + pointer_type m_i; + pointer_type m_end; + }; + + class const_iterator; + + /** + * A forward iterator to Buffer. + */ + class iterator : public iterator_base // Type of reference to element + { + friend class const_iterator; + + public: + explicit iterator(GWBUF* pBuffer = NULL) + : iterator_base(pBuffer) + {} + + iterator& operator++() + { + advance(); + return *this; + } + + iterator operator++(int) + { + iterator rv(*this); + ++(*this); + return rv; + } + + bool operator == (const iterator& rhs) const + { + return eq(rhs); + } + + bool operator != (const iterator& rhs) const + { + return neq(rhs); + } + + reference operator*() + { + ss_dassert(m_i); + return *m_i; + } + }; + + /** + * A const forward iterator to Buffer. + */ + class const_iterator : public iterator_base // Type of referece to element + { + public: + explicit const_iterator(const GWBUF* pBuffer = NULL) + : iterator_base(pBuffer) + {} + + const_iterator(const Buffer::iterator& rhs) + : iterator_base(rhs.m_pBuffer) + {} + + const_iterator& operator++() + { + advance(); + return *this; + } + + const_iterator operator++(int) + { + const_iterator rv(*this); + ++(*this); + return rv; + } + + bool operator == (const const_iterator& rhs) const + { + return eq(rhs); + } + + bool operator != (const const_iterator& rhs) const + { + return neq(rhs); + } + + reference operator*() const + { + ss_dassert(m_i); + return *m_i; + } + }; + + /** + * Creates an empty buffer. + */ + Buffer() + : m_pBuffer(NULL) + {} + + /** + * Copy constructor. + * + * @param rhs The @c Buffer to copy. + * + * @throws @c std::bad_alloc if the underlying @c GWBUF cannot be cloned. + * + */ + Buffer(const Buffer& rhs) + { + if (rhs.m_pBuffer) + { + m_pBuffer = gwbuf_clone(rhs.m_pBuffer); + + if (!m_pBuffer) + { + ss_dassert(!true); + throw std::bad_alloc(); + } + } + else + { + m_pBuffer = NULL; + } + } + +#if __cplusplus >= 201103 + /** + * Move constructor. + * + * @param rhs The @c Buffer to be moved. + */ + Buffer(Buffer&& rhs) + : m_pBuffer(NULL) + { + swap(rhs); + } +#endif + + /** + * Creates a @Buffer from a @ GWBUF + * + * @param pBuffer The buffer to create the @c Buffer from. + * + * @attention The ownership of @c pBuffer is transferred to the + * @c Buffer being created. + */ + Buffer(GWBUF* pBuffer) + : m_pBuffer(pBuffer) + {} + + /** + * Creates a buffer of specified size. + * + * @param size The size of the buffer. + * + * @attention @c std::bad_alloc is thrown if the allocation fails. + */ + Buffer(size_t size) + : m_pBuffer(gwbuf_alloc(size)) + { + if (!m_pBuffer) + { + throw std::bad_alloc(); + } + } + + /** + * Creates a buffer from existing data. + * + * @param pData Pointer to data. + * @param size The size of the data. + * + * @attention @c std::bad_alloc is thrown if the allocation fails. + */ + Buffer(const void* pData, size_t size) + : m_pBuffer(gwbuf_alloc_and_load(size, pData)) + { + if (!m_pBuffer) + { + throw std::bad_alloc(); + } + } + + /** + * Creates a buffer from a std::vector. + * + * @param data The data to be copied. + * + * @attention @c std::bad_alloc is thrown if the allocation fails. + */ + Buffer(const std::vector& data) + : m_pBuffer(gwbuf_alloc(data.size())) + { + if (m_pBuffer) + { + std::copy(data.begin(), data.end(), GWBUF_DATA(m_pBuffer)); + } + else + { + throw std::bad_alloc(); + } + } + + /** + * Destructor + */ + ~Buffer() + { + reset(); + } + + /** + * Assignment operator + * + * @param rhs The @c Buffer to be assigned to this. + * + * @return this + * + * @attention The @c Buffer provided as argument will be copied, which + * may cause @c std::bad_alloc to be thrown. + * + * @attention Does not invalidates iterators, but after the call, the iterators + * will refer to the data of the other @c Buffer. + * + * @see Buffer::copy_from + */ + Buffer& operator = (Buffer rhs) + { + swap(rhs); + return *this; + } + +#if __cplusplus >= 201103 + /** + * Move assignment operator + * + * @param rhs The @c Buffer to be moves. + */ + Buffer& operator = (Buffer&& rhs) + { + reset(); + swap(rhs); + return *this; + } +#endif + + /** + * Returns a forward iterator to the beginning of the Buffer. + * + * @return A forward iterator. + */ + iterator begin() + { + return iterator(m_pBuffer); + } + + /** + * Returns a forward iterator to the end of the Buffer. + * + * @return A forward iterator. + */ + iterator end() + { + return iterator(); + } + + /** + * Returns a const forward iterator to the beginning of the Buffer. + * + * @return A const forward iterator. + */ + const_iterator begin() const + { + return const_iterator(m_pBuffer); + } + + /** + * Returns a const forward iterator to the end of the Buffer. + * + * @return A const forward iterator. + */ + const_iterator end() const + { + return const_iterator(); + } + + /** + * Swap the contents with another @c Buffer + * + * @param buffer The @c Buffer to swap contents with. + */ + void swap(Buffer& buffer) + { + GWBUF* pBuffer = buffer.m_pBuffer; + buffer.m_pBuffer = m_pBuffer; + m_pBuffer = pBuffer; + } + + /** + * Clones the underlying @c GWBUF of the provided @c Buffer, and frees + * the current buffer. Effectively an assignment operator that does + * not throw. + * + * @param rhs The @c Buffer to be copied. + * + * @return True, if the buffer could be copied. + * + * @attention Invalidates all iterators. + * + * @see Buffer::operator = + */ + bool copy_from(const Buffer& rhs) + { + bool copied = true; + GWBUF* pBuffer = rhs.m_pBuffer; + + if (pBuffer) + { + GWBUF* pBuffer = gwbuf_clone(pBuffer); + + if (!pBuffer) + { + copied = false; + } + } + + if (copied) + { + reset(pBuffer); + } + + return copied; + } + + /** + * Compare content with another @ Buffer + * + * @param buffer The buffer to compare with. + * + * @return 0 if identical, + * -1 if this less that @c buffer, and + * +1 if @c buffer less than this. + */ + int compare(const Buffer& buffer) const + { + return gwbuf_compare(m_pBuffer, buffer.m_pBuffer); + } + + /** + * Compare content with a @c GWBUF + * + * @param buffer The buffer to compare with. + * + * @return 0 if identical, + * -1 if this less that @c buffer, and + * +1 if @c buffer less than this. + */ + int compare(const GWBUF& buffer) const + { + return gwbuf_compare(m_pBuffer, &buffer); + } + + /** + * Is content identical + * + * @param buffer The buffer to compare with. + * + * @return True, if identical, otherwise false. + */ + bool eq(const Buffer& buffer) const + { + return compare(buffer) == 0; + } + + /** + * Is content identical. + * + * @param pBuffer The buffer to compare with. + * + * @return True, if identical, otherwise false. + */ + bool eq(const GWBUF& buffer) const + { + return compare(buffer) == 0; + } + + /** + * Appends a @GWBUF to this. + * + * @param pBuffer The buffer to be appended to this @c Buffer. Becomes + * the property of the buffer. + * + * @return this + * + * @attention Does not invalidate any iterators, but an iterator + * that has reached the end will remain there. + */ + Buffer& append(GWBUF* pBuffer) + { + m_pBuffer = gwbuf_append(m_pBuffer, pBuffer); + return *this; + } + + /** + * Appends a @Buffer to this. + * + * @param buffer The buffer to be appended to this Buffer. + * + * @return this + * + * @attention After the call, the @c Buffer provided as argument + * will be empty. + * + * @attention Does not invalidate any iterators, but an iterator + * that has reached the end will remain there. + */ + Buffer& append(Buffer& buffer) + { + m_pBuffer = gwbuf_append(m_pBuffer, buffer.release()); + return *this; + } + + /** + * Resets the underlying GWBUF. + * + * @param pBuffer The @c GWBUF the @c Buffer should be reset with. + * + * @attention The ownership of @c pBuffer is moved to the @c Buffer. + * + * @attention Invalidates all iterators. + */ + void reset(GWBUF* pBuffer = NULL) + { + gwbuf_free(m_pBuffer); + m_pBuffer = pBuffer; + } + + /** + * Releases the underlying GWBUF. + * + * @return The underlying @c GWBUF. + * + * @attention The ownership of the buffer is transferred to the caller. + * + * @attention Does not invalidate existing iterators, but any manipulation + * of the returned @c GWBUF may invalidate them. + */ + GWBUF* release() + { + GWBUF* pBuffer = m_pBuffer; + m_pBuffer = NULL; + return pBuffer; + } + + /** + * Returns the address of the underlying @ GWBUF. This is intended to only + * be used in a context where a function returns a @c GWBUF as an out argument. + * For instance: + * + * void get_gwbuf(GWBUF** ppBuffer); + * ... + * Buffer buffer; + * + * get_gwbuf(&buffer); + * + * @return The address of the internal @c GWBUF pointer. + * + * @attention If the @c Buffer already refers to a @c GWBUF, that underlying + * buffer will first be freed. + * + * @attention Invalidates all iterators. + */ + GWBUF** operator & () + { + reset(); + return &m_pBuffer; + } + + /** + * The total length of the buffer. + * + * @return The total length of the buffer. + */ + size_t length() const + { + return gwbuf_length(m_pBuffer); + } + + /** + * Whether the buffer is contiguous. + * + * @return True, if the buffer is contiguous. + */ + bool is_contiguous() const + { + return GWBUF_IS_CONTIGUOUS(m_pBuffer); + } + + /** + * Make the buffer contiguous. + * + * @return True, if the buffer could be made contiguous. + * + * @attention Invalidates all iterators. + */ + bool make_contiguous(std::nothrow_t) + { + GWBUF* pBuffer = gwbuf_make_contiguous(m_pBuffer); + + if (pBuffer) + { + m_pBuffer = pBuffer; + } + + return pBuffer != NULL; + } + + /** + * Make the buffer contiguous. + * + * @throws @c std::bad_alloc if an allocation failed. + * + * @attention Invalidates all iterators. + */ + void make_contiguous() + { + if (!make_contiguous(std::nothrow)) + { + ss_dassert(!true); + throw std::bad_alloc(); + } + } + +private: + // To prevent @c Buffer from being created on the heap. + void* operator new(size_t); // standard new + void* operator new(size_t, void*); // placement new + void* operator new[](size_t); // array new + void* operator new[](size_t, void*); // placement array new + +private: + GWBUF* m_pBuffer; +}; + +/** + * Checks two @c Buffers for equality. + * + * @return True if equal, false otherwise. + */ +inline bool operator == (const Buffer& lhs, const Buffer& rhs) +{ + return lhs.eq(rhs); +} + +/** + * Checks a @c Buffer and a @c GWBUF for equality. + * + * @return True if equal, false otherwise. + */ +inline bool operator == (const Buffer& lhs, const GWBUF& rhs) +{ + return lhs.eq(rhs); +} + +/** + * Checks two @c Buffers for un-equality. + * + * @return True if un-equal, false otherwise. + */ +inline bool operator != (const Buffer& lhs, const Buffer& rhs) +{ + return !lhs.eq(rhs); +} + +/** + * Checks a @c Buffer and a @c GWBUF for un-equality. + * + * @return True if un-equal, false otherwise. + */ +inline bool operator != (const Buffer& lhs, const GWBUF& rhs) +{ + return !lhs.eq(rhs); +} + +}