• Lua类型系统详解(一)


    Lua是一种动态类型的脚本语言,意味着变量没有类型,类型信息包含在值中。目前lua支持八种基本类型:nil,boolean,number,string,table,function,userdata,thread。所有的值都是第一类值,都是可以存储在变量中或者作为函数参数传递,以及作为函数返回值。

    1. /*
    2. ** basic types
    3. */
    4. #define LUA_TNONE (-1)
    5. #define LUA_TNIL 0
    6. #define LUA_TBOOLEAN 1
    7. #define LUA_TLIGHTUSERDATA 2
    8. #define LUA_TNUMBER 3
    9. #define LUA_TSTRING 4
    10. #define LUA_TTABLE 5
    11. #define LUA_TFUNCTION 6
    12. #define LUA_TUSERDATA 7
    13. #define LUA_TTHREAD 8
    14. #define LUA_NUMTYPES 9

            源码中实际定义了九种类型,其中 LUA_TLIGHTUSERDATA 与 LUA_TUSERDATA统称为userdata,lightuserdata被认为是一种特殊的userdata,它仅表示一个c的指针,类似数字:你不用创建它,它没有原表,也不需要被gc。Lua如何实现变量可以存储任意类型呢?源码中用一个大的联合体来实现。

    1. /*
    2. ** Union of all Lua values
    3. */
    4. typedef union Value {
    5. struct GCObject *gc; /* collectable objects */
    6. void *p; /* light userdata */
    7. lua_CFunction f; /* light C functions */
    8. lua_Integer i; /* integer numbers */
    9. lua_Number n; /* float numbers */
    10. } Value;
    11. /*
    12. ** Tagged Values. This is the basic representation of values in Lua:
    13. ** an actual value plus a tag with its type.
    14. */
    15. #define TValuefields Value value_; lu_byte tt_
    16. typedef struct TValue {
    17. TValuefields;
    18. } TValue;

            Value中包含各种实际类型需要用到的变量,其中GCobject用于存储需要被gc类型的地址,目前string, userdata, table, function, thread是需要gc的类型,其他几个字段分别用于存储lightuserdata指针,c导出的函数地址,整数,浮点数。TValue包含Value和一个字节的类型信息,是Lua内部存储变量的基础结构。下面我们详细分析一下每种类型的实现。

    一,nil

           只有一个值nil,与其他类型均不相同。通常用于区分一个值是否是有效值。

    二,boolean

           类型 boolean 有两个值,false 和 true。 nil 和 false 都使条件为假;它们统称为假值。任何其他值都会使条件为真。boolean在内部实现上,直接给LUA_TBOOLEAN定义了两个扩展类型:LUA_VFALSE和LUA_VTRUE。

    1. /*
    2. ** tags for Tagged Values have the following use of bits:
    3. ** bits 0-3: actual tag (a LUA_T* constant)
    4. ** bits 4-5: variant bits
    5. ** bit 6: whether value is collectable
    6. */
    7. /* add variant bits to a type */
    8. #define makevariant(t,v) ((t) | ((v) << 4))
    9. #define LUA_VFALSE makevariant(LUA_TBOOLEAN, 0)
    10. #define LUA_VTRUE makevariant(LUA_TBOOLEAN, 1)

    类型的0到3位用于表示基本类型,4到5位被扩展用于表示其他附加信息。

    三,number

            类型 number 表示整数和实数(浮点数),包含两个子类型:整数和浮点数。标准 Lua 使用 64 位整数和双精度(64 位)浮点数,也可以修改宏定义重新编译 Lua,使其使用 32 位整数和/或单精度(32 位)浮点数。整数和浮点数均为 32 位的选项对于小型机器和嵌入式系统特别有吸引力。 (参见文件 luaconf.h 中的宏 LUA_32BITS。)

    1. /* Variant tags for numbers */
    2. #define LUA_VNUMINT makevariant(LUA_TNUMBER, 0) /* integer numbers */
    3. #define LUA_VNUMFLT makevariant(LUA_TNUMBER, 1) /* float numbers */
    4. #define ttisnumber(o) checktype((o), LUA_TNUMBER)
    5. #define ttisfloat(o) checktag((o), LUA_VNUMFLT)
    6. #define ttisinteger(o) checktag((o), LUA_VNUMINT)

            源码中定义了LUA_TNUMBER两个子类型LUA_VNUMFLT和LUA_VNUMINT分别表示浮点数和整数。实际上内部将整数和浮点数已经按子类型区分开了。

    四,string

    1. /*
    2. ** Header for a string value.
    3. */
    4. typedef struct TString {
    5. CommonHeader;
    6. lu_byte extra; /* reserved words for short strings; "has hash" for longs */
    7. lu_byte shrlen; /* length for short strings */
    8. unsigned int hash;
    9. union {
    10. size_t lnglen; /* length for long strings */
    11. struct TString *hnext; /* linked list for hash table */
    12. } u;
    13. char contents[1];
    14. } TString;

             上面是字符串的底层结构,CommonHeader就是支持gc的相关成员。lua中将字符串按长度是否大于LUAI_MAXSHORTLEN(40)分成两种类型LUA_VLNGSTR和LUA_VSHRSTR。extra默认为0,为1表示已经对长字符串hash过,hash字段可以直接使用。如果是短字符串,shrlen表示其长度;如果是长字符串,lnglen表示长字符串的长度。对于短字符串是存放在内部hash表中,hnext指向hash表同一hash桶的下一个短字符串。contents长度为1的数组,用于在字符串末尾添加0,后续分配字符串空间的时候只需要分配TSting和字符串长度即可使用。为了重用短字符串,在global_State中使用hash表strt字段存放。hash表类型stringtable

    定义如下:

    1. typedef struct stringtable {
    2. TString **hash;
    3. int nuse; /* number of elements */
    4. int size;
    5. } stringtable;

    其中hash字段就是hash表的桶数组,每个桶是代表这个hash值的TString的链表的头结点。

    再回头梳理下字符串的创建过程,可以发现还有一个strcache的过程:

    1. /*
    2. ** Create or reuse a zero-terminated string, first checking in the
    3. ** cache (using the string address as a key). The cache can contain
    4. ** only zero-terminated strings, so it is safe to use 'strcmp' to
    5. ** check hits.
    6. */
    7. TString *luaS_new (lua_State *L, const char *str) {
    8. unsigned int i = point2uint(str) % STRCACHE_N; /* hash */
    9. int j;
    10. TString **p = G(L)->strcache[i];
    11. for (j = 0; j < STRCACHE_M; j++) {
    12. if (strcmp(str, getstr(p[j])) == 0) /* hit? */
    13. return p[j]; /* that is it */
    14. }
    15. /* normal route */
    16. for (j = STRCACHE_M - 1; j > 0; j--)
    17. p[j] = p[j - 1]; /* move out last element */
    18. /* new element is first in the list */
    19. p[0] = luaS_newlstr(L, str, strlen(str));
    20. return p[0];
    21. }

    stcache是在globalstate中定义的一个二维数组:TString *strcache[STRCACHE_N][STRCACHE_M];

    这个缓存是按照字符串指针地址取模来定位的。无论是长短字符串,如果是经常使用的情况下,会在缓存中快速找到能复用的字符串,提高程序性能。

  • 相关阅读:
    053:mapboxGL中sources的6种类型及各类型的示例代码
    SpringBoot中日志的使用log4j
    HP E1740A 模拟量输入模块
    多云系列|10个关键的多云战略:云计算成本的完整可视性
    股权转让项目:厦门古龙温泉山庄开发有限公司60%股权转让
    STC51单片机学习笔记10——AD测试(stc15w408as)
    JAVA开发 使用Apache PDFBox库生成PDF文件,绘制表格
    并发编程day06
    20.支持向量机—数学原理知识
    C语言处理参数的 getopt() 函数
  • 原文地址:https://blog.csdn.net/summerhust/article/details/126409028