摘自:http://www.daileinote.com/computer/openresty/03

我们知道,openresty 使用的开发语言为 Lua,内置了 LuaJIT 解释器,速度比 Lua 官方解释器要快很多。

LuaJIT 基于 Lua 5.1,但在不破坏兼容性的前提下适当引入了一些 5.2 和 5.3 的语言特性,还提供了很多特别的优化和库,例如 table.new、bit、ffi。LuaJIT 是开源的,官网地址为 http://luajit.org

continue

Lua 语言不支持 continue 语法,有时候很不方便,好在 LuaJIT 加入了 Lua 5.2 的 goto 语句,变相的实现了 continue。goto 语句需要配合标签 ::label::使用,例如

1
2
3
4
5
6
7
8
9
10
11
12
--tmp.lua
for i=0,6,1 do
if i<5 then
goto continue
else
print(i)
end
::continue::
end
[root@192 lua]# /usr/local/openresty/luajit/bin/luajit tmp.lua
5
6

bit

LuaJIT 为 Lua5.1 增加了对于位的处理能力。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
--tmp.lua
local bit = require("bit")

local x = 5 --101
local y = 7 --111

print(bit.band(x,y)) --&
print(bit.bor(x,y)) --|
print(bit.bnot(x)) --非~
print(bit.bxor(x,y)) --异或^

print(bit.lshift(x,1)) --左移1位
print(bit.rshift(x,1)) --右移1位
[root@192 lua]# /usr/local/openresty/luajit/bin/luajit tmp.lua
5
7
-6
2
10
2

ffi

ffi 是 LuaJIT 里最强大也是最有价值的一个库,极大的简化了在 Lua 里调用 c/c++ 函数的工作,而且执行效率更高。

ffi 不仅可以调用系统函数和 openresty/nginx 内部的 C 函数,还可以加载 so 形式的动态库,调用动态库的函数,从而轻松扩展 Lua 的功能。

ffi.load 加载 *.so 动态库
ffi.null 相当于c的 NULL
ffi.string 把C指针指向的内存转化为 Lua 字符串
ffi.cdef 声明C的数据结构
ffi.typeof 声明一个 C 类型
ffi.new 利用上面的 C 类型分配内存
ffi.C C函数所在的全局命名空间,可以用 . 来执行

1
2
3
4
5
local ffi = require("ffi")
ffi.cdef[[
int printf(const char *fmt, ...);
]]
ffi.C.printf("Hello %s!\n", "world")

在 Lua 里想要跟C交互的都是 cdata 类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
local ffi = require("ffi")
ffi.cdef[[
int printf(const char *fmt, ...);
]]

ffi.C.printf("直接打印失败 %d\n", 10)

--下面先生成 cdata 类型
--第一种
t1 = ffi.typeof("int")
local cint_a = ffi.new(t1, 10)

--第二种
local cint_b = ffi.new("int", 11)

--第三种
local cint_c = t1(12)

ffi.C.printf("第二种方式成功 %d %d %d\n", cint_a, cint_b, cint_c)
[root@192 lua]# /usr/local/openresty/luajit/bin/luajit tmp.lua
直接打印失败 0
第二种方式成功 10 11 12

对于字符串,ffi.new 第一个参数只能是 const char *、const char[size] 或 char[size]。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
local ffi = require("ffi")
ffi.cdef[[
int printf(const char *fmt, ...);
]]

local s_a = ffi.new("const char *", "http://www.freecls.com")

local s_b = ffi.new("char [5]", "hello")
local s_c = ffi.new("const char [5]", "hello")

ffi.C.printf("%s %s %s\n", s_a, s_b, s_c)

print(s_a)
print(ffi.string(s_a))
[root@192 lua]# /usr/local/openresty/luajit/bin/luajit tmp.lua
http://www.freecls.com hello hello
cdata<const char *>: 0x4151b608
http://www.freecls.com

对于结构体,可以使用表格赋值

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
local ffi = require("ffi")
ffi.cdef[[
int printf(const char *fmt, ...);

struct son{
int c;
int d;
const char *name;
};

struct person {
int a;
int b;

struct son e;
};

struct eat{
char *name;
char sex[7];
};

]]

local ss = ffi.new("const char *", "freecls")
local p1 = ffi.new("struct person", {1,2,{3,4, ss}})

p1.e.name = "changed"

print(ffi.string(p1.e.name))


local p2 = ffi.new("struct eat")
p2.name = ffi.new("char[7]")
ffi.copy(p2.name, "freecls")
ffi.copy(p2.sex, "1234567")

print(ffi.string(p2.sex))

p2.sex = "3333333"

print(ffi.string(p2.sex))
[root@192 lua]# /usr/local/openresty/luajit/bin/luajit tmp.lua
changed
1234567
3333333

加载外部动态库

1
2
3
4
5
6
7
8
//main.c
int add(int x, int y){
return x + y;
}

int add1(int *x, int *y){
return *x + *y;
}

接下来编译成动态库并拷贝到默认搜索路径,我的是 /lib64

1
2
gcc -g -fPIC -Wall main.c -shared -o libtest.so
cp libtest.so /lib64/

int 型指针可以用数组模拟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
local ffi = require("ffi")

ffi.cdef[[
int add(int,int);
int add1(int *, int*);
]]

local test = ffi.load("test")
print(test.add(1,2))

local i1 = ffi.new("int[1]", 3)
local i2 = ffi.new("int[1]", {4})

print(test.add1(i1, i2))
[root@192 lua]# /usr/local/openresty/luajit/bin/luajit tmp.lua
3
7

如果希望上面的库函数可以在 ffi.C 里调用,那么 load 第二个参数设为 true。

1
2
3
4
5
6
7
8
9
10
11
local ffi = require("ffi")

ffi.cdef[[
int add(int,int);
int add1(int *, int*);
]]

local test = ffi.load("test", true)
print(ffi.C.add(1,2))
[root@192 lua]# /usr/local/openresty/luajit/bin/luajit tmp.lua
3

更多用法请参考 ffi tutorial