699 lines
16 KiB
C++
699 lines
16 KiB
C++
#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/bsl11.
|
|
*
|
|
* 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
|
|
{
|
|
public:
|
|
/**
|
|
* Returns address of the internal pointer to a GWBUF.
|
|
*
|
|
* @attention This is provided as a backdoor for situations where it is
|
|
* unavoidable to access the interal pointer directly. It should
|
|
* carefully be assessed whether it actually can be avoided.
|
|
*
|
|
* @return Pointer to pointer to GWBUF.
|
|
*/
|
|
pointer_type* address_of()
|
|
{
|
|
return &m_i;
|
|
}
|
|
|
|
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;
|
|
|
|
// Buffer type, type of pointer to element and type of reference to element.
|
|
typedef iterator_base<GWBUF*, uint8_t*, uint8_t&> iterator_base_typedef;
|
|
|
|
/**
|
|
* A forward iterator to Buffer.
|
|
*/
|
|
class iterator : public iterator_base_typedef
|
|
{
|
|
friend class const_iterator;
|
|
|
|
public:
|
|
explicit iterator(GWBUF* pBuffer = NULL)
|
|
: iterator_base_typedef(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;
|
|
}
|
|
};
|
|
|
|
// Buffer type, type of pointer to element and type of reference to element.
|
|
typedef iterator_base<const GWBUF*, const uint8_t*, const uint8_t&> const_iterator_base_typedef;
|
|
|
|
/**
|
|
* A const forward iterator to Buffer.
|
|
*/
|
|
class const_iterator : public const_iterator_base_typedef
|
|
{
|
|
public:
|
|
explicit const_iterator(const GWBUF* pBuffer = NULL)
|
|
: const_iterator_base_typedef(pBuffer)
|
|
{}
|
|
|
|
const_iterator(const Buffer::iterator& rhs)
|
|
: const_iterator_base_typedef(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);
|
|
}
|
|
|
|
}
|