1 /*
2 **      cdecl -- C gibberish translator
3 **      src/slist.h
4 **
5 **      Copyright (C) 2017-2021  Paul J. Lucas
6 **
7 **      This program is free software: you can redistribute it and/or modify
8 **      it under the terms of the GNU General Public License as published by
9 **      the Free Software Foundation, either version 3 of the License, or
10 **      (at your option) any later version.
11 **
12 **      This program is distributed in the hope that it will be useful,
13 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
14 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 **      GNU General Public License for more details.
16 **
17 **      You should have received a copy of the GNU General Public License
18 **      along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 #ifndef cdecl_slist_H
22 #define cdecl_slist_H
23 
24 /**
25  * @file
26  * Declares a singly-linked-list data structure and functions to manipulate it.
27  */
28 
29 // local
30 #include "pjl_config.h"                 /* must go first */
31 #include "util.h"
32 
33 /// @cond DOXYGEN_IGNORE
34 
35 // standard
36 #include <stdbool.h>
37 #include <stddef.h>                     /* for NULL, size_t */
38 
39 _GL_INLINE_HEADER_BEGIN
40 #ifndef SLIST_INLINE
41 # define SLIST_INLINE _GL_INLINE
42 #endif /* SLIST_INLINE */
43 
44 /// @endcond
45 
46 /**
47  * @defgroup slist-group Singly-Linked List
48  * Types and functions for manipulating singly-linked lists.
49  * @{
50  */
51 
52 /**
53  * Convenience macro for iterating over all the nodes of \a SLIST.
54  *
55  * @param VAR The slist_node loop variable.
56  * @param SLIST A pointer to the \ref slist to iterate over.
57  *
58  * @sa #FOREACH_SLIST_NODE_UNTIL()
59  */
60 #define FOREACH_SLIST_NODE(VAR,SLIST) \
61   FOREACH_SLIST_NODE_UNTIL( VAR, SLIST, /*END=*/NULL )
62 
63 /**
64  * Convenience macro for iterating over the nodes of \a SLIST up to but not
65  * including \a END.
66  *
67  * @param VAR The slist_node loop variable.
68  * @param SLIST A pointer to the \ref slist to iterate over.
69  * @param END A pointer to the node to end before; may be NULL.
70  *
71  * @sa #FOREACH_SLIST_NODE()
72  */
73 #define FOREACH_SLIST_NODE_UNTIL(VAR,SLIST,END) \
74   for ( slist_node_t *VAR = CONST_CAST( slist_t*, SLIST )->head; VAR != (END); VAR = VAR->next )
75 
76 /**
77  * Creates a single-node \ref slist on the stack with \a NODE_DATA.
78  *
79  * @param VAR The name for the \ref slist variable.
80  * @param NODE_DATA A pointer to the node data.
81  */
82 #define SLIST_VAR_INIT(VAR,NODE_DATA)                                 \
83   slist_node_t VAR##_node = { NULL, CONST_CAST(void*, (NODE_DATA)) }; \
84   slist_t const VAR = { &VAR##_node, &VAR##_node, 1 }
85 
86 ///////////////////////////////////////////////////////////////////////////////
87 
88 typedef struct slist      slist_t;
89 typedef struct slist_node slist_node_t;
90 
91 /**
92  * The signature for a function passed to slist_cmp() used to compare data
93  * associated with each node (if necessary).
94  *
95  * @param i_data A pointer to the first data to compare.
96  * @param j_data A pointer to the second data to compare.
97  * @return Returns a number less than 0, 0, or greater than 0 if \a i_data is
98  * less than, equal to, or greater than \a j_data, respectively.
99  */
100 typedef int (*slist_cmp_fn_t)( void const *i_data, void const *j_data );
101 
102 /**
103  * The signature for a function passed to slist_dup() used to duplicate data
104  * associated with each node (if necessary).
105  *
106  * @param data A pointer to the data to duplicate.
107  * @return Returns a duplicate of \a data.
108  */
109 typedef void* (*slist_dup_fn_t)( void const *data );
110 
111 /**
112  * The signature for a function passed to slist_cleanup() used to free data
113  * associated with each node (if necessary).
114  *
115  * @param data A pointer to the data to free.
116  */
117 typedef void (*slist_free_fn_t)( void *data );
118 
119 /**
120  * The signature for a function passed to slist_free_if() used to determine
121  * whether a node should be freed.
122  *
123  * @param data A pointer to the data to check.
124  * @return Returns `true` only if the node should be freed.
125  */
126 typedef bool (*slist_pred_fn_t)( void *data );
127 
128 ///////////////////////////////////////////////////////////////////////////////
129 
130 /**
131  * Singly-linked list.
132  */
133 struct slist {
134   slist_node_t *head;                   ///< Pointer to list head.
135   slist_node_t *tail;                   ///< Pointer to list tail.
136   size_t        len;                    ///< Length of list.
137 };
138 
139 /**
140  * Singly-linked-list node.
141  */
142 struct slist_node {
143   slist_node_t *next;                   ///< Pointer to next node or NULL.
144   void         *data;                   ///< Pointer to user data.
145 };
146 
147 ////////// extern functions ///////////////////////////////////////////////////
148 
149 /**
150  * Cleans-up all memory associated with \a list but _not_ \a list itself.
151  *
152  * @param list A pointer to the list to clean up.  If NULL, does nothing;
153  * otherwise, reinitializes \a list upon completion.
154  * @param free_fn A pointer to a function to use to free the data at each node
155  * of \a list or NULL if none is required.
156  *
157  * @sa slist_free_if()
158  * @sa slist_init()
159  */
160 void slist_cleanup( slist_t *list, slist_free_fn_t free_fn );
161 
162 /**
163  * Compares two lists.
164  *
165  * @param i_list The first list.
166  * @param j_list The second list.
167  * @param cmp_fn A pointer to a function to use to compare data at each node of
168  * \a i_list and \a j_list or NULL if none is required (hence the data will be
169  * compared directly as signed integers).
170  * @return Returns a number less than 0, 0, or greater than 0 if \a i_list is
171  * less than, equal to, or greater than \a j_list, respectively.
172  */
173 PJL_WARN_UNUSED_RESULT
174 int slist_cmp( slist_t const *i_list, slist_t const *j_list,
175                slist_cmp_fn_t cmp_fn );
176 
177 /**
178  * Duplicates \a src_list and all of its nodes.
179  *
180  * @param src_list The \ref slist to duplicate; may ne NULL.
181  * @param n The number of nodes to duplicate; -1 is equivalent to slist_len().
182  * @param dup_fn A pointer to a function to use to duplicate the data at each
183  * node of \a src_list or NULL if none is required (hence a shallow copy will
184  * be done).
185  * @return Returns a duplicate of \a src_list.  The caller is responsible for
186  * calling slist_cleanup() on it.
187  *
188  * @sa slist_cleanup()
189  */
190 PJL_WARN_UNUSED_RESULT
191 slist_t slist_dup( slist_t const *src_list, ssize_t n, slist_dup_fn_t dup_fn );
192 
193 /**
194  * Gets whether \a list is empty.
195  *
196  * @param list A pointer to the \ref slist to check.
197  * @return Returns `true` only if \a list is empty.
198  *
199  * @note This is an O(1) operation.
200  *
201  * @sa slist_len()
202  */
203 SLIST_INLINE PJL_WARN_UNUSED_RESULT
slist_empty(slist_t const * list)204 bool slist_empty( slist_t const *list ) {
205   return list->head == NULL;
206 }
207 
208 /**
209  * Frees select nodes from \a list for which \a pred_fn returns `true`.
210  *
211  * @param list A pointer to the list to possibly free nodes from.
212  * @param pred_fn The predicate function to use.
213  *
214  * @note This function _only_ frees matching nodes from \a list and _not_ the
215  * data at each node.  If the data at each node needs to be freed, \a pred_fn
216  * can do that before returning `true`.
217  * @note This is an O(n) operation.
218  *
219  * @sa slist_cleanup()
220  */
221 void slist_free_if( slist_t *list, slist_pred_fn_t pred_fn );
222 
223 /**
224  * Initializes \a list.  This is not necessary for either global or `static`
225  * lists.
226  *
227  * @param list A pointer to the \ref slist to initialize.
228  *
229  * @sa slist_cleanup()
230  */
231 SLIST_INLINE
slist_init(slist_t * list)232 void slist_init( slist_t *list ) {
233   MEM_ZERO( list );
234 }
235 
236 /**
237  * Gets the length of \a list.
238  *
239  * @param list A pointer to the \ref slist to get the length of.
240  * @return Returns said length.
241  *
242  * @note This is an O(1) operation.
243  *
244  * @sa slist_empty()
245  */
246 SLIST_INLINE PJL_WARN_UNUSED_RESULT
slist_len(slist_t const * list)247 size_t slist_len( slist_t const *list ) {
248   return list->len;
249 }
250 
251 /**
252  * Peeks at the data at \a offset of \a list.
253  *
254  * @param list A pointer to the \ref slist.
255  * @param offset The offset (starting at 0) of the data to get.
256  * @return Returns the data from the node at \a offset or NULL if \a offset
257  * &ge; slist_len().
258  *
259  * @note This is an O(n) operation.
260  *
261  * @sa slist_peek_atr()
262  * @sa slist_peek_head()
263  * @sa slist_peek_tail()
264  */
265 PJL_WARN_UNUSED_RESULT
266 void* slist_peek_at( slist_t const *list, size_t offset );
267 
268 /**
269  * Peeks at the data at \a roffset from the tail of \a list.
270  *
271  * @param list A pointer to the \ref slist.
272  * @param roffset The reverse offset (starting at 0) of the data to get.
273  * @return Returns the data from the node at \a roffset or NULL if \a roffset
274  * &ge; slist_len().
275  *
276  * @note This is an O(n) operation.
277  *
278  * @sa slist_peek_at()
279  * @sa slist_peek_head()
280  * @sa slist_peek_tail()
281  */
282 SLIST_INLINE PJL_WARN_UNUSED_RESULT
slist_peek_atr(slist_t const * list,size_t roffset)283 void* slist_peek_atr( slist_t const *list, size_t roffset ) {
284   return roffset < list->len ?
285     slist_peek_at( list, list->len - (roffset + 1) ) : NULL;
286 }
287 
288 /**
289  * Peeks at the data at the head of \a list.
290  *
291  * @param list A pointer to the \ref slist.
292  * @return Returns the data from the node at the head of \a list or NULL if \a
293  * list is empty.
294  *
295  * @note This is an O(1) operation.
296  *
297  * @sa slist_peek_at()
298  * @sa slist_peek_atr()
299  * @sa slist_peek_tail()
300  */
301 SLIST_INLINE PJL_WARN_UNUSED_RESULT
slist_peek_head(slist_t const * list)302 void* slist_peek_head( slist_t const *list ) {
303   return list->head != NULL ? list->head->data : NULL;
304 }
305 
306 /**
307  * Peeks at the data at the tail of \a list.
308  *
309  * @param list A pointer to the \ref slist.
310  * @return Returns the data from the node at the tail of \a list or NULL if \a
311  * list is empty.
312  *
313  * @note This is an O(1) operation.
314  *
315  * @sa slist_peek_at()
316  * @sa slist_peek_atr()
317  * @sa slist_peek_head()
318  */
319 SLIST_INLINE PJL_WARN_UNUSED_RESULT
slist_peek_tail(slist_t const * list)320 void* slist_peek_tail( slist_t const *list ) {
321   return list->tail != NULL ? list->tail->data : NULL;
322 }
323 
324 /**
325  * Pops data from the head of \a list.
326  *
327  * @param list The pointer to the \ref slist.
328  * @return Returns the data from the head of \a list.  The caller is
329  * responsible for freeing it (if necessary).
330  *
331  * @note This is an O(1) operation.
332  */
333 PJL_WARN_UNUSED_RESULT
334 void* slist_pop_head( slist_t *list );
335 
336 /**
337  * Pushes a node onto the head of \a list.
338  *
339  * @param list A pointer to the \ref slist.
340  * @param data The pointer to the data to add.
341  *
342  * @note This is an O(1) operation.
343  *
344  * @sa slist_push_list_head()
345  * @sa slist_push_tail()
346  */
347 void slist_push_head( slist_t *list, void *data );
348 
349 /**
350  * Pushes \a src_list onto the head of \a dst_list.
351  *
352  * @param dst_list The \ref slist to push onto.
353  * @param src_list The \ref slist to push.  It is made empty.
354  *
355  * @note This is an O(1) operation.
356  *
357  * @sa slist_push_head()
358  * @sa slist_push_list_tail()
359  */
360 void slist_push_list_head( slist_t *dst_list, slist_t *src_list );
361 
362 /**
363  * Pushes \a src_list onto the tail of \a dst_list.
364  *
365  * @param dst_list The \ref slist to push onto.
366  * @param src_list The \ref slist to push.  It is made empty.
367  *
368  * @note This is an O(1) operation.
369  *
370  * @sa slist_push_list_head()
371  * @sa slist_push_tail()
372  */
373 void slist_push_list_tail( slist_t *dst_list, slist_t *src_list );
374 
375 /**
376  * Appends \a data onto the tail of \a list.
377  *
378  * @param list The \ref slist to push onto.
379  * @param data The data to pushed.
380  *
381  * @note This is an O(1) operation.
382  *
383  * @sa slist_push_head()
384  * @sa slist_push_list_tail()
385  */
386 void slist_push_tail( slist_t *list, void *data );
387 
388 ///////////////////////////////////////////////////////////////////////////////
389 
390 /** @} */
391 
392 _GL_INLINE_HEADER_END
393 
394 #endif /* cdecl_slist_H */
395 /* vim:set et sw=2 ts=2: */
396