Lua1.0 代碼分析 hash.c

hash.c 代碼分析

Lua 中最重要的一個數據結構及相關操做。

主要看下幾個對外的接口。

node

/*
** 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);
釋放關聯數組。


算法

/*
** 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 的實現:
數組

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 的實現
數據結構

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 的實現
測試

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):

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
指針

相關文章
相關標籤/搜索