Go語言移植Linux核心資料結構hlist

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

         hlist(雜湊鏈表)可以通過相應的Hash演算法,迅速找到相關的鏈表Head及節點.
在有些應用情境,比Go標準庫提供的list(一種常見的雙向鏈表)更合適。
    依照list.h中的源碼,我實現了一個Go語言版本的hlist例子。

首先說下hlist的構成:
            在hlist(雜湊鏈表)中,
            頭結點使用struct hlist_head來表示,hlist_head僅一個first指標.
            普通節點使用struct hlist_node來表示。


源碼中有幾個特別的地方:
1. 在struct hlist_node中有一個**pprev中的二級指標,
          pprev指向的是當前hlist_node變數中的前一個變數的next的地址,
               如果是第一個元素的話,這個值指向的是first的地址,
               如果是最後一個節點的話,指向的是NULL。
  
2.container_of
   源碼裡面有個宏定義:
   #define hlist_entry(ptr, type, member) container_of(ptr,type,member)  
利用一些技巧,可通過結構體內部成員member(即鏈表node)得到所在結構體的首地址
所以C/C++應用中調用時,常用如下定義方式:
struct MYST {
              int data;
              struct list_head  head;
              struct hlist_node node;
} *myst;

在Go中,我將其變更為:
type HLElement struct {
                Next  *HLElement
                PPrev **HLElement
                Value interface{}
}
利用interface{}的特性,可將自訂struct之類對應設定給Value.在使用時,通過interface{}
轉換回原本對象即可

3.在hlist_del()中使用LIST_POISON而不是(== NULL)的形式

    n->next = LIST_POISON1;   
    n->pprev = LIST_POISON2;
    原因可能是,通過這個標記刪除node,如果在其它地方用到這種node,
    可以用來區分是否引用了已被刪除的node。
  在Go版中,我直接去掉了LIST_POISON,直接將其設為nil。

4.在遍曆鏈表時,提供了safe版本,防止因為節點被刪除而引起的中斷
       在Go版中也提供了相關實現。


具體的實現及測試代碼:

package main//hlist  (仿Linux核心資料結構hlist, 可參考核心源碼list.h)//author: Xiong Chuan Liang//date: 2015-2-12import ("fmt")func main() {/////////////////////////////////////*    Hash桶HLHead[0]HLHead[1]-> HLElement[0] ->HLElement[1]HLHead[2]-> HLElement[0]HLHead[3]-> HLElement[0] ->HLElement[1]-> HLElement[2] ->HLElement[3]HLHead[4]-> HLElement[0]*/fmt.Println(" \n //////////////////////////////////// ")fmt.Println(" hlist用法示範 : ")lst := [20]HLHead{}for i := 0; i < 20; i++ {hash := getHash(i)elem1 := &HLElement{Value: i}AddHead(elem1, &lst[hash])fmt.Println("i:", i, "  Hash:", hash)}fmt.Println(" \n 遍曆其中一個Head(lst[1])所對應的鏈表: ")f := func(e *HLElement) bool {fmt.Println("HLElement.Value =", e.Value)return true}For_each_safe(&lst[1], f)fmt.Println(" \n //////////////////////////////////// ")fmt.Println(" 示範InsertAfter/InsertBefore : ")hHead := &HLHead{}elem1 := &HLElement{Value: 1}elem2 := &HLElement{Value: 2}elem3 := &HLElement{Value: 300}elem4 := &HLElement{Value: 400}AddHead(elem1, hHead)InsertAfter(elem1, elem2)InsertAfter(elem2, elem3)InsertBefore(elem4, elem3)fmt.Println(" \n 遍曆鏈表看效果: ")For_each(hHead, f)fmt.Println(" \n //////////////////////////////////// ")fmt.Println(" \n 測試MoveList: lst[1] => MoveList(0->1) ")MoveList(&lst[0], &lst[1])For_each_safe(&lst[1], f)fmt.Println(" \n //////////////////////////////////// ")fmt.Println("  從指定element開始遍曆鏈表: ")For_each_safe_from(elem2, f)fmt.Println(" \n //////////////////////////////////// ")fmt.Println("  將自訂結構體放入鏈表: ")elemA := &HLElement{Value: &Demo{"a"}}elemB := &HLElement{Value: &Demo{"b"}}elemC := &HLElement{Value: &Demo{"c"}}AddHead(elemA, hHead)AddHead(elemB, hHead)AddHead(elemC, hHead)fs := func(e *HLElement) bool {m, ok := e.Value.(*Demo)if ok {fmt.Println("struct =", m.Data)} else {fmt.Println("int  =", e.Value)}return true}For_each_safe(hHead, fs)fmt.Println(" \n //////////////////////////////////// ")fmt.Println("  Remove(elemB): ")Remove(elemB)For_each_safe(hHead, fs)fmt.Println(" \n //////////////////////////////////// ")fmt.Println("  Empty()/Unhashed(): ")if Empty(hHead) {fmt.Println("  Empty(hHead) = true: hHead對應的鏈表為空白! ")} else {fmt.Println("  Empty(hHead) = false: hHead對應的鏈表不為空白! ")}hHeadEmpty := &HLHead{}if Empty(hHeadEmpty) {fmt.Println("  Empty(hHeadEmpty) = true: hHeadEmpty對應的鏈表為空白! ")} else {fmt.Println("  Empty(hHeadEmpty) = false : hHeadEmpty對應的鏈表不為空白! ")}if Unhashed(elem1) {fmt.Println("  Unhashed(elem1) = true: elem1不在鏈表中! ")} else {fmt.Println("  Unhashed(elem1) = false: elem1在鏈表中!  ")}}//示範用的Structtype Demo struct {Data string}//示範用的Hash演算法func getHash(c int) int {return (c % 16)}///////////////////////////////////////////////////////////////////type HLHead struct {First *HLElement}type HLElement struct {Next  *HLElementPPrev **HLElementValue interface{}}type ElemFunc func(e *HLElement) boolfunc For_each(h *HLHead, f ElemFunc) {pos := h.Firstfor pos != nil {if !f(pos) {break}pos = pos.Next}}func For_each_safe(h *HLHead, f ElemFunc) {pos := h.Firstfor pos != nil {n := pos.Nextif !f(pos) {break}pos = n}}func For_each_safe_from(h *HLElement, f ElemFunc) {pos := h.Nextfor pos != nil {n := pos.Nextif !f(pos) {break}pos = n}}//將普通結點n插入到頭結點h對應的hash桶的第一個結點的位置func AddHead(n *HLElement, h *HLHead) {first := h.Firstn.Next = firstif first != nil {first.PPrev = &n.Next}h.First = nn.PPrev = &h.First}//n: 新節點,  next:鏈表func InsertBefore(n *HLElement, next *HLElement) {pprev := next.PPrevn.PPrev = pprevn.Next = nextnext.PPrev = &n.Nextif pprev != nil {//將原來上一個結點的next的值,指向新節點n*pprev = n}}//n: 鏈表, next:新節點func InsertAfter(n *HLElement, next *HLElement) {next.Next = n.Nextn.Next = nextnext.PPrev = &n.Nextif next.Next != nil {next.Next.PPrev = &next.Next}}//移動listfunc MoveList(old, new *HLHead) {new.First = old.Firstif new.First != nil {//鏈表所指定向的第一個元素不為空白,更改其pprev指向到Newnew.First.PPrev = &new.First}old.First = nil}//判斷結點是否已經在鏈表中,如返回true,表示不在其中func Unhashed(h *HLElement) bool {return (h.PPrev == nil)}//判斷鏈表是否為空白func Empty(h *HLHead) bool {return (h.First == nil)}//刪除節點func Remove(n *HLElement) {next := n.Nextpprev := n.PPrevif pprev != nil {*pprev = next}if next != nil {next.PPrev = pprev}n.Next = niln.PPrev = nil}////////////////////////////////////////////////////////////////////* //////////////////////////////////// hlist用法示範 :i: 0   Hash: 0i: 1   Hash: 1i: 2   Hash: 2i: 3   Hash: 3i: 4   Hash: 4i: 5   Hash: 5i: 6   Hash: 6i: 7   Hash: 7i: 8   Hash: 8i: 9   Hash: 9i: 10   Hash: 10i: 11   Hash: 11i: 12   Hash: 12i: 13   Hash: 13i: 14   Hash: 14i: 15   Hash: 15i: 16   Hash: 0i: 17   Hash: 1i: 18   Hash: 2i: 19   Hash: 3 遍曆其中一個Head(lst[1])所對應的鏈表:HLElement.Value = 17HLElement.Value = 1 //////////////////////////////////// 示範InsertAfter/InsertBefore : 遍曆鏈表看效果:HLElement.Value = 1HLElement.Value = 2HLElement.Value = 400HLElement.Value = 300 //////////////////////////////////// 測試MoveList: lst[1] => MoveList(0->1)HLElement.Value = 16HLElement.Value = 0 ////////////////////////////////////  從指定element開始遍曆鏈表:HLElement.Value = 400HLElement.Value = 300 ////////////////////////////////////  將自訂結構體放入鏈表:struct = cstruct = bstruct = aint  = 1int  = 2int  = 400int  = 300 ////////////////////////////////////  Remove(elemB):struct = cstruct = aint  = 1int  = 2int  = 400int  = 300 ////////////////////////////////////  Empty()/Unhashed():  Empty(hHead) = false: hHead對應的鏈表不為空白!  Empty(hHeadEmpty) = true: hHeadEmpty對應的鏈表為空白!  Unhashed(elem1) = false: elem1在鏈表中!*/

      例子基本實現了常用的東西。

       在實現InsertBefore()的過程中,發現指標的使用與C實現有些許差別,原因不甚了了,有空再測測。


部份參考資料:

一份list.h的源碼:
http://lxr.free-electrons.com/source/include/linux/list.h
一篇很細的hlist的說明:
http://blog.chinaunix.net/uid-22566367-id-2192969.html



MAIL:  xcl_168@aliyun.com

BLOG: http://blog.csdn.net/xcl168



相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.