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 "lv_ll.h"
11 #include "../stdlib/lv_mem.h"
12 
13 /*********************
14  *      DEFINES
15  *********************/
16 #define LL_NODE_META_SIZE (sizeof(lv_ll_node_t *) + sizeof(lv_ll_node_t *))
17 #define LL_PREV_P_OFFSET(ll_p) (ll_p->n_size)
18 #define LL_NEXT_P_OFFSET(ll_p) (ll_p->n_size + sizeof(lv_ll_node_t *))
19 
20 /**********************
21  *      TYPEDEFS
22  **********************/
23 
24 /**********************
25  *  STATIC PROTOTYPES
26  **********************/
27 static void node_set_prev(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * prev);
28 static void node_set_next(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * next);
29 
30 /**********************
31  *  STATIC VARIABLES
32  **********************/
33 
34 /**********************
35  *      MACROS
36  **********************/
37 
38 /**********************
39  *   GLOBAL FUNCTIONS
40  **********************/
41 
lv_ll_init(lv_ll_t * ll_p,uint32_t node_size)42 void lv_ll_init(lv_ll_t * ll_p, uint32_t node_size)
43 {
44     ll_p->head = NULL;
45     ll_p->tail = NULL;
46 #ifdef LV_ARCH_64
47     /*Round the size up to 8*/
48     node_size = (node_size + 7) & (~0x7);
49 #else
50     /*Round the size up to 4*/
51     node_size = (node_size + 3) & (~0x3);
52 #endif
53 
54     ll_p->n_size = node_size;
55 }
56 
lv_ll_ins_head(lv_ll_t * ll_p)57 void * lv_ll_ins_head(lv_ll_t * ll_p)
58 {
59     lv_ll_node_t * n_new;
60 
61     n_new = lv_malloc(ll_p->n_size + LL_NODE_META_SIZE);
62 
63     if(n_new != NULL) {
64         node_set_prev(ll_p, n_new, NULL);       /*No prev. before the new head*/
65         node_set_next(ll_p, n_new, ll_p->head); /*After new comes the old head*/
66 
67         if(ll_p->head != NULL) { /*If there is old head then before it goes the new*/
68             node_set_prev(ll_p, ll_p->head, n_new);
69         }
70 
71         ll_p->head = n_new;      /*Set the new head in the dsc.*/
72         if(ll_p->tail == NULL) { /*If there is no tail (1. node) set the tail too*/
73             ll_p->tail = n_new;
74         }
75     }
76 
77     return n_new;
78 }
79 
lv_ll_ins_prev(lv_ll_t * ll_p,void * n_act)80 void * lv_ll_ins_prev(lv_ll_t * ll_p, void * n_act)
81 {
82     lv_ll_node_t * n_new;
83 
84     if(NULL == ll_p || NULL == n_act) return NULL;
85 
86     if(lv_ll_get_head(ll_p) == n_act) {
87         n_new = lv_ll_ins_head(ll_p);
88         if(n_new == NULL) return NULL;
89     }
90     else {
91         n_new = lv_malloc(ll_p->n_size + LL_NODE_META_SIZE);
92         if(n_new == NULL) return NULL;
93 
94         lv_ll_node_t * n_prev;
95         n_prev = lv_ll_get_prev(ll_p, n_act);
96         node_set_next(ll_p, n_prev, n_new);
97         node_set_prev(ll_p, n_new, n_prev);
98         node_set_prev(ll_p, n_act, n_new);
99         node_set_next(ll_p, n_new, n_act);
100     }
101 
102     return n_new;
103 }
104 
lv_ll_ins_tail(lv_ll_t * ll_p)105 void * lv_ll_ins_tail(lv_ll_t * ll_p)
106 {
107     lv_ll_node_t * n_new;
108 
109     n_new = lv_malloc(ll_p->n_size + LL_NODE_META_SIZE);
110 
111     if(n_new != NULL) {
112         node_set_next(ll_p, n_new, NULL);       /*No next after the new tail*/
113         node_set_prev(ll_p, n_new, ll_p->tail); /*The prev. before new is the old tail*/
114         if(ll_p->tail != NULL) {                /*If there is old tail then the new comes after it*/
115             node_set_next(ll_p, ll_p->tail, n_new);
116         }
117 
118         ll_p->tail = n_new;      /*Set the new tail in the dsc.*/
119         if(ll_p->head == NULL) { /*If there is no head (1. node) set the head too*/
120             ll_p->head = n_new;
121         }
122     }
123 
124     return n_new;
125 }
126 
lv_ll_remove(lv_ll_t * ll_p,void * node_p)127 void lv_ll_remove(lv_ll_t * ll_p, void * node_p)
128 {
129     if(ll_p == NULL) return;
130 
131     if(lv_ll_get_head(ll_p) == node_p) {
132         /*The new head will be the node after 'node_p'*/
133         ll_p->head = lv_ll_get_next(ll_p, node_p);
134         if(ll_p->head == NULL) {
135             ll_p->tail = NULL;
136         }
137         else {
138             node_set_prev(ll_p, ll_p->head, NULL);
139         }
140     }
141     else if(lv_ll_get_tail(ll_p) == node_p) {
142         /*The new tail will be the node before 'node_p'*/
143         ll_p->tail = lv_ll_get_prev(ll_p, node_p);
144         if(ll_p->tail == NULL) {
145             ll_p->head = NULL;
146         }
147         else {
148             node_set_next(ll_p, ll_p->tail, NULL);
149         }
150     }
151     else {
152         lv_ll_node_t * n_prev = lv_ll_get_prev(ll_p, node_p);
153         lv_ll_node_t * n_next = lv_ll_get_next(ll_p, node_p);
154 
155         node_set_next(ll_p, n_prev, n_next);
156         node_set_prev(ll_p, n_next, n_prev);
157     }
158 }
159 
lv_ll_clear_custom(lv_ll_t * ll_p,void (* cleanup)(void *))160 void lv_ll_clear_custom(lv_ll_t * ll_p, void(*cleanup)(void *))
161 {
162     void * i;
163     void * i_next;
164 
165     i      = lv_ll_get_head(ll_p);
166     i_next = NULL;
167 
168     while(i != NULL) {
169         i_next = lv_ll_get_next(ll_p, i);
170         if(cleanup == NULL) {
171             lv_ll_remove(ll_p, i);
172             lv_free(i);
173         }
174         else {
175             cleanup(i);
176         }
177         i = i_next;
178     }
179 }
180 
lv_ll_chg_list(lv_ll_t * ll_ori_p,lv_ll_t * ll_new_p,void * node,bool head)181 void lv_ll_chg_list(lv_ll_t * ll_ori_p, lv_ll_t * ll_new_p, void * node, bool head)
182 {
183     lv_ll_remove(ll_ori_p, node);
184 
185     if(head) {
186         /*Set node as head*/
187         node_set_prev(ll_new_p, node, NULL);
188         node_set_next(ll_new_p, node, ll_new_p->head);
189 
190         if(ll_new_p->head != NULL) { /*If there is old head then before it goes the new*/
191             node_set_prev(ll_new_p, ll_new_p->head, node);
192         }
193 
194         ll_new_p->head = node;       /*Set the new head in the dsc.*/
195         if(ll_new_p->tail == NULL) { /*If there is no tail (first node) set the tail too*/
196             ll_new_p->tail = node;
197         }
198     }
199     else {
200         /*Set node as tail*/
201         node_set_prev(ll_new_p, node, ll_new_p->tail);
202         node_set_next(ll_new_p, node, NULL);
203 
204         if(ll_new_p->tail != NULL) { /*If there is old tail then after it goes the new*/
205             node_set_next(ll_new_p, ll_new_p->tail, node);
206         }
207 
208         ll_new_p->tail = node;       /*Set the new tail in the dsc.*/
209         if(ll_new_p->head == NULL) { /*If there is no head (first node) set the head too*/
210             ll_new_p->head = node;
211         }
212     }
213 }
214 
lv_ll_get_head(const lv_ll_t * ll_p)215 void * lv_ll_get_head(const lv_ll_t * ll_p)
216 {
217     if(ll_p == NULL) return NULL;
218     return ll_p->head;
219 }
220 
lv_ll_get_tail(const lv_ll_t * ll_p)221 void * lv_ll_get_tail(const lv_ll_t * ll_p)
222 {
223     if(ll_p == NULL) return NULL;
224     return ll_p->tail;
225 }
226 
lv_ll_get_next(const lv_ll_t * ll_p,const void * n_act)227 void * lv_ll_get_next(const lv_ll_t * ll_p, const void * n_act)
228 {
229     /*Pointer to the next node is stored in the end of this node.
230      *Go there and return the address found there*/
231     const lv_ll_node_t * n_act_d = n_act;
232     n_act_d += LL_NEXT_P_OFFSET(ll_p);
233     return *((lv_ll_node_t **)n_act_d);
234 }
235 
lv_ll_get_prev(const lv_ll_t * ll_p,const void * n_act)236 void * lv_ll_get_prev(const lv_ll_t * ll_p, const void * n_act)
237 {
238     /*Pointer to the prev. node is stored in the end of this node.
239      *Go there and return the address found there*/
240     const lv_ll_node_t * n_act_d = n_act;
241     n_act_d += LL_PREV_P_OFFSET(ll_p);
242     return *((lv_ll_node_t **)n_act_d);
243 }
244 
lv_ll_get_len(const lv_ll_t * ll_p)245 uint32_t lv_ll_get_len(const lv_ll_t * ll_p)
246 {
247     uint32_t len = 0;
248     void * node;
249 
250     for(node = lv_ll_get_head(ll_p); node != NULL; node = lv_ll_get_next(ll_p, node)) {
251         len++;
252     }
253 
254     return len;
255 }
256 
lv_ll_move_before(lv_ll_t * ll_p,void * n_act,void * n_after)257 void lv_ll_move_before(lv_ll_t * ll_p, void * n_act, void * n_after)
258 {
259     if(n_act == n_after) return; /*Can't move before itself*/
260 
261     void * n_before;
262     if(n_after != NULL)
263         n_before = lv_ll_get_prev(ll_p, n_after);
264     else
265         n_before = lv_ll_get_tail(ll_p); /*if `n_after` is NULL `n_act` should be the new tail*/
266 
267     if(n_act == n_before) return; /*Already before `n_after`*/
268 
269     /*It's much easier to remove from the list and add again*/
270     lv_ll_remove(ll_p, n_act);
271 
272     /*Add again by setting the prev. and next nodes*/
273     node_set_next(ll_p, n_before, n_act);
274     node_set_prev(ll_p, n_act, n_before);
275     node_set_prev(ll_p, n_after, n_act);
276     node_set_next(ll_p, n_act, n_after);
277 
278     /*If `n_act` was moved before NULL then it become the new tail*/
279     if(n_after == NULL) ll_p->tail = n_act;
280 
281     /*If `n_act` was moved before `NULL` then it's the new head*/
282     if(n_before == NULL) ll_p->head = n_act;
283 }
284 
lv_ll_is_empty(lv_ll_t * ll_p)285 bool lv_ll_is_empty(lv_ll_t * ll_p)
286 {
287     if(ll_p == NULL) return true;
288 
289     if(ll_p->head == NULL && ll_p->tail == NULL) return true;
290 
291     return false;
292 }
293 
lv_ll_clear(lv_ll_t * ll_p)294 void lv_ll_clear(lv_ll_t * ll_p)
295 {
296     lv_ll_clear_custom(ll_p, NULL);
297 }
298 
299 /**********************
300  *   STATIC FUNCTIONS
301  **********************/
302 
303 /**
304  * Set the previous node pointer of a node
305  * @param ll_p pointer to linked list
306  * @param act pointer to a node which prev. node pointer should be set
307  * @param prev pointer to a node which should be the previous node before 'act'
308  */
node_set_prev(lv_ll_t * ll_p,lv_ll_node_t * act,lv_ll_node_t * prev)309 static void node_set_prev(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * prev)
310 {
311     if(act == NULL) return; /*Can't set the prev node of `NULL`*/
312 
313     uint8_t * act8 = (uint8_t *)act;
314 
315     act8 += LL_PREV_P_OFFSET(ll_p);
316 
317     lv_ll_node_t ** act_node_p = (lv_ll_node_t **) act8;
318     lv_ll_node_t ** prev_node_p = (lv_ll_node_t **) &prev;
319 
320     *act_node_p = *prev_node_p;
321 }
322 
323 /**
324  * Set the 'next node pointer' of a node
325  * @param ll_p pointer to linked list
326  * @param act pointer to a node which next node pointer should be set
327  * @param next pointer to a node which should be the next node before 'act'
328  */
node_set_next(lv_ll_t * ll_p,lv_ll_node_t * act,lv_ll_node_t * next)329 static void node_set_next(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * next)
330 {
331     if(act == NULL) return; /*Can't set the next node of `NULL`*/
332     uint8_t * act8 = (uint8_t *)act;
333 
334     act8 += LL_NEXT_P_OFFSET(ll_p);
335     lv_ll_node_t ** act_node_p = (lv_ll_node_t **) act8;
336     lv_ll_node_t ** next_node_p = (lv_ll_node_t **) &next;
337 
338     *act_node_p = *next_node_p;
339 }
340