1 #include <assert.h>
2
3 #include "list.h"
4 #include "osi.h"
5
6 typedef struct list_node_t {
7 struct list_node_t *next;
8 void *data;
9 } list_node_t;
10
11 typedef struct list_t {
12 list_node_t *head;
13 list_node_t *tail;
14 size_t length;
15 list_free_cb free_cb;
16 } list_t;
17
18 static list_node_t *list_free_node_(list_t *list, list_node_t *node);
19
20 // Returns a new, empty list. Returns NULL if not enough memory could be allocated
21 // for the list structure. The returned list must be freed with |list_free|. The
22 // |callback| specifies a function to be called whenever a list element is removed
23 // from the list. It can be used to release resources held by the list element, e.g.
24 // memory or file descriptor. |callback| may be NULL if no cleanup is necessary on
25 // element removal.
list_new(list_free_cb callback)26 list_t *list_new(list_free_cb callback) {
27 list_t *list = (list_t *)calloc(sizeof(list_t), 1);
28 if (list)
29 list->free_cb = callback;
30 return list;
31 }
32
33 // Frees the list. This function accepts NULL as an argument, in which case it
34 // behaves like a no-op.
list_free(list_t * list)35 void list_free(list_t *list) {
36 if (list != NULL)
37 list_clear(list);
38
39 free(list);
40 }
41
42 // Returns true if the list is empty (has no elements), false otherwise.
43 // Note that a NULL list is not the same as an empty list. This function
44 // does not accept a NULL list.
list_is_empty(const list_t * list)45 bool list_is_empty(const list_t *list) {
46 assert(list != NULL);
47 return (list->length == 0);
48 }
49
50 // Returns the length of the list. This function does not accept a NULL list.
list_length(const list_t * list)51 size_t list_length(const list_t *list) {
52 assert(list != NULL);
53 return list->length;
54 }
55
56 // Returns the first element in the list without removing it. |list| may not
57 // be NULL or empty.
list_front(const list_t * list)58 void *list_front(const list_t *list) {
59 assert(list != NULL);
60 assert(!list_is_empty(list));
61
62 return list->head->data;
63 }
64
65 // Returns the last element in the list without removing it. |list| may not
66 // be NULL or empty.
list_back(const list_t * list)67 void *list_back(const list_t *list) {
68 assert(list != NULL);
69 assert(!list_is_empty(list));
70
71 return list->tail->data;
72 }
73
list_insert_after(list_t * list,list_node_t * prev_node,void * data)74 bool list_insert_after(list_t *list, list_node_t *prev_node, void *data) {
75 assert(list != NULL);
76 assert(node != NULL);
77 assert(data != NULL);
78
79 list_node_t *node = (list_node_t *)malloc(sizeof(list_node_t));
80 if (!node)
81 return false;
82
83 node->next = prev_node->next;
84 node->data = data;
85 prev_node->next = node;
86 if (list->tail == prev_node)
87 list->tail = node;
88 ++list->length;
89 return true;
90 }
91
92 // Inserts |data| at the beginning of |list|. Neither |data| nor |list| may be NULL.
93 // This function does not make a copy of |data| so the pointer must remain valid
94 // at least until the element is removed from the list or the list is freed.
95 // Returns true if |data| could be inserted, false otherwise (e.g. out of memory).
list_prepend(list_t * list,void * data)96 bool list_prepend(list_t *list, void *data) {
97 assert(list != NULL);
98 assert(data != NULL);
99
100 list_node_t *node = (list_node_t *)malloc(sizeof(list_node_t));
101 if (!node)
102 return false;
103 node->next = list->head;
104 node->data = data;
105 list->head = node;
106 if (list->tail == NULL)
107 list->tail = list->head;
108 ++list->length;
109 return true;
110 }
111
112 // Inserts |data| at the end of |list|. Neither |data| nor |list| may be NULL.
113 // This function does not make a copy of |data| so the pointer must remain valid
114 // at least until the element is removed from the list or the list is freed.
115 // Returns true if |data| could be inserted, false otherwise (e.g. out of memory).
list_append(list_t * list,void * data)116 bool list_append(list_t *list, void *data) {
117 assert(list != NULL);
118 assert(data != NULL);
119
120 list_node_t *node = (list_node_t *)malloc(sizeof(list_node_t));
121 if (!node)
122 return false;
123 node->next = NULL;
124 node->data = data;
125 if (list->tail == NULL) {
126 list->head = node;
127 list->tail = node;
128 } else {
129 list->tail->next = node;
130 list->tail = node;
131 }
132 ++list->length;
133 return true;
134 }
135
136 // Removes |data| from the list. Neither |list| nor |data| may be NULL. If |data|
137 // is inserted multiple times in the list, this function will only remove the first
138 // instance. If a free function was specified in |list_new|, it will be called back
139 // with |data|. This function returns true if |data| was found in the list and removed,
140 // false otherwise.
list_remove(list_t * list,void * data)141 bool list_remove(list_t *list, void *data) {
142 assert(list != NULL);
143 assert(data != NULL);
144
145 if (list_is_empty(list))
146 return false;
147
148 if (list->head->data == data) {
149 list_node_t *next = list_free_node_(list, list->head);
150 if (list->tail == list->head)
151 list->tail = next;
152 list->head = next;
153 return true;
154 }
155
156 for (list_node_t *prev = list->head, *node = list->head->next; node; prev = node, node = node->next)
157 if (node->data == data) {
158 prev->next = list_free_node_(list, node);
159 if (list->tail == node)
160 list->tail = prev;
161 return true;
162 }
163
164 return false;
165 }
166
167 // Removes all elements in the list. Calling this function will return the list to the
168 // same state it was in after |list_new|. |list| may not be NULL.
list_clear(list_t * list)169 void list_clear(list_t *list) {
170 assert(list != NULL);
171 for (list_node_t *node = list->head; node; )
172 node = list_free_node_(list, node);
173 list->head = NULL;
174 list->tail = NULL;
175 list->length = 0;
176 }
177
178 // Iterates through the entire |list| and calls |callback| for each data element.
179 // If the list is empty, |callback| will never be called. It is safe to mutate the
180 // list inside the callback. If an element is added before the node being visited,
181 // there will be no callback for the newly-inserted node. Neither |list| nor
182 // |callback| may be NULL.
list_foreach(const list_t * list,list_iter_cb callback)183 void list_foreach(const list_t *list, list_iter_cb callback) {
184 assert(list != NULL);
185 assert(callback != NULL);
186
187 for (list_node_t *node = list->head; node; ) {
188 list_node_t *next = node->next;
189 callback(node->data);
190 node = next;
191 }
192 }
193
194 // Returns an iterator to the first element in |list|. |list| may not be NULL.
195 // The returned iterator is valid as long as it does not equal the value returned
196 // by |list_end|.
list_begin(const list_t * list)197 list_node_t *list_begin(const list_t *list) {
198 assert(list != NULL);
199 return list->head;
200 }
201
202 // Returns an iterator that points past the end of the list. In other words,
203 // this function returns the value of an invalid iterator for the given list.
204 // When an iterator has the same value as what's returned by this function, you
205 // may no longer call |list_next| with the iterator. |list| may not be NULL.
list_end(UNUSED_ATTR const list_t * list)206 list_node_t *list_end(UNUSED_ATTR const list_t *list) {
207 assert(list != NULL);
208 return NULL;
209 }
210
211 // Given a valid iterator |node|, this function returns the next value for the
212 // iterator. If the returned value equals the value returned by |list_end|, the
213 // iterator has reached the end of the list and may no longer be used for any
214 // purpose.
list_next(const list_node_t * node)215 list_node_t *list_next(const list_node_t *node) {
216 assert(node != NULL);
217 return node->next;
218 }
219
220 // Returns the value stored at the location pointed to by the iterator |node|.
221 // |node| must not equal the value returned by |list_end|.
list_node(const list_node_t * node)222 void *list_node(const list_node_t *node) {
223 assert(node != NULL);
224 return node->data;
225 }
226
list_free_node_(list_t * list,list_node_t * node)227 static list_node_t *list_free_node_(list_t *list, list_node_t *node) {
228 assert(list != NULL);
229 assert(node != NULL);
230
231 list_node_t *next = node->next;
232
233 if (list->free_cb)
234 list->free_cb(node->data);
235 free(node);
236 --list->length;
237
238 return next;
239 }
240