# 哈希链表及其变种

## 普通链表

struct list_head {
};


## 哈希链表

struct hlist_head {
struct hlist_node *first;
};

struct hlist_node {
struct hlist_node *next, **pprev;
};


### 设计原理

Linux链表设计者认为双指针表头双循环链表对于HASH表来说过于浪费，因而另行设计了一套用于HASH表的hlist数据结构，

Q：为什么不使用struct hlist_node *prev，即让prev指向前一个节点呢？

struct hlist_node **pprev，pprev指向前一个元素的next指针，不用管前一个元素是节点还是表头。

### 常用操作

(1) 初始化

/*
* Mostly useful for hash tables where the two pointer list head is too wasteful.
* You lose the ability to access the tail in O(1).
*/
#define HLIST_HEAD_INIT { .first = NULL }
#define HLIST_HEAD (name) struct hlist_head name = { .first = NULL }
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)

(2) 插入

/* next must be != NULL */
static inline void hlist_add_before(struct hlist_node *n, struct hlist_node *next)
{
n->pprev = next->pprev;
n->next = next;
next->pprev = &n->next;
*(n->pprev) = n;
}


(3) 删除

static inline void __hlist_del(struct hlist_node *n)
{
struct hlist_node *next = n->next;
struct hlist_node **prev = n->pprev;
*pprev = next;
if (next)
next->pprev = pprev;
}


(4) 遍历

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *) 0)->MEMBER)

/*
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*/
#define container_of(ptr, type, member) ({    \
const typeof(((type *) 0)->member) * __mptr = (ptr);    \
(type *) ((char *) __mptr - offsetof(type, member)); })

#define hlist_entry(ptr, type, member) container_of(ptr, type, member)

for (pos = (head)->first; pos; pos = pos->next)

/**
* hlist_for_each_entry - iterate over list of given type
* @tpos: the type * to use as a loop cursor.
* @pos: the &struct hlist_node to use a loop cursor.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry(tpos, pos, head, member)    \
pos && ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;});    \
pos = pos->next)


## 哈希链表变种

Special version of lists, where end of list is not a NULL pointer,

but a 'nulls' marker, which can have many different values.

(up to 2^31 different values guaranteed on all platforms)

In the standard hlist, termination of a list is the NULL pointer.

In this special 'nulls' variant, we use the fact the objects stored in

a list are aligned on a word (4 or 8 bytes alignment).

We therefore use the last significant bit of 'ptr':

Set to 1: This is a 'nulls' end-of-list maker (ptr >> 1)

Set to 0: This is a pointer to some object (ptr)

### 设计原理

Q：为什么可以根据节点指针的最后一位是否为1来判断链表是否结束？

A：因为在一个结构体中，其元素是按4字节(32位机器)或者8字节(64位机器)对齐的。所以有效的节点指针的

最后一位总是为0。因此我们可以通过把节点指针的最后一位置为1，来作为结束标志。

/* 表头 */
struct hlist_nulls_node *first;
};

/* 节点 */
struct hlist_nulls_node {
struct hlist_nulls_node *next, **pprev;
};


### 常用操作

(1) 初始化

#define INIT_HLIST_NULLS_HEAD(ptr, nulls)    \
((ptr)->first = (struct hlist_nulls_node *) (1UL | (((long) nulls) << 1)))


(2) 判断是否为结束标志

/*
* ptr_is_a_nulls - Test if a ptr is a nulls
* @ptr: ptr to be tested
*/
static inline int is_a_nulls(const struct hlist_nulls_node *ptr)
{
return ((unsigned long) ptr & 1);
}


(3) 获取结束标志

/*
* get_nulls_value - Get the 'nulls' value of the end of chain
* @ptr: end of chain
* Should be called only if is_a_nulls(ptr);
*/
static inline unsigned long get_nulls_value(const struct hlist_nulls_node *ptr)
{
return ((unsigned long)ptr) >> 1;
}


(4) 插入

static inline void hlist_nulls_add_head(struct hlist_nulls_node *n, struct hlist_nulls_head *h)
{
struct hlist_nulls_node *first = h->first;
n->next = first;
n->pprev = &h->first;
h->first = n;
if (! is_a_nulls(first))
first->pprev = &n->next;
}


(5) 删除

/*
* These are non-NULL pointers that will result in page faults
* under normal circumstances, used to verify that nobody uses
* non-initialized list entries.
*/
#define LIST_POISON1 ((void *) 0x00100100 + POISON_POINTER_DELTA)
#define LIST_POISON2 ((void *) 0x00200200 + POISON_POINTER_DELTA)

static inline void __hlist_nulls_del(struct hlist_nulls_node *n)
{
struct hlist_nulls_node *next = n->next;
struct hlist_nulls_node **pprev = n->pprev;
*pprev = next;
if (! is_a_nulls(next))
next->pprev = pprev;
}

static inline void hlist_nulls_del(struct hlist_nulls_node *n)
{
__hlist_nulls_del(n);
n->pprev = LIST_POISON2; /* 防止再通过n访问链表 */
}


(6) 遍历

hlist_nulls_for_each_entry_from(tpos, pos, member)

## Author

zhangskd @ csdn blog

• 广告
• 抄袭
• 版权
• 政治
• 色情
• 无意义
• 其他

120