惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Y
Y Combinator Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园 - 司徒正美
Blog — PlanetScale
Blog — PlanetScale
博客园 - 聂微东
月光博客
月光博客
量子位
大猫的无限游戏
大猫的无限游戏
Stack Overflow Blog
Stack Overflow Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
The Cloudflare Blog
P
Proofpoint News Feed
B
Blog RSS Feed
美团技术团队
腾讯CDC
C
Check Point Blog
Engineering at Meta
Engineering at Meta
F
Fortinet All Blogs
N
Netflix TechBlog - Medium
Recent Announcements
Recent Announcements
J
Java Code Geeks
S
SegmentFault 最新的问题
WordPress大学
WordPress大学
宝玉的分享
宝玉的分享

ccagml

使用valgrind观察luajit进程内存 - ccagml 从生产环境报错学习protobuf编码规则 - ccagml 跟着vscode插件学设计模式-工厂模式 - ccagml 拨开迷雾,探寻深夜游戏集群启动失败真相 - ccagml 从技能状态图标显示错误到给 LuaJIT 报告bug - ccagml 游戏系统MySQl执行超时问题排查 - ccagml lua中pairs和ipairs都做了什么操作 - ccagml lua中#号是怎么计算字符串长度的 - ccagml lua中tonumber做了什么 - ccagml
lua中#号是怎么计算长度的 - ccagml
2022-04-23 · via ccagml

1. 一些简单的例子

从例子可以看出,#并不是取得从1开始连续部分数组的长度

print("#{1}:", #{1})
print("#{1,2}:", #{1, 2})
print("#{1,2, 3}:", #{1, 2, 3})
print("#{1,2, 3, 4}:", #{1, 2, 3, 4})
print("#{1, 2, 4}:", #{1, 2, 4})
print("#{1, 3}:", #{1, 3})


输出的结果
#{1}:   1
#{1,2}: 2
#{1,2, 3}:      3
#{1,2, 3, 4}:   4
#{1, 2, 4}:     3
#{1, 3}:        2


// 一个特殊的例子
a = {}
a[1] = 1
a[2] = 2
a[4] = 4
a_len = #a
print(a_len) // 得到 4 并不是2, 也不是3

2.那么#号是在取什么东西呢?

我们跟随下面一个简单的例子看看#在取什么值

 a = #{1, 2, 4}

3.在2中解析到的token是

TK_NAME(285) -> = (61) -> # OPR_LEN (35) -> { (123) -> TK_NUMBER(284) -> ,(44) ->TK_NUMBER(284) -> ,(44) ->TK_NUMBER(284) -> ,(44) -> } (125)

{ 的时候会触发一个gafqK_codeABC操作认为是新建一个table
} 只会会触发一个gafqK_codeABC(fs, OP_SETLIST, base, b, c); OP_SETLIST

4.解析完token后在vm中的执行

OP_NEWTABLE
OP_LOADK
OP_LOADK
OP_LOADK
OP_SETLIST
OP_LEN // 来到了我们本章的重点,获取长度
'''

5.OP_LEN做了什么

1.先判断类型是table、string、或者有元方法

2.我们本章是table,所以会执行一下内容

setnvalue(ra, cast_num(gafqH_getn(hvalue(rb))));

6.gafqH_getn做了什么

int gafqH_getn(Table *t)
{
    unsigned int j = t->sizearray; // 初始化的时候,创建的数组部分大小是3
    // 通过二分查找获取长度 这边会判断j - 1 的地方是否是空的,如果是空的会二分查找,知道不是空
    if (j > 0 && ttisnil(&t->array[j - 1]))
    {
        unsigned int i = 0;
        while (j - i > 1)
        {
            unsigned int m = (i + j) / 2;
            if (ttisnil(&t->array[m - 1]))
                j = m;
            else
                i = m;
        }
        return i;
    }
    else if (t->node == dummynode) // 这边判断哈希部分存的是不是空的
        return j;     // 在我们的例子中 {1, 2, 4} 因为取 array[j - 1] 是4然后哈希部分又是空的,所以获得的长度直接认为是3
    else
        return unbound_search(t, j); // 就算有哈希部分例如{1, 2, 4, a = 1} 因为array[j - 1]不是空的,也会认为长度是3
}

我们开始看一个特殊的例子

    a = {}
    a[1] = 1
    a[2] = 2
    a[4] = 4
    a_len = #a
    print(a_len) // 得到 4 并不是2, 也不是3

在这个例子中,会返回a_len的长度是4, 可是我们只设置了3个值,这是为什么呢?

同样回到gafqH_getn的操作的时候,我们得到了 unsigned int j = t->sizearray; 长度是4,那么就涉及到lua是怎么执行上面三句赋值操作的

8. 与一开始的例子不同, 这次我们多了 OP_SETTABLE操作

    TValue *oldval = gafqH_set(L, h, key); // 在 a[1] = 1中 会查找key = 1是否存在我们一开始table是空的显然不存在,那么就会触发newkey操作

9.gafqH_set做了什么

TValue *gafqH_set(gafq_State *L, Table *t, const TValue *key)
{
    const TValue *p = gafqH_get(t, key); // a[1] 中 获取 key = 1是否存在
    t->flags = 0;
    if (p != gafqO_nilobject)
        return cast(TValue *, p);
    else
    {
        return newkey(L, t, key); // 因为不存在,所以创建一个新的
    }
}

10.newkey做什么

static TValue *newkey(gafq_State *L, Table *t, const TValue *key)
{
    Node *n = getfreepos(t); // 获取空余的位置
    if (n == NULL)
    {                                
        rehash(L, t, key); //插入rehash
        return gafqH_set(L, t, key); 
    }
}

11. rehash做什么

因为没有空余位置,所以需要重新计算hash 给 a[1] 弄出一个位置来

static void rehash(gafq_State *L, Table *t, const TValue *ek)
{
    '''
    nasize = numusearray(t, nums); // 原本数组部分使用长度
    totaluse = nasize;                        
    totaluse += numusehash(t, nums, &nasize);  // 原本哈希部分使用长度
    nasize += countint(ek, nums); // 给新key 一个位置
    totaluse++;

    na = computesizes(nums, &nasize); // 需要一个新key nasize 是1
    resize(L, t, nasize, totaluse - na);
}

12. computesizes计算新的大小

这里可以看出在计算大小的时候, 每次增长是 * 2的 也就是 twotoi *= 2

那么 当 a[4] 的时候, 需要的narray 是3个, 然后因为增长是 * 2, 最后narray会变成4

static int computesizes(int nums[], int *narray)
{
    int i;
    int twotoi; /* 2^i */
    int a = 0;  /* 小于 2^i 的元素个数 */
    int na = 0; /* 要进入数组部分的元素数量 */
    int n = 0;  /* 数组部分的最佳大小 */
    for (i = 0, twotoi = 1; twotoi / 2 < *narray; i++, twotoi *= 2)
    {
        if (nums[i] > 0)
        {
            a += nums[i];
            if (a > twotoi / 2)
            {               /* 超过一半的元素存在? */
                n = twotoi; /* 最佳尺寸(到现在为止) */
                na = a;     /* 所有小于 n 的元素将进入数组部分 */
            }
        }
        if (a == *narray)
            break; /* 所有元素都已经计算过了 */
    }
    *narray = n;
    gafq_assert(*narray / 2 <= na && na <= *narray);
    return na;
}

总结 因为12计算出新的array部分是4个,那么11重新计算resize后就会有array长度部分是4,那么现在10中的gafqH_set就会把a[4]的值插入到数组部分,然后在#取值得时候又因为有array[4-1]出有值,所以认为特殊例子中的长度是4