/**1* @file lv_ll.c2* Handle linked lists.3* The nodes are dynamically allocated by the 'lv_mem' module,4*/56/*********************7* INCLUDES8*********************/9#include <stdint.h>10#include <string.h>1112#include "lv_ll.h"13#include "lv_mem.h"1415/*********************16* DEFINES17*********************/18#define LL_NODE_META_SIZE (sizeof(lv_ll_node_t*) + sizeof(lv_ll_node_t*))19#define LL_PREV_P_OFFSET(ll_p) (ll_p->n_size)20#define LL_NEXT_P_OFFSET(ll_p) (ll_p->n_size + sizeof(lv_ll_node_t*))2122/**********************23* TYPEDEFS24**********************/2526/**********************27* STATIC PROTOTYPES28**********************/29static void node_set_prev(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * prev);30static void node_set_next(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * next);3132/**********************33* STATIC VARIABLES34**********************/3536/**********************37* MACROS38**********************/3940/**********************41* GLOBAL FUNCTIONS42**********************/4344/**45* Initialize linked list46* @param ll_dsc pointer to ll_dsc variable47* @param node_size the size of 1 node in bytes48*/49void lv_ll_init(lv_ll_t * ll_p, uint32_t node_size)50{51ll_p->head = NULL;52ll_p->tail = NULL;53#ifdef LV_MEM_ENV6454/*Round the size up to 8*/55if(node_size & 0x7) {56node_size = node_size & (~0x7);57node_size += 8;58}59#else60/*Round the size up to 4*/61if(node_size & 0x3) {62node_size = node_size & (~0x3);63node_size += 4;64}65#endif6667ll_p->n_size = node_size;68}6970/**71* Add a new head to a linked list72* @param ll_p pointer to linked list73* @return pointer to the new head74*/75void * lv_ll_ins_head(lv_ll_t * ll_p)76{77lv_ll_node_t * n_new;7879n_new = lv_mem_alloc(ll_p->n_size + LL_NODE_META_SIZE);8081if(n_new != NULL) {82node_set_prev(ll_p, n_new, NULL); /*No prev. before the new head*/83node_set_next(ll_p, n_new, ll_p->head); /*After new comes the old head*/8485if(ll_p->head != NULL) { /*If there is old head then before it goes the new*/86node_set_prev(ll_p, ll_p->head, n_new);87}8889ll_p->head = n_new; /*Set the new head in the dsc.*/90if(ll_p->tail == NULL) {/*If there is no tail (1. node) set the tail too*/91ll_p->tail = n_new;92}93}9495return n_new;96}9798/**99* Insert a new node in front of the n_act node100* @param ll_p pointer to linked list101* @param n_act pointer a node102* @return pointer to the new head103*/104void * lv_ll_ins_prev(lv_ll_t * ll_p, void * n_act)105{106lv_ll_node_t * n_new;107lv_ll_node_t * n_prev;108109if(NULL == ll_p || NULL == n_act) return NULL;110111if(lv_ll_get_head(ll_p) == n_act) {112n_new = lv_ll_ins_head(ll_p);113if(n_new == NULL) return NULL;114} else {115n_new = lv_mem_alloc(ll_p->n_size + LL_NODE_META_SIZE);116if(n_new == NULL) return NULL;117118n_prev = lv_ll_get_prev(ll_p, n_act);119node_set_next(ll_p, n_prev, n_new);120node_set_prev(ll_p, n_new, n_prev);121node_set_prev(ll_p, n_act, n_new);122node_set_next(ll_p, n_new, n_act);123}124125return n_new;126}127128/**129* Add a new tail to a linked list130* @param ll_p pointer to linked list131* @return pointer to the new tail132*/133void * lv_ll_ins_tail(lv_ll_t * ll_p)134{135lv_ll_node_t * n_new;136137n_new = lv_mem_alloc(ll_p->n_size + LL_NODE_META_SIZE);138if(n_new == NULL) return NULL;139140if(n_new != NULL) {141node_set_next(ll_p, n_new, NULL); /*No next after the new tail*/142node_set_prev(ll_p, n_new, ll_p->tail); /*The prev. before new is tho old tail*/143if(ll_p->tail != NULL) { /*If there is old tail then the new comes after it*/144node_set_next(ll_p, ll_p->tail, n_new);145}146147ll_p->tail = n_new; /*Set the new tail in the dsc.*/148if(ll_p->head == NULL) { /*If there is no head (1. node) set the head too*/149ll_p->head = n_new;150}151}152153return n_new;154}155156157/**158* Remove the node 'node_p' from 'll_p' linked list.159* It does not free the the memory of node.160* @param ll_p pointer to the linked list of 'node_p'161* @param node_p pointer to node in 'll_p' linked list162*/163void lv_ll_rem(lv_ll_t * ll_p, void * node_p)164{165if(lv_ll_get_head(ll_p) == node_p) {166/*The new head will be the node after 'n_act'*/167ll_p->head = lv_ll_get_next(ll_p, node_p);168if(ll_p->head == NULL) {169ll_p->tail = NULL;170} else {171node_set_prev(ll_p, ll_p->head, NULL);172}173} else if(lv_ll_get_tail(ll_p) == node_p) {174/*The new tail will be the node before 'n_act'*/175ll_p->tail = lv_ll_get_prev(ll_p, node_p);176if(ll_p->tail == NULL) {177ll_p->head = NULL;178} else {179node_set_next(ll_p, ll_p->tail, NULL);180}181} else {182lv_ll_node_t * n_prev = lv_ll_get_prev(ll_p, node_p);183lv_ll_node_t * n_next = lv_ll_get_next(ll_p, node_p);184185node_set_next(ll_p, n_prev, n_next);186node_set_prev(ll_p, n_next, n_prev);187}188}189190/**191* Remove and free all elements from a linked list. The list remain valid but become empty.192* @param ll_p pointer to linked list193*/194void lv_ll_clear(lv_ll_t * ll_p)195{196void * i;197void * i_next;198199i = lv_ll_get_head(ll_p);200i_next = NULL;201202while(i != NULL) {203i_next = lv_ll_get_next(ll_p, i);204205lv_ll_rem(ll_p, i);206lv_mem_free(i);207208i = i_next;209}210}211212/**213* Move a node to a new linked list214* @param ll_ori_p pointer to the original (old) linked list215* @param ll_new_p pointer to the new linked list216* @param node pointer to a node217* @return head changed218*/219bool lv_ll_chg_list(lv_ll_t * ll_ori_p, lv_ll_t * ll_new_p, void * node)220{221bool changed = ll_new_p->head != node;222223lv_ll_rem(ll_ori_p, node);224225/*Set node as head*/226node_set_prev(ll_new_p, node, NULL);227node_set_next(ll_new_p, node, ll_new_p->head);228229if(ll_new_p->head != NULL) { /*If there is old head then before it goes the new*/230node_set_prev(ll_new_p, ll_new_p->head, node);231}232233ll_new_p->head = node; /*Set the new head in the dsc.*/234if(ll_new_p->tail == NULL) { /*If there is no tail (first node) set the tail too*/235ll_new_p->tail = node;236}237238return changed;239}240241/**242* Return with head node of the linked list243* @param ll_p pointer to linked list244* @return pointer to the head of 'll_p'245*/246void * lv_ll_get_head(const lv_ll_t * ll_p)247{248void * head = NULL;249250if(ll_p != NULL) {251head = ll_p->head;252}253254return head;255}256257/**258* Return with tail node of the linked list259* @param ll_p pointer to linked list260* @return pointer to the head of 'll_p'261*/262void * lv_ll_get_tail(const lv_ll_t * ll_p)263{264void * tail = NULL;265266if(ll_p != NULL) {267tail = ll_p->tail;268}269270return tail;271}272273/**274* Return with the pointer of the next node after 'n_act'275* @param ll_p pointer to linked list276* @param n_act pointer a node277* @return pointer to the next node278*/279void * lv_ll_get_next(const lv_ll_t * ll_p, const void * n_act)280{281void * next = NULL;282283if(ll_p != NULL) {284const lv_ll_node_t * n_act_d = n_act;285memcpy(&next, n_act_d + LL_NEXT_P_OFFSET(ll_p), sizeof(void *));286}287288return next;289}290291/**292* Return with the pointer of the previous node after 'n_act'293* @param ll_p pointer to linked list294* @param n_act pointer a node295* @return pointer to the previous node296*/297void * lv_ll_get_prev(const lv_ll_t * ll_p, const void * n_act)298{299void * prev = NULL;300301if(ll_p != NULL) {302const lv_ll_node_t * n_act_d = n_act;303memcpy(&prev, n_act_d + LL_PREV_P_OFFSET(ll_p), sizeof(void *));304}305306return prev;307}308309void lv_ll_swap(lv_ll_t * ll_p, void * n1_p, void * n2_p)310{311(void)(ll_p);312(void)(n1_p);313(void)(n2_p);314/*TODO*/315}316317/**318* Move a nodw before an other node in the same linked list319* @param ll_p pointer to a linked list320* @param n_act pointer to node to move321* @param n_after pointer to a node which should be after `n_act`322*/323void lv_ll_move_before(lv_ll_t * ll_p, void * n_act, void * n_after)324{325if(n_act == n_after) return; /*Can't move before itself*/326327328void * n_before;329if(n_after != NULL) n_before = lv_ll_get_prev(ll_p, n_after);330else n_before = lv_ll_get_tail(ll_p); /*if `n_after` is NULL `n_act` should be the new tail*/331332if(n_act == n_before) return; /*Already before `n_after`*/333334/*It's much easier to remove from the list and add again*/335lv_ll_rem(ll_p, n_act);336337/*Add again by setting the prev. and next nodes*/338node_set_next(ll_p, n_before, n_act);339node_set_prev(ll_p, n_act, n_before);340node_set_prev(ll_p, n_after, n_act);341node_set_next(ll_p, n_act, n_after);342343/*If `n_act` was moved before NULL then it become the new tail*/344if(n_after == NULL) ll_p->tail = n_act;345}346347/**********************348* STATIC FUNCTIONS349**********************/350351/**352* Set the 'pervious node pointer' of a node353* @param ll_p pointer to linked list354* @param act pointer to a node which prev. node pointer should be set355* @param prev pointer to a node which should be the previous node before 'act'356*/357static void node_set_prev(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * prev)358{359if(act == NULL) return; /*Can't set the prev node of `NULL`*/360361uint32_t node_p_size = sizeof(lv_ll_node_t *);362if(prev) memcpy(act + LL_PREV_P_OFFSET(ll_p), &prev, node_p_size);363else memset(act + LL_PREV_P_OFFSET(ll_p), 0, node_p_size);364}365366/**367* Set the 'next node pointer' of a node368* @param ll_p pointer to linked list369* @param act pointer to a node which next node pointer should be set370* @param next pointer to a node which should be the next node before 'act'371*/372static void node_set_next(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * next)373{374if(act == NULL) return; /*Can't set the next node of `NULL`*/375376uint32_t node_p_size = sizeof(lv_ll_node_t *);377if(next) memcpy(act + LL_NEXT_P_OFFSET(ll_p), &next, node_p_size);378else memset(act + LL_NEXT_P_OFFSET(ll_p), 0, node_p_size);379}380381382383