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

推荐订阅源

K
Kaspersky official blog
Martin Fowler
Martin Fowler
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
V
Visual Studio Blog
博客园_首页
Engineering at Meta
Engineering at Meta
The Cloudflare Blog
MongoDB | Blog
MongoDB | Blog
Blog — PlanetScale
Blog — PlanetScale
T
The Blog of Author Tim Ferriss
雷峰网
雷峰网
D
Docker
博客园 - 司徒正美
S
SegmentFault 最新的问题
M
MIT News - Artificial intelligence
博客园 - 叶小钗
博客园 - 三生石上(FineUI控件)
U
Unit 42
J
Java Code Geeks
A
About on SuperTechFans
N
Netflix TechBlog - Medium
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
S
Security Affairs
I
Intezer
Cisco Talos Blog
Cisco Talos Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
B
Blog RSS Feed
P
Privacy & Cybersecurity Law Blog
T
Tenable Blog
T
Threatpost
H
Hacker News: Front Page
G
Google Developers Blog
博客园 - 【当耐特】
Hugging Face - Blog
Hugging Face - Blog
Apple Machine Learning Research
Apple Machine Learning Research
L
Lohrmann on Cybersecurity
大猫的无限游戏
大猫的无限游戏
Google DeepMind News
Google DeepMind News
A
Arctic Wolf
S
Secure Thoughts
GbyAI
GbyAI
NISL@THU
NISL@THU
S
Security @ Cisco Blogs
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Webroot Blog
Webroot Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
O
OpenAI News
Spread Privacy
Spread Privacy
Application and Cybersecurity Blog
Application and Cybersecurity Blog

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