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.
This commit is contained in:
parent
18008dec6d
commit
cb0cb4bb09
680
include/maxscale/buffer.hh
Normal file
680
include/maxscale/buffer.hh
Normal file
@ -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 <maxscale/cppdefs.hh>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <new>
|
||||
#include <vector>
|
||||
#include <maxscale/buffer.h>
|
||||
|
||||
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 buf_type, class pointer_type, class reference_type>
|
||||
class iterator_base : public std::iterator<std::forward_iterator_tag, // The type of the iterator
|
||||
uint8_t, // The type of the elems
|
||||
std::ptrdiff_t, // Difference between two its
|
||||
pointer_type, // The type of pointer to an elem
|
||||
reference_type> // 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<GWBUF*, // Buffer type
|
||||
uint8_t*, // Type of pointer to element
|
||||
uint8_t&> // 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<const GWBUF*, // Buffer type
|
||||
const uint8_t*, // Type of pointer to element
|
||||
const uint8_t&> // 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<uint8_t>& 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);
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user