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_ARCH_64
55     /*Round the size up to 8*/
56     node_size = (node_size + 7) & (~0x7);
57 #else
58     /*Round the size up to 4*/
59     node_size = (node_size + 3) & (~0x3);
60 #endif
61 
62     ll_p->n_size = node_size;
63 }
64 
65 /**
66  * Add a new head to a linked list
67  * @param ll_p pointer to linked list
68  * @return pointer to the new head
69  */
_lv_ll_ins_head(lv_ll_t * ll_p)70 void * _lv_ll_ins_head(lv_ll_t * ll_p)
71 {
72     lv_ll_node_t * n_new;
73 
74     n_new = lv_mem_alloc(ll_p->n_size + LL_NODE_META_SIZE);
75 
76     if(n_new != NULL) {
77         node_set_prev(ll_p, n_new, NULL);       /*No prev. before the new head*/
78         node_set_next(ll_p, n_new, ll_p->head); /*After new comes the old head*/
79 
80         if(ll_p->head != NULL) { /*If there is old head then before it goes the new*/
81             node_set_prev(ll_p, ll_p->head, n_new);
82         }
83 
84         ll_p->head = n_new;      /*Set the new head in the dsc.*/
85         if(ll_p->tail == NULL) { /*If there is no tail (1. node) set the tail too*/
86             ll_p->tail = n_new;
87         }
88     }
89 
90     return n_new;
91 }
92 
93 /**
94  * Insert a new node in front of the n_act node
95  * @param ll_p pointer to linked list
96  * @param n_act pointer a node
97  * @return pointer to the new head
98  */
_lv_ll_ins_prev(lv_ll_t * ll_p,void * n_act)99 void * _lv_ll_ins_prev(lv_ll_t * ll_p, void * n_act)
100 {
101     lv_ll_node_t * n_new;
102 
103     if(NULL == ll_p || NULL == n_act) return NULL;
104 
105     if(_lv_ll_get_head(ll_p) == n_act) {
106         n_new = _lv_ll_ins_head(ll_p);
107         if(n_new == NULL) return NULL;
108     }
109     else {
110         n_new = lv_mem_alloc(ll_p->n_size + LL_NODE_META_SIZE);
111         if(n_new == NULL) return NULL;
112 
113         lv_ll_node_t * n_prev;
114         n_prev = _lv_ll_get_prev(ll_p, n_act);
115         node_set_next(ll_p, n_prev, n_new);
116         node_set_prev(ll_p, n_new, n_prev);
117         node_set_prev(ll_p, n_act, n_new);
118         node_set_next(ll_p, n_new, n_act);
119     }
120 
121     return n_new;
122 }
123 
124 /**
125  * Add a new tail to a linked list
126  * @param ll_p pointer to linked list
127  * @return pointer to the new tail
128  */
_lv_ll_ins_tail(lv_ll_t * ll_p)129 void * _lv_ll_ins_tail(lv_ll_t * ll_p)
130 {
131     lv_ll_node_t * n_new;
132 
133     n_new = lv_mem_alloc(ll_p->n_size + LL_NODE_META_SIZE);
134 
135     if(n_new != NULL) {
136         node_set_next(ll_p, n_new, NULL);       /*No next after the new tail*/
137         node_set_prev(ll_p, n_new, ll_p->tail); /*The prev. before new is the old tail*/
138         if(ll_p->tail != NULL) {                /*If there is old tail then the new comes after it*/
139             node_set_next(ll_p, ll_p->tail, n_new);
140         }
141 
142         ll_p->tail = n_new;      /*Set the new tail in the dsc.*/
143         if(ll_p->head == NULL) { /*If there is no head (1. node) set the head too*/
144             ll_p->head = n_new;
145         }
146     }
147 
148     return n_new;
149 }
150 
151 /**
152  * Remove the node 'node_p' from 'll_p' linked list.
153  * It does not free the the memory of node.
154  * @param ll_p pointer to the linked list of 'node_p'
155  * @param node_p pointer to node in 'll_p' linked list
156  */
_lv_ll_remove(lv_ll_t * ll_p,void * node_p)157 void _lv_ll_remove(lv_ll_t * ll_p, void * node_p)
158 {
159     if(_lv_ll_get_head(ll_p) == node_p) {
160         /*The new head will be the node after 'n_act'*/
161         ll_p->head = _lv_ll_get_next(ll_p, node_p);
162         if(ll_p->head == NULL) {
163             ll_p->tail = NULL;
164         }
165         else {
166             node_set_prev(ll_p, ll_p->head, NULL);
167         }
168     }
169     else if(_lv_ll_get_tail(ll_p) == node_p) {
170         /*The new tail will be the  node before 'n_act'*/
171         ll_p->tail = _lv_ll_get_prev(ll_p, node_p);
172         if(ll_p->tail == NULL) {
173             ll_p->head = NULL;
174         }
175         else {
176             node_set_next(ll_p, ll_p->tail, NULL);
177         }
178     }
179     else {
180         lv_ll_node_t * n_prev = _lv_ll_get_prev(ll_p, node_p);
181         lv_ll_node_t * n_next = _lv_ll_get_next(ll_p, node_p);
182 
183         node_set_next(ll_p, n_prev, n_next);
184         node_set_prev(ll_p, n_next, n_prev);
185     }
186 }
187 
188 /**
189  * Remove and free all elements from a linked list. The list remain valid but become empty.
190  * @param ll_p pointer to linked list
191  */
_lv_ll_clear(lv_ll_t * ll_p)192 void _lv_ll_clear(lv_ll_t * ll_p)
193 {
194     void * i;
195     void * i_next;
196 
197     i      = _lv_ll_get_head(ll_p);
198     i_next = NULL;
199 
200     while(i != NULL) {
201         i_next = _lv_ll_get_next(ll_p, i);
202 
203         _lv_ll_remove(ll_p, i);
204         lv_mem_free(i);
205 
206         i = i_next;
207     }
208 }
209 
210 /**
211  * Move a node to a new linked list
212  * @param ll_ori_p pointer to the original (old) linked list
213  * @param ll_new_p pointer to the new linked list
214  * @param node pointer to a node
215  * @param head true: be the head in the new list
216  *             false be the head in the new list
217  */
_lv_ll_chg_list(lv_ll_t * ll_ori_p,lv_ll_t * ll_new_p,void * node,bool head)218 void _lv_ll_chg_list(lv_ll_t * ll_ori_p, lv_ll_t * ll_new_p, void * node, bool head)
219 {
220     _lv_ll_remove(ll_ori_p, node);
221 
222     if(head) {
223         /*Set node as head*/
224         node_set_prev(ll_new_p, node, NULL);
225         node_set_next(ll_new_p, node, ll_new_p->head);
226 
227         if(ll_new_p->head != NULL) { /*If there is old head then before it goes the new*/
228             node_set_prev(ll_new_p, ll_new_p->head, node);
229         }
230 
231         ll_new_p->head = node;       /*Set the new head in the dsc.*/
232         if(ll_new_p->tail == NULL) { /*If there is no tail (first node) set the tail too*/
233             ll_new_p->tail = node;
234         }
235     }
236     else {
237         /*Set node as tail*/
238         node_set_prev(ll_new_p, node, ll_new_p->tail);
239         node_set_next(ll_new_p, node, NULL);
240 
241         if(ll_new_p->tail != NULL) { /*If there is old tail then after it goes the new*/
242             node_set_next(ll_new_p, ll_new_p->tail, node);
243         }
244 
245         ll_new_p->tail = node;       /*Set the new tail in the dsc.*/
246         if(ll_new_p->head == NULL) { /*If there is no head (first node) set the head too*/
247             ll_new_p->head = node;
248         }
249     }
250 }
251 
252 /**
253  * Return with head node of the linked list
254  * @param ll_p pointer to linked list
255  * @return pointer to the head of 'll_p'
256  */
_lv_ll_get_head(const lv_ll_t * ll_p)257 void * _lv_ll_get_head(const lv_ll_t * ll_p)
258 {
259     void * head = NULL;
260 
261     if(ll_p != NULL) {
262         head = ll_p->head;
263     }
264 
265     return head;
266 }
267 
268 /**
269  * Return with tail node of the linked list
270  * @param ll_p pointer to linked list
271  * @return pointer to the head of 'll_p'
272  */
_lv_ll_get_tail(const lv_ll_t * ll_p)273 void * _lv_ll_get_tail(const lv_ll_t * ll_p)
274 {
275     void * tail = NULL;
276 
277     if(ll_p != NULL) {
278         tail = ll_p->tail;
279     }
280 
281     return tail;
282 }
283 
284 /**
285  * Return with the pointer of the next node after 'n_act'
286  * @param ll_p pointer to linked list
287  * @param n_act pointer a node
288  * @return pointer to the next node
289  */
_lv_ll_get_next(const lv_ll_t * ll_p,const void * n_act)290 void * _lv_ll_get_next(const lv_ll_t * ll_p, const void * n_act)
291 {
292     if(ll_p == NULL) return NULL;
293 
294     /* Pointer to the next node is stored in the end of this node.
295      * Go there and return the address found there */
296     const lv_ll_node_t * n_act_d = n_act;
297     n_act_d += LL_NEXT_P_OFFSET(ll_p);
298     return *((lv_ll_node_t **)n_act_d);
299 }
300 
301 /**
302  * Return with the pointer of the previous node after 'n_act'
303  * @param ll_p pointer to linked list
304  * @param n_act pointer a node
305  * @return pointer to the previous node
306  */
_lv_ll_get_prev(const lv_ll_t * ll_p,const void * n_act)307 void * _lv_ll_get_prev(const lv_ll_t * ll_p, const void * n_act)
308 {
309     if(ll_p == NULL) return NULL;
310 
311     /* Pointer to the prev. node is stored in the end of this node.
312      * Go there and return the address found there */
313     const lv_ll_node_t * n_act_d = n_act;
314     n_act_d += LL_PREV_P_OFFSET(ll_p);
315     return *((lv_ll_node_t **)n_act_d);
316 }
317 
318 /**
319  * Return the length of the linked list.
320  * @param ll_p pointer to linked list
321  * @return length of the linked list
322  */
_lv_ll_get_len(const lv_ll_t * ll_p)323 uint32_t _lv_ll_get_len(const lv_ll_t * ll_p)
324 {
325     uint32_t len = 0;
326     void * node;
327 
328     for(node = _lv_ll_get_head(ll_p); node != NULL; node = _lv_ll_get_next(ll_p, node)) {
329         len++;
330     }
331 
332     return len;
333 }
334 
335 /**
336  * Move a node before an other node in the same linked list
337  * @param ll_p pointer to a linked list
338  * @param n_act pointer to node to move
339  * @param n_after pointer to a node which should be after `n_act`
340  */
_lv_ll_move_before(lv_ll_t * ll_p,void * n_act,void * n_after)341 void _lv_ll_move_before(lv_ll_t * ll_p, void * n_act, void * n_after)
342 {
343     if(n_act == n_after) return; /*Can't move before itself*/
344 
345     void * n_before;
346     if(n_after != NULL)
347         n_before = _lv_ll_get_prev(ll_p, n_after);
348     else
349         n_before = _lv_ll_get_tail(ll_p); /*if `n_after` is NULL `n_act` should be the new tail*/
350 
351     if(n_act == n_before) return; /*Already before `n_after`*/
352 
353     /*It's much easier to remove from the list and add again*/
354     _lv_ll_remove(ll_p, n_act);
355 
356     /*Add again by setting the prev. and next nodes*/
357     node_set_next(ll_p, n_before, n_act);
358     node_set_prev(ll_p, n_act, n_before);
359     node_set_prev(ll_p, n_after, n_act);
360     node_set_next(ll_p, n_act, n_after);
361 
362     /*If `n_act` was moved before NULL then it become the new tail*/
363     if(n_after == NULL) ll_p->tail = n_act;
364 
365     /*If `n_act` was moved before `NULL` then it's the new head*/
366     if(n_before == NULL) ll_p->head = n_act;
367 }
368 
369 /**
370  * Check if a linked list is empty
371  * @param ll_p pointer to a linked list
372  * @return true: the linked list is empty; false: not empty
373  */
_lv_ll_is_empty(lv_ll_t * ll_p)374 bool _lv_ll_is_empty(lv_ll_t * ll_p)
375 {
376     if(ll_p == NULL) return true;
377 
378     if(ll_p->head == NULL && ll_p->tail == NULL) return true;
379 
380     return false;
381 }
382 
383 /**********************
384  *   STATIC FUNCTIONS
385  **********************/
386 
387 /**
388  * Set the previous node pointer of a node
389  * @param ll_p pointer to linked list
390  * @param act pointer to a node which prev. node pointer should be set
391  * @param prev pointer to a node which should be the previous node before 'act'
392  */
node_set_prev(lv_ll_t * ll_p,lv_ll_node_t * act,lv_ll_node_t * prev)393 static void node_set_prev(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * prev)
394 {
395     if(act == NULL) return; /*Can't set the prev node of `NULL`*/
396 
397     uint8_t * act8 = (uint8_t *) act;
398 
399     act8 += LL_PREV_P_OFFSET(ll_p);
400 
401     lv_ll_node_t ** act_node_p = (lv_ll_node_t **) act8;
402     lv_ll_node_t ** prev_node_p = (lv_ll_node_t **) &prev;
403 
404     *act_node_p = *prev_node_p;
405 }
406 
407 /**
408  * Set the 'next node pointer' of a node
409  * @param ll_p pointer to linked list
410  * @param act pointer to a node which next node pointer should be set
411  * @param next pointer to a node which should be the next node before 'act'
412  */
node_set_next(lv_ll_t * ll_p,lv_ll_node_t * act,lv_ll_node_t * next)413 static void node_set_next(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * next)
414 {
415     if(act == NULL) return; /*Can't set the next node of `NULL`*/
416     uint8_t * act8 = (uint8_t *) act;
417 
418     act8 += LL_NEXT_P_OFFSET(ll_p);
419     lv_ll_node_t ** act_node_p = (lv_ll_node_t **) act8;
420     lv_ll_node_t ** next_node_p = (lv_ll_node_t **) &next;
421 
422     *act_node_p = *next_node_p;
423 }
424