littlevgl的list理解与分析

Littlevgl的list主要在lv_ll.c中。在不熟悉littlevgl的代码情况下,楞一看确实没看懂,再一看其实也挺简单的,本以为它会按照linux的方式来写,没想到并不是如此。


原因估计如:

1.list的定义是head与tail,而节点内容的含义是prev与next,所以只能在程序编写的时候,通过函数去强制将含义进行转换。另外head与tail它抽象出来指向的是一个个节点。

2.list中的n_size表示存储的数据大小。而节点中则将根据该值来实际申请内存空间。所以,节点的含义与list是不同的。

总之,如果是我来写这部分的代码,我会改写成如下方式:

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

ll_p->n_size = node_size;
}/<code>

这样在函数处理的过程中,可以直接将申请的内存强制转换成lv_ll_node_t_1,所以后面的处理也就可以直接改写成prev,next。

直接将它的代码列出来分析:

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

ll_p->n_size = node_size;
}/<code>

其中,LL_NODE_META_SIZE表示head与tail两个指针占用的空间大小。LL_PREV_P_OFFSET

表示head在结构体(内存单元)中的偏移,LL_NEXT_P_OFFSET表示tail在结构体(内存单元)中的偏移。


<code>void lv_ll_init(lv_ll_t * ll_p, uint32_t node_size)
{
ll_p->head = NULL;
ll_p->tail = NULL;
#ifdef LV_MEM_ENV64
/*Round the size up to 8*/
if(node_size & 0x7) {
node_size = node_size & (~0x7);
node_size += 8;
}
#else

/*Round the size up to 4*/
if(node_size & 0x3) {
node_size = node_size & (~0x3);
node_size += 4;
}
#endif

ll_p->n_size = node_size;
}/<code>

这个函数是用来对list进行初始化。


<code>void lv_ll_chg_list(lv_ll_t * ll_ori_p, lv_ll_t * ll_new_p, void * node, bool head)
{
lv_ll_rem(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;
}
}
}/<code>

这个函数是表示给list添加一个新的节点。可以看到,函数里面基本上把新申请的内存都当地址来做,而申请的内存都刚好是一个list的大小。所以,可以将里面的一些函数,例如:node_set_prev(ll_p, n_new, NULL)直接翻译就是n_new->head=NULL;node_set_next(ll_p, n_new, ll_p->head);翻译一下就是n_new->tail=ll_p->head。


这个函数的意义其实就是将list的head与tail指向一个新添加的节点。这样,ll_p就相当于做好了指针的准备。


<code>void * lv_ll_get_tail(const lv_ll_t * ll_p)
{
void * tail = NULL;

if(ll_p != NULL) {
tail = ll_p->tail;
}

return tail;
}/<code>

这个函数作用是在n_act之前插入一个新的节点。


<code>void * lv_ll_get_next(const lv_ll_t * ll_p, const void * n_act)
{
void * next = NULL;

if(ll_p != NULL) {
const lv_ll_node_t * n_act_d = n_act;
memcpy(&next, n_act_d + LL_NEXT_P_OFFSET(ll_p), sizeof(void *));
}

return next;
}/<code>

这个函数作用是在list的尾部插入一个新的节点。


<code>void * lv_ll_get_tail(const lv_ll_t * ll_p)
{
void * tail = NULL;

if(ll_p != NULL) {
tail = ll_p->tail;
}

return tail;
}/<code>

这个函数作用是将node_p从list中删除掉。


<code>void * lv_ll_get_next(const lv_ll_t * ll_p, const void * n_act)
{
void * next = NULL;

if(ll_p != NULL) {
const lv_ll_node_t * n_act_d = n_act;
memcpy(&next, n_act_d + LL_NEXT_P_OFFSET(ll_p), sizeof(void *));
}

return next;
}/<code>

这个函数的作用是将节点数据都释放掉。


<code>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;
}/<code>

这个函数的作用是将node节点从ll_ori_p中删除,并且添加到ll_new_p中去。如果head=1表明需要将node节点添加到ll_new_p的头部位置去,而如果head=0,则需要添加到ll_new_p的尾部去。这个函数写的不好,完全可以和前面的

lv_ll_ins_head()lv_ll_ins_tail()复用。


<code>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_rem(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;
}/<code>


这个函数是用来获取list的头部,也就是第一个节点。


<code>void * lv_ll_get_tail(const lv_ll_t * ll_p)
{
void * tail = NULL;

if(ll_p != NULL) {
tail = ll_p->tail;
}

return tail;
}/<code>

这个函数是用来获取list的尾部节点。


<code>void * lv_ll_get_next(const lv_ll_t * ll_p, const void * n_act)
{
void * next = NULL;

if(ll_p != NULL) {
const lv_ll_node_t * n_act_d = n_act;
memcpy(&next, n_act_d + LL_NEXT_P_OFFSET(ll_p), sizeof(void *));
}

return next;
}/<code>

这个函数是用来获取list中的n_act节点的下一个节点。


<code>void * lv_ll_get_prev(const lv_ll_t * ll_p, const void * n_act)
{
void * prev = NULL;

if(ll_p != NULL) {
const lv_ll_node_t * n_act_d = n_act;
memcpy(&prev, n_act_d + LL_PREV_P_OFFSET(ll_p), sizeof(void *));
}

return prev;
}/<code>

这个函数是用来获取list中的n_act节点的上一个节点。


<code>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;
}/<code>


这个函数是用来获取list的节点数目。


<code>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_rem(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;
}/<code>


这个函数是将n_act节点插入到n_after之前。它里面为了方便,就将n_act先从list中删除,然后再添加回去。


<code>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;
}/<code>

这个函数作用是判断list是否为空。


<code>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`*/

uint32_t node_p_size = sizeof(lv_ll_node_t *);
if(prev)
memcpy(act + LL_PREV_P_OFFSET(ll_p), &prev, node_p_size);
else
memset(act + LL_PREV_P_OFFSET(ll_p), 0, node_p_size);
}/<code>

这个函数其实就相当于act->head=prev。


<code>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`*/

uint32_t node_p_size = sizeof(lv_ll_node_t *);
if(next)

memcpy(act + LL_NEXT_P_OFFSET(ll_p), &next, node_p_size);
else
memset(act + LL_NEXT_P_OFFSET(ll_p), 0, node_p_size);
}/<code>

这个函数就相当于act->tail=next。


注意:

上面说的那么多相当于(act->tail=next),也就是不等于。littlevgl在申请一个节点单元的数据的时候,它的布局是:(n_size个数据大小的内存+prev指针+next指针)。而list的布局是(size值+head指针+tail指针)。


分享到:


相關文章: