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