aboutsummaryrefslogtreecommitdiff
path: root/src/misc/lv_ll.c
blob: ca1339ec5659376170aa53c3b03275aaeca439ca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
/**
 * @file lv_ll.c
 * Handle linked lists.
 * The nodes are dynamically allocated by the 'lv_mem' module,
 */

/*********************
 *      INCLUDES
 *********************/
#include "lv_ll.h"
#include "../stdlib/lv_mem.h"

/*********************
 *      DEFINES
 *********************/
#define LL_NODE_META_SIZE (sizeof(lv_ll_node_t *) + sizeof(lv_ll_node_t *))
#define LL_PREV_P_OFFSET(ll_p) (ll_p->n_size)
#define LL_NEXT_P_OFFSET(ll_p) (ll_p->n_size + sizeof(lv_ll_node_t *))

/**********************
 *      TYPEDEFS
 **********************/

/**********************
 *  STATIC PROTOTYPES
 **********************/
static void node_set_prev(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * prev);
static void node_set_next(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * next);

/**********************
 *  STATIC VARIABLES
 **********************/

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

void lv_ll_init(lv_ll_t * ll_p, uint32_t node_size)
{
    ll_p->head = NULL;
    ll_p->tail = NULL;
#ifdef LV_ARCH_64
    /*Round the size up to 8*/
    node_size = (node_size + 7) & (~0x7);
#else
    /*Round the size up to 4*/
    node_size = (node_size + 3) & (~0x3);
#endif

    ll_p->n_size = node_size;
}

void * lv_ll_ins_head(lv_ll_t * ll_p)
{
    lv_ll_node_t * n_new;

    n_new = lv_malloc(ll_p->n_size + LL_NODE_META_SIZE);

    if(n_new != NULL) {
        node_set_prev(ll_p, n_new, NULL);       /*No prev. before the new head*/
        node_set_next(ll_p, n_new, ll_p->head); /*After new comes the old head*/

        if(ll_p->head != NULL) { /*If there is old head then before it goes the new*/
            node_set_prev(ll_p, ll_p->head, n_new);
        }

        ll_p->head = n_new;      /*Set the new head in the dsc.*/
        if(ll_p->tail == NULL) { /*If there is no tail (1. node) set the tail too*/
            ll_p->tail = n_new;
        }
    }

    return n_new;
}

void * lv_ll_ins_prev(lv_ll_t * ll_p, void * n_act)
{
    lv_ll_node_t * n_new;

    if(NULL == ll_p || NULL == n_act) return NULL;

    if(lv_ll_get_head(ll_p) == n_act) {
        n_new = lv_ll_ins_head(ll_p);
        if(n_new == NULL) return NULL;
    }
    else {
        n_new = lv_malloc(ll_p->n_size + LL_NODE_META_SIZE);
        if(n_new == NULL) return NULL;

        lv_ll_node_t * n_prev;
        n_prev = lv_ll_get_prev(ll_p, n_act);
        node_set_next(ll_p, n_prev, n_new);
        node_set_prev(ll_p, n_new, n_prev);
        node_set_prev(ll_p, n_act, n_new);
        node_set_next(ll_p, n_new, n_act);
    }

    return n_new;
}

void * lv_ll_ins_tail(lv_ll_t * ll_p)
{
    lv_ll_node_t * n_new;

    n_new = lv_malloc(ll_p->n_size + LL_NODE_META_SIZE);

    if(n_new != NULL) {
        node_set_next(ll_p, n_new, NULL);       /*No next after the new tail*/
        node_set_prev(ll_p, n_new, ll_p->tail); /*The prev. before new is the old tail*/
        if(ll_p->tail != NULL) {                /*If there is old tail then the new comes after it*/
            node_set_next(ll_p, ll_p->tail, n_new);
        }

        ll_p->tail = n_new;      /*Set the new tail in the dsc.*/
        if(ll_p->head == NULL) { /*If there is no head (1. node) set the head too*/
            ll_p->head = n_new;
        }
    }

    return n_new;
}

void lv_ll_remove(lv_ll_t * ll_p, void * node_p)
{
    if(ll_p == NULL) return;

    if(lv_ll_get_head(ll_p) == node_p) {
        /*The new head will be the node after 'n_act'*/
        ll_p->head = lv_ll_get_next(ll_p, node_p);
        if(ll_p->head == NULL) {
            ll_p->tail = NULL;
        }
        else {
            node_set_prev(ll_p, ll_p->head, NULL);
        }
    }
    else if(lv_ll_get_tail(ll_p) == node_p) {
        /*The new tail will be the node before 'n_act'*/
        ll_p->tail = lv_ll_get_prev(ll_p, node_p);
        if(ll_p->tail == NULL) {
            ll_p->head = NULL;
        }
        else {
            node_set_next(ll_p, ll_p->tail, NULL);
        }
    }
    else {
        lv_ll_node_t * n_prev = lv_ll_get_prev(ll_p, node_p);
        lv_ll_node_t * n_next = lv_ll_get_next(ll_p, node_p);

        node_set_next(ll_p, n_prev, n_next);
        node_set_prev(ll_p, n_next, n_prev);
    }
}

void lv_ll_clear_custom(lv_ll_t * ll_p, void(*cleanup)(void *))
{
    void * i;
    void * i_next;

    i      = lv_ll_get_head(ll_p);
    i_next = NULL;

    while(i != NULL) {
        i_next = lv_ll_get_next(ll_p, i);
        if(cleanup == NULL) {
            lv_ll_remove(ll_p, i);
            lv_free(i);
        }
        else {
            cleanup(i);
        }
        i = i_next;
    }
}

void lv_ll_chg_list(lv_ll_t * ll_ori_p, lv_ll_t * ll_new_p, void * node, bool head)
{
    lv_ll_remove(ll_ori_p, node);

    if(head) {
        /*Set node as head*/
        node_set_prev(ll_new_p, node, NULL);
        node_set_next(ll_new_p, node, ll_new_p->head);

        if(ll_new_p->head != NULL) { /*If there is old head then before it goes the new*/
            node_set_prev(ll_new_p, ll_new_p->head, node);
        }

        ll_new_p->head = node;       /*Set the new head in the dsc.*/
        if(ll_new_p->tail == NULL) { /*If there is no tail (first node) set the tail too*/
            ll_new_p->tail = node;
        }
    }
    else {
        /*Set node as tail*/
        node_set_prev(ll_new_p, node, ll_new_p->tail);
        node_set_next(ll_new_p, node, NULL);

        if(ll_new_p->tail != NULL) { /*If there is old tail then after it goes the new*/
            node_set_next(ll_new_p, ll_new_p->tail, node);
        }

        ll_new_p->tail = node;       /*Set the new tail in the dsc.*/
        if(ll_new_p->head == NULL) { /*If there is no head (first node) set the head too*/
            ll_new_p->head = node;
        }
    }
}

void * lv_ll_get_head(const lv_ll_t * ll_p)
{
    if(ll_p == NULL) return NULL;
    return ll_p->head;
}

void * lv_ll_get_tail(const lv_ll_t * ll_p)
{
    if(ll_p == NULL) return NULL;
    return ll_p->tail;
}

void * lv_ll_get_next(const lv_ll_t * ll_p, const void * n_act)
{
    /*Pointer to the next node is stored in the end of this node.
     *Go there and return the address found there*/
    const lv_ll_node_t * n_act_d = n_act;
    n_act_d += LL_NEXT_P_OFFSET(ll_p);
    return *((lv_ll_node_t **)n_act_d);
}

void * lv_ll_get_prev(const lv_ll_t * ll_p, const void * n_act)
{
    /*Pointer to the prev. node is stored in the end of this node.
     *Go there and return the address found there*/
    const lv_ll_node_t * n_act_d = n_act;
    n_act_d += LL_PREV_P_OFFSET(ll_p);
    return *((lv_ll_node_t **)n_act_d);
}

uint32_t lv_ll_get_len(const lv_ll_t * ll_p)
{
    uint32_t len = 0;
    void * node;

    for(node = lv_ll_get_head(ll_p); node != NULL; node = lv_ll_get_next(ll_p, node)) {
        len++;
    }

    return len;
}

void lv_ll_move_before(lv_ll_t * ll_p, void * n_act, void * n_after)
{
    if(n_act == n_after) return; /*Can't move before itself*/

    void * n_before;
    if(n_after != NULL)
        n_before = lv_ll_get_prev(ll_p, n_after);
    else
        n_before = lv_ll_get_tail(ll_p); /*if `n_after` is NULL `n_act` should be the new tail*/

    if(n_act == n_before) return; /*Already before `n_after`*/

    /*It's much easier to remove from the list and add again*/
    lv_ll_remove(ll_p, n_act);

    /*Add again by setting the prev. and next nodes*/
    node_set_next(ll_p, n_before, n_act);
    node_set_prev(ll_p, n_act, n_before);
    node_set_prev(ll_p, n_after, n_act);
    node_set_next(ll_p, n_act, n_after);

    /*If `n_act` was moved before NULL then it become the new tail*/
    if(n_after == NULL) ll_p->tail = n_act;

    /*If `n_act` was moved before `NULL` then it's the new head*/
    if(n_before == NULL) ll_p->head = n_act;
}

bool lv_ll_is_empty(lv_ll_t * ll_p)
{
    if(ll_p == NULL) return true;

    if(ll_p->head == NULL && ll_p->tail == NULL) return true;

    return false;
}

void lv_ll_clear(lv_ll_t * ll_p)
{
    lv_ll_clear_custom(ll_p, NULL);
}

/**********************
 *   STATIC FUNCTIONS
 **********************/

/**
 * Set the previous node pointer of a node
 * @param ll_p pointer to linked list
 * @param act pointer to a node which prev. node pointer should be set
 * @param prev pointer to a node which should be the previous node before 'act'
 */
static void node_set_prev(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * prev)
{
    if(act == NULL) return; /*Can't set the prev node of `NULL`*/

    uint8_t * act8 = (uint8_t *)act;

    act8 += LL_PREV_P_OFFSET(ll_p);

    lv_ll_node_t ** act_node_p = (lv_ll_node_t **) act8;
    lv_ll_node_t ** prev_node_p = (lv_ll_node_t **) &prev;

    *act_node_p = *prev_node_p;
}

/**
 * Set the 'next node pointer' of a node
 * @param ll_p pointer to linked list
 * @param act pointer to a node which next node pointer should be set
 * @param next pointer to a node which should be the next node before 'act'
 */
static void node_set_next(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * next)
{
    if(act == NULL) return; /*Can't set the next node of `NULL`*/
    uint8_t * act8 = (uint8_t *)act;

    act8 += LL_NEXT_P_OFFSET(ll_p);
    lv_ll_node_t ** act_node_p = (lv_ll_node_t **) act8;
    lv_ll_node_t ** next_node_p = (lv_ll_node_t **) &next;

    *act_node_p = *next_node_p;
}