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