1296 lines
33 KiB
C++
1296 lines
33 KiB
C++
/*
|
|
* Copyright (c) 2018 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: 2026-01-04
|
|
*
|
|
* 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.
|
|
*/
|
|
#pragma once
|
|
|
|
#include <maxscale/ccdefs.hh>
|
|
#include <algorithm>
|
|
#include <iterator>
|
|
#include <memory>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <vector>
|
|
#include <maxbase/assert.h>
|
|
#include <maxscale/hint.h>
|
|
|
|
class SERVER;
|
|
|
|
namespace maxscale
|
|
{
|
|
class Buffer;
|
|
}
|
|
|
|
/**
|
|
* Buffer properties - used to store properties related to the buffer
|
|
* contents. This may be added at any point during the processing of the
|
|
* data, especially in the protocol stage of the processing.
|
|
*/
|
|
struct BUF_PROPERTY
|
|
{
|
|
char* name;
|
|
char* value;
|
|
BUF_PROPERTY* next;
|
|
};
|
|
|
|
enum gwbuf_type_t
|
|
{
|
|
GWBUF_TYPE_UNDEFINED = 0,
|
|
GWBUF_TYPE_HTTP = (1 << 0),
|
|
GWBUF_TYPE_IGNORABLE = (1 << 1),
|
|
GWBUF_TYPE_COLLECT_RESULT = (1 << 2),
|
|
GWBUF_TYPE_RESULT = (1 << 3),
|
|
GWBUF_TYPE_REPLY_OK = (1 << 4),
|
|
GWBUF_TYPE_REPLAYED = (1 << 5),
|
|
GWBUF_TYPE_TRACK_STATE = (1 << 6),
|
|
};
|
|
|
|
#define GWBUF_IS_TYPE_UNDEFINED(b) ((b)->gwbuf_type == 0)
|
|
#define GWBUF_IS_IGNORABLE(b) ((b)->gwbuf_type & GWBUF_TYPE_IGNORABLE)
|
|
#define GWBUF_IS_COLLECTED_RESULT(b) ((b)->gwbuf_type & GWBUF_TYPE_RESULT)
|
|
#define GWBUF_SHOULD_COLLECT_RESULT(b) ((b)->gwbuf_type & GWBUF_TYPE_COLLECT_RESULT)
|
|
#define GWBUF_IS_REPLY_OK(b) ((b)->gwbuf_type & GWBUF_TYPE_REPLY_OK)
|
|
|
|
// True if the query is not initiated by the client but an internal replaying mechanism
|
|
#define GWBUF_IS_REPLAYED(b) ((b)->gwbuf_type & GWBUF_TYPE_REPLAYED)
|
|
|
|
// Track session state change response
|
|
#define GWBUF_SHOULD_TRACK_STATE(b) ((b)->gwbuf_type & GWBUF_TYPE_TRACK_STATE)
|
|
|
|
enum gwbuf_info_t
|
|
{
|
|
GWBUF_INFO_NONE = 0x0,
|
|
GWBUF_INFO_PARSED = 0x1
|
|
};
|
|
|
|
#define GWBUF_IS_PARSED(b) (b->sbuf->info & GWBUF_INFO_PARSED)
|
|
|
|
/**
|
|
* A structure for cleaning up memory allocations of structures which are
|
|
* referred to by GWBUF and deallocated in gwbuf_free but GWBUF doesn't
|
|
* know what they are.
|
|
* All functions on the list are executed before freeing memory of GWBUF struct.
|
|
*/
|
|
enum bufobj_id_t
|
|
{
|
|
GWBUF_PARSING_INFO
|
|
};
|
|
|
|
struct buffer_object_t
|
|
{
|
|
bufobj_id_t bo_id;
|
|
void* bo_data;
|
|
void (* bo_donefun_fp)(void*);
|
|
buffer_object_t* bo_next;
|
|
};
|
|
|
|
/**
|
|
* A structure to encapsulate the data in a form that the data itself can be
|
|
* shared between multiple GWBUF's without the need to make multiple copies
|
|
* but still maintain separate data pointers.
|
|
*/
|
|
struct SHARED_BUF
|
|
{
|
|
buffer_object_t* bufobj; /*< List of objects referred to by GWBUF */
|
|
int32_t refcount; /*< Reference count on the buffer */
|
|
uint32_t info; /*< Info bits */
|
|
unsigned char data[1]; /*< Actual memory that was allocated */
|
|
};
|
|
|
|
/**
|
|
* The buffer structure used by the descriptor control blocks.
|
|
*
|
|
* Linked lists of buffers are created as data is read from a descriptor
|
|
* or written to a descriptor. The use of linked lists of buffers with
|
|
* flexible data pointers is designed to minimise the need for data to
|
|
* be copied within the gateway.
|
|
*/
|
|
struct GWBUF
|
|
{
|
|
GWBUF* next; /*< Next buffer in a linked chain of buffers */
|
|
GWBUF* tail; /*< Last buffer in a linked chain of buffers */
|
|
void* start; /*< Start of the valid data */
|
|
void* end; /*< First byte after the valid data */
|
|
SHARED_BUF* sbuf; /*< The shared buffer with the real data */
|
|
HINT* hint; /*< Hint data for this buffer */
|
|
BUF_PROPERTY* properties; /*< Buffer properties */
|
|
SERVER* server; /*< The target server where the buffer is executed */
|
|
uint32_t gwbuf_type; /*< buffer's data type information */
|
|
#ifdef SS_DEBUG
|
|
int owner; /*< Owner of the thread, only for debugging */
|
|
#endif
|
|
};
|
|
|
|
/*<
|
|
* Macros to access the data in the buffers
|
|
*/
|
|
/*< First valid, unconsumed byte in the buffer */
|
|
inline uint8_t* gwbuf_link_data(GWBUF* b)
|
|
{
|
|
return static_cast<uint8_t*>(b->start);
|
|
}
|
|
|
|
inline const uint8_t* gwbuf_link_data(const GWBUF* b)
|
|
{
|
|
return static_cast<uint8_t*>(b->start);
|
|
}
|
|
|
|
#define GWBUF_DATA(b) gwbuf_link_data(b)
|
|
|
|
/*< Number of bytes in the individual buffer */
|
|
inline size_t gwbuf_link_length(const GWBUF* b)
|
|
{
|
|
return (size_t)((char*)b->end - (char*)b->start);
|
|
}
|
|
|
|
#define GWBUF_LENGTH(b) gwbuf_link_length(b)
|
|
|
|
/*< Check whether the buffer is contiguous*/
|
|
inline bool gwbuf_is_contiguous(const GWBUF* b)
|
|
{
|
|
mxb_assert(b);
|
|
return b->next == nullptr;
|
|
}
|
|
|
|
#define GWBUF_IS_CONTIGUOUS(b) gwbuf_is_contiguous(b)
|
|
|
|
/*< True if all bytes in the buffer have been consumed */
|
|
inline bool gwbuf_link_empty(const GWBUF* b)
|
|
{
|
|
return (char*)b->start >= (char*)b->end;
|
|
}
|
|
|
|
#define GWBUF_EMPTY(b) gwbuf_link_empty(b)
|
|
|
|
/*< Consume a number of bytes in the buffer */
|
|
inline void gwbuf_link_consume(GWBUF* b, unsigned int bytes)
|
|
{
|
|
b->start = bytes > ((char*)b->end - (char*)b->start) ? b->end : (void*)((char*)b->start + bytes);
|
|
}
|
|
|
|
#define GWBUF_CONSUME(b, bytes) gwbuf_link_consume(b, bytes)
|
|
|
|
inline void gwbuf_link_rtrim(GWBUF* b, unsigned int bytes)
|
|
{
|
|
b->end = bytes > ((char*)b->end - (char*)b->start) ? b->start : (void*)((char*)b->end - bytes);
|
|
}
|
|
|
|
#define GWBUF_RTRIM(b, bytes) gwbuf_link_rtrim(b, bytes)
|
|
|
|
inline uint32_t gwbuf_type(const GWBUF* b)
|
|
{
|
|
return b->gwbuf_type;
|
|
}
|
|
#define GWBUF_TYPE(b) gwbuf_type(b)
|
|
|
|
/*<
|
|
* Function prototypes for the API to maniplate the buffers
|
|
*/
|
|
|
|
/**
|
|
* Allocate a new gateway buffer of specified size.
|
|
*
|
|
* @param size The size in bytes of the data area required
|
|
*
|
|
* @return Pointer to the buffer structure or NULL if memory could not
|
|
* be allocated.
|
|
*/
|
|
extern GWBUF* gwbuf_alloc(unsigned int size);
|
|
|
|
/**
|
|
* Allocate a new gateway buffer structure of specified size and load with data.
|
|
*
|
|
* @param size The size in bytes of the data area required
|
|
* @param data Pointer to the data (size bytes) to be loaded
|
|
*
|
|
* @return Pointer to the buffer structure or NULL if memory could not
|
|
* be allocated.
|
|
*/
|
|
extern GWBUF* gwbuf_alloc_and_load(unsigned int size, const void* data);
|
|
|
|
/**
|
|
* Free a chain of gateway buffers
|
|
*
|
|
* @param buf The head of the list of buffers to free, can be NULL.
|
|
*/
|
|
extern void gwbuf_free(GWBUF* buf);
|
|
|
|
/**
|
|
* Clone a GWBUF. Note that if the GWBUF is actually a list of
|
|
* GWBUFs, then every GWBUF in the list will be cloned. Note that but
|
|
* for the GWBUF structure itself, the data is shared.
|
|
*
|
|
* @param buf The GWBUF to be cloned.
|
|
*
|
|
* @return The cloned GWBUF, or NULL if any part of @buf could not be cloned.
|
|
*/
|
|
extern GWBUF* gwbuf_clone(GWBUF* buf);
|
|
|
|
/**
|
|
* @brief Deep clone a GWBUF
|
|
*
|
|
* Clone the data inside a GWBUF into a new buffer. The created buffer has its
|
|
* own internal buffer and any modifications to the deep cloned buffer will not
|
|
* reflect on the original one. Any buffer objects attached to the original buffer
|
|
* will not be copied. Only the buffer type of the original buffer will be copied
|
|
* over to the cloned buffer.
|
|
*
|
|
* @param buf Buffer to clone
|
|
*
|
|
* @return Deep copy of @c buf or NULL on error
|
|
*/
|
|
extern GWBUF* gwbuf_deep_clone(const GWBUF* buf);
|
|
|
|
/**
|
|
* Compare two GWBUFs. Two GWBUFs are considered identical if their
|
|
* content is identical, irrespective of whether one is segmented and
|
|
* the other is not.
|
|
*
|
|
* @param lhs One GWBUF
|
|
* @param rhs Another GWBUF
|
|
*
|
|
* @return 0 if the content is identical,
|
|
* -1 if @c lhs is less than @c rhs, and
|
|
* 1 if @c lhs is more than @c rhs.
|
|
*
|
|
* @attention A shorter @c GWBUF less than a longer one. Otherwise the
|
|
* sign of the return value is determined by the sign of the
|
|
* difference between the first pair of bytes (interpreted as
|
|
* unsigned char) that differ in lhs and rhs.
|
|
*/
|
|
extern int gwbuf_compare(const GWBUF* lhs, const GWBUF* rhs);
|
|
|
|
/**
|
|
* Append a buffer onto a linked list of buffer structures.
|
|
*
|
|
* @param head The current head of the linked list or NULL.
|
|
* @param tail Another buffer to make the tail of the linked list, must not be NULL
|
|
*
|
|
* @return The new head of the linked list
|
|
*/
|
|
extern GWBUF* gwbuf_append(GWBUF* head, GWBUF* tail);
|
|
|
|
/**
|
|
* @brief Consume data from buffer chain
|
|
*
|
|
* Data is consumed from @p head until either @p length bytes have been
|
|
* processed or @p head is empty. If @p head points to a chain of buffers,
|
|
* those buffers are counted as a part of @p head and will also be consumed if
|
|
* @p length exceeds the size of the first buffer.
|
|
*
|
|
* @param head The head of the linked list
|
|
* @param length Number of bytes to consume
|
|
*
|
|
* @return The head of the linked list or NULL if everything was consumed
|
|
*/
|
|
extern GWBUF* gwbuf_consume(GWBUF* head, unsigned int length);
|
|
|
|
/**
|
|
* Trim bytes from the end of a GWBUF structure that may be the first
|
|
* in a list. If the buffer has n_bytes or less then it will be freed and
|
|
* the next buffer in the list will be returned, or if none, NULL.
|
|
*
|
|
* @param head The buffer to trim
|
|
* @param n_bytes The number of bytes to trim off
|
|
*
|
|
* @return The buffer chain or NULL if buffer chain now empty
|
|
*/
|
|
extern GWBUF* gwbuf_rtrim(GWBUF* head, unsigned int length);
|
|
|
|
/**
|
|
* Return the number of bytes of data in the linked list.
|
|
*
|
|
* @param head The current head of the linked list
|
|
*
|
|
* @return The number of bytes of data in the linked list
|
|
*/
|
|
extern unsigned int gwbuf_length(const GWBUF* head);
|
|
|
|
/**
|
|
* Return the number of individual buffers in the linked list.
|
|
*
|
|
* Currently not used, provided mainly for use during debugging sessions.
|
|
*
|
|
* @param head The current head of the linked list
|
|
*
|
|
* @return The number of bytes of data in the linked list
|
|
*/
|
|
extern int gwbuf_count(const GWBUF* head);
|
|
|
|
/**
|
|
* @brief Copy bytes from a buffer
|
|
*
|
|
* Copy bytes from a chain of buffers. Supports copying data from buffers where
|
|
* the data is spread across multiple buffers.
|
|
*
|
|
* @param buffer Buffer to copy from
|
|
* @param offset Offset into the buffer
|
|
* @param bytes Number of bytes to copy
|
|
* @param dest Destination where the bytes are copied
|
|
*
|
|
* @return Number of bytes copied.
|
|
*/
|
|
extern size_t gwbuf_copy_data(const GWBUF* buffer, size_t offset, size_t bytes, uint8_t* dest);
|
|
|
|
/**
|
|
* @brief Split a buffer in two
|
|
*
|
|
* The returned value will be @c length bytes long. If the length of @c buf
|
|
* exceeds @c length, the remaining buffers are stored in @buf.
|
|
*
|
|
* @param buf Buffer chain to split
|
|
* @param length Number of bytes that the returned buffer should contain
|
|
*
|
|
* @return Head of the buffer chain.
|
|
*/
|
|
extern GWBUF* gwbuf_split(GWBUF** buf, size_t length);
|
|
|
|
/**
|
|
* Set given type to all buffers on the list.
|
|
* *
|
|
* @param buf The shared buffer
|
|
* @param type Type to be added, mask of @c gwbuf_type_t values.
|
|
*/
|
|
extern void gwbuf_set_type(GWBUF* head, uint32_t type);
|
|
|
|
/**
|
|
* Add a property to a buffer.
|
|
*
|
|
* @param buf The buffer to add the property to
|
|
* @param name The property name
|
|
* @param value The property value
|
|
*
|
|
* @return True on success, false otherwise.
|
|
*/
|
|
extern bool gwbuf_add_property(GWBUF* buf, const char* name, const char* value);
|
|
|
|
/**
|
|
* Return the value of a buffer property
|
|
*
|
|
* @param buf The buffer itself
|
|
* @param name The name of the property to return
|
|
*
|
|
* @return The property value or NULL if the property was not found.
|
|
*/
|
|
extern char* gwbuf_get_property(GWBUF* buf, const char* name);
|
|
|
|
/**
|
|
* Convert a chain of GWBUF structures into a single GWBUF structure
|
|
*
|
|
* @param orig The chain to convert, must not be used after the function call
|
|
*
|
|
* @return A contiguous version of @c buf.
|
|
*
|
|
* @attention Never returns NULL, memory allocation failures abort the process
|
|
*/
|
|
extern GWBUF* gwbuf_make_contiguous(GWBUF* buf);
|
|
|
|
/**
|
|
* Add a buffer object to GWBUF buffer.
|
|
*
|
|
* @param buf GWBUF where object is added
|
|
* @param id Type identifier for object
|
|
* @param data Object data
|
|
* @param donefun_fp Clean-up function to be executed before buffer is freed.
|
|
*/
|
|
void gwbuf_add_buffer_object(GWBUF* buf,
|
|
bufobj_id_t id,
|
|
void* data,
|
|
void (* donefun_fp)(void*));
|
|
|
|
/**
|
|
* Search buffer object which matches with the id.
|
|
*
|
|
* @param buf GWBUF to be searched
|
|
* @param id Identifier for the object
|
|
*
|
|
* @return Searched buffer object or NULL if not found
|
|
*/
|
|
void* gwbuf_get_buffer_object_data(GWBUF* buf, bufobj_id_t id);
|
|
#if defined (BUFFER_TRACE)
|
|
extern void dprintAllBuffers(void* pdcb);
|
|
#endif
|
|
|
|
/**
|
|
* Debug function for dumping buffer contents to log
|
|
*
|
|
* @see mxs::Buffer::hexdump
|
|
*
|
|
* @param buffer Buffer to dump
|
|
* @param log_level Log priority where the message is written
|
|
*/
|
|
void gwbuf_hexdump(GWBUF* buffer, int log_level = LOG_INFO);
|
|
|
|
/**
|
|
* Debug function for pretty-printing buffer contents to log
|
|
*
|
|
* @see mxs::Buffer::hexdump_pretty
|
|
*
|
|
* @param buffer Buffer to dump
|
|
* @param log_level Log priority where the message is written
|
|
*/
|
|
void gwbuf_hexdump_pretty(GWBUF* buffer, int log_level = LOG_INFO);
|
|
|
|
/**
|
|
* Return pointer of the byte at offset from start of chained buffer
|
|
* Warning: It not guaranteed to point to a contiguous segment of memory,
|
|
* it is only safe to modify the first byte this pointer point to.
|
|
*
|
|
* @param buffer one or more chained buffer
|
|
* @param offset Offset into the buffer
|
|
* @return if total buffer length is bigger than offset then return
|
|
* the offset byte pointer, otherwise return null
|
|
*/
|
|
extern uint8_t* gwbuf_byte_pointer(GWBUF* buffer, size_t offset);
|
|
|
|
namespace std
|
|
{
|
|
|
|
template<>
|
|
struct default_delete<GWBUF>
|
|
{
|
|
void operator()(GWBUF* pBuffer)
|
|
{
|
|
gwbuf_free(pBuffer);
|
|
}
|
|
};
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Advance the iterator
|
|
*
|
|
* This provides similar behavior to random access iterators with operator+= but does it in
|
|
* non-constant time.
|
|
*
|
|
* @param i Number of steps to advance the iterator
|
|
*/
|
|
void advance(int i)
|
|
{
|
|
mxb_assert(m_i != m_end);
|
|
mxb_assert(i >= 0);
|
|
|
|
while (m_i && m_i + i >= m_end)
|
|
{
|
|
i -= m_end - m_i;
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (m_i)
|
|
{
|
|
m_i += 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()
|
|
{
|
|
mxb_assert(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*()
|
|
{
|
|
mxb_assert(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)
|
|
{
|
|
m_i = rhs.m_i;
|
|
m_end = rhs.m_end;
|
|
}
|
|
|
|
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
|
|
{
|
|
mxb_assert(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)
|
|
{
|
|
mxb_assert(!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)
|
|
{
|
|
mxb_assert(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=(const Buffer& rhs)
|
|
{
|
|
Buffer temp(rhs);
|
|
swap(temp);
|
|
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)
|
|
{
|
|
return copy_from(rhs.m_pBuffer);
|
|
}
|
|
|
|
/**
|
|
* Clone a GWBUF and free the current buffer
|
|
*
|
|
* @param buf Buffer to clone
|
|
*
|
|
* @return True if buffer was copied
|
|
*
|
|
* @attention Invalidates all iterators.
|
|
*/
|
|
bool copy_from(GWBUF* pBuffer)
|
|
{
|
|
bool copied = true;
|
|
|
|
if (pBuffer)
|
|
{
|
|
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;
|
|
}
|
|
|
|
iterator erase(const_iterator first, const_iterator last)
|
|
{
|
|
if (first == end())
|
|
{
|
|
// Nothing to do
|
|
return end();
|
|
}
|
|
else if (first == last)
|
|
{
|
|
// Empty range deletion is a no-op that must return a non-const version of the given iterators
|
|
iterator it = begin();
|
|
it.advance(std::distance(const_iterator(it), first));
|
|
mxb_assert(const_iterator(it) == first);
|
|
return it;
|
|
}
|
|
else if (first == begin() && last == end())
|
|
{
|
|
// Clear out the whole buffer
|
|
reset();
|
|
return end();
|
|
}
|
|
|
|
iterator rval;
|
|
const_iterator b = begin();
|
|
auto offset = std::distance(b, first);
|
|
auto num_bytes = std::distance(first, last);
|
|
mxb_assert(num_bytes > 0);
|
|
|
|
auto head = gwbuf_split(&m_pBuffer, offset);
|
|
|
|
if (m_pBuffer && (m_pBuffer = gwbuf_consume(m_pBuffer, num_bytes)))
|
|
{
|
|
if (head)
|
|
{
|
|
m_pBuffer = gwbuf_append(head, m_pBuffer);
|
|
}
|
|
else
|
|
{
|
|
mxb_assert(offset == 0);
|
|
}
|
|
|
|
rval = begin();
|
|
rval.advance(offset + 1);
|
|
}
|
|
else
|
|
{
|
|
m_pBuffer = head;
|
|
rval = end();
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
/**
|
|
* Get the underlying GWBUF.
|
|
*
|
|
* @return The underlying @c GWBUF.
|
|
*
|
|
* @attention This does not release ownership of the buffer. The returned pointer must never be
|
|
* freed by the caller.
|
|
*/
|
|
GWBUF* get()
|
|
{
|
|
return m_pBuffer;
|
|
}
|
|
|
|
/**
|
|
* 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 m_pBuffer ? gwbuf_length(m_pBuffer) : 0;
|
|
}
|
|
|
|
/**
|
|
* Whether the buffer is empty.
|
|
*
|
|
* @return True if the buffer is empty
|
|
*/
|
|
bool empty() const
|
|
{
|
|
return m_pBuffer == nullptr;
|
|
}
|
|
|
|
/**
|
|
* 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))
|
|
{
|
|
mxb_assert(!true);
|
|
throw std::bad_alloc();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Debug function for dumping buffer contents to log
|
|
*
|
|
* Prints contents as hexadecimal. Only the first 1024 bytes are dumped to avoid filling up the log.
|
|
*
|
|
* @param log_level Log priority where the message is written
|
|
*/
|
|
void hexdump(int log_level = LOG_INFO) const;
|
|
|
|
/**
|
|
* Debug function for pretty-printing buffer contents to log
|
|
*
|
|
* The output format is similar to `hexdump -C` and provides both hex and human-readable values.
|
|
*
|
|
* @param log_level Log priority where the message is written
|
|
*/
|
|
void hexdump_pretty(int log_level = LOG_INFO) const;
|
|
|
|
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);
|
|
}
|
|
}
|