Blame src/vma/util/vma_list.h

Packit 6d2c1b
/*
Packit 6d2c1b
 * Copyright (c) 2001-2020 Mellanox Technologies, Ltd. All rights reserved.
Packit 6d2c1b
 *
Packit 6d2c1b
 * This software is available to you under a choice of one of two
Packit 6d2c1b
 * licenses.  You may choose to be licensed under the terms of the GNU
Packit 6d2c1b
 * General Public License (GPL) Version 2, available from the file
Packit 6d2c1b
 * COPYING in the main directory of this source tree, or the
Packit 6d2c1b
 * BSD license below:
Packit 6d2c1b
 *
Packit 6d2c1b
 *     Redistribution and use in source and binary forms, with or
Packit 6d2c1b
 *     without modification, are permitted provided that the following
Packit 6d2c1b
 *     conditions are met:
Packit 6d2c1b
 *
Packit 6d2c1b
 *      - Redistributions of source code must retain the above
Packit 6d2c1b
 *        copyright notice, this list of conditions and the following
Packit 6d2c1b
 *        disclaimer.
Packit 6d2c1b
 *
Packit 6d2c1b
 *      - Redistributions in binary form must reproduce the above
Packit 6d2c1b
 *        copyright notice, this list of conditions and the following
Packit 6d2c1b
 *        disclaimer in the documentation and/or other materials
Packit 6d2c1b
 *        provided with the distribution.
Packit 6d2c1b
 *
Packit 6d2c1b
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
Packit 6d2c1b
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
Packit 6d2c1b
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
Packit 6d2c1b
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
Packit 6d2c1b
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
Packit 6d2c1b
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
Packit 6d2c1b
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
Packit 6d2c1b
 * SOFTWARE.
Packit 6d2c1b
 */
Packit 6d2c1b
Packit 6d2c1b
#ifndef VMA_LIST_H
Packit 6d2c1b
#define VMA_LIST_H
Packit 6d2c1b
Packit 6d2c1b
#include "vma/util/list.h"
Packit 6d2c1b
#include "vlogger/vlogger.h"
Packit 6d2c1b
Packit 6d2c1b
#define VLIST_DEBUG        0
Packit 6d2c1b
#define VLIST_ID_SIZE    200
Packit 6d2c1b
Packit 6d2c1b
#define vlist_logwarn(log_fmt, log_args...)     vlog_printf(VLOG_WARNING, "vlist[%p]:%d:%s() " log_fmt "\n", this, __LINE__, __FUNCTION__, ##log_args)
Packit 6d2c1b
#define vlist_logerr(log_fmt, log_args...)      vlog_printf(VLOG_ERROR,   "vlist[%p]:%d:%s() " log_fmt "\n", this, __LINE__, __FUNCTION__, ##log_args)
Packit 6d2c1b
Packit 6d2c1b
#if VLIST_DEBUG
Packit 6d2c1b
template <class T, size_t offset(void)>
Packit 6d2c1b
class vma_list_t;
Packit 6d2c1b
#define VLIST_DEBUG_PRINT_ERROR_IS_MEMBER          vlist_logerr("Buff is already a member in a list! parent.id=[%s], this.id=[%s]", node_obj->list_id(), this->list_id())
Packit 6d2c1b
#define VLIST_DEBUG_SET_PARENT(node_obj, val)      node_obj->parent = val
Packit 6d2c1b
#else
Packit 6d2c1b
#define VLIST_DEBUG_PRINT_ERROR_IS_MEMBER          vlist_logerr("Buff is already a member in a list!")
Packit 6d2c1b
#define VLIST_DEBUG_SET_PARENT(node_obj, val)
Packit 6d2c1b
#endif
Packit 6d2c1b
Packit 6d2c1b
#define NODE_OFFSET(_obj_type, _node_name) \
Packit 6d2c1b
		((size_t)(&(char &)(((_obj_type *) 1)->_node_name)) - 1)
Packit 6d2c1b
#define GET_NODE(_obj, _obj_type, _offset_func) \
Packit 6d2c1b
		((list_node<_obj_type, _offset_func> *) ((size_t)(_obj) + (size_t)(_offset_func())))
Packit 6d2c1b
Packit 6d2c1b
template <class T, size_t offset(void)>
Packit 6d2c1b
class list_node {
Packit 6d2c1b
public :
Packit 6d2c1b
Packit 6d2c1b
	/* head must be the first field! */
Packit 6d2c1b
	struct list_head head;
Packit 6d2c1b
	T *obj_ptr;
Packit 6d2c1b
Packit 6d2c1b
#if VLIST_DEBUG
Packit 6d2c1b
	vma_list_t<T, offset> * parent;
Packit 6d2c1b
Packit 6d2c1b
	char* list_id() {
Packit 6d2c1b
		return this->parent->list_id();
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
#endif
Packit 6d2c1b
Packit 6d2c1b
	list_node() : obj_ptr(NULL) {
Packit 6d2c1b
		this->head.next = &this->head;
Packit 6d2c1b
		this->head.prev = &this->head;
Packit 6d2c1b
		VLIST_DEBUG_SET_PARENT(this, NULL);
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	/* is_list_member - check if the node is already a member in a list. */
Packit 6d2c1b
	bool is_list_member() {
Packit 6d2c1b
		return this->head.next != &this->head || this->head.prev != &this->head;
Packit 6d2c1b
	}
Packit 6d2c1b
};
Packit 6d2c1b
Packit 6d2c1b
template<typename T, size_t offset(void)>
Packit 6d2c1b
class list_iterator_t : public std::iterator<std::random_access_iterator_tag, T, std::ptrdiff_t, T*, T&>
Packit 6d2c1b
{
Packit 6d2c1b
public:
Packit 6d2c1b
Packit 6d2c1b
	list_iterator_t(T* ptr = NULL) : m_ptr(ptr) {}
Packit 6d2c1b
Packit 6d2c1b
	list_iterator_t(const list_iterator_t<T, offset>& iter) : m_ptr(iter.m_ptr) {}
Packit 6d2c1b
Packit 6d2c1b
	~list_iterator_t() {}
Packit 6d2c1b
Packit 6d2c1b
	list_iterator_t<T, offset>& operator=(T* ptr) {
Packit 6d2c1b
		m_ptr = ptr;
Packit 6d2c1b
		return (*this);
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	list_iterator_t<T, offset>& operator=(const list_iterator_t<T, offset>& iter) {
Packit 6d2c1b
		m_ptr = iter.m_ptr;
Packit 6d2c1b
		return (*this);
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	operator bool() const {
Packit 6d2c1b
		return m_ptr ? true : false;
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	bool operator==(const list_iterator_t<T, offset>& iter) const {
Packit 6d2c1b
		return m_ptr == iter.getConstPtr();
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	bool operator!=(const list_iterator_t<T, offset>& iter) const {
Packit 6d2c1b
		return m_ptr != iter.getConstPtr();
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	list_iterator_t<T, offset> operator++(int) {
Packit 6d2c1b
		list_iterator_t<T, offset> iter(*this);
Packit 6d2c1b
		increment_ptr();
Packit 6d2c1b
		return iter;
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	list_iterator_t<T, offset>& operator++() {
Packit 6d2c1b
		increment_ptr();
Packit 6d2c1b
		return *this;
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	list_iterator_t<T, offset> operator--(int) {
Packit 6d2c1b
		list_iterator_t<T, offset> iter(*this);
Packit 6d2c1b
		decrement_ptr();
Packit 6d2c1b
		return iter;
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	list_iterator_t<T, offset>& operator--() {
Packit 6d2c1b
		decrement_ptr();
Packit 6d2c1b
		return *this;
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	T* operator*() {
Packit 6d2c1b
		return m_ptr;
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	const T* operator*() const {
Packit 6d2c1b
		return m_ptr;
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	T* operator->() {
Packit 6d2c1b
		return m_ptr;
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	T* getPtr() const {
Packit 6d2c1b
		return m_ptr;
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	const T* getConstPtr() const {
Packit 6d2c1b
		return m_ptr;
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
private:
Packit 6d2c1b
Packit 6d2c1b
	T*	m_ptr;
Packit 6d2c1b
Packit 6d2c1b
	inline void increment_ptr() {
Packit 6d2c1b
		m_ptr = ((list_node<T, offset> *)GET_NODE(m_ptr, T, offset)->head.next)->obj_ptr;
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	inline void decrement_ptr() {
Packit 6d2c1b
		m_ptr = ((list_node<T, offset> *)GET_NODE(m_ptr, T, offset)->head.prev)->obj_ptr;
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
};
Packit 6d2c1b
Packit 6d2c1b
template <class T, size_t offset(void)>
Packit 6d2c1b
class  vma_list_t
Packit 6d2c1b
{
Packit 6d2c1b
public:
Packit 6d2c1b
	typedef list_iterator_t<T, offset> iterator;
Packit 6d2c1b
	vma_list_t() {
Packit 6d2c1b
		init_list();
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	void set_id(const char *format, ...) {
Packit 6d2c1b
		if (format) {
Packit 6d2c1b
	#if VLIST_DEBUG
Packit 6d2c1b
			va_list arg;
Packit 6d2c1b
			va_start (arg, format);
Packit 6d2c1b
			vsnprintf (id, sizeof(id) ,format, arg);
Packit 6d2c1b
			va_end (arg);
Packit 6d2c1b
	#endif
Packit 6d2c1b
		}
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	~vma_list_t() {
Packit 6d2c1b
		if (!empty()) {
Packit 6d2c1b
			vlist_logwarn("Destructor is not supported for non-empty list! size=%zu", m_size);
Packit 6d2c1b
		}
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	vma_list_t<T, offset> (const vma_list_t<T, offset>& other) {
Packit 6d2c1b
		if (!other.empty())
Packit 6d2c1b
			vlist_logwarn("Copy constructor is not supported for non-empty list! other.size=%zu", other.m_size);
Packit 6d2c1b
		init_list();
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	vma_list_t<T, offset>& operator=(const vma_list_t<T, offset>& other) {
Packit 6d2c1b
		if (!empty() || !other.empty())
Packit 6d2c1b
			vlist_logwarn("Operator= is not supported for non-empty list! size=%zu, other.size=%zu", m_size, other.m_size);
Packit 6d2c1b
		if (this != &other) {
Packit 6d2c1b
			init_list();
Packit 6d2c1b
		}
Packit 6d2c1b
		return *this;
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	T* operator[](size_t idx) const {
Packit 6d2c1b
		return get(idx);
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	inline bool empty() const {
Packit 6d2c1b
		return m_size == 0;
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	inline size_t size() const {
Packit 6d2c1b
		return m_size;
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	inline T* front() const {
Packit 6d2c1b
		if (unlikely(empty()))
Packit 6d2c1b
			return NULL;
Packit 6d2c1b
		return ((list_node<T, offset> *)m_list.head.next)->obj_ptr;
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	inline T* back() const {
Packit 6d2c1b
		if (unlikely(empty()))
Packit 6d2c1b
			return NULL;
Packit 6d2c1b
/* clang analyzer reports:
Packit 6d2c1b
 * Use of memory after it is freed
Packit 6d2c1b
 * This issue comes from ~chunk_list_t() 
Packit 6d2c1b
 * Root cause is unknown.
Packit 6d2c1b
 * TODO: Fix based on root cause instead of supressing
Packit 6d2c1b
 */
Packit 6d2c1b
#ifndef __clang_analyzer__
Packit 6d2c1b
                return ((list_node<T, offset> *)m_list.head.prev)->obj_ptr;
Packit 6d2c1b
#endif
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	inline void pop_front() {
Packit 6d2c1b
		erase(front());
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	inline void pop_back() {
Packit 6d2c1b
		erase(back());
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	inline T* get_and_pop_front() {
Packit 6d2c1b
		T* list_front = front();
Packit 6d2c1b
		pop_front();
Packit 6d2c1b
		return list_front;
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	inline T* get_and_pop_back() {
Packit 6d2c1b
		T* list_back = back();
Packit 6d2c1b
		pop_back();
Packit 6d2c1b
		return list_back;
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	void erase(T* obj) {
Packit 6d2c1b
		if (unlikely(!obj)) {
Packit 6d2c1b
			vlist_logwarn("Got NULL object - ignoring");
Packit 6d2c1b
			return;
Packit 6d2c1b
		}
Packit 6d2c1b
Packit 6d2c1b
		list_node<T, offset> *node_obj = GET_NODE(obj, T, offset);
Packit 6d2c1b
		VLIST_DEBUG_SET_PARENT(node_obj, NULL);
Packit 6d2c1b
		list_del_init(&node_obj->head);
Packit 6d2c1b
		m_size--;
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	/**
Packit 6d2c1b
	 * Clear content
Packit 6d2c1b
	 * Removes all elements from the list container (which are NOT destroyed), and leaving the container with a size of 0.
Packit 6d2c1b
	 *
Packit 6d2c1b
	 * NOTE: we don't expect calling this method in normal situations (it is workaround at application shutdown); Hence, there is no cleanup of node.parent
Packit 6d2c1b
	 */
Packit 6d2c1b
	void clear_without_cleanup() {
Packit 6d2c1b
		init_list();
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	void push_back(T* obj) {
Packit 6d2c1b
		if (unlikely(!obj)) {
Packit 6d2c1b
			vlist_logwarn("Got NULL object - ignoring");
Packit 6d2c1b
			return;
Packit 6d2c1b
		}
Packit 6d2c1b
Packit 6d2c1b
		list_node<T, offset> *node_obj = GET_NODE(obj, T, offset);
Packit 6d2c1b
		if (unlikely(node_obj->is_list_member())) {
Packit 6d2c1b
			VLIST_DEBUG_PRINT_ERROR_IS_MEMBER;
Packit 6d2c1b
		}
Packit 6d2c1b
Packit 6d2c1b
		VLIST_DEBUG_SET_PARENT(node_obj, this);
Packit 6d2c1b
		node_obj->obj_ptr = obj;
Packit 6d2c1b
		list_add_tail(&node_obj->head, &m_list.head);
Packit 6d2c1b
		m_size++;
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	void push_front(T* obj) {
Packit 6d2c1b
		if (unlikely(!obj)) {
Packit 6d2c1b
			vlist_logwarn("Got NULL object - ignoring");
Packit 6d2c1b
			return;
Packit 6d2c1b
		}
Packit 6d2c1b
Packit 6d2c1b
		list_node<T, offset> *node_obj = GET_NODE(obj, T, offset);
Packit 6d2c1b
		if (unlikely(node_obj->is_list_member())) {
Packit 6d2c1b
			VLIST_DEBUG_PRINT_ERROR_IS_MEMBER;
Packit 6d2c1b
		}
Packit 6d2c1b
Packit 6d2c1b
		VLIST_DEBUG_SET_PARENT(node_obj, this);
Packit 6d2c1b
		node_obj->obj_ptr = obj;
Packit 6d2c1b
		list_add(&node_obj->head, &m_list.head);
Packit 6d2c1b
		m_size++;
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	T* get(size_t index) const {
Packit 6d2c1b
		if (m_size <= index) {
Packit 6d2c1b
			return NULL;
Packit 6d2c1b
		} else {
Packit 6d2c1b
			list_head* ans = m_list.head.next;
Packit 6d2c1b
			for (size_t i = 0 ; i < index ; i++) {
Packit 6d2c1b
				ans = ans->next;
Packit 6d2c1b
			}
Packit 6d2c1b
			return ((list_node<T, offset> *)ans)->obj_ptr;
Packit 6d2c1b
		}
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	// concatenate 'from' at the head of this list
Packit 6d2c1b
	void splice_head(vma_list_t<T, offset>& from) {
Packit 6d2c1b
Packit 6d2c1b
		this->m_size += from.m_size;
Packit 6d2c1b
		list_splice(&from.m_list.head, &this->m_list.head);
Packit 6d2c1b
		from.init_list();
Packit 6d2c1b
		// TODO: in case VLIST_DEBUG, this invalidates parent list of all nodes in the list
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	// concatenate 'from' at the tail of this list
Packit 6d2c1b
	void splice_tail(vma_list_t<T, offset>& from) {
Packit 6d2c1b
		this->m_size += from.m_size;
Packit 6d2c1b
		list_splice_tail(&from.m_list.head, &this->m_list.head);
Packit 6d2c1b
		from.init_list();
Packit 6d2c1b
		// TODO: in case VLIST_DEBUG, this invalidates parent list of all nodes in the list
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	/**
Packit 6d2c1b
	 * Swap content
Packit 6d2c1b
	 * Exchanges the content of the container by the content of x, which is another list of the same type. Sizes may differ.
Packit 6d2c1b
	 *
Packit 6d2c1b
	 * After the call to this member function, the elements in this container are those which were in x before the call, and
Packit 6d2c1b
	 * the elements of x are those which were in this. All iterators, references and pointers remain valid for the swapped objects.
Packit 6d2c1b
	 */
Packit 6d2c1b
	void swap (vma_list_t<T, offset>& x) {
Packit 6d2c1b
		vma_list_t<T, offset> temp_list;
Packit 6d2c1b
		this->move_to_empty(temp_list);
Packit 6d2c1b
		x.move_to_empty(*this);
Packit 6d2c1b
		temp_list.move_to_empty(x);
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	list_iterator_t<T, offset> begin() {
Packit 6d2c1b
		return list_iterator_t<T, offset>(front());
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	list_iterator_t<T,offset> end() {
Packit 6d2c1b
		return list_iterator_t<T, offset>(NULL);
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
#if VLIST_DEBUG
Packit 6d2c1b
	char* list_id() {
Packit 6d2c1b
		return (char*)&id;
Packit 6d2c1b
	}
Packit 6d2c1b
#endif
Packit 6d2c1b
Packit 6d2c1b
private:
Packit 6d2c1b
Packit 6d2c1b
	list_node<T, offset> m_list;
Packit 6d2c1b
	size_t m_size;
Packit 6d2c1b
Packit 6d2c1b
#if VLIST_DEBUG
Packit 6d2c1b
	char id[VLIST_ID_SIZE];
Packit 6d2c1b
#endif
Packit 6d2c1b
Packit 6d2c1b
	void move_to_empty(vma_list_t<T, offset>& to) {
Packit 6d2c1b
		assert(to.empty());
Packit 6d2c1b
		to.m_size   = this->m_size;
Packit 6d2c1b
		list_splice_tail(&this->m_list.head, &to.m_list.head);
Packit 6d2c1b
		this->init_list();
Packit 6d2c1b
		// TODO: in case VLIST_DEBUG, this invalidates parent list of all nodes in the list
Packit 6d2c1b
	}
Packit 6d2c1b
Packit 6d2c1b
	void init_list() {
Packit 6d2c1b
		m_size = 0;
Packit 6d2c1b
		INIT_LIST_HEAD(&m_list.head);
Packit 6d2c1b
	#if VLIST_DEBUG
Packit 6d2c1b
		id[0] = '\0';
Packit 6d2c1b
	#endif
Packit 6d2c1b
	}
Packit 6d2c1b
};
Packit 6d2c1b
Packit 6d2c1b
#endif /* VMA_LIST_H */