libevent base file minheap-internal.h (minimum heap implementation)

The timeout event in libevent is managed using the minimum heap, the code is located in: minheap-internal.h.
typedef struct min_heap//a minimum heap structure
{
    struct  event ** p;//Point to the address of a continuous event pointer
    unsigned n, a; // The number of n queue elements, a represents the size of the queue space. 
} min_heap_t;

The comment was added by me, this name, n ah a ah, the ghost knows what it means.... I have to spit it out.

 The minimum heap structure manages a batch of events, and these events are stored in the order of the minimum heap according to the size of event->ev_timeout. When inserting events into the min heap, in order to maintain the min heap order, readjustment is required.

First let's talk about what a min heap is:

1. The heap is a binary tree

2. Min heap: the value of the parent node is always less than or equal to the child node

As shown below:

The number next to the circle in the above figure represents its subscript in the array. The heap is generally stored in an array, that is to say, the actual storage structure is continuous, but logically it is a tree structure . The advantage of this is that It's easy to find the element at the top of the heap, and for Libevent, it's easy to find the closest timeout event to the current time.

 Now think about how we want to insert an element, how do we move the position of the element in the array so that it remains logically a small heap? It is easy to see with the following figure:

1. Suppose the element we want to insert is 6 greater than the value of its parent node 2. Put the element on the corresponding index of the array, and the insertion is complete.

 

2. Suppose what we want to insert is 2 less than the value of its parent node 3. Then exchange the value of the node and its parent node. For the following figure, the insertion is complete after the exchange is completed. If the index=2 is found after the exchange The element is still smaller than its parent node index=0? It has to be exchanged again, and so on, until the root node is reached or it is not greater than the parent node.

When we get here, we can see the implementation code in libevent.

copy code
int min_heap_push(min_heap_t* s, struct  event * e)//Insert elements into the minimum heap
{
    if (min_heap_reserve(s, s->n + 1))
        return -1;
    min_heap_shift_up_(s, s->n++, e);
    return 0;
}

// Allocate queue size. n represents the number of queue elements. 
int min_heap_reserve(min_heap_t* s, unsigned n)
{
    if (s->a < n)     // The size of the queue is less than the number of elements, and the space is reallocated. 
    {
         struct  event ** p;
        unsigned a = s->a ? s->a * 2 : 8 ;   // Initially allocate 8 pointer size space, otherwise the original space size is doubled. 
        if (a < n)
            a = n;    // The space is still insufficient after doubling, then allocate n. 
        if (!(p = ( struct  event **)mm_realloc(s->p, a * sizeof *p))) // Reallocate memory 
            return - 1 ;
        s ->p = p; // Reassign the queue address and size. 
        s->a = a; //
     }
     return  0 ;
}

void min_heap_shift_up_(min_heap_t* s, unsigned hole_index, struct event* e)
{
    unsigned parent = (hole_index - 1 ) / 2 ;
     while (hole_index && min_heap_elem_greater(s->p[parent], e)) // smaller than the parent node or reaching the root node. Then swap positions. Loop. 
    {
        (s->p[hole_index] = s->p[parent])->ev_timeout_pos.min_heap_idx = hole_index;
        hole_index = parent;
        parent = (hole_index - 1) / 2;
    }
    (s->p[hole_index] = e)->ev_timeout_pos.min_heap_idx = hole_index;
}
copy code


copy code
void min_heap_shift_down_(min_heap_t* s, unsigned hole_index, struct event* e)
{
    unsigned min_child = 2 * (hole_index + 1);
    while (min_child <= s->n)
    {
    //找出较小子节点
    min_child -= min_child == s->n || min_heap_elem_greater(s->p[min_child], s->p[min_child - 1]);
    //比子节点小正常.不需要再交换位置,跳出循环.
    if (!(min_heap_elem_greater(e, s->p[min_child])))
        break;
    //比子节点大,要交换位置
    (s->p[hole_index] = s->p[min_child])->ev_timeout_pos.min_heap_idx = hole_index;
    hole_index = min_child;
    min_child = 2 * (hole_index + 1);
    }
    (s->p[hole_index] = e)->ev_timeout_pos.min_heap_idx = hole_index;
}
copy code

这里的hole_index是我们要填入某个值的下标,e是要填入的值.还是画图比较好理解:

当这个值(下标为hole_Index=1)比其父节点1(index = 0)小时,要向上移动调整.

当这个值(下标为hole_Index=1)比其最小子节点6(index = 3)还大时,要向下移动调整.

Libevent里是这么使用他们的:

copy code
int min_heap_erase(min_heap_t* s, struct event* e)
{
    if (-1 != e->ev_timeout_pos.min_heap_idx)
    {
        struct event *last = s->p[--s->n];//把最后一个值作为要填入hole_index的值
        unsigned parent = (e->ev_timeout_pos.min_heap_idx - 1) / 2;
        /* we replace e with the last element in the heap.  We might need to
           shift it upward if it is less than its parent, or downward if it is
           greater than one or both its children. Since the children are known
           to be less than the parent, it can't need to shift both up and
           down. */
        if (e->ev_timeout_pos.min_heap_idx > 0 && min_heap_elem_greater(s->p[parent], last))
            min_heap_shift_up_(s, e->ev_timeout_pos.min_heap_idx, last);
        else
            min_heap_shift_down_(s, e->ev_timeout_pos.min_heap_idx, last);
        e->ev_timeout_pos.min_heap_idx = -1;
        return 0;
    }
    return -1;
}
copy code
copy code
struct event* min_heap_pop(min_heap_t* s)
{
    if (s->n)
    {
        struct event* e = *s->p;
        min_heap_shift_down_(s, 0u, s->p[--s->n]);
        e->ev_timeout_pos.min_heap_idx = -1;
        return e;
    }
    return 0;
}
copy code

 

Finally, to sum up, due to the logical relationship of the heap structure, whether it is inserted or deleted, it is a comparison with the parent node or child node and then adjusting the position. This process is repeated until the boundary is reached. Conditional process. Keeping this in mind, it's not hard to write code.

Binary tree node i: The parent node is (i-1)/2. The child node is 2i+1, 2(i+1).


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325435931&siteId=291194637