summaryrefslogtreecommitdiff
path: root/src/eli.h
blob: 4a387f702e11d14c76d3f92e9b1ef20f2a031670 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/*
Copyright (C) 2017 Paweł Redman

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 3
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
*/

#ifndef _ELI_H
#define _ELI_H

#include <stddef.h> // size_t

#define eli_rebase(x, xm, y) \
((void*)((char*)(y) + ((char*)(xm) - (char*)(x))))

typedef struct {
	void *prev, *next;
} eli_header;

// Link an element to the end of a list.
static inline void eli_append_real(void **head, void *entry,
                                   eli_header *head_h, eli_header *entry_h)
{
	eli_header *last_h;

	// Empty list.
	if (__builtin_expect(!*head, 0)) {
		*head = entry;
		entry_h->prev = entry;
		entry_h->next = NULL;
		return;
	}

	entry_h->prev = head_h->prev;
	entry_h->next = NULL;

	last_h = eli_rebase(entry, entry_h, head_h->prev);
	last_h->next = entry;

	head_h->prev = entry;
}

#define eli_append(pphead, entry, member) \
eli_append_real((void**)(pphead), (entry), &((*(pphead))->member), &((entry)->member))

// Unlink an element (from anywhere in a list).
static inline void eli_unlink_real(void **head, void *entry,
                                   eli_header *head_h, eli_header *entry_h)
{
	if (entry_h->prev && *head != entry) {
		eli_header *prev_h;

		prev_h = eli_rebase(entry, entry_h, head_h->prev);
		prev_h->next = entry_h->next;
	}

	if (entry_h->next) {
		eli_header *next_h;

		next_h = eli_rebase(entry, entry_h, head_h->next);
		next_h->prev = entry_h->prev;
	} else {
		head_h->prev = entry_h->prev;
	}

	if (*head == entry)
		*head = entry_h->next;

}

#define eli_unlink(pphead, entry, member) \
eli_unlink_real((void**)(pphead), (entry), &((*(pphead))->member), &((entry)->member))

// Loop over a list
#define eli_for(i, head, member) \
for ((i) = (head); (i); (i) = (i)->member.next)

#endif // _ELI_H