Lua1.0 代码分析 hash.c

转载出处: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 *refint 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

posted @ 2015-11-04 09:30  vd01  阅读(252)  评论(0编辑  收藏  举报