summaryrefslogtreecommitdiff
path: root/doc/inline_list.rst
blob: e60e89fb4a50a7d2eecf9352d9053155f038b5ed (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
=========================
Inline doubly-linked list
=========================

Classical linked-list implementations, such as HXdeque, either store the actual
data within a node, or indirectly through a pointer, but the “inline
doubly-linked list” instead does it reverse and has the list head within the
data structure.

A classic linked-list implementations with direct/indirect data blocks may look
like so:

.. code-block:: c

	struct package_desc {
		char *package_name;
		int version;

	};

	struct classic_direct_node {
		struct classic_direct_node *next, *prev;
		struct package_desc direct_data;
	};
	struct classic_indirect_node {
		struct classic_indirect_node *next, *prev;
		void *indirect_data;
	};

Whereas in an inline list, the list head (next,prev pointers) inlined into the data
block:

.. code-block:: c

	struct package_desc {
		struct HXlist_head list;
		char *package_name;
		int version;
	};

At first glance, an inline list does not look much different from ``struct
classic_direct_data``, it is mostly a viewpoint decision which struct is in the
foreground.


Synopsis
========

.. code-block:: c

	#include <libHX/list.h>

	struct HXlist_head {struct HXlist_head
		/* All fields considered private */
	};

	HXLIST_HEAD_INIT(name);
	HXLIST_HEAD(name);
	void HXlist_init(struct HXlist_head *list);
	void HXlist_add(struct HXlist_head *list, struct HXlist_head *elem);
	void HXlist_add_tail(struct HXlist_head *list, struct HXlist_head *elem);
	void HXlist_del(struct HXlist_head *element);
	bool HXlist_empty(const struct HXlist_head *list);

``HXLIST_HEAD_INIT``
	This macro expands to the static initializer for a list head.

``HXLIST_HEAD``
	This macro expands to the definition of a list head (i.e. ``struct
	HXlist_head name = HXLIST_HEAD_INIT;``).

``HXlist_init``
	Initializes the list head. This function is generally used when the
	list head is on the heap where the static initializer cannot be used.

``HXlist_add``
	Adds ``elem`` to the front of the list.

``HXlist_add_tail``
	Adds ``elem`` to the end of the list.

``HXlist_del``
	Deletes the given element from the list.

``HXlist_empty``
	Tests whether the list is empty. (Note: For clists, you could also use
	``clist->items == 0``).


Traversal
=========

Traversal is implemented using macros that expand to ``for()`` statements which
can syntactically be used like them, i.e. curly braces may be omitted if only
a single statement is in the body of the loop.

The head parameter specifies the list head (``struct HXlist_head``), ``pos``
specifies an iterator, also of type ``struct HXlist_head``. Lists can either be
traversed in forward direction, or, using the ``*_rev`` variants, in reverse
direction. The ``*_safe`` variants use a temporary ``n`` to hold the next
object in the list, which is needed when ``pos`` itself is going to be
inaccessible at the end of the block, through, for example, freeing its
encompassing object.

.. code-block:: c

	HXlist_for_each(pos, head)
	HXlist_for_each_rev(pos, head)
	HXlist_for_each_safe(pos, n, head)
	HXlist_for_each_rev_safe(pos, n, head)

``HXlist_for_each``
	Forward iteration over the list heads.

``HXlist_for_each_rev``
	Reverse iteration over the list heads.

``HXlist_for_each_safe``
	Forward iteration over the list heads that is safe against freeing pos.

``HXlist_for_each_rev_safe``
	Reverse iteration over the list heads that is safe against freeing pos.

The ``*_entry`` variants use an iterator ``pos`` of the type of the
encompassing object (e.g. ``struct item`` in below's example), so that the
manual ``HXlist_entry`` invocation is not needed. ``member`` is the name of the
list structure embedded into the item.

.. code-block:: c

	HXlist_for_each_entry(pos, head, member)HXlist_for_each_entry
	HXlist_for_each_entry_rev(pos, head, member)HXlist_for_each_entry_rev
	HXlist_for_each_entry_safe(pos, n, head, member)HXlist_for_each_entry_safe

``HXlist_for_each_entry``
	Forward iteration over the list elements.

``HXlist_for_each_entry_rev``
	Reverse iteration over the list elements.

``HXlist_for_each_entry_safe``
	Forward iteration over the list elements that is safe against freeing
	``pos``.


Examples
========

.. code-block:: c

	struct item {
		struct HXlist_head anchor;
		char name[32];
	};

	struct HXlist_head *e;
	struct item *i, *j;
	HXLIST_HEAD(list);

	i = malloc(sizeof(*i));
	HXlist_init(&e->anchor);
	strcpy(i->name, "foo");
	HXlist_add_tail(&list, &i->anchor);

	i = malloc(sizeof(*i));
	HXlist_init(&e->anchor);
	strcpy(i->name, "bar");
	HXlist_add_tail(&list, &i->anchor);

	HXlist_for_each(e, &list) {
		i = HXlist_entry(e, typeof(*i), anchor);
		printf("e=%p i=%p name=%s\n", e, i, i->name);
	}

	HXlist_for_each_entry(i, &list, anchor)
		printf("i=%p name=%s\n", i, i->name);

	HXlist_for_each_entry_rev(i, &list, anchor)
		printf("i=%p name=%s\n", i, i->name);

	HXlist_for_each_entry_safe(i, j, &list, anchor) {
		printf("i=%p name=%s\n", i, i->name);
		free(i);
	}


When to use HXdeque/HXlist
==========================

The choice whether to use HXdeque or HXlist/HXclist depends on whether one
wants the list head handling on the developer or on the library. Especially for
“atomic” and “small” data, it might be easier to just let HXdeque do the
management. Compare the following two code examples to store strings in a
HXdeque:

.. code-block:: c

	int main(int argc, const char **argv)
	{
		struct HXdeque *dq = HXdeque_init();
		while (--argc)
			 HXdeque_push(dq, ++argv);
		return 0;
	}

...and to store strings in a HXlist:

.. code-block:: c

	struct element {
		struct HXlist_head list;
		char *data;
	};

	int main(int main, const char **argv)
	{
		HXLIST_HEAD(lh);
		while (--argc) {
			struct element *e = malloc(sizeof(*e));
			e->data = *++argv;
			HXlist_init(&e->list);
			HXlist_add_tail(&e->list);
		}
		return 0;
	}

These examples assume that ``argv`` is persistent, which, for the sample, is
true.

With HXlist, one needs to have a struct with a ``HXlist_head`` in it, and if
one does not already have such a struct, e.g. by means of wanting to store more
than just one value, one will need to create it first, as shown, and this may
lead to an expansion of code.

This however does not mean that HXlist is the better solution over HXdeque for
data already available in a struct. As each struct has a ``list_head`` that is
unique to the node, it is not possible to share this data. Trying to add a
HXlist_head to another list is not going to end well, while HXdeque has no
problem with this as list heads are detached from the actual data in HXdeque.

Data can be added multiple times in a HXdeque without ill effects:

.. code-block:: c

	struct point p = {15, 30};
	HXdeque_push(dq, &p);
	HXdeque_push(dq, &p);

To support this, an extra allocation is needed on the other hand. In a HXlist,
to store *n* elements of compound data (e.g. ``struct point``), *n* allocations
are needed, assuming the list head is a stack object, and the points are not.
HXdeque will need at least *2n+1* allocations, *n* for the nodes, *n* for the
points and another for the head.