summaryrefslogtreecommitdiffstats
path: root/src/libdnssec/list/ucw_clists.h
blob: 080d01e8c0ce65f3aa85d5bda1d6b783e30ce2c1 (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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
/*
 *	UCW Library -- Circular Linked Lists
 *
 *	(c) 2003--2010 Martin Mares <mj@ucw.cz>
 *
 *	This software may be freely distributed and used according to the terms
 *	of the GNU Lesser General Public License.
 */

#ifndef _UCW_CLISTS_H
#define _UCW_CLISTS_H

/**
 * Common header for list nodes.
 **/
typedef struct cnode {
  struct cnode *next, *prev;
} cnode;

/**
 * Circular doubly linked list.
 **/
typedef struct clist {
  struct cnode head;
} clist;

/**
 * Initialize a new circular linked list. Must be called before any other function.
 **/
static inline void clist_init(clist *l)
{
  cnode *head = &l->head;
  head->next = head->prev = head;
}

/**
 * Return the first node on \p l or NULL if \p l is empty.
 **/
static inline void *clist_head(clist *l)
{
  return (l->head.next != &l->head) ? l->head.next : NULL;
}

/**
 * Return the last node on \p l or NULL if \p l is empty.
 **/
static inline void *clist_tail(clist *l)
{
  return (l->head.prev != &l->head) ? l->head.prev : NULL;
}

/**
 * Find the next node to \p n or NULL if \p n is the last one.
 **/
static inline void *clist_next(clist *l, cnode *n)
{
  return (n->next != &l->head) ? (void *) n->next : NULL;
}

/**
 * Find the previous node to \p n or NULL if \p n is the first one.
 **/
static inline void *clist_prev(clist *l, cnode *n)
{
  return (n->prev != &l->head) ? (void *) n->prev : NULL;
}

/**
 * Return a non-zero value iff \p l is empty.
 **/
static inline int clist_empty(clist *l)
{
  return (l->head.next == &l->head);
}

/**
 * Loop over all nodes in the \ref list and perform the next C statement on them. The current node is stored in \p n which must be defined before as pointer to any type.
 * The list should not be changed during this loop command.
 **/
#define CLIST_WALK(n,list) for(n=(void*)(list).head.next; (cnode*)(n) != &(list).head; n=(void*)((cnode*)(n))->next)

/**
 * Same as \ref CLIST_WALK(), but allows removal of the current node. This macro requires one more variable to store some temporary pointers.
 **/
#define CLIST_WALK_DELSAFE(n,list,tmp) for(n=(void*)(list).head.next; tmp=(void*)((cnode*)(n))->next, (cnode*)(n) != &(list).head; n=(void*)tmp)

/**
 * Same as \ref CLIST_WALK(), but it defines the variable for the current node in place. \p type should be a pointer type.
 **/
#define CLIST_FOR_EACH(type,n,list) for(type n=(void*)(list).head.next; (cnode*)(n) != &(list).head; n=(void*)((cnode*)(n))->next)

/**
 * Same as \ref CLIST_WALK_DELSAFE(), but it defines the variable for the current node in place. \p type should be a pointer type. The temporary variable must be still known before.
 **/
#define CLIST_FOR_EACH_DELSAFE(type,n,list,tmp) for(type n=(void*)(list).head.next; tmp=(void*)((cnode*)(n))->next, (cnode*)(n) != &(list).head; n=(void*)tmp)

/**
 * Reversed version of \ref CLIST_FOR_EACH().
 **/
#define CLIST_FOR_EACH_BACKWARDS(type,n,list) for(type n=(void*)(list).head.prev; (cnode*)(n) != &(list).head; n=(void*)((cnode*)(n))->prev)

/**
 * Insert a new node just after the node \p after. To insert at the head of the list, use \ref clist_add_head() instead.
 **/
static inline void clist_insert_after(cnode *what, cnode *after)
{
  cnode *before = after->next;
  what->next = before;
  what->prev = after;
  before->prev = what;
  after->next = what;
}

/**
 * Insert a new node just before the node \p before. To insert at the tail of the list, use \ref clist_add_tail() instead.
 **/
static inline void clist_insert_before(cnode *what, cnode *before)
{
  cnode *after = before->prev;
  what->next = before;
  what->prev = after;
  before->prev = what;
  after->next = what;
}

/**
 * Insert a new node in front of all other nodes.
 **/
static inline void clist_add_head(clist *l, cnode *n)
{
  clist_insert_after(n, &l->head);
}

/**
 * Insert a new node after all other nodes.
 **/
static inline void clist_add_tail(clist *l, cnode *n)
{
  clist_insert_before(n, &l->head);
}

/**
 * Remove node \p n.
 **/
static inline void clist_remove(cnode *n)
{
  cnode *before = n->prev;
  cnode *after = n->next;
  before->next = after;
  after->prev = before;
}

/**
 * Remove the first node in \p l, if it exists. Return the pointer to that node or NULL.
 **/
static inline void *clist_remove_head(clist *l)
{
  cnode *n = clist_head(l);
  if (n)
    clist_remove(n);
  return n;
}

/**
 * Remove the last node in \p l, if it exists. Return the pointer to that node or NULL.
 **/
static inline void *clist_remove_tail(clist *l)
{
  cnode *n = clist_tail(l);
  if (n)
    clist_remove(n);
  return n;
}

/**
 * Merge two lists by inserting the list \p what just after the node \p after in a different list.
 * The first list is then cleared.
 **/
static inline void clist_insert_list_after(clist *what, cnode *after)
{
  if (!clist_empty(what))
    {
      cnode *w = &what->head;
      w->prev->next = after->next;
      after->next->prev = w->prev;
      w->next->prev = after;
      after->next = w->next;
      clist_init(what);
    }
}

/**
 * Move all items from a source list to a destination list. The source list
 * becomes empty, the original contents of the destination list are destroyed.
 **/
static inline void clist_move(clist *to, clist *from)
{
  clist_init(to);
  clist_insert_list_after(from, &to->head);
  clist_init(from);
}

/**
 * Compute the number of nodes in \p l. Beware of linear time complexity.
 **/
static inline unsigned int clist_size(clist *l)
{
  unsigned int i = 0;
  CLIST_FOR_EACH(cnode *, n, *l)
    i++;
  return i;
}

/**
 * Remove a node \p n and mark it as unlinked by setting the previous and next pointers to NULL.
 **/
static inline void clist_unlink(cnode *n)
{
  clist_remove(n);
  n->prev = n->next = NULL;
}

/**
 * Remove the first node on \p l and mark it as unlinked.
 * Return the pointer to that node or NULL.
 **/
static inline void *clist_unlink_head(clist *l)
{
  cnode *n = clist_head(l);
  if (n)
    clist_unlink(n);
  return n;
}

/**
 * Remove the last node on \p l and mark it as unlinked.
 * Return the pointer to that node or NULL.
 **/
static inline void *clist_unlink_tail(clist *l)
{
  cnode *n = clist_tail(l);
  if (n)
    clist_unlink(n);
  return n;
}

/**
 * Check if a node is linked a list. Unlinked nodes are recognized by having their
 * previous and next pointers equal to NULL. Returns 0 or 1.
 *
 * Nodes initialized to all zeroes are unlinked, inserting a node anywhere in a list
 * makes it linked. Normal removal functions like \ref clist_remove() do not mark nodes
 * as unlinked, you need to call \ref clist_unlink() instead.
 **/
static inline int clist_is_linked(cnode *n)
{
  return !!n->next;
}

#endif