From 0008d1fa192c8333b18178b352724cfa2ce8ff25 Mon Sep 17 00:00:00 2001
From: "Dmitry E. Oboukhov" <unera@debian.org>
Date: Tue, 16 Oct 2012 01:54:28 +0400
Subject: [PATCH] Add rlist library and tests

---
 include/rlist.h   | 183 ++++++++++++++++++++++++++++++++++++++++++++++
 include/test.h    |  93 +++++++++++++++++++++++
 test/unit/rlist.c | 106 +++++++++++++++++++++++++++
 3 files changed, 382 insertions(+)
 create mode 100644 include/rlist.h
 create mode 100644 include/test.h
 create mode 100644 test/unit/rlist.c

diff --git a/include/rlist.h b/include/rlist.h
new file mode 100644
index 0000000000..e24f1b5d31
--- /dev/null
+++ b/include/rlist.h
@@ -0,0 +1,183 @@
+#ifndef TARANTOOL_RLIST_H_INCLUDED
+#define TARANTOOL_RLIST_H_INCLUDED
+
+/**
+ * list entry and head structure
+ */
+
+struct rlist {
+	struct rlist *prev;
+	struct rlist *next;
+};
+
+
+/**
+ * init list head (or list entry as ins't included in list)
+ */
+
+inline static void
+rlist_init(struct rlist *list)
+{
+	list->next = list;
+	list->prev = list;
+}
+
+/**
+ * add item to list
+ */
+inline static void
+rlist_add(struct rlist *head, struct rlist *item)
+{
+	item->prev = head;
+	item->next = head->next;
+	item->prev->next = item;
+	item->next->prev = item;
+}
+
+/**
+ * add item to list tail
+ */
+inline static void
+rlist_add_tail(struct rlist *head, struct rlist *item)
+{
+	item->next = head;
+	item->prev = head->prev;
+	item->prev->next = item;
+	item->next->prev = item;
+}
+
+/**
+ * delete element
+ */
+inline static void
+rlist_del(struct rlist *item)
+{
+	item->prev->next = item->next;
+	item->next->prev = item->prev;
+	rlist_init(item);
+}
+
+/**
+ * return first element
+ */
+inline static struct rlist *
+rlist_first(struct rlist *head)
+{
+	return head->next;
+}
+
+/**
+ * return last element
+ */
+inline static struct rlist *
+rlist_last(struct rlist *head)
+{
+	return head->prev;
+}
+
+/**
+ * return next element by element
+ */
+inline static struct rlist *
+rlist_next(struct rlist *item)
+{
+	return item->next;
+}
+
+/**
+ * return previous element
+ */
+inline static struct rlist *
+rlist_prev(struct rlist *item)
+{
+	return item->prev;
+}
+
+/**
+ * return TRUE if list is empty
+ */
+inline static int
+rlist_empty(struct rlist *item)
+{
+	return item->next == item->prev && item->next == item;
+}
+
+/**
+ * allocate and init head of list
+ */
+#define RLIST_HEAD(name)	\
+	struct rlist name = { &(name), &(name) }
+
+/**
+ * return entry by list item
+ */
+#define rlist_entry(item, type, member) ({				\
+	const typeof( ((type *)0)->member ) *__mptr = (item);		\
+	(type *)( (char *)__mptr - ((size_t) &((type *)0)->member) ); })
+
+/**
+ * return first entry
+ */
+#define rlist_first_entry(head, type, member)				\
+	rlist_entry(rlist_first(head), type, member)
+
+/**
+ * return last entry
+ */
+#define rlist_last_entry(head, type, member)				\
+	rlist_entry(rlist_last(head), type, member)
+
+/**
+ * return next entry
+ */
+#define rlist_next_entry(item, member)					\
+	rlist_entry(rlist_next(&(item)->member), typeof(*item), member)
+
+/**
+ * return previous entry
+ */
+#define rlist_prev_entry(item, member)					\
+	rlist_entry(rlist_prev(&(item)->member), typeof(*item), member)
+
+/**
+ * add entry to list
+ */
+#define rlist_add_entry(head, item, member)				\
+	rlist_add((head), &(item)->member)
+
+/**
+ * add entry to list tail
+ */
+#define rlist_add_tail_entry(head, item, member)			\
+	rlist_add_tail((head), &(item)->member)
+
+/**
+ * foreach through list
+ */
+#define rlist_foreach(item, head)					\
+	for(item = rlist_first(head); item != (head); item = rlist_next(item))
+
+/**
+ * foreach backward through list
+ */
+#define rlist_foreach_reverse(item, head)				\
+	for(item = rlist_last(head); item != (head); item = rlist_prev(item))
+
+/**
+ * foreach through all list entries
+ */
+#define rlist_foreach_entry(item, head, member)				\
+	for(item = rlist_first_entry((head), typeof(*item), member); \
+		&item->member != (head); \
+		item = rlist_next_entry((item), member))
+
+/**
+ * foreach backward through all list entries
+ */
+#define rlist_foreach_entry_reverse(item, head, member)			\
+	for(item = rlist_last_entry((head), typeof(*item), member); \
+		&item->member != (head); \
+		item = rlist_prev_entry((item), member))
+
+
+#endif /* TARANTOOL_RLIST_H_INCLUDED */
diff --git a/include/test.h b/include/test.h
new file mode 100644
index 0000000000..b12e350a1b
--- /dev/null
+++ b/include/test.h
@@ -0,0 +1,93 @@
+#ifndef TARANTOOL_TEST_H_INCLUDED
+#define TARANTOOL_TEST_H_INCLUDED
+
+#include <stdio.h>
+#include <stdarg.h>
+
+static int tests_done = 0;
+static int tests_failed = 0;
+static int plan_test = 0;
+
+static inline void
+plan(int count)
+{
+	plan_test = count;
+	static showed_plan = 0;
+	if (!showed_plan)
+		printf("%d..%d\n", 1, plan_test);
+	showed_plan = 1;
+}
+
+static inline int
+check_plan(void)
+{
+	int res;
+	if (tests_done != plan_test) {
+		fprintf(stderr,
+			"# Looks like you planned %d tests but ran %d.\n",
+			plan_test, tests_done);
+		res = -1;
+	}
+
+	if (tests_failed) {
+		fprintf(stderr,
+			"# Looks like you failed %d test of %d run.\n",
+			tests_failed, tests_done);
+		res = tests_failed;
+	}
+	return res;
+}
+
+static inline int
+__ok(int condition, const char *fmt, ...)
+{
+	va_list ap;
+
+	printf("%s %d - ", condition ? "ok" : "not ok", ++tests_done);
+	if (!condition)
+		tests_failed++;
+	va_start(ap, fmt);
+	vprintf(fmt, ap);
+	printf("\n");
+	return condition;
+}
+
+#define ok(condition, fmt, args...)	{		\
+	int res = __ok(condition, fmt, ##args);		\
+	if (!res) {					\
+		fprintf(stderr, "#   Failed test '");	\
+		fprintf(stderr, fmt, ##args);		\
+		fprintf(stderr, "'\n");			\
+		fprintf(stderr, "#   in %s at line %d\n", __FILE__, __LINE__); \
+	}						\
+	res;						\
+}
+
+#define is(a, b, fmt, args...)	{			\
+	int res = __ok((a) == (b), fmt, ##args);	\
+	if (!res) {					\
+		fprintf(stderr, "#   Failed test '");	\
+		fprintf(stderr, fmt, ##args);		\
+		fprintf(stderr, "'\n");			\
+		fprintf(stderr, "#   in %s at line %d\n", __FILE__, __LINE__); \
+	}						\
+	res;						\
+}
+
+#define isnt(a, b, fmt, args...) {			\
+	int res = __ok((a) != (b), fmt, ##args);	\
+	if (!res) {					\
+		fprintf(stderr, "#   Failed test '");	\
+		fprintf(stderr, fmt, ##args);		\
+		fprintf(stderr, "'\n");			\
+		fprintf(stderr, "#   in %s at line %d\n", __FILE__, __LINE__); \
+	}						\
+	res;						\
+}
+
+#define fail(fmt, args...)		\
+	ok(0, fmt, ##args)
+
+
+#endif /* TARANTOOL_TEST_H_INCLUDED */
+
diff --git a/test/unit/rlist.c b/test/unit/rlist.c
new file mode 100644
index 0000000000..68768763fd
--- /dev/null
+++ b/test/unit/rlist.c
@@ -0,0 +1,106 @@
+#include <rlist.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <test.h>
+
+
+#define PLAN		66
+
+#define ITEMS		7
+
+struct test {
+	char ch;
+	int  no;
+	struct rlist list;
+};
+
+static struct test items[ITEMS];
+
+static RLIST_HEAD(head);
+
+int
+main(void)
+{
+	int i;
+	struct test *it;
+	struct rlist *rlist;
+
+	plan(PLAN);
+	ok(rlist_empty(&head), "list is empty");
+	for (i = 0; i < ITEMS; i++) {
+		items[i].no = i;
+		rlist_add_tail(&head, &(items[i].list));
+	}
+
+
+	is(rlist_first(&head), &items[0].list, "first item");
+	isnt(rlist_first(&head), &items[ITEMS - 1].list, "first item");
+
+	is(rlist_last(&head), &items[ITEMS - 1].list, "last item");
+	isnt(rlist_last(&head), &items[0].list, "last item");
+
+	is(rlist_next(&head), &items[0].list, "rlist_next");
+	is(rlist_prev(&head), &items[ITEMS - 1].list, "rlist_prev");
+
+	i = 0;
+	rlist_foreach(rlist, &head) {
+		is(rlist, &items[i].list, "element (foreach) %d", i);
+		i++;
+	}
+	rlist_foreach_reverse(rlist, &head) {
+		i--;
+		is(rlist, &items[i].list, "element (foreach_reverse) %d", i);
+	}
+
+
+	is(rlist_entry(&items[0].list, struct test, list), &items[0],
+		"rlist_entry");
+	is(rlist_first_entry(&head, struct test, list), &items[0],
+		"rlist_first_entry");
+	is(rlist_next_entry(&items[0], list), &items[1], "rlist_next_entry");
+	is(rlist_prev_entry(&items[2], list), &items[1], "rlist_prev_entry");
+
+
+	i = 0;
+	rlist_foreach_entry(it, &head, list) {
+		is(it, items + i, "element (foreach_entry) %d", i);
+		i++;
+	}
+	rlist_foreach_entry_reverse(it, &head, list) {
+		i--;
+		is(it, items + i, "element (foreach_entry_reverse) %d", i);
+	}
+
+	rlist_del(&items[2].list);
+	rlist_foreach_entry(it, &head, list) {
+		is(it, items + i, "element (second deleted) %d", i);
+		i++;
+		if (i == 2)
+			i++;
+	}
+	rlist_foreach_entry_reverse(it, &head, list) {
+		i--;
+		if (i == 2)
+			i--;
+		is(it, items + i, "element (second deleted) %d", i);
+	}
+
+
+	rlist_init(&head);
+	ok(rlist_empty(&head), "list is empty");
+	for (i = 0; i < ITEMS; i++) {
+		items[i].no = i;
+		rlist_add(&head, &(items[i].list));
+	}
+	i = 0;
+	rlist_foreach_entry_reverse(it, &head, list) {
+		is(it, items + i, "element (foreach_entry_reverse) %d", i);
+		i++;
+	}
+	rlist_foreach_entry(it, &head, list) {
+		i--;
+		is(it, items + i, "element (foreach_entry) %d", i);
+	}
+	return check_plan();
+}
+
-- 
GitLab