/* * 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 #include #include #include #include #include #include #include #include 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(b->start); } inline const uint8_t* gwbuf_link_data(const GWBUF* b) { return static_cast(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 { 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 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 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_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& 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); } }