aboutsummaryrefslogtreecommitdiff
path: root/include/doubly-linked-list.h
blob: 3f5ea2808f992a7de95ca5135ca1305ef82b900d (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
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
/* Manipulate doubly linked lists.
   Copyright (C) 2025 Free Software Foundation, Inc.

   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, see <http://www.gnu.org/licenses/>.  */


#ifndef _DOUBLY_LINKED_LIST_H
#define _DOUBLY_LINKED_LIST_H

/* Doubly linked list implementation enforcing typing.

   This implementation of doubly linked list tries to achieve the enforcement of
   typing similarly to C++ templates, but without encapsulation.

   All the functions are prefixed with the type of the value: "AType_xxx".
   Some functions are prefixed with "_AType_xxx" and are not part of the public
   API, so should not be used, except for _##LTYPE##_merge_sort with a caveat
   (see note above its definition).

   Each function (### is a placeholder for method name) has a macro for:
   (1) its invocation LINKED_LIST_###(LTYPE).
   (2) its prototype LINKED_LIST_DECL_###(A, A2, scope). To add in a header
       file, or a source file for forward declaration. 'scope' should be set
       respectively to 'extern', or 'static'.
   (3) its definition LINKED_LIST_DEFN_###(A, A2, scope). To add in a source
       file with the 'scope' set respectively to nothing, or 'static' depending
       on (2).

   Data structures requirements:
   - LTYPE corresponds to the node of a doubly linked list. It needs to define
     attributes 'prev' and 'next' which are pointers on the type of a node.
     For instance:
       struct my_list_node
       {
	 T value;
	 struct my_list_node *prev;
	 struct my_list_node *next;
       };
   - LWRAPPERTYPE is a structure wrapping the nodes and others metadata (first,
     last, size).
 */


/* Mutative operations:
    - append
    - prepend
    - insert_before
    - pop_front
    - pop_back
    - remove
    - swap
   The header and body of each of those operation can be declared individually,
   or as a whole via LINKED_LIST_MUTATIVE_OPS_PROTOTYPE for the prototypes, and
   LINKED_LIST_MUTATIVE_OPS_DECL for the implementations.  */

/* Append the given node new_ to the exising list.
   Precondition: prev and next of new_ must be NULL.  */
#define LINKED_LIST_APPEND(LTYPE)		LTYPE##_append

#define LINKED_LIST_DECL_APPEND(LWRAPPERTYPE, LTYPE, EXPORT)		\
  EXPORT void								\
  LTYPE##_append (LWRAPPERTYPE *wrapper, LTYPE *new_)

#define LINKED_LIST_DEFN_APPEND(LWRAPPERTYPE, LTYPE, EXPORT)		\
EXPORT void								\
LTYPE##_append (LWRAPPERTYPE *wrapper, LTYPE *new_)			\
{									\
  if (wrapper->last == NULL)						\
    wrapper->first = new_;						\
  else									\
    {									\
      new_->prev = wrapper->last;					\
      wrapper->last->next = new_;					\
    }									\
  wrapper->last = new_;							\
  ++wrapper->size;							\
}

/* Prepend the given node new_ to the existing list.
   Precondition: prev and next of new_ must be NULL.  */
#define LINKED_LIST_PREPEND(LTYPE)		LTYPE##_prepend

#define LINKED_LIST_DECL_PREPEND(LWRAPPERTYPE, LTYPE, EXPORT)		\
  EXPORT void								\
  LTYPE##_prepend (LWRAPPERTYPE *wrapper, LTYPE *new_)

#define LINKED_LIST_DEFN_PREPEND(LWRAPPERTYPE, LTYPE, EXPORT)		\
EXPORT void								\
LTYPE##_prepend (LWRAPPERTYPE *wrapper, LTYPE *new_)			\
{									\
  if (wrapper->first == NULL)						\
    wrapper->last = new_;						\
  else									\
    {									\
      new_->next = wrapper->first;					\
      wrapper->first->prev = new_;					\
    }									\
  wrapper->first = new_;						\
  ++wrapper->size;							\
}

/* Insert the given node new_ before 'where' in the existing list.
   If where == NULL, the insertion is equivalent to an append.
   If where == first, the insertion is equivalent to a prepend.  */
#define LINKED_LIST_INSERT_BEFORE(LTYPE)	LTYPE##_insert_before

#define LINKED_LIST_DECL_INSERT_BEFORE(LWRAPPERTYPE, LTYPE, EXPORT)	\
  EXPORT void								\
  LTYPE##_insert_before (LWRAPPERTYPE *wrapper,				\
			 LTYPE *new_,					\
			 LTYPE *where)

#define LINKED_LIST_DEFN_INSERT_BEFORE(LWRAPPERTYPE, LTYPE, EXPORT)	\
EXPORT void								\
LTYPE##_insert_before (LWRAPPERTYPE *wrapper,				\
		       LTYPE *new_,					\
		       LTYPE *where)					\
{									\
  if (where == wrapper->first)						\
    LTYPE##_prepend (wrapper, new_);					\
  else if (where == NULL)						\
    LTYPE##_append (wrapper, new_);					\
  else									\
    {									\
      where->prev->next = new_;						\
      new_->prev = where->prev;						\
      where->prev = new_;						\
      new_->next = where;						\
      ++wrapper->size;							\
    }									\
}

/* Pop the first node of the list.  */
#define LINKED_LIST_POP_FRONT(LTYPE)		LTYPE##_pop_front

#define LINKED_LIST_DECL_POP_FRONT(LWRAPPERTYPE, LTYPE, EXPORT)		\
  EXPORT LTYPE *							\
  LTYPE##_pop_front (LWRAPPERTYPE *wrapper)

#define LINKED_LIST_DEFN_POP_FRONT(LWRAPPERTYPE, LTYPE, EXPORT)		\
EXPORT LTYPE *								\
LTYPE##_pop_front (LWRAPPERTYPE *wrapper)				\
{									\
  LTYPE *front_node = wrapper->first;					\
  if (front_node != NULL)						\
    {									\
      wrapper->first = front_node->next;				\
      if (wrapper->last == front_node)					\
	wrapper->last = NULL;						\
      else								\
	{								\
	  front_node->next->prev = NULL;				\
	  front_node->next = NULL;					\
	}								\
      front_node->next = NULL;						\
      --wrapper->size;							\
    }									\
  return front_node;							\
}

/* Pop the last node of the list.  */
#define LINKED_LIST_POP_BACK(LTYPE)		LTYPE##_pop_back

#define LINKED_LIST_DECL_POP_BACK(LWRAPPERTYPE, LTYPE, EXPORT)		\
  EXPORT LTYPE *							\
  LTYPE##_pop_back (LWRAPPERTYPE *wrapper)

#define LINKED_LIST_DEFN_POP_BACK(LWRAPPERTYPE, LTYPE, EXPORT)		\
EXPORT LTYPE *								\
LTYPE##_pop_back (LWRAPPERTYPE *wrapper)				\
{									\
  LTYPE *back_node = wrapper->last;					\
  if (back_node != NULL)						\
    {									\
      wrapper->last = back_node->prev;					\
      if (wrapper->first == back_node)					\
	wrapper->first = NULL;						\
      else								\
	{								\
	  back_node->prev->next = NULL;					\
	  back_node->prev = NULL;					\
	}								\
      back_node->prev = NULL;						\
      --wrapper->size;							\
    }									\
  return back_node;							\
}

/* Remove the given node from the existing list, and return the previous
   node.  */
#define LINKED_LIST_REMOVE(LTYPE)		LTYPE##_remove

#define LINKED_LIST_DECL_REMOVE(LWRAPPERTYPE, LTYPE, EXPORT)		\
  EXPORT LTYPE *							\
  LTYPE##_remove (LWRAPPERTYPE *wrapper, LTYPE *node)

#define LINKED_LIST_DEFN_REMOVE(LWRAPPERTYPE, LTYPE, EXPORT)		\
EXPORT LTYPE *								\
LTYPE##_remove (LWRAPPERTYPE *wrapper, LTYPE *node)			\
{									\
  LTYPE *previous = NULL;						\
									\
  if (node->prev != NULL)						\
    {									\
      node->prev->next = node->next;					\
      if (node->next == NULL)						\
	wrapper->last = node->prev;					\
      else								\
	node->next->prev = node->prev;					\
      previous = node->prev;						\
      node->next = NULL;						\
      node->prev = NULL;						\
      --wrapper->size;							\
    }									\
  else									\
    LTYPE##_pop_front (wrapper);					\
									\
  return previous;							\
}

/* Generic swap.  */
#define LINKED_LIST_SWAP(LTYPE)			LTYPE##_swap

#define LINKED_LIST_DECL_SWAP(LWRAPPERTYPE, LTYPE, EXPORT)		\
  EXPORT void								\
  LTYPE##_swap (LWRAPPERTYPE *wrapper, LTYPE *node1, LTYPE *node2)

/* Swap two nodes in a list.  */
#define LINKED_LIST_DEFN_SWAP(LWRAPPERTYPE, LTYPE, EXPORT)		\
EXPORT void								\
LTYPE##_swap (LWRAPPERTYPE *wrapper, LTYPE *node1, LTYPE *node2)	\
{									\
  LTYPE *prev1 = node1->prev;						\
  LTYPE *next1 = node1->next;						\
  LTYPE *prev2 = node2->prev;						\
  LTYPE *next2 = node2->next;						\
									\
  if (prev1 != NULL)							\
    prev1->next = node2;						\
  else									\
    wrapper->first = node2;						\
  if (prev2 != NULL)							\
    prev2->next = node1;						\
  else									\
    wrapper->first = node1;						\
									\
  if (next1 != NULL)							\
    next1->prev = node2;						\
  else									\
    wrapper->last = node2;						\
  if (next2 != NULL)							\
    next2->prev = node1;						\
  else									\
    wrapper->last = node1;						\
									\
  {									\
    LTYPE *temp = node1->next;						\
    node1->next = node2->next;						\
    node2->next = temp;							\
  }									\
  {									\
    LTYPE *temp = node1->prev;						\
    node1->prev = node2->prev;						\
    node2->prev = temp;							\
  }									\
}

/* Note: all the mutative operations below also update the data in the wrapper,
   i.e. first, last and size.  */
#define LINKED_LIST_MUTATIVE_OPS_PROTOTYPE(LWRAPPERTYPE, LTYPE, EXPORT)	\
  LINKED_LIST_DECL_APPEND(LWRAPPERTYPE, LTYPE, EXPORT);			\
  LINKED_LIST_DECL_PREPEND(LWRAPPERTYPE, LTYPE, EXPORT);		\
  LINKED_LIST_DECL_INSERT_BEFORE(LWRAPPERTYPE, LTYPE, EXPORT);		\
  LINKED_LIST_DECL_POP_FRONT(LWRAPPERTYPE, LTYPE, EXPORT);		\
  LINKED_LIST_DECL_POP_BACK(LWRAPPERTYPE, LTYPE, EXPORT);		\
  LINKED_LIST_DECL_REMOVE(LWRAPPERTYPE, LTYPE, EXPORT);			\
  LINKED_LIST_DECL_SWAP(LWRAPPERTYPE, LTYPE, EXPORT)

#define LINKED_LIST_MUTATIVE_OPS_DECL(LWRAPPERTYPE, LTYPE, EXPORT)	\
  LINKED_LIST_DEFN_APPEND(LWRAPPERTYPE, LTYPE, EXPORT)			\
  LINKED_LIST_DEFN_PREPEND(LWRAPPERTYPE, LTYPE, EXPORT)			\
  LINKED_LIST_DEFN_INSERT_BEFORE(LWRAPPERTYPE, LTYPE, EXPORT)		\
  LINKED_LIST_DEFN_POP_FRONT(LWRAPPERTYPE, LTYPE, EXPORT)		\
  LINKED_LIST_DEFN_POP_BACK(LWRAPPERTYPE, LTYPE, EXPORT)		\
  LINKED_LIST_DEFN_REMOVE(LWRAPPERTYPE, LTYPE, EXPORT)			\
  LINKED_LIST_DEFN_SWAP(LWRAPPERTYPE, LTYPE, EXPORT)


/* Sorting.  */

#define LINKED_LIST_MERGE_SORT_(LTYPE)	LTYPE##_merge_sort_

#define LINKED_LIST_MERGE_SORT(LTYPE)	LTYPE##_merge_sort

#define LINKED_LIST_MERGE_SORT_PROTOTYPE_(LTYPE, EXPORT)		\
  EXPORT LTYPE *							\
  LTYPE##_merge_sort_ (LTYPE *node,					\
		       int (*fn_cmp) (const LTYPE *, const LTYPE *))

#define LINKED_LIST_MERGE_SORT_PROTOTYPE(LWRAPPERTYPE, LTYPE, EXPORT)	\
  EXPORT void								\
  LTYPE##_merge_sort (LWRAPPERTYPE *wrapper,				\
		      int (*fn_cmp) (const LTYPE *, const LTYPE *))

/* Note: all the functions and macros below starting with "_" should be
   considered private.  */

/* Compute the middle element of the list based on the turtle and hare
   approach, i.e. the hare runs twice faster than the turtle.  */
#define _MERGE_SORT_IMPL_COMPUTE_TURTLE(LTYPE)				\
static inline LTYPE *							\
LTYPE##_merge_sort_compute_turtle_ (LTYPE *node)			\
{									\
  if (node == NULL)							\
    return node;							\
									\
  LTYPE *turtle = node, *hare = node->next;				\
  while (hare != NULL && hare->next != NULL)				\
    {									\
      turtle = turtle->next;						\
      hare = hare->next->next;						\
    }									\
  return turtle;							\
}

/* Append n at the end of l_out, and return the next node after n.
   l_out and l_last should be ideally encapsulated into a list structure
   but this is overkill for what we need here.  */
#define _MERGE_SORT_IMPL_OUT_APPEND(LTYPE)				\
static inline LTYPE *							\
LTYPE##_merge_sort_out_append_ (LTYPE **l_out, LTYPE **l_last,		\
				LTYPE *n)				\
{									\
  if (*l_last == NULL)							\
    {									\
      *l_last = n;							\
      *l_out = n;							\
      n->prev = NULL;							\
    }									\
  else									\
    {									\
      (*l_last)->next = n;						\
      n->prev = *l_last;						\
      *l_last = n;							\
    }									\
									\
  return n->next;							\
}

/* Merge two sorted lists together.
   The returned value corresponds to the first element of the list.
   Note: both input lists are invalidated after the call.  */
#define _MERGE_SORT_IMPL_MERGE(LTYPE)					\
static inline LTYPE *							\
LTYPE##_merge_sort_merge_ (LTYPE *l_left, LTYPE *l_right,		\
			   int (*fn_cmp) (const LTYPE *, const LTYPE *))\
{									\
  if (l_left == NULL)							\
    return l_right;							\
  else if (l_right == NULL)						\
    return l_left;							\
									\
  LTYPE *l_out = NULL, *l_last = NULL;					\
									\
  LTYPE *l_l = l_left, *l_r = l_right;					\
  while (l_l != NULL && l_r != NULL)					\
    {									\
      int cmp = fn_cmp (l_l, l_r);					\
      if (cmp <= 0)							\
	l_l = LTYPE##_merge_sort_out_append_ (&l_out, &l_last, l_l);	\
      else								\
	l_r = LTYPE##_merge_sort_out_append_ (&l_out, &l_last, l_r);	\
    }									\
									\
  LTYPE *l_remaining = (l_l != NULL) ? l_l : l_r;			\
  while (l_remaining != NULL)						\
    l_remaining =							\
      LTYPE##_merge_sort_out_append_ (&l_out, &l_last, l_remaining);	\
									\
  return l_out;								\
}

/* Merge sort implementation taking the first node of the list to sort,
   and the comparison function. Returns the first node of the sorted list.
   Note: use this if you don't care about updating the information in the
   wrapper.  */
#define _MERGE_SORT_DEFN_SORT(LTYPE, EXPORT)				\
EXPORT LTYPE *								\
LTYPE##_merge_sort_ (LTYPE *node,					\
		     int (*fn_cmp)(const LTYPE *, const LTYPE *))	\
{									\
  if (node == NULL)							\
    return NULL;							\
  else if (node->next == NULL)						\
    return node;							\
									\
  LTYPE *left_end = LTYPE##_merge_sort_compute_turtle_ (node);		\
  LTYPE *left_begin = node;						\
  LTYPE *right_begin = left_end->next;					\
  /* break the list. */							\
  left_end->next = NULL;						\
  right_begin->prev = NULL;						\
									\
  left_begin = LTYPE##_merge_sort_ (left_begin, fn_cmp);		\
  right_begin = LTYPE##_merge_sort_ (right_begin, fn_cmp);		\
  return LTYPE##_merge_sort_merge_ (left_begin, right_begin, fn_cmp);	\
}

/* Merge sort wrapper that the end-user should be using as it updates the
   first and last metadata of the list in wrapper as well.
   If the user does not want to pay the cost of the update of the data,
   it can directly use _##LTYPE##_merge_sort_merge.  */
#define _MERGE_SORT_DEFN_WRAPPER_SORT(LWRAPPERTYPE, LTYPE, EXPORT)	\
EXPORT void								\
LTYPE##_merge_sort (LWRAPPERTYPE *wrapper,				\
		    int (*fn_cmp) (const LTYPE *, const LTYPE *))	\
{									\
  wrapper->first = LTYPE##_merge_sort_ (wrapper->first, fn_cmp);	\
									\
  if (wrapper->first == NULL || wrapper->first->next == NULL)		\
    wrapper->last = wrapper->first;					\
  else									\
    for (LTYPE *node = wrapper->first;					\
	 node != NULL;							\
	 node = node->next)						\
      wrapper->last = node;						\
}

#define LINKED_LIST_MERGE_SORT_DECL(LWRAPPERTYPE, LTYPE, EXPORT)	\
  _MERGE_SORT_IMPL_COMPUTE_TURTLE(LTYPE)				\
  _MERGE_SORT_IMPL_OUT_APPEND(LTYPE)					\
  _MERGE_SORT_IMPL_MERGE(LTYPE)						\
  _MERGE_SORT_DEFN_SORT(LTYPE, EXPORT)					\
  _MERGE_SORT_DEFN_WRAPPER_SORT(LWRAPPERTYPE, LTYPE, EXPORT)

#endif /* _DOUBLY_LINKED_LIST_H */