literwave game.

lua源码分析

Word count: 4.3kReading time: 22 min
2022/12/31

前言

  • 这是一篇关于lua源码分析的博客,每个阶段会有不同的理解,所以这篇博客会更新自己的认知。

正文

  • 首先我们看的是lua.c的入口,也就是我们使用lua.exe执行lua文件的地方,从这里可以知道lua执行lua文本文件的过程。
  • 刚开始使用luaL_newstatenew了一个lua_State结构体

数据类型

  • lua使用lua_TValue表示lua中所有的类型,具体如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // lobject.h
    typedef struct lua_TValue {
    TValuefields;
    } TValue;

    #define TValuefields Value value_; int tt_

    /*
    ** Union of all Lua values
    */
    typedef union Value {
    GCObject *gc; /* collectable objects */
    void *p; /* light userdata */
    int b; /* booleans */
    lua_CFunction f; /* light C functions */
    lua_Integer i; /* integer numbers */
    lua_Number n; /* float numbers */
    } Value;
    • 为什么可以表示所有类型呢,如果是类似于需要垃圾回收的,就可以使用GCObject *指针表示,比如TString,Table,而其他的就表示如不需要lua进行垃圾回收,但需要释放内存的,需要靠自己释放内存;
  • lua使用GCUnion表示使用需要垃圾回收的对象,具体如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //lstate.h
    /*
    ** Union of all collectable objects (only for conversions)
    */
    union GCUnion {
    GCObject gc; /* common header */
    struct TString ts;
    struct Udata u;
    union Closure cl;
    struct Table h;
    struct Proto p;
    struct lua_State th; /* thread */
    };
    • 这里面每个类型都定义了一个gcobject,也就是公共头,这样可以用上面的GCObject表示,GCObject其实就是用来内存对齐的,这是使用联合体的好处。

字符串的实现

  • luaS_newlstr判断完是否长短字符串

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    /*
    ** new string (with explicit length)
    */
    TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
    if (l <= LUAI_MAXSHORTLEN) /* short string? */
    return internshrstr(L, str, l);
    else {
    TString *ts;
    if (l >= (MAX_SIZE - sizeof(TString))/sizeof(char))
    luaM_toobig(L);
    ts = luaS_createlngstrobj(L, l);
    memcpy(getstr(ts), str, l * sizeof(char));
    return ts;
    }
    }
    • 这个是带长度的字符串
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    /*
    ** Create or reuse a zero-terminated string, first checking in the
    ** cache (using the string address as a key). The cache can contain
    ** only zero-terminated strings, so it is safe to use 'strcmp' to
    ** check hits.
    */
    TString *luaS_new (lua_State *L, const char *str) {
    unsigned int i = point2uint(str) % STRCACHE_N; /* hash */
    int j;
    TString **p = G(L)->strcache[i];
    for (j = 0; j < STRCACHE_M; j++) {
    if (strcmp(str, getstr(p[j])) == 0) /* hit? */
    return p[j]; /* that is it */
    }
    /* normal route */
    for (j = STRCACHE_M - 1; j > 0; j--)
    p[j] = p[j - 1]; /* move out last element */
    /* new element is first in the list */
    p[0] = luaS_newlstr(L, str, strlen(str));
    return p[0];
    }
    • 创建或重新使用一个带\0终止的字符串,首次检查缓存,缓存只包含0终止的字符串,所以它是安全的,去使用strcmp去检查缓存是否存在。其实就是从strcache里面中找,如果找不到,就把p[0]缓存为新创建的字符串。
  • 短字符串(长度小于等于40)

    • 短字符串走internshrstr(L, str, l)

      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
      /*
      ** checks whether short string exists and reuses it or creates a new one
      */
      static TString *internshrstr (lua_State *L, const char *str, size_t l) {
      TString *ts;
      global_State *g = G(L);
      unsigned int h = luaS_hash(str, l, g->seed);
      TString **list = &g->strt.hash[lmod(h, g->strt.size)];
      lua_assert(str != NULL); /* otherwise 'memcmp'/'memcpy' are undefined */
      for (ts = *list; ts != NULL; ts = ts->u.hnext) {
      if (l == ts->shrlen &&
      (memcmp(str, getstr(ts), l * sizeof(char)) == 0)) {
      /* found! */
      if (isdead(g, ts)) /* dead (but not collected yet)? */
      changewhite(ts); /* resurrect it */
      return ts;
      }
      }
      if (g->strt.nuse >= g->strt.size && g->strt.size <= MAX_INT/2) {
      luaS_resize(L, g->strt.size * 2);
      list = &g->strt.hash[lmod(h, g->strt.size)]; /* recompute with new size */
      }
      ts = createstrobj(L, l, LUA_TSHRSTR, h);
      memcpy(getstr(ts), str, l * sizeof(char));
      ts->shrlen = cast_byte(l);
      ts->u.hnext = *list;
      *list = ts;
      g->strt.nuse++;
      return ts;
      }
    • luaS_hash(str, l, g->seed);这里面是产生hash的接口,其实是根据g->seed种子去做的hash值,

      1
      2
      3
      4
      5
      6
      7
      unsigned int luaS_hash (const char *str, size_t l, unsigned int seed) {
      unsigned int h = seed ^ cast(unsigned int, l);
      size_t step = (l >> LUAI_HASHLIMIT) + 1;
      for (; l >= step; l -= step)
      h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1]));
      return h;
      }
      • 种子去异或长度,由异或的运算可知,相同为0,不同为1;然后step右移5位,这样循环的次数会变少,关于hash算法的谈论,可以看云风大神关于lua hash函数的一点谈论,这里就不深入研究了,hash函数的设计其实就是为了减少少的碰撞,不至于吃更多的内存。
    • 继续回到hash后面的讲解,取到g->strt.hash其实lmod其实就是为了防止不超过当前strt.size长度的大小,然后开始顺着列表开始遍历,如果产生碰撞,其实就是产生一条链子,顺着这条链表查找下去如果有申请的字符串,就使用,而且先检查长度,这样会减少strcmp的比较效率,如果找到了,判断一下字符串是否在可回收阶段,返回查找到的字符串;如果没找到,肯定是需要申请内存去放TStirng,这样肯定先检查需不需要扩容,具体如下

      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
      /*
      ** resizes the string table
      */
      void luaS_resize (lua_State *L, int newsize) {
      int i;
      stringtable *tb = &G(L)->strt;
      if (newsize > tb->size) { /* grow table if needed */
      luaM_reallocvector(L, tb->hash, tb->size, newsize, TString *);
      for (i = tb->size; i < newsize; i++)
      tb->hash[i] = NULL;
      }
      for (i = 0; i < tb->size; i++) { /* rehash */
      TString *p = tb->hash[i];
      tb->hash[i] = NULL;
      while (p) { /* for each node in the list */
      TString *hnext = p->u.hnext; /* save next */
      unsigned int h = lmod(p->hash, newsize); /* new position */
      p->u.hnext = tb->hash[h]; /* chain it */
      tb->hash[h] = p;
      p = hnext;
      }
      }
      if (newsize < tb->size) { /* shrink table if needed */
      /* vanishing slice should be empty */
      lua_assert(tb->hash[newsize] == NULL && tb->hash[tb->size - 1] == NULL);
      luaM_reallocvector(L, tb->hash, tb->size, newsize, TString *);
      }
      tb->size = newsize;
      }
      • 调整大小,这个接口支持扩容和缩小,分配一个newsize * (TString *)的大小,然后把newsize-oldsize的部分初始化,赋值为空指针,再把旧的元素重新与新大小与一遍,装进新的里面。
    • 回到createstrobj,这里采用了一个巧妙的方法,申请了一个TString的内存大小,再加上字符串的内存大小,转换为字符串的内存的时候就直接使用cast(char*, (ts)) + sizeof (UTString),且使用ts->next = g->allgc头插法,去方便gc遍历

  • 长字符串(长度小于等于40)

    • 长字符串没有放进g->strt里面,而且hash值就是g->seed,简单粗暴,就是创建了一个TString对象,而且长字符串好像不需要查找原来是否有创建好的字符串,也就是说有一个就new一个。

table的实现

  • 看下table结构体的定义,如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    typedef struct Table {
    CommonHeader;
    lu_byte flags; /* 1<<p means tagmethod(p) is not present */
    lu_byte lsizenode; /* log2 of size of 'node' array */
    unsigned int sizearray; /* size of 'array' array */
    TValue *array; /* array part */
    Node *node;
    Node *lastfree; /* any free position is before this position */
    struct Table *metatable;
    GCObject *gclist;
    } Table;
  • 首先如何分配一个Table *luaH_new (lua_State *L)去分配一个table所需要的内存,如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Table *luaH_new (lua_State *L) {
    GCObject *o = luaC_newobj(L, LUA_TTABLE, sizeof(Table));
    Table *t = gco2t(o);
    t->metatable = NULL;
    t->flags = cast_byte(~0);
    t->array = NULL;
    t->sizearray = 0;
    setnodevector(L, t, 0);
    return t;
    }
    • 这里setnodevector(L,t,0)会把hash相关的变量初始化,初始化第一个node,把keyvalue赋值为nil
  • 看一下这个table赋值的接口,如下,这个是使用keyint的时候去插入value的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value) {
    const TValue *p = luaH_getint(t, key);
    TValue *cell;
    if (p != luaO_nilobject)
    cell = cast(TValue *, p);
    else {
    TValue k;
    setivalue(&k, key);
    cell = luaH_newkey(L, t, &k);
    }
    setobj2t(L, cell, value);
    }
    • 这里首先使用luaH_getint(t,key)去找这个元素,会判断这个值在数组部分还是在hash部分,如果找到了,直接覆盖原来的值,如果没找到,这个时候就需要new一个key,且返回key对应的value,这个key为啥要在hash部分呢,因为数组部分查找竟然没有,那肯定只能是在hash部分。
  • luaH_newkey(L, t, &k),这里是hash部分的通用接口,如下

    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
    TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
    Node *mp;
    TValue aux;
    if (ttisnil(key)) luaG_runerror(L, "table index is nil");
    else if (ttisfloat(key)) {
    lua_Integer k;
    if (luaV_tointeger(key, &k, 0)) { /* does index fit in an integer? */
    setivalue(&aux, k);
    key = &aux; /* insert it as an integer */
    }
    else if (luai_numisnan(fltvalue(key)))
    luaG_runerror(L, "table index is NaN");
    }
    mp = mainposition(t, key);
    if (!ttisnil(gval(mp)) || isdummy(t)) { /* main position is taken? */
    Node *othern;
    Node *f = getfreepos(t); /* get a free place */
    if (f == NULL) { /* cannot find a free place? */
    rehash(L, t, key); /* grow table */
    /* whatever called 'newkey' takes care of TM cache */
    return luaH_set(L, t, key); /* insert key into grown table */
    }
    lua_assert(!isdummy(t));
    othern = mainposition(t, gkey(mp));
    if (othern != mp) { /* is colliding node out of its main position? */
    /* yes; move colliding node into free position */
    while (othern + gnext(othern) != mp) /* find previous */
    othern += gnext(othern);
    gnext(othern) = cast_int(f - othern); /* rechain to point to 'f' */
    *f = *mp; /* copy colliding node into free pos. (mp->next also goes) */
    if (gnext(mp) != 0) {
    gnext(f) += cast_int(mp - f); /* correct 'next' */
    gnext(mp) = 0; /* now 'mp' is free */
    }
    setnilvalue(gval(mp));
    }
    else { /* colliding node is in its own main position */
    /* new node will go into free position */
    if (gnext(mp) != 0)
    gnext(f) = cast_int((mp + gnext(mp)) - f); /* chain new position */
    else lua_assert(gnext(f) == 0);
    gnext(mp) = cast_int(f - mp);
    mp = f;
    }
    }
    setnodekey(L, &mp->i_key, key);
    luaC_barrierback(L, t, key);
    lua_assert(ttisnil(gval(mp)));
    return gval(mp);
    }
  • 首先,先翻译这个解释把,插入一个新keyhash table,首先检查keymp位置是否是空闲的,如果不是,检查碰撞的节点它本来的位置是否在这个mp,如果不是,移动这个碰撞的节点放入空的位置,新key放入它自己的mp;除此之外,碰撞的节点在它自己的mp,新key直接放入空的位置。

  • 分析一下mainposition方法,如下

    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
    /*
    ** returns the 'main' position of an element in a table (that is, the index
    ** of its hash value)
    */
    static Node *mainposition (const Table *t, const TValue *key) {
    switch (ttype(key)) {
    case LUA_TNUMINT:
    return hashint(t, ivalue(key));
    case LUA_TNUMFLT:
    return hashmod(t, l_hashfloat(fltvalue(key)));
    case LUA_TSHRSTR:
    return hashstr(t, tsvalue(key));
    case LUA_TLNGSTR:
    return hashpow2(t, luaS_hashlongstr(tsvalue(key)));
    case LUA_TBOOLEAN:
    return hashboolean(t, bvalue(key));
    case LUA_TLIGHTUSERDATA:
    return hashpointer(t, pvalue(key));
    case LUA_TLCF:
    return hashpointer(t, fvalue(key));
    default:
    lua_assert(!ttisdeadkey(key));
    return hashpointer(t, gcvalue(key));
    }
    }
    • 函数的作用就是取tablekey对应node的位置,如果是int类型,hash出来就是int&lsizenode的值
  • 如果是mp位置不为 nil,或者没有元素,首先获取一个空闲位置freemp,如果没有的话,就需要调整表的大小,也就是rehash(L, t, key)重新hash,如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    /*
    ** nums[i] = number of keys 'k' where 2^(i - 1) < k <= 2^i
    */
    static void rehash (lua_State *L, Table *t, const TValue *ek) {
    unsigned int asize; /* optimal size for array part */
    unsigned int na; /* number of keys in the array part */
    unsigned int nums[MAXABITS + 1];
    int i;
    int totaluse;
    for (i = 0; i <= MAXABITS; i++) nums[i] = 0; /* reset counts */
    na = numusearray(t, nums); /* count keys in array part */
    totaluse = na; /* all those keys are integer keys */
    totaluse += numusehash(t, nums, &na); /* count keys in hash part */
    /* count extra key */
    na += countint(ek, nums);
    totaluse++;
    /* compute new size for array part */
    asize = computesizes(nums, &na);
    /* resize the table to new computed sizes */
    luaH_resize(L, t, asize, totaluse - na);
    }
    • nums[32],然后初始化nums[i] = 0,na表示数组里面有多少keys,具体代码如下

      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
      /*
      ** Count keys in array part of table 't': Fill 'nums[i]' with
      ** number of keys that will go into corresponding slice and return
      ** total number of non-nil keys.
      */
      static unsigned int numusearray (const Table *t, unsigned int *nums) {
      int lg;
      unsigned int ttlg; /* 2^lg */
      unsigned int ause = 0; /* summation of 'nums' */
      unsigned int i = 1; /* count to traverse all array keys */
      /* traverse each slice */
      for (lg = 0, ttlg = 1; lg <= MAXABITS; lg++, ttlg *= 2) {
      unsigned int lc = 0; /* counter */
      unsigned int lim = ttlg;
      if (lim > t->sizearray) {
      lim = t->sizearray; /* adjust upper limit */
      if (i > lim)
      break; /* no more elements to count */
      }
      /* count elements in range (2^(lg - 1), 2^lg] */
      for (; i <= lim; i++) {
      if (!ttisnil(&t->array[i-1]))
      lc++;
      }
      nums[lg] += lc;
      ause += lc;
      }
      return ause;
      }
      • 各变量的含义,其实变量limit,就是防止超出数组大小,count elements in range (2^(lg - 1), 2^lg],i就是保存下遍历到多少了,这样就得到数组部分位图和总共使用的数量。
    • hash部分的统计元素有点巧妙,如下

      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
      static int numusehash (const Table *t, unsigned int *nums, unsigned int *pna) {
      int totaluse = 0; /* total number of elements */
      int ause = 0; /* elements added to 'nums' (can go to array part) */
      int i = sizenode(t);
      while (i--) {
      Node *n = &t->node[i];
      if (!ttisnil(gval(n))) {
      ause += countint(gkey(n), nums);
      totaluse++;
      }
      }
      *pna += ause;
      return totaluse;
      }

      static int countint (const TValue *key, unsigned int *nums) {
      unsigned int k = arrayindex(key);
      if (k != 0) { /* is 'key' an appropriate array index? */
      nums[luaO_ceillog2(k)]++; /* count as such */
      return 1;
      }
      else
      return 0;
      }

      /*
      ** Computes ceil(log2(x))
      */
      int luaO_ceillog2 (unsigned int x) {
      static const lu_byte log_2[256] = { /* log_2[i] = ceil(log2(i - 1)) */
      0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
      6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
      7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
      7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
      8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
      8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
      8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
      8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
      };
      int l = 0;
      x--;
      while (x >= 256) { l += 8; x >>= 8; }
      return l + log_2[x];
      }
      • 首先只统计值不是nil的元素的key,重点看一下luaO_ceillog2,这样循环会减少遍历,类似于空间换时间;其实log_2[i]表示的是 2^log_2[i] >= i也就是log_2[i] = ceil(log2(i - 1)),如果大于256说明一定是在2^8方以上,
    • 在加上新增加的key

    • computesizes(nums, &na)就是重新计算大小了,如下

      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
      /*
      ** Compute the optimal size for the array part of table 't'. 'nums' is a
      ** "count array" where 'nums[i]' is the number of integers in the table
      ** between 2^(i - 1) + 1 and 2^i. 'pna' enters with the total number of
      ** integer keys in the table and leaves with the number of keys that
      ** will go to the array part; return the optimal size.
      */
      static unsigned int computesizes (unsigned int nums[], unsigned int *pna) {
      int i;
      unsigned int twotoi; /* 2^i (candidate for optimal size) */
      unsigned int a = 0; /* number of elements smaller than 2^i */
      unsigned int na = 0; /* number of elements to go to array part */
      unsigned int optimal = 0; /* optimal size for array part */
      /* loop while keys can fill more than half of total size */
      for (i = 0, twotoi = 1;
      twotoi > 0 && *pna > twotoi / 2;
      i++, twotoi *= 2) {
      if (nums[i] > 0) {
      a += nums[i];
      if (a > twotoi/2) { /* more than half elements present? */
      optimal = twotoi; /* optimal size (till now) */
      na = a; /* all elements up to 'optimal' will go to array part */
      }
      }
      }
      lua_assert((optimal == 0 || optimal / 2 < na) && na <= optimal);
      *pna = na;
      return optimal;
      }


      static int countint (const TValue *key, unsigned int *nums) {
      unsigned int k = arrayindex(key);
      if (k != 0) { /* is 'key' an appropriate array index? */
      nums[luaO_ceillog2(k)]++; /* count as such */
      return 1;
      }
      else
      return 0;
      }
  • 接下来重点分析下解决放入元素的问题,直接在代码里面写注释比较好

    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
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
    Node *mp;
    TValue aux;
    if (ttisnil(key)) luaG_runerror(L, "table index is nil");
    else if (ttisfloat(key)) {
    lua_Integer k;
    if (luaV_tointeger(key, &k, 0)) { /* does index fit in an integer? */
    setivalue(&aux, k);
    key = &aux; /* insert it as an integer */
    }
    else if (luai_numisnan(fltvalue(key)))
    luaG_runerror(L, "table index is NaN");
    }
    /*
    mp是根据key根据hash得到node的地址,
    */
    mp = mainposition(t, key);
    /*
    如果发现node里的value不是nilobj,或者是第一个元素的话,也就是当为hash碰撞了
    */
    if (!ttisnil(gval(mp)) || isdummy(t)) { /* main position is taken? */
    /*
    开始找空闲位置,空闲位置是从最后一个node[i]的位置,开始往左边找
    */
    Node *othern;
    Node *f = getfreepos(t); /* get a free place */
    if (f == NULL) { /* cannot find a free place? */
    /*
    没有空闲位置就直接扩大hash表的容量
    */
    rehash(L, t, key); /* grow table */
    /* whatever called 'newkey' takes care of TM cache */
    return luaH_set(L, t, key); /* insert key into grown table */
    }
    lua_assert(!isdummy(t));
    /*
    取一下在这个位置node[i]的key,然后找一下它本来在的位置
    情况1:原本位置不在这
    情况2:位置本来是在这里
    */
    othern = mainposition(t, gkey(mp));
    if (othern != mp) { /* is colliding node out of its main position? */
    /* yes; move colliding node into free position */
    /*
    当本来位置不在这里的时候,从他本来的位置开始找 othern->ikey.next ->...->mp
    一定会串起来

    othern->ikey.next = 到空闲位置f的距离
    空闲位置f = mp

    f->next = mp - f
    好处应该是尽量hash一次到这个位置
    */
    while (othern + gnext(othern) != mp) /* find previous */
    othern += gnext(othern);
    gnext(othern) = cast_int(f - othern); /* rechain to point to 'f' */
    *f = *mp; /* copy colliding node into free pos. (mp->next also goes) */
    if (gnext(mp) != 0) {
    gnext(f) += cast_int(mp - f); /* correct 'next' */
    gnext(mp) = 0; /* now 'mp' is free */
    }
    setnilvalue(gval(mp));
    }
    else { /* colliding node is in its own main position */
    /* new node will go into free position */
    /*
    它的key根据hash就在原本的位置了
    mp = f f也就是新放入key的元素了
    */
    if (gnext(mp) != 0)
    gnext(f) = cast_int((mp + gnext(mp)) - f); /* chain new position */
    else lua_assert(gnext(f) == 0);
    gnext(mp) = cast_int(f - mp);
    mp = f;
    }
    }
    setnodekey(L, &mp->i_key, key);
    luaC_barrierback(L, t, key);
    lua_assert(ttisnil(gval(mp)));
    return gval(mp);
    }

luaL_newstate()讲解

lua异常处理机制

  • lua异常处理机制是通过setjmplongjmp实现的,当longjmp(jmp_buf b, 2)程序会回到setjmp重新执行,且返回值是longjmp的第二个参数;lua就是通过lua_longjmp结构体的status来判断虚拟机是否成功调用函数

lua垃圾回收机制

  • lua采用增量式标记-清除算法,
CATALOG
  1. 1. 前言
  2. 2. 正文
  3. 3. 数据类型
  4. 4. 字符串的实现
  5. 5. table的实现
  6. 6. luaL_newstate()讲解
  7. 7. lua异常处理机制
  8. 8. lua垃圾回收机制