1 /**
2  * @file lv_ll.c
3  * Handle linked lists.
4  * The nodes are dynamically allocated by the 'lv_mem' module,
5  */
6 
7 /*********************
8  *      INCLUDES
9  *********************/
10 #include <stdint.h>
11 #include <string.h>
12 
13 #include "lv_ll.h"
14 #include "lv_mem.h"
15 
16 /*********************
17  *      DEFINES
18  *********************/
19 #define LL_NODE_META_SIZE (sizeof(lv_ll_node_t *) + sizeof(lv_ll_node_t *))
20 #define LL_PREV_P_OFFSET(ll_p) (ll_p->n_size)
21 #define LL_NEXT_P_OFFSET(ll_p) (ll_p->n_size + sizeof(lv_ll_node_t *))
22 
23 /**********************
24  *      TYPEDEFS
25  **********************/
26 
27 /**********************
28  *  STATIC PROTOTYPES
29  **********************/
30 static void node_set_prev(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * prev);
31 static void node_set_next(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * next);
32 
33 /**********************
34  *  STATIC VARIABLES
35  **********************/
36 
37 /**********************
38  *      MACROS
39  **********************/
40 
41 /**********************
42  *   GLOBAL FUNCTIONS
43  **********************/
44 
45 /**
46  * Initialize linked list
47  * @param ll_dsc pointer to ll_dsc variable
48  * @param node_size the size of 1 node in bytes
49  */
lv_ll_init(lv_ll_t * ll_p,uint32_t node_size)50 void lv_ll_init(lv_ll_t * ll_p, uint32_t node_size)
51 {
52     ll_p->head = NULL;
53     ll_p->tail = NULL;
54 #ifdef LV_MEM_ENV64
55     /*Round the size up to 8*/
56     if(node_size & 0x7) {
57         node_size = node_size & (~0x7);
58         node_size += 8;
59     }
60 #else
61     /*Round the size up to 4*/
62     if(node_size & 0x3) {
63         node_size = node_size & (~0x3);
64         node_size += 4;
65     }
66 #endif
67 
68     ll_p->n_size = node_size;
69 }
70 
71 /**
72  * Add a new head to a linked list
73  * @param ll_p pointer to linked list
74  * @return pointer to the new head
75  */
lv_ll_ins_head(lv_ll_t * ll_p)76 void * lv_ll_ins_head(lv_ll_t * ll_p)
77 {
78     lv_ll_node_t * n_new;
79 
80     n_new = lv_mem_alloc(ll_p->n_size + LL_NODE_META_SIZE);
81 
82     if(n_new != NULL) {
83         node_set_prev(ll_p, n_new, NULL);       /*No prev. before the new head*/
84         node_set_next(ll_p, n_new, ll_p->head); /*After new comes the old head*/
85 
86         if(ll_p->head != NULL) { /*If there is old head then before it goes the new*/
87             node_set_prev(ll_p, ll_p->head, n_new);
88         }
89 
90         ll_p->head = n_new;      /*Set the new head in the dsc.*/
91         if(ll_p->tail == NULL) { /*If there is no tail (1. node) set the tail too*/
92             ll_p->tail = n_new;
93         }
94     }
95 
96     return n_new;
97 }
98 
99 /**
100  * Insert a new node in front of the n_act node
101  * @param ll_p pointer to linked list
102  * @param n_act pointer a node
103  * @return pointer to the new head
104  */
lv_ll_ins_prev(lv_ll_t * ll_p,void * n_act)105 void * lv_ll_ins_prev(lv_ll_t * ll_p, void * n_act)
106 {
107     lv_ll_node_t * n_new;
108     lv_ll_node_t * n_prev;
109 
110     if(NULL == ll_p || NULL == n_act) return NULL;
111 
112     if(lv_ll_get_head(ll_p) == n_act) {
113         n_new = lv_ll_ins_head(ll_p);
114         if(n_new == NULL) return NULL;
115     } else {
116         n_new = lv_mem_alloc(ll_p->n_size + LL_NODE_META_SIZE);
117         if(n_new == NULL) return NULL;
118 
119         n_prev = lv_ll_get_prev(ll_p, n_act);
120         node_set_next(ll_p, n_prev, n_new);
121         node_set_prev(ll_p, n_new, n_prev);
122         node_set_prev(ll_p, n_act, n_new);
123         node_set_next(ll_p, n_new, n_act);
124     }
125 
126     return n_new;
127 }
128 
129 /**
130  * Add a new tail to a linked list
131  * @param ll_p pointer to linked list
132  * @return pointer to the new tail
133  */
lv_ll_ins_tail(lv_ll_t * ll_p)134 void * lv_ll_ins_tail(lv_ll_t * ll_p)
135 {
136     lv_ll_node_t * n_new;
137 
138     n_new = lv_mem_alloc(ll_p->n_size + LL_NODE_META_SIZE);
139     if(n_new == NULL) return NULL;
140 
141     if(n_new != NULL) {
142         node_set_next(ll_p, n_new, NULL);       /*No next after the new tail*/
143         node_set_prev(ll_p, n_new, ll_p->tail); /*The prev. before new is tho old tail*/
144         if(ll_p->tail != NULL) {                /*If there is old tail then the new comes after it*/
145             node_set_next(ll_p, ll_p->tail, n_new);
146         }
147 
148         ll_p->tail = n_new;      /*Set the new tail in the dsc.*/
149         if(ll_p->head == NULL) { /*If there is no head (1. node) set the head too*/
150             ll_p->head = n_new;
151         }
152     }
153 
154     return n_new;
155 }
156 
157 /**
158  * Remove the node 'node_p' from 'll_p' linked list.
159  * It does not free the the memory of node.
160  * @param ll_p pointer to the linked list of 'node_p'
161  * @param node_p pointer to node in 'll_p' linked list
162  */
lv_ll_rem(lv_ll_t * ll_p,void * node_p)163 void lv_ll_rem(lv_ll_t * ll_p, void * node_p)
164 {
165     if(lv_ll_get_head(ll_p) == node_p) {
166         /*The new head will be the node after 'n_act'*/
167         ll_p->head = lv_ll_get_next(ll_p, node_p);
168         if(ll_p->head == NULL) {
169             ll_p->tail = NULL;
170         } else {
171             node_set_prev(ll_p, ll_p->head, NULL);
172         }
173     } else if(lv_ll_get_tail(ll_p) == node_p) {
174         /*The new tail will be the  node before 'n_act'*/
175         ll_p->tail = lv_ll_get_prev(ll_p, node_p);
176         if(ll_p->tail == NULL) {
177             ll_p->head = NULL;
178         } else {
179             node_set_next(ll_p, ll_p->tail, NULL);
180         }
181     } else {
182         lv_ll_node_t * n_prev = lv_ll_get_prev(ll_p, node_p);
183         lv_ll_node_t * n_next = lv_ll_get_next(ll_p, node_p);
184 
185         node_set_next(ll_p, n_prev, n_next);
186         node_set_prev(ll_p, n_next, n_prev);
187     }
188 }
189 
190 /**
191  * Remove and free all elements from a linked list. The list remain valid but become empty.
192  * @param ll_p pointer to linked list
193  */
lv_ll_clear(lv_ll_t * ll_p)194 void lv_ll_clear(lv_ll_t * ll_p)
195 {
196     void * i;
197     void * i_next;
198 
199     i      = lv_ll_get_head(ll_p);
200     i_next = NULL;
201 
202     while(i != NULL) {
203         i_next = lv_ll_get_next(ll_p, i);
204 
205         lv_ll_rem(ll_p, i);
206         lv_mem_free(i);
207 
208         i = i_next;
209     }
210 }
211 
212 /**
213  * Move a node to a new linked list
214  * @param ll_ori_p pointer to the original (old) linked list
215  * @param ll_new_p pointer to the new linked list
216  * @param node pointer to a node
217  * @param head true: be the head in the new list
218  *             false be the head in the new list
219  */
lv_ll_chg_list(lv_ll_t * ll_ori_p,lv_ll_t * ll_new_p,void * node,bool head)220 void lv_ll_chg_list(lv_ll_t * ll_ori_p, lv_ll_t * ll_new_p, void * node, bool head)
221 {
222     lv_ll_rem(ll_ori_p, node);
223 
224     if(head) {
225         /*Set node as head*/
226         node_set_prev(ll_new_p, node, NULL);
227         node_set_next(ll_new_p, node, ll_new_p->head);
228 
229         if(ll_new_p->head != NULL) { /*If there is old head then before it goes the new*/
230             node_set_prev(ll_new_p, ll_new_p->head, node);
231         }
232 
233         ll_new_p->head = node;       /*Set the new head in the dsc.*/
234         if(ll_new_p->tail == NULL) { /*If there is no tail (first node) set the tail too*/
235             ll_new_p->tail = node;
236         }
237     } else {
238         /*Set node as tail*/
239         node_set_prev(ll_new_p, node, ll_new_p->tail);
240         node_set_next(ll_new_p, node, NULL);
241 
242         if(ll_new_p->tail != NULL) { /*If there is old tail then after it goes the new*/
243             node_set_next(ll_new_p, ll_new_p->tail, node);
244         }
245 
246         ll_new_p->tail = node;       /*Set the new tail in the dsc.*/
247         if(ll_new_p->head == NULL) { /*If there is no head (first node) set the head too*/
248             ll_new_p->head = node;
249         }
250     }
251 }
252 
253 /**
254  * Return with head node of the linked list
255  * @param ll_p pointer to linked list
256  * @return pointer to the head of 'll_p'
257  */
lv_ll_get_head(const lv_ll_t * ll_p)258 void * lv_ll_get_head(const lv_ll_t * ll_p)
259 {
260     void * head = NULL;
261 
262     if(ll_p != NULL) {
263         head = ll_p->head;
264     }
265 
266     return head;
267 }
268 
269 /**
270  * Return with tail node of the linked list
271  * @param ll_p pointer to linked list
272  * @return pointer to the head of 'll_p'
273  */
lv_ll_get_tail(const lv_ll_t * ll_p)274 void * lv_ll_get_tail(const lv_ll_t * ll_p)
275 {
276     void * tail = NULL;
277 
278     if(ll_p != NULL) {
279         tail = ll_p->tail;
280     }
281 
282     return tail;
283 }
284 
285 /**
286  * Return with the pointer of the next node after 'n_act'
287  * @param ll_p pointer to linked list
288  * @param n_act pointer a node
289  * @return pointer to the next node
290  */
lv_ll_get_next(const lv_ll_t * ll_p,const void * n_act)291 void * lv_ll_get_next(const lv_ll_t * ll_p, const void * n_act)
292 {
293     void * next = NULL;
294 
295     if(ll_p != NULL) {
296         const lv_ll_node_t * n_act_d = n_act;
297         memcpy(&next, n_act_d + LL_NEXT_P_OFFSET(ll_p), sizeof(void *));
298     }
299 
300     return next;
301 }
302 
303 /**
304  * Return with the pointer of the previous node after 'n_act'
305  * @param ll_p pointer to linked list
306  * @param n_act pointer a node
307  * @return pointer to the previous node
308  */
lv_ll_get_prev(const lv_ll_t * ll_p,const void * n_act)309 void * lv_ll_get_prev(const lv_ll_t * ll_p, const void * n_act)
310 {
311     void * prev = NULL;
312 
313     if(ll_p != NULL) {
314         const lv_ll_node_t * n_act_d = n_act;
315         memcpy(&prev, n_act_d + LL_PREV_P_OFFSET(ll_p), sizeof(void *));
316     }
317 
318     return prev;
319 }
320 
321 /**
322  * Return the length of the linked list.
323  * @param ll_p pointer to linked list
324  * @return length of the linked list
325  */
lv_ll_get_len(const lv_ll_t * ll_p)326 uint32_t lv_ll_get_len(const lv_ll_t * ll_p)
327 {
328     uint32_t len = 0;
329     void * node;
330 
331     for(node = lv_ll_get_head(ll_p); node != NULL; node = lv_ll_get_next(ll_p, node)) {
332         len++;
333     }
334 
335     return len;
336 }
337 
lv_ll_swap(lv_ll_t * ll_p,void * n1_p,void * n2_p)338 void lv_ll_swap(lv_ll_t * ll_p, void * n1_p, void * n2_p)
339 {
340     (void)(ll_p);
341     (void)(n1_p);
342     (void)(n2_p);
343     /*TODO*/
344 }
345 
346 /**
347  * Move a nodw before an other node in the same linked list
348  * @param ll_p pointer to a linked list
349  * @param n_act pointer to node to move
350  * @param n_after pointer to a node which should be after `n_act`
351  */
lv_ll_move_before(lv_ll_t * ll_p,void * n_act,void * n_after)352 void lv_ll_move_before(lv_ll_t * ll_p, void * n_act, void * n_after)
353 {
354     if(n_act == n_after) return; /*Can't move before itself*/
355 
356     void * n_before;
357     if(n_after != NULL)
358         n_before = lv_ll_get_prev(ll_p, n_after);
359     else
360         n_before = lv_ll_get_tail(ll_p); /*if `n_after` is NULL `n_act` should be the new tail*/
361 
362     if(n_act == n_before) return; /*Already before `n_after`*/
363 
364     /*It's much easier to remove from the list and add again*/
365     lv_ll_rem(ll_p, n_act);
366 
367     /*Add again by setting the prev. and next nodes*/
368     node_set_next(ll_p, n_before, n_act);
369     node_set_prev(ll_p, n_act, n_before);
370     node_set_prev(ll_p, n_after, n_act);
371     node_set_next(ll_p, n_act, n_after);
372 
373     /*If `n_act` was moved before NULL then it become the new tail*/
374     if(n_after == NULL) ll_p->tail = n_act;
375 
376     /*If `n_act` was moved before `NULL` then it's the new head*/
377     if(n_before == NULL) ll_p->head = n_act;
378 }
379 
380 /**
381  * Check if a linked list is empty
382  * @param ll_p pointer to a linked list
383  * @return true: the linked list is empty; false: not empty
384  */
lv_ll_is_empty(lv_ll_t * ll_p)385 bool lv_ll_is_empty(lv_ll_t * ll_p)
386 {
387     if(ll_p == NULL) return true;
388 
389     if(ll_p->head == NULL && ll_p->tail == NULL) return true;
390 
391     return false;
392 }
393 
394 /**********************
395  *   STATIC FUNCTIONS
396  **********************/
397 
398 /**
399  * Set the 'pervious node pointer' of a node
400  * @param ll_p pointer to linked list
401  * @param act pointer to a node which prev. node pointer should be set
402  * @param prev pointer to a node which should be the previous node before 'act'
403  */
node_set_prev(lv_ll_t * ll_p,lv_ll_node_t * act,lv_ll_node_t * prev)404 static void node_set_prev(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * prev)
405 {
406     if(act == NULL) return; /*Can't set the prev node of `NULL`*/
407 
408     uint32_t node_p_size = sizeof(lv_ll_node_t *);
409     if(prev)
410         memcpy(act + LL_PREV_P_OFFSET(ll_p), &prev, node_p_size);
411     else
412         memset(act + LL_PREV_P_OFFSET(ll_p), 0, node_p_size);
413 }
414 
415 /**
416  * Set the 'next node pointer' of a node
417  * @param ll_p pointer to linked list
418  * @param act pointer to a node which next node pointer should be set
419  * @param next pointer to a node which should be the next node before 'act'
420  */
node_set_next(lv_ll_t * ll_p,lv_ll_node_t * act,lv_ll_node_t * next)421 static void node_set_next(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * next)
422 {
423     if(act == NULL) return; /*Can't set the next node of `NULL`*/
424 
425     uint32_t node_p_size = sizeof(lv_ll_node_t *);
426     if(next)
427         memcpy(act + LL_NEXT_P_OFFSET(ll_p), &next, node_p_size);
428     else
429         memset(act + LL_NEXT_P_OFFSET(ll_p), 0, node_p_size);
430 }
431