转载出处:http://my.oschina.net/xhan/blog/308325
hash.c 代码分析
Lua 中最重要的一个数据结构及相关操作。
主要看下几个对外的接口。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
/* ** Create a new hash. Return the hash pointer or NULL on error. */ Hash *lua_hashcreate (unsigned int nhash) { Hash *t = new (Hash); if (t == NULL) { lua_error ( "not enough memory" ); return NULL; } nhash(t) = nhash; markarray(t) = 0; nodelist(t) = newvector (nhash, Node*); if (nodelist(t) == NULL) { lua_error ( "not enough memory" ); return NULL; } return t; } |
新建一个关联数组,入参是关联数组的大小。
新建一个关联数组。
设置大小。
打标记。
新建指针数组。
void lua_hashdelete (Hash *h);
释放关联数组。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
/* ** If the hash node is present, return its pointer, otherwise create a new ** node for the given reference and also return its pointer. ** On error, return NULL. */ Object *lua_hashdefine (Hash *t, Object * ref ) { int h; Node *n; h = head (t, ref ); if (h < 0) return NULL; n = present(t, ref , h); if (n == NULL) { n = new (Node); if (n == NULL) { lua_error ( "not enough memory" ); return NULL; } n-> ref = * ref ; tag(&n->val) = T_NIL; n->next = list(t,h); /* link node to head of list */ list(t,h) = n; } return (&n->val); } |
在关联数组中查看指定项是否存在,如果存在,返回它的指针。
如果不存在,新建一个结点,也同样返回它的指针。
返回关联引用在关联数组中的头。
跟据关联数组的头,查看引用在关联数组中是否存在:
如果不存在,新建一个结点,设置其引用为传入的参数,同时设置其值为空,把新建的结点插入到表头。
如果存在,直接返回它的值。
来看看 head 和 present 的实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
static int head (Hash *t, Object * ref ) /* hash function */ { if (tag( ref ) == T_NUMBER) return ((( int )nvalue( ref ))%nhash(t)); else if (tag( ref ) == T_STRING) { int h; char *name = svalue( ref ); for (h=0; *name!=0; name++) /* interpret name as binary number */ { h <<= 8; h += (unsigned char ) *name; /* avoid sign extension */ h %= nhash(t); /* make it a valid index */ } return h; } else { lua_reportbug ( "unexpected type to index table" ); return -1; } } |
关联数组分为两个部分,数值部分和引用部分。
数值部分的下标是通过数值的大小和关联数组的大小取余得到的。
而引用部分目前只支持字符串类型。
字符串部分是通过一个算法得到它的散列值的。
具体算法是把字符串的 ASCII 码左移 8 位后相加之和与关联数组的大小取余。
再看 present 的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
static Node *present(Hash *t, Object * ref , int h) { Node *n=NULL, *p; if (tag( ref ) == T_NUMBER) { for (p=NULL,n=list(t,h); n!=NULL; p=n, n=n->next) if (ref_tag(n) == T_NUMBER && nvalue( ref ) == ref_nvalue(n)) break ; } else if (tag( ref ) == T_STRING) { for (p=NULL,n=list(t,h); n!=NULL; p=n, n=n->next) if (ref_tag(n) == T_STRING && streq(svalue( ref ),ref_svalue(n))) break ; } if (n==NULL) /* name not present */ return NULL; #if 0 if (p!=NULL) /* name present but not first */ { p->next=n->next; /* move-to-front self-organization */ n->next=list(t,h); list(t,h)=n; } #endif return n; } |
通过数组和下标找到相应的链表,在链表中查找是否有指定的值。如果有,返回结点,如果没有,返回空。
void lua_hashmark (Hash *h)
标记关联数组中所有的结点。
再看看 lua_next 的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
void lua_next ( void ) { Hash *a; Object *o = lua_getparam (1); Object *r = lua_getparam (2); if (o == NULL || r == NULL) { lua_error ( "too few arguments to function `next'" ); return ; } if (lua_getparam (3) != NULL) { lua_error ( "too many arguments to function `next'" ); return ; } if (tag(o) != T_ARRAY) { lua_error ( "first argument of function `next' is not a table" ); return ; } a = avalue(o); if (tag(r) == T_NIL) { firstnode (a, 0); return ; } else { int h = head (a, r); if (h >= 0) { Node *n = list(a,h); while (n) { if ( memcmp (&n-> ref ,r, sizeof (Object)) == 0) { if (n->next == NULL) { firstnode (a, h+1); return ; } else if (tag(&n->next->val) != T_NIL) { lua_pushobject (&n->next-> ref ); lua_pushobject (&n->next->val); return ; } else { Node *next = n->next->next; while (next != NULL && tag(&next->val) == T_NIL) next = next->next; if (next == NULL) { firstnode (a, h+1); return ; } else { lua_pushobject (&next-> ref ); lua_pushobject (&next->val); } return ; } } n = n->next; } if (n == NULL) lua_error ( "error in function 'next': reference not found" ); } } } |
在 Lua 脚本中调用 next 时调用的就是它。作用是数组遍历。
给定一个数组和引用,返回数组中给定引用的下一个结点。
如果给的是一个空值,返回数组的头一个结点。
否则返回数组中该值的下一个非空结点。
这里返回了两个值到 Lua 的脚本中。
看下自带的一个用到它的测试程序(array.lua):
a = @() i=0 while i<10 do a[i] = i*i i=i+1 end r,v = next(a,nil) while r ~= nil do print ("array["..r.."] = "..v) r,v = next(a,r) end
这个程序会打印出以下:
array[0] = 0
array[1] = 1
array[2] = 4
array[3] = 9
array[4] = 16
array[5] = 25
array[6] = 36
array[7] = 49
array[8] = 64
array[9] = 81