/*
 * hlist.h - stripped down version of hash list implementation using
 * singly linked list.
 * Doubly linked list is wastage of space for big hash-tables. If cost of
 * iterating a hash list is significant, it means the hash function is NOT
 * formulated well and should be revisited.
 *
 * Copyright (C) 2021 IOPSYS Software Solutions AB. All rights reserved.
 *
 * Author: anjan.chanda@iopsys.eu
 *
 */

#ifndef _HLIST_H
#define _HLIST_H

struct hlist_node {
	struct hlist_node *next;
};

struct hlist_head {
	struct hlist_node *first;
};

#define HLIST_HEAD_INIT(name) { &(name) }

#define HLIST_HEAD(name) struct hlist_head name = HLIST_HEAD_INIT(name)

static inline void INIT_HLIST_HEAD(struct hlist_head *h)
{
	h->first = NULL;
}

static inline void INIT_HLIST_NODE(struct hlist_node *n)
{
	n->next = NULL;
}

static inline int hlist_empty(const struct hlist_head *h)
{
	return !h->first;
}

static inline void __hlist_del(struct hlist_node *prev, struct hlist_node *n)
{
	prev->next = n->next;
	n->next = NULL;
}

static inline void hlist_del(struct hlist_node *n, struct hlist_head *h)
{
	struct hlist_node *p;

	if (h->first == n) {
		h->first = n->next;
		n->next = NULL;
		return;
	}

	for (p = h->first; p; p = p->next) {
		if (p->next == n)
			__hlist_del(p, n);
	}
}

static inline void _hlist_add(struct hlist_node *_new, struct hlist_head *h)
{
	_new->next = h->first;
	h->first = _new;
}

static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
{
	_hlist_add(n, h);
}

#define hlist_for_each(pos, head) \
	for (pos = (head)->first; pos ; pos = pos->next)

#define hlist_for_each_safe(pos, n, head) \
	for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
	     pos = n)

#define hlist_entry(ptr, type, member) container_of(ptr, type, member)

#define hlist_entry_safe(ptr, type, member) \
	({ typeof(ptr) ____ptr = (ptr); \
	   ____ptr ? hlist_entry(____ptr, type, member) : NULL; \
	})

#define hlist_for_each_entry(pos, head, member)                                \
	for (pos = hlist_entry_safe((head)->first, typeof(*(pos)), member);    \
	     pos;                                                              \
	     pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))

#define hlist_for_each_entry_safe(pos, n, head, member)                   \
	for (pos = hlist_entry_safe((head)->first, typeof(*pos), member); \
	     pos && ({ n = pos->member.next; 1; });                       \
	     pos = hlist_entry_safe(n, typeof(*pos), member))

#endif /* _HLIST_H */