├── .gitignore ├── testsrc ├── string_array.json ├── string_array.fbs ├── test.lua ├── string_array.lua ├── test.json ├── test.fbs └── buf_read_test.lua ├── README.md ├── Makefile ├── LICENSE ├── stringx.lua ├── buflib.inl ├── lfb.lua └── buflib.c /.gitignore: -------------------------------------------------------------------------------- 1 | /bin_out 2 | /json_out 3 | 4 | /inspect.lua 5 | -------------------------------------------------------------------------------- /testsrc/string_array.json: -------------------------------------------------------------------------------- 1 | { 2 | f1 : ['lua', 'flat buffers'] 3 | } 4 | -------------------------------------------------------------------------------- /testsrc/string_array.fbs: -------------------------------------------------------------------------------- 1 | namespace lfb; 2 | 3 | table Test { 4 | f1 : [string]; 5 | } 6 | 7 | root_type Test; 8 | 9 | file_extension "lfb"; 10 | file_identifier "TEST"; // exactly 4 chars 11 | -------------------------------------------------------------------------------- /testsrc/test.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua53 2 | 3 | local inspect = require 'inspect' 4 | local FlatBuffers = require 'lfb' 5 | 6 | local function test() 7 | local df = io.open(arg[1] or 'bin_out/test.lfb', 'rb') 8 | local sf = io.open(arg[2] or 'bin_out/test.bfbs', 'rb') 9 | if df and sf then 10 | local fbs = FlatBuffers.bfbs(sf:read 'a') 11 | local fbmsg = fbs:decode(df:read 'a') 12 | print(inspect(fbmsg)) 13 | end 14 | end 15 | 16 | test() 17 | -------------------------------------------------------------------------------- /testsrc/string_array.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua53 2 | 3 | local inspect = require 'inspect' 4 | local FlatBuffers = require 'lfb' 5 | 6 | -- 2019-10-27 Sun 23:30 7 | -- string array read test 8 | -- fix crash when invalid input 9 | 10 | local schema_file = io.open('bin_out/string_array.bfbs', 'rb') 11 | local data_file = io.open('bin_out/string_array.lfb', 'rb') 12 | 13 | if schema_file and data_file then 14 | local fbs = FlatBuffers.bfbs(schema_file:read 'a') 15 | local data_buf = data_file:read 'a' 16 | local obj = fbs:decode(data_buf) 17 | print(inspect(obj)) 18 | end 19 | -------------------------------------------------------------------------------- /testsrc/test.json: -------------------------------------------------------------------------------- 1 | { 2 | f1 : [true, false, true, true, false], 3 | f2 : [521, 520], 4 | f3 : [3.1415926, 1.414], 5 | f4 : ['flat buffers', 'lua'], 6 | f5 : { 7 | f1 : 't2', 8 | f2 : 12345, 9 | f3 : [ {f1 : 1, f2 : 0.1}, {f1 : 2, f2 : 0.2} ], 10 | f4_type : T3, 11 | f4 : {f1 : 2, f2: 'union t3'}, 12 | f5_type : T4, 13 | f5 : {f1 : 2, f2: 0.125}, 14 | }, 15 | f6 : [A, B], 16 | f7 : { x : 1, y : 2 }, 17 | f8 : [{ x : 1, y : 2 }, {x : 3, y : 4}], 18 | f9 : { 19 | bl : true, 20 | db : 3.1415, 21 | by : 127, 22 | ub : 255, 23 | in : 0, 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lua-flatbuffers 2 | 3 | a work-in-progress Lua 5.3 library for *reading* [FlatBuffers][flatbuffers] 4 | 5 | ## 开发计划: 6 | 7 | *目前官方已经有了非反射版的Lua库,我自己也暂时不使用flatbuffers了,暂停开发* 8 | 9 | * [x] buffer.read: 检查buffer边界范围 10 | * [x] 去掉对string元表的修改 11 | * [ ] 写FlatBuffers 12 | * [ ] 完善测试 13 | * [ ] 完善文档 14 | * [ ] 直接解析schema文件, 不再依赖flatc编译schema到bfbs或json 15 | 16 | ## TODO 17 | 18 | * parse schema, do not depend flatc to compile schema to bfbs/json 19 | * flatbuffers write support 20 | 21 | # Quick start 22 | 23 | 24 | ```lua 25 | 26 | os.execute 'flatc --binary --schema test.fb' 27 | 28 | FlatBuffersSchema = FlatBuffers.bfbs('test.bfbs') 29 | 30 | your_message_as_a_lua_table = FlatBuffersSchema:decode('a buffer encode a message in FlatBuffers format') 31 | 32 | ``` 33 | 34 | [flatbuffers]: https://github.com/google/flatbuffers 35 | -------------------------------------------------------------------------------- /testsrc/test.fbs: -------------------------------------------------------------------------------- 1 | namespace lfb; 2 | 3 | struct Vector { 4 | x : int; 5 | y : int; 6 | } 7 | 8 | struct CS { 9 | bl : bool; 10 | db : double; 11 | by : byte; 12 | ub : ubyte; 13 | in : int; 14 | } 15 | 16 | enum ET : ubyte { 17 | A, 18 | B, 19 | } 20 | 21 | table Test { 22 | f1 : [bool]; 23 | f2 : [int]; 24 | f3 : [double]; 25 | f4 : [string]; 26 | f5 : T2; 27 | f6 : [ET]; 28 | f7 : Vector; 29 | f8 : [Vector]; 30 | f9 : CS; 31 | } 32 | 33 | union T34 { T3, T4 } 34 | 35 | table T2 { 36 | f1 : string; 37 | f2 : int; 38 | f3 : [T4]; 39 | f4 : T34; 40 | f5 : T34; 41 | } 42 | 43 | struct S1 { 44 | s1 : int; 45 | s2 : int; 46 | s3 : short; 47 | s4 : short; 48 | } 49 | 50 | table T3 { 51 | f1 : int; 52 | f2 : string; 53 | } 54 | 55 | table T4 { 56 | f1 : int ; 57 | f2 : double; 58 | } 59 | 60 | root_type Test; 61 | 62 | file_extension "lfb"; 63 | file_identifier "TEST"; // exactly 4 chars 64 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -std=c99 2 | CFLAGS += -O2 3 | CFLAGS += -fPIC 4 | CFLAGS += -Wall 5 | 6 | UNAME_S := $(shell uname -s) 7 | ifeq ($(UNAME_S), Darwin) 8 | SHARED_LIB = -dynamiclib -Wl,-undefined,dynamic_lookup 9 | else 10 | SHARED_LIB = -shared 11 | endif 12 | 13 | ifdef LUAPATH 14 | CFLAGS += -I$(LUAPATH) 15 | endif 16 | 17 | .PHONY: all test clean 18 | 19 | all: buffer.so inspect.lua 20 | # @./testsrc/string_array.lua 21 | # @./testsrc/buf_read_test.lua 22 | @./testsrc/test.lua 23 | 24 | buffer.so: buflib.c buflib.inl 25 | $(CC) $(SHARED_LIB) -o $@ $(CFLAGS) $< 26 | 27 | inspect.lua: 28 | wget https://raw.githubusercontent.com/kikito/inspect.lua/master/inspect.lua 29 | 30 | clean: 31 | rm -f buffer.so 32 | 33 | test: 34 | @flatc -o bin_out --binary --schema ./testsrc/test.fbs 35 | @flatc -o bin_out --binary ./testsrc/test.fbs ./testsrc/test.json 36 | @flatc -o json_out --json --defaults-json --raw-binary ./testsrc/test.fbs -- bin_out/test.lfb 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 David Feng 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /stringx.lua: -------------------------------------------------------------------------------- 1 | 2 | 3 | --- xxd style string hex dump 4 | function string:xxd() 5 | 6 | local non_print_pattern = '%c' -- or use '%G' 7 | 8 | local max_width = 8 9 | local width = 1 10 | local address = 0 11 | 12 | local line = {} 13 | 14 | local s = (self:gsub('(..)', function (c) 15 | if width == max_width then 16 | 17 | line[max_width] = c 18 | 19 | address = address + max_width * 2 20 | width = 1 21 | 22 | local b1, b2 = c:byte(1, 2) 23 | return ('%02x%02x %s\n'):format(b1, b2, 24 | table.concat(line):gsub(non_print_pattern, '.')) 25 | 26 | elseif width == 1 then 27 | 28 | line[1] = c 29 | 30 | width = 2 31 | 32 | return ('%08x: %02x%02x '):format(address, c:byte(1, 2)) 33 | 34 | else 35 | 36 | line[width] = c 37 | 38 | width = width + 1 39 | 40 | return ('%02x%02x '):format(c:byte(1, 2)) 41 | end 42 | 43 | end)) 44 | 45 | if #self % 2 ~= 0 then 46 | s = s:gsub('(.)$', function (c) 47 | return ('%02x'):format(c:byte()) 48 | end) 49 | end 50 | 51 | if #self == 1 then 52 | s = ('%08x: %02x'):format(0, self:byte()) 53 | end 54 | 55 | local rm = #self % (max_width * 2) 56 | if rm ~= 0 then 57 | local start_index = 10 + 5 * 8 + 1 58 | local line_width = start_index + 16 + 1 59 | local rc = #s % line_width 60 | rc = start_index - rc 61 | s = s .. (' '):rep(rc) 62 | .. self:sub(-rm, -1):gsub(non_print_pattern, '.') .. '\n' 63 | end 64 | 65 | return s 66 | end 67 | 68 | function string:from_hex() 69 | return (self:gsub('%X', ''):gsub('(..)', function (bs) 70 | return string.char(tonumber(bs, 16)) 71 | end)) 72 | end 73 | 74 | return string 75 | -------------------------------------------------------------------------------- /buflib.inl: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "lua.h" 9 | #include "lauxlib.h" 10 | #include "lualib.h" 11 | 12 | /* 13 | scalar types: 14 | 8 bit: byte ubyte bool 15 | 16 bit: short ushort 16 | 32 bit: int uint float 17 | 64 bit: long ulong double 18 | 19 | 所有的整数类型都转换到lua_Integer, 20 | float/double 转换到lua_Number 21 | bool 转换成bool 22 | 64位无符号类型暂时不做特殊处理 23 | 24 | non-scalar types: 25 | vector: 26 | string: utf-8 7-bit ascii, 其他类型的字符串应该用[ubyte] [uint]表示 27 | reference to enum, struct, union 28 | */ 29 | 30 | 31 | /* 32 | 33 | > 大端, < 小端 34 | 35 | $[类型描述] 创建中间变量,通过 $n 使用,不作为结果返回 36 | &[类型描述] 引用,此值后续可以通过 $n 来引用,同时也作为结果返回 37 | $n 访问变量 38 | +[n|%n|()] 当前指针向前移动n个字节 39 | -[n|%n|()] 当前指针向后移动n个字节 40 | 41 | = 本次读不移动指针 42 | 43 | * 重复读标记, 下一项操作 重复n次 n >= 1, 当结果没有放入table时,当前限制最大值127 44 | *A: 数值字面量 45 | *$B: 临时栈空间中的变量. $1, $2 46 | *[C]: Lua数值表达式, 支持使用临时栈空间变量. *[ $1 + 2] 47 | 48 | 49 | { 括号中的内容保存到table中,不能嵌套table } 50 | 51 | [@ or ^] 返回当前位置相当于buffer首地址的偏移 52 | b[n] bool值 默认1 53 | i[n] 有符号整数 默认4 54 | u[n] 无符号整数 默认4 55 | f float 32 56 | d float 64, double 57 | s[n] 字符串 没有选项n时是零结尾字符串,存在n时,固定长度字符串, n: [1 - 4] 58 | c[n] 指定长度的字符串 n [1 - 2^32] 59 | 60 | */ 61 | 62 | struct State { 63 | const char * buffer; // buffer 首地址 64 | const char * buffer_end; // buffer 最高地址+1, buffer + size 65 | const char * pointer; // 当前指针位置 66 | size_t buffer_size; // buffer size, 当直接传递地址,此值为0时,不检查边界安全 67 | const char * instructions; // 指令列表 68 | 69 | int64_t * variable; // 变量数组 70 | int index; // 变量序号 71 | int create_var; // $ 中间变量,不作为结果返回 72 | int create_ref; // & 可以引用,作为结果返回 73 | int dont_move; // = 74 | 75 | int in_tb; // {} 76 | int tb_idx; // table index 77 | 78 | int little; // 小端标记 79 | uint32_t repeat; // 重复标记 80 | lua_State * L; // userdata 81 | int stack_space; // check_stack 82 | int ret; // 结果数量 83 | }; 84 | 85 | -------------------------------------------------------------------------------- /testsrc/buf_read_test.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua53 2 | 3 | require 'stringx' 4 | 5 | local print = print 6 | 7 | local buf_read = require 'buffer'.read 8 | 9 | local function check_ok(ok, msg, ...) 10 | if ok then 11 | return msg, ... 12 | else 13 | return '[error] ', msg, debug.traceback() 14 | end 15 | end 16 | 17 | local function buf_read_ok(buf, ins) 18 | return check_ok(pcall(buf_read, buf, ins)) 19 | end 20 | 21 | local function check_failed(ok, msg, ...) 22 | if ok then 23 | return msg, ... 24 | else 25 | return 'expected error : ', msg, ... 26 | end 27 | end 28 | 29 | local function buf_read_error(buf, ins) 30 | return check_failed(pcall(buf_read, buf, ins)) 31 | end 32 | 33 | local buf = ('030000006c7561'):from_hex() 34 | print(buf_read(buf, 's4')) 35 | 36 | -- move pointer 37 | print(buf_read_ok(buf, '+7')) 38 | print(buf_read_ok(buf, '+2 +5 -7')) 39 | print(buf_read_error(buf, '+2 +5 -8')) 40 | 41 | -- read boolean value 42 | buf = ('0100'):from_hex() 43 | print(buf_read_ok(buf, 'b')) 44 | print(buf_read_ok(buf, 'b1')) 45 | print(buf_read_ok(buf, 'b1 b1')) 46 | print(buf_read_ok(buf, '*2 b1')) 47 | print(buf_read_ok(buf, 'b2')) 48 | print(buf_read_error(buf, '*3 b1')) 49 | print(buf_read_error(buf, 'b1 b1 b1')) 50 | 51 | -- read integer 52 | buf = ('0102'):from_hex() 53 | print(buf_read_ok(buf, 'i1')) 54 | print(0x0201, buf_read_ok(buf, '< i2')) 55 | print(0x0102, buf_read_ok(buf, '> i2')) 56 | 57 | print(buf_read_ok(buf, '+1 i1')) 58 | 59 | buf = ('0100FF0100'):from_hex() 60 | print('should be two 1: ', buf_read_ok(buf, '&i2 +$1 i2')) 61 | 62 | buf = ('0102 FF01 000F'):from_hex() 63 | print(buf_read_ok(buf, '*2 &i1 +$1 +$1 i1')) 64 | print(buf_read_ok(buf, '*2 &i1 +$1 +$2 i1')) 65 | print(buf_read_ok(buf, '*6 &i1')) 66 | print(buf_read_ok(buf, '$i1 +$1 *4 i1')) 67 | print(buf_read_error(buf, '*7 i1')) 68 | print(buf_read_error(buf, '$i1 +$1 *5 i1')) 69 | 70 | -- abc 71 | buf = ('61 62 63 00'):from_hex() 72 | print(buf_read_ok(buf, 's')) 73 | buf = ('61 62 63'):from_hex() 74 | print(buf_read_error(buf, 's')) 75 | print(buf_read_ok(buf, 'c1')) 76 | print(buf_read_ok(buf, 'c3')) 77 | print(buf_read_error(buf, 'c4')) 78 | 79 | buf = ('03 61 62 63'):from_hex() 80 | print(buf_read_ok(buf, 's1')) 81 | 82 | buf = ('03 00 61 62 63'):from_hex() 83 | print(buf_read_ok(buf, '< s2')) 84 | 85 | buf = ('03 00 61 62 63'):from_hex() 86 | print(buf_read_error(buf, '> s2')) 87 | 88 | buf = ('00 03 61 62 63'):from_hex() 89 | print(buf_read_ok(buf, '> s2')) 90 | 91 | 92 | -------------------------------------------------------------------------------- /lfb.lua: -------------------------------------------------------------------------------- 1 | local assert, type = assert, type 2 | 3 | local buf_read = require 'buffer'.read 4 | 5 | local BaseType = { 6 | None = 0, 7 | UType = 1, 8 | Bool = 2, 9 | Byte = 3, 10 | UByte = 4, 11 | Short = 5, 12 | UShort = 6, 13 | Int = 7, 14 | UInt = 8, 15 | Long = 9, 16 | ULong = 10, 17 | Float = 11, 18 | Double = 12, 19 | String = 13, 20 | Vector = 14, 21 | Obj = 15, 22 | Union = 16, 23 | } 24 | 25 | local field_reader = { 26 | 27 | ['string'] = '< +%d =$u4 +$1 s4', 28 | 29 | ['bool'] = '< +%d b1', 30 | 31 | ['byte'] = '< +%d i1', 32 | ['ubyte'] = '< +%d u1', 33 | 34 | ['short'] = '< +%d i2', 35 | ['ushort'] = '< +%d u2', 36 | 37 | ['int'] = '< +%d i', 38 | ['uint'] = '< +%d u', 39 | 40 | ['long'] = '< +%d i8', 41 | ['ulong'] = '< +%d u8', 42 | 43 | ['float'] = '< +%d f', 44 | ['double'] = '< +%d d', 45 | 46 | [BaseType.Bool ] = 'b1', 47 | [BaseType.Byte ] = 'i1', 48 | [BaseType.UByte ] = 'u1', 49 | [BaseType.Short ] = 'i2', 50 | [BaseType.UShort] = 'u2', 51 | [BaseType.Int ] = 'i', 52 | [BaseType.UInt ] = 'u', 53 | [BaseType.Long ] = 'i8', 54 | [BaseType.ULong ] = 'u8', 55 | [BaseType.Float ] = 'f', 56 | [BaseType.Double] = 'd', 57 | } 58 | 59 | local function simple_reader(fb_type) 60 | local type_reader = field_reader[fb_type] 61 | return function (buf, offset, field, default_value) 62 | if field ~= 0 then 63 | return buf_read(buf, type_reader:format(offset + field)) 64 | else 65 | return default_value 66 | end 67 | end 68 | end 69 | 70 | local read_bool = simple_reader 'bool' 71 | local read_byte = simple_reader 'byte' 72 | local read_ubyte = simple_reader 'ubyte' 73 | local read_short = simple_reader 'short' 74 | local read_ushort = simple_reader 'ushort' 75 | local read_int = simple_reader 'int' 76 | local read_uint = simple_reader 'uint' 77 | local read_long = simple_reader 'long' 78 | local read_ulong = simple_reader 'ulong' 79 | local read_float = simple_reader 'float' 80 | local read_double = simple_reader 'double' 81 | local read_string = simple_reader 'string' 82 | 83 | local function subtable_offset(buf, offset) 84 | return buf_read(buf, ('< +%d =$u4 +$1 @'):format(offset)) -- TODO confirm u4 or i4 85 | end 86 | 87 | 88 | local function read_table_type(buf, offset) 89 | local r = {} 90 | local vt_reader = '< +%d =$i4 -$1 $u2 +2 {*[($2 - 4) // 2] u2}' 91 | local fields = buf_read(buf, vt_reader:format(offset)) 92 | 93 | r.base_type = read_byte(buf, offset, fields[1]) 94 | if fields[2] ~= 0 then 95 | assert(r.base_type == BaseType.Vector) 96 | r.element = read_byte(buf, offset, fields[2]) 97 | end 98 | r.index = read_int(buf, offset, fields[3], -1) 99 | 100 | return r 101 | end 102 | 103 | local function parse_key_value(buf, offset) 104 | local r = {} 105 | local vt_reader = '< +%d =$i4 -$1 $u2 +2 {*[($2 - 4) // 2] u2}' 106 | local fields = buf_read(buf, vt_reader:format(offset)) 107 | 108 | r.key = read_string(buf, offset, fields[1]) 109 | r.value = read_string(buf, offset, fields[2]) 110 | 111 | return r 112 | end 113 | 114 | local function read_table_array(buf, offset, field, obj_reader) 115 | if field == 0 then return nil end 116 | 117 | local r = {} 118 | 119 | local size, addr = buf_read(buf, ('< +%d =$u4 +$1 u4 @'):format(offset + field)) 120 | for i = 1, size do 121 | local elem_offset = buf_read(buf, ('< +%d =$u4 +$1 @'):format(addr)) 122 | r[i] = obj_reader(buf, elem_offset) 123 | 124 | addr = addr + 4 125 | end 126 | return r 127 | end 128 | 129 | local function read_table_field(buf, offset) 130 | local r = {} 131 | local vt_reader = '< +%d =$i4 -$1 $u2 +2 {*[($2 - 4) // 2] u2}' 132 | local fields = buf_read(buf, vt_reader:format(offset)) 133 | 134 | r.name = read_string(buf, offset, fields[1]) 135 | r.type = read_table_type(buf, subtable_offset(buf, offset + fields[2])) 136 | r.id = read_ushort(buf, offset, fields[3], 0) 137 | r.offset = read_ushort(buf, offset, fields[4], 0) 138 | 139 | if fields[5] ~= 0 or fields[6] ~= 0 then 140 | local bt = r.type.base_type 141 | if BaseType.Byte <= bt and bt <= BaseType.ULong then 142 | r.default_value = read_long(buf, offset, fields[5], 0) 143 | elseif bt == BaseType.Bool then 144 | r.default_value = read_long(buf, offset, fields[5], 0) ~= 0 145 | elseif bt == BaseType.Float or bt == BaseType.Double then 146 | r.default_value = read_double(buf, offset, fields[6], 0.0) 147 | end 148 | end 149 | 150 | r.deprecated = read_bool(buf, offset, fields[7], false) 151 | r.required = read_bool(buf, offset, fields[8], false) 152 | r.key = read_bool(buf, offset, fields[9], false) 153 | r.attributes = read_table_array(buf, offset, fields[10], parse_key_value) 154 | 155 | return r 156 | end 157 | 158 | 159 | local function parse_object(buf, offset) 160 | local r = {} 161 | local vt_reader = '< +%d =$i4 -$1 $u2 +2 {*[($2 - 4) // 2] u2}' 162 | local fields = buf_read(buf, vt_reader:format(offset)) 163 | r.name = read_string(buf, offset, fields[1]) 164 | r.fields = read_table_array(buf, offset, fields[2], read_table_field) 165 | 166 | local fields_array = {} 167 | for _, v in ipairs(r.fields) do 168 | fields_array[v.id + 1] = v -- id: 0-based lua array index: 1-based 169 | end 170 | r.fields_array = fields_array 171 | 172 | r.is_struct = read_bool(buf, offset, fields[3], false) 173 | r.minalign = read_int(buf, offset, fields[4], 0) 174 | r.bytesize = read_int(buf, offset, fields[5], 0) 175 | r.attributes = read_table_array(buf, offset, fields[6], parse_key_value) 176 | 177 | return r 178 | end 179 | 180 | local function parse_enum_val(buf, offset) 181 | local r = {} 182 | local vt_reader = '< +%d =$i4 -$1 $u2 +2 {*[($2 - 4) // 2] u2}' 183 | local fields = buf_read(buf, vt_reader:format(offset)) 184 | 185 | r.name = read_string(buf, offset, fields[1]) 186 | r.value = read_long(buf, offset, fields[2], 0) 187 | if fields[3] ~= 0 then 188 | r.object = parse_object(buf, subtable_offset(buf, offset + fields[3])) 189 | end 190 | 191 | return r 192 | end 193 | 194 | local function parse_enum(buf, offset) 195 | local r = {} 196 | local vt_reader = '< +%d =$i4 -$1 $u2 +2 {*[($2 - 4) // 2] u2}' 197 | local fields = buf_read(buf, vt_reader:format(offset)) 198 | 199 | r.name = read_string(buf, offset, fields[1]) 200 | r.values = read_table_array(buf, offset, fields[2], parse_enum_val) 201 | 202 | local t = {} 203 | for _, v in ipairs(r.values) do t[v.value] = v end 204 | r.values_lookup_dict = t 205 | 206 | r.is_union = read_bool(buf, offset, fields[3], false) 207 | 208 | -- required 209 | r.underlying_type = read_table_type(buf, subtable_offset(buf, offset + fields[4])) 210 | r.attributes = read_table_array(buf, offset, fields[5], parse_key_value) 211 | 212 | return r 213 | end 214 | 215 | -- TODO replace with `decode_table` 216 | local function parse_schema(schema_buf) 217 | local r = {} 218 | local schema_reader = '< =&u4 +$1 =$i4 -$2 $u2 +2 {*[($3 - 4) // 2] u2}' 219 | local of, fields = buf_read(schema_buf, schema_reader) 220 | 221 | r.objects = read_table_array(schema_buf, of, fields[1], parse_object) 222 | 223 | local t = {} 224 | for _, v in ipairs(r.objects) do t[v.name] = v end 225 | r.objects_name_dict = t 226 | 227 | r.enums = read_table_array(schema_buf, of, fields[2], parse_enum) 228 | r.file_ident = read_string(schema_buf, of, fields[3]) 229 | r.file_ext = read_string(schema_buf, of, fields[4]) 230 | if fields[5] ~= 0 then 231 | r.root_table = parse_object(schema_buf, subtable_offset(schema_buf, of + fields[5])) 232 | end 233 | 234 | return r 235 | end 236 | 237 | local schema_info_cache = setmetatable({}, {__mode = 'kv'}) 238 | 239 | local function decode_schema_with_cache(schema) 240 | local schema_info = schema_info_cache[schema] 241 | if not schema_info then 242 | schema_info = parse_schema(schema) 243 | 244 | local dict = {} 245 | for _, v in ipairs(schema_info.objects) do 246 | dict[v.name] = v 247 | end 248 | schema_info.objects_dict = dict 249 | 250 | dict = {} 251 | for _, v in ipairs(schema_info.enums) do 252 | dict[v.name] = v 253 | end 254 | schema_info.enums_dict = dict 255 | 256 | 257 | schema_info_cache[schema] = schema_info 258 | end 259 | return schema_info 260 | end 261 | 262 | local field_type_reader = { 263 | [BaseType.Bool] = read_bool, 264 | [BaseType.Byte] = read_byte, 265 | [BaseType.UByte] = read_ubyte, 266 | [BaseType.Short] = read_short, 267 | [BaseType.UShort] = read_ushort, 268 | [BaseType.Int] = read_int, 269 | [BaseType.UInt] = read_uint, 270 | [BaseType.Long] = read_long, 271 | [BaseType.ULong] = read_ulong, 272 | [BaseType.Float] = read_float, 273 | [BaseType.Double] = read_double, 274 | [BaseType.String] = read_string, 275 | } 276 | 277 | local decode_table, decode_array 278 | 279 | local function decode_struct(buf, offset, table_info) 280 | local r = {} 281 | 282 | for _, field_info in ipairs(table_info.fields) do 283 | local field_type = field_info.type 284 | local basetype = field_type.base_type 285 | local rd = field_reader[basetype] 286 | local addr = offset + field_info.offset 287 | r[field_info.name] = buf_read(buf, ('< +%d %s'):format(addr, rd)) 288 | end 289 | 290 | return r 291 | end 292 | 293 | -- types may in array: bool-string, table(struct) 294 | -- types may NOT in array: vector, union 295 | function decode_array(schema, field_type, buf, offset, fcb) 296 | local array_info_reader = '< +%d =$u4 +$1 u4 @' 297 | -- ^ ^ ^ 298 | -- | | | array element address 299 | -- | | 300 | -- | | array size 301 | -- | 302 | -- | array offset 303 | local size, addr = buf_read(buf, array_info_reader:format(offset)) 304 | 305 | -- array的元素类型不能是array,即没有嵌套的数组 306 | 307 | local element_type = field_type.element 308 | if BaseType.Bool <= element_type and element_type <= BaseType.Double then 309 | 310 | local rd = field_reader[element_type] 311 | return buf_read(buf, ('< +%d {*%d %s }'):format(addr, size, rd)) 312 | 313 | elseif element_type == BaseType.String then 314 | 315 | local result = {} 316 | for i = 1, size do 317 | result[i] = buf_read(buf, ('< +%d =$u4 +$1 s4'):format(addr)) 318 | addr = addr + 4 319 | end 320 | return result 321 | 322 | elseif element_type == BaseType.Obj then 323 | local ti = schema.objects[field_type.index + 1] -- 1-based index 324 | local r = {} 325 | 326 | if ti.is_struct then 327 | for i = 1, size do 328 | r[i] = decode_struct(buf, addr, ti) 329 | addr = addr + ti.bytesize 330 | end 331 | else 332 | for i = 1, size do 333 | local elem_offset = buf_read(buf, ('< +%d =$u4 +$1 @'):format(addr)) 334 | r[i] = decode_table(schema, buf, elem_offset, ti, fcb) 335 | addr = addr + 4 336 | end 337 | end 338 | 339 | 340 | return r 341 | end 342 | end 343 | 344 | --[[ 345 | 346 | --]] 347 | function decode_table(schema, buf, offset, table_info, fcb) 348 | local fields_info = table_info.fields_array 349 | 350 | if table_info.is_struct then return decode_struct(buf, offset, table_info) end 351 | 352 | -- table starts with signed offset to a v-table 353 | -- elements of vtable are all type of unsigned int 16 354 | -- vtable: [ size of vtable, size of table, N offsets] 355 | 356 | local vt_reader = '< +%d =$i4 -$1 $u2 +2 {*[($2 - 4) // 2] u2}' 357 | -- ^ ^ ^ ^ 358 | -- | | | | 读取(vtable-size - 4) / 2 个u16 359 | -- | | | 360 | -- | | | size of vtable, including this field 361 | -- | | 362 | -- | | vtable start = object start - offset 363 | -- | 364 | -- | i4: offset to vtable 365 | local fields = buf_read(buf, vt_reader:format(offset)) 366 | 367 | local r = {} 368 | 369 | local i, fields_len = 0, # fields 370 | 371 | while i < fields_len do 372 | i = i + 1 373 | local v = fields[i] 374 | 375 | local field_info = fields_info[i] 376 | local field_type = field_info.type 377 | local basetype = field_type.base_type 378 | 379 | if fcb and fcb(table_info, r, offset, field_info, v) then goto continue end 380 | 381 | if BaseType.Bool <= basetype and basetype <= BaseType.String then 382 | 383 | if v ~= 0 then 384 | r[field_info.name] = field_type_reader[basetype](buf, offset, v) 385 | else 386 | r[field_info.name] = field_info.default_value 387 | end 388 | 389 | elseif basetype == BaseType.Vector then 390 | 391 | if v ~= 0 then 392 | r[field_info.name] = decode_array(schema, field_type, buf, offset + v, fcb) 393 | end 394 | 395 | 396 | elseif basetype == BaseType.Obj then 397 | 398 | if v ~= 0 then 399 | local sub_offset = subtable_offset(buf, offset + v) 400 | local ti = schema.objects[field_type.index + 1] -- 1-based index 401 | if ti.is_struct then 402 | r[field_info.name] = decode_struct(buf, offset + v, ti) 403 | else 404 | r[field_info.name] = decode_table(schema, buf, sub_offset, ti, fcb) 405 | end 406 | end 407 | 408 | elseif basetype == BaseType.UType then 409 | 410 | i = i + 1 -- skip next field: basetype Union 411 | 412 | -- union only contain table 413 | 414 | if v ~= 0 then 415 | local union_index = buf_read(buf, ('< +%d u1'):format(offset + v)) 416 | local next_field_info = fields_info[i] 417 | local next_field_type = next_field_info.type 418 | local next_v = fields[i] 419 | assert(next_field_type.base_type == BaseType.Union and next_v ~= 0) 420 | 421 | -- 1-based index 422 | local enum_info = schema.enums[next_field_type.index + 1] 423 | local ti = enum_info.values_lookup_dict[union_index].object 424 | 425 | local sub_offset = subtable_offset(buf, offset + next_v) 426 | 427 | r[field_info.name] = ti.name 428 | r[next_field_info.name] = decode_table(schema, buf, sub_offset, ti, fcb) 429 | end 430 | end 431 | 432 | ::continue:: 433 | end 434 | 435 | return r 436 | end 437 | 438 | local FlatBuffersMethods = { } 439 | 440 | function FlatBuffersMethods:decode(buf, offset, ti) 441 | offset = offset or buf_read(buf, '< u4') 442 | if ti then 443 | ti = assert(self.objects_name_dict[ti], 'bad type name') 444 | else 445 | ti = self.root_table 446 | end 447 | return decode_table(self, buf, offset, ti) 448 | end 449 | 450 | function FlatBuffersMethods:decode_ex(buf, fcb) 451 | return decode_table(self, buf, buf_read(buf, '< u4'), self.root_table, fcb) 452 | end 453 | 454 | local FlatBuffers = {} 455 | 456 | local fbs_mt = { __index = FlatBuffersMethods } 457 | 458 | -- TODO create schema with json, or parse from fbs source file with lpeg 459 | function FlatBuffers.bfbs(schema) 460 | assert(type(schema) == 'string') 461 | return setmetatable(decode_schema_with_cache(schema), fbs_mt) 462 | end 463 | 464 | return FlatBuffers 465 | -------------------------------------------------------------------------------- /buflib.c: -------------------------------------------------------------------------------- 1 | #include "buflib.inl" 2 | 3 | #define MAX_SIZET ((size_t)(~(size_t)0)) 4 | #define MAXSIZE (sizeof(size_t) < sizeof(int) ? MAX_SIZET : (size_t)(INT_MAX)) 5 | 6 | #define NB CHAR_BIT // char 类型的位数,通常是8 7 | 8 | //mask for one character (NB 1's) 9 | 10 | #define MC ((1 << NB) - 1) 11 | #define SIZE_LUA_INTEGER ((int)sizeof(lua_Integer)) // size of a lua_Integer 12 | 13 | /* dummy union to get native endianness */ 14 | static const union { 15 | int dummy; 16 | char little; /* true iff machine is little endian */ 17 | } nativeendian = {1}; 18 | 19 | 20 | #define ISDIGIT(c) ('0' <= c && c <= '9') 21 | 22 | static uint32_t read_optional_integer (const char **s, uint32_t default_value) { 23 | if (ISDIGIT(**s)) { 24 | uint32_t a = 0; 25 | do { 26 | a = a * 10 + (*((*s)++) - '0'); 27 | } while (ISDIGIT(**s) && a <= ((uint32_t)MAXSIZE - 9)/10); 28 | return a; 29 | } else { 30 | return default_value; 31 | } 32 | } 33 | 34 | #define EXPR_CALC_LUA_SCRIPT \ 35 | "local tonumber = tonumber\n" \ 36 | "local args, str = ...\n" \ 37 | "str = str:gsub('%$(%d+)', function (k)\n" \ 38 | " return args[tonumber(k)]\n" \ 39 | "end)\n" \ 40 | "return load('return ' .. str, '', 't', {})()\n" 41 | 42 | // 移动指针操作(+/-)和重复操作的数值部分的表达式计算 43 | static int64_t calc_integral_expression (struct State * st, const char **s, uint32_t df) { 44 | if (**s == '[') { 45 | const char * p = *s; 46 | luaL_loadstring(st->L, EXPR_CALC_LUA_SCRIPT); 47 | lua_createtable(st->L, 32, 0); 48 | while (*((*s)++) != ']') { 49 | if (**s == '$') { 50 | ++(*s); 51 | uint32_t idx = read_optional_integer(s, 0); 52 | if (idx == 0 || idx >= st->index) { 53 | luaL_error(st->L, "[calc_integral_expression] bad variable index: %d", idx); 54 | } 55 | lua_pushinteger(st->L, st->variable[idx]); 56 | lua_rawseti(st->L, -2, idx); 57 | } 58 | } 59 | 60 | lua_pushlstring(st->L, p + 1, *s - p - 2); 61 | lua_call(st->L, 2, 1); 62 | 63 | int64_t r = (int64_t)luaL_checkinteger(st->L, -1); 64 | lua_pop(st->L, 1); 65 | return r; 66 | } else if (**s == '$') { 67 | ++(*s); 68 | uint32_t idx = read_optional_integer(s, 0); 69 | if (idx == 0 || idx >= st->index) { 70 | luaL_error(st->L, "[calc_integral_expression] bad variable index: %d", idx); 71 | } 72 | return st->variable[idx]; 73 | } else { 74 | return read_optional_integer(s, df); 75 | } 76 | } 77 | 78 | // Read an integer numeral and raises an error if it is larger 79 | // than the maximum size for integers. 80 | static uint32_t 81 | get_opt_int_size (struct State *st, const char **s, 82 | uint32_t df, uint32_t min, uint32_t max) { 83 | 84 | uint32_t num = read_optional_integer(s, df); 85 | if (max < num || num < min) { 86 | luaL_error(st->L, "(%I) out of limits [%I, %I]", num, min, max); 87 | } 88 | 89 | switch (num) { 90 | case 0: case 1: case 2: case 4: case 8: break; 91 | default: luaL_error(st->L, "invalid integer size (%I)", num); 92 | } 93 | return num; 94 | } 95 | 96 | // Unpack an integer with 'size' bytes and 'islittle' endianness. 97 | // If size is smaller than the size of a Lua integer and integer 98 | // is signed, must do sign extension (propagating the sign to the 99 | // higher bits); if size is larger than the size of a Lua integer, 100 | // it must check the unread bytes to see whether they do not cause an 101 | // overflow. 102 | static lua_Integer 103 | unpackint (struct State *st, const char *str, 104 | int islittle, int size, int issigned) { 105 | lua_Unsigned res = 0; 106 | int i; 107 | int limit = (size <= SIZE_LUA_INTEGER) ? size : SIZE_LUA_INTEGER; 108 | for (i = limit - 1; i >= 0; i--) { 109 | res <<= NB; 110 | res |= (lua_Unsigned)(unsigned char)str[islittle ? i : size - 1 - i]; 111 | } 112 | if (size < SIZE_LUA_INTEGER) { /* real size smaller than lua_Integer? */ 113 | if (issigned) { /* needs sign extension? */ 114 | lua_Unsigned mask = (lua_Unsigned)1 << (size*NB - 1); 115 | res = ((res ^ mask) - mask); /* do sign extension */ 116 | } 117 | } else if (size > SIZE_LUA_INTEGER) { /* must check unread bytes */ 118 | int mask = (!issigned || (lua_Integer)res >= 0) ? 0 : MC; 119 | for (i = limit; i < size; i++) { 120 | if ((unsigned char)str[islittle ? i : size - 1 - i] != mask) 121 | luaL_error(st->L, "%d-byte integer does not fit into Lua Integer", size); 122 | } 123 | } 124 | return (lua_Integer)res; 125 | } 126 | 127 | #define INIT_MEM_SIZE 32 128 | #define MEM_GROW_SIZE 16 129 | 130 | #define INIT_LUA_STACK_SPACE 32 131 | #define LUA_STACK_GROW_SPACE 16 132 | 133 | 134 | union Ftypes { 135 | float f; 136 | double d; 137 | lua_Number n; 138 | char buff[5 * sizeof(lua_Number)]; /* enough for any float type */ 139 | }; 140 | 141 | static void copy_with_endian (volatile char *dest, volatile const char *src, 142 | int size, int islittle) { 143 | if (islittle == nativeendian.little) { 144 | while (size-- != 0) 145 | *(dest++) = *(src++); 146 | } 147 | else { 148 | dest += size - 1; 149 | while (size-- != 0) 150 | *(dest--) = *(src++); 151 | } 152 | } 153 | 154 | #define CHECK_MOVE_POINTER(len) \ 155 | if (st->dont_move == 0) st->pointer += (len); else st->dont_move = 0 156 | 157 | #define CHECK_STACK_SPACE(st) \ 158 | if (st->ret + 2 >= st->stack_space) \ 159 | st->stack_space += LUA_STACK_GROW_SPACE, \ 160 | luaL_checkstack(st->L, st->stack_space, "too many results") 161 | 162 | static void read_boolean(struct State * st, const char **s) { 163 | uint32_t len = get_opt_int_size(st, s, 1, 1, 8); 164 | 165 | if (st->buffer_size != 0 && st->pointer + st->repeat * len > st->buffer_end) { 166 | luaL_error(st->L, "read boolean: out of buffer"); 167 | } 168 | while (st->repeat-- > 0) { 169 | lua_pushboolean(st->L, unpackint(st, st->pointer, st->little, len, 0)); 170 | CHECK_MOVE_POINTER(len); 171 | 172 | if (st->in_tb == 0) { 173 | st->ret++; 174 | CHECK_STACK_SPACE(st); 175 | } else { 176 | lua_rawseti(st->L, -2, st->tb_idx++); 177 | } 178 | } 179 | } 180 | 181 | static void read_integer(struct State *st, const char **s, int is_signed) { 182 | uint32_t len = get_opt_int_size(st, s, 4, 1, 8); 183 | 184 | if (st->buffer_size != 0 && st->pointer + len * st->repeat > st->buffer_end) { 185 | luaL_error(st->L, "read integer: out of buffer"); 186 | } 187 | int c_var = st->create_var; 188 | int c_ref = st->create_ref; 189 | st->create_ref = 0; 190 | st->create_var = 0; 191 | while (st->repeat-- > 0) { 192 | int64_t num = unpackint(st, st->pointer, st->little, len, is_signed); 193 | CHECK_MOVE_POINTER(len); 194 | 195 | if (c_var != 0) { 196 | st->variable[st->index++] = num; 197 | } else { 198 | if (c_ref != 0) { 199 | st->variable[st->index++] = num; 200 | } 201 | lua_pushinteger(st->L, num); 202 | if (st->in_tb == 0) { 203 | st->ret++; 204 | CHECK_STACK_SPACE(st); 205 | } else { 206 | lua_rawseti(st->L, -2, st->tb_idx++); 207 | } 208 | } 209 | } 210 | } 211 | 212 | static void read_float32(struct State *st) { 213 | if (st->buffer_size != 0 && st->pointer + 4 * st->repeat > st->buffer_end) { 214 | luaL_error(st->L, "read float32: out of buffer"); 215 | } 216 | 217 | while (st->repeat-- > 0) { 218 | volatile union Ftypes u; 219 | copy_with_endian(u.buff, st->pointer, 4, st->little); 220 | CHECK_MOVE_POINTER(4); 221 | lua_pushnumber(st->L, (lua_Number)u.f); 222 | if (st->in_tb == 0) { 223 | st->ret++; 224 | CHECK_STACK_SPACE(st); 225 | } else { 226 | lua_rawseti(st->L, -2, st->tb_idx++); 227 | } 228 | } 229 | } 230 | 231 | static void read_float64(struct State *st) { 232 | if (st->buffer_size != 0 && st->pointer + 8 * st->repeat > st->buffer_end) { 233 | luaL_error(st->L, "read float64: out of buffer"); 234 | } 235 | 236 | while (st->repeat-- > 0) { 237 | volatile union Ftypes u; 238 | copy_with_endian(u.buff, st->pointer, 8, st->little); 239 | CHECK_MOVE_POINTER(8); // TODO 重复读时其实没有必要检测指针移动, 优化此处 240 | lua_pushnumber(st->L, (lua_Number)u.d); 241 | // TODO 是否为数组模式也不用在重复读循环中每次检测 242 | if (st->in_tb == 0) { 243 | st->ret++; 244 | CHECK_STACK_SPACE(st); 245 | } else { 246 | lua_rawseti(st->L, -2, st->tb_idx++); 247 | } 248 | } 249 | } 250 | 251 | static void read_string(struct State *st, const char **s) { 252 | // s[n] 253 | // s: zero-terminated string 254 | // s[1-4]: header + string 255 | uint32_t len = get_opt_int_size(st, s, 0, 0, 4); 256 | 257 | while (st->repeat -- > 0) { 258 | if (len == 0) { 259 | size_t slen = strlen(st->pointer); 260 | int max_size = st->buffer_size - (st->pointer - st->buffer) - 1; 261 | if (st->buffer_size != 0 && slen > max_size) { 262 | luaL_error(st->L, "read string s[0]: out of buffer"); 263 | } 264 | lua_pushlstring(st->L, st->pointer, slen); 265 | CHECK_MOVE_POINTER(slen + 1); // skip a terminal zero 266 | } else { 267 | uint32_t slen = unpackint(st, st->pointer, st->little, len, 0); 268 | if (st->buffer_size != 0 && st->pointer + len + slen > st->buffer_end) { 269 | luaL_error(st->L, "read string s[%d]: out of buffer", slen); 270 | } 271 | lua_pushlstring(st->L, st->pointer + len, slen); 272 | CHECK_MOVE_POINTER(len + slen); 273 | } 274 | 275 | if (st->in_tb == 0) { 276 | st->ret++; 277 | CHECK_STACK_SPACE(st); 278 | } else { 279 | lua_rawseti(st->L, -2, st->tb_idx++); 280 | } 281 | } 282 | } 283 | 284 | static void read_fixed_string(struct State *st, const char **s) { 285 | uint32_t len = read_optional_integer(s, 1); 286 | if (len == 0) { 287 | luaL_error(st->L, "read fixed string c[%d]: zero", len); 288 | } 289 | if (st->buffer_size != 0 && st->pointer + len > st->buffer_end) { 290 | luaL_error(st->L, "read fixed string c[%d]: out of buffer", len); 291 | } 292 | 293 | while (st->repeat-- > 0) { 294 | lua_pushlstring(st->L, st->pointer, len); 295 | CHECK_MOVE_POINTER(len); 296 | if (st->in_tb == 0) { 297 | st->ret++; 298 | CHECK_STACK_SPACE(st); 299 | } else { 300 | lua_rawseti(st->L, -2, st->tb_idx++); 301 | } 302 | } 303 | } 304 | 305 | 306 | static void run_instructions(struct State * st) { 307 | const char * pc = st->instructions; 308 | char c; 309 | 310 | // 变量空间(栈上分配) 311 | // layout [剩余空间容量, ] 312 | // index: 栈顶下标 313 | st->variable = alloca(sizeof(int64_t) * INIT_MEM_SIZE); 314 | st->variable[0] = INIT_MEM_SIZE - 1; 315 | st->index = 1; 316 | 317 | while ((c = *pc++)) { 318 | 319 | switch((c)) { 320 | // skip white spaces 321 | case ' ': case '\t': case '\r': case '\n': continue; 322 | 323 | // set flags 324 | case '>': st->little = 0; continue; 325 | case '<': st->little = 1; continue; 326 | case '=': st->dont_move = 1; continue; 327 | 328 | case '{': 329 | { 330 | if (st->in_tb != 0) luaL_error(st->L, "nested table detected"); 331 | st->in_tb = st->tb_idx = 1; 332 | st->ret++; 333 | CHECK_STACK_SPACE(st); 334 | lua_createtable(st->L, 32, 0); 335 | continue; 336 | } 337 | case '}': 338 | { 339 | if (st->in_tb == 0) luaL_error(st->L, "missing corresponding {"); 340 | st->in_tb = 0; 341 | continue; 342 | } 343 | 344 | case '$': 345 | case '&': 346 | { 347 | if (*pc != 'u' && *pc != 'i') luaL_error(st->L, "u/i expected after &"); 348 | 349 | // 变量栈增长 350 | int64_t cur_size = st->variable[0]; 351 | if (cur_size - st->index - st->repeat < 0) { 352 | int grow_size = st->repeat + MEM_GROW_SIZE; 353 | int64_t * ns = alloca(sizeof(int64_t) * (cur_size + grow_size)); 354 | ns[0] = cur_size + grow_size; 355 | memmove(ns + 1, st->variable + 1, sizeof(int64_t) * cur_size); 356 | st->variable = ns; 357 | } 358 | if (c == '$') st->create_var = 1; else st->create_ref = 1; 359 | continue; 360 | } 361 | 362 | case '+': 363 | { 364 | st->pointer += calc_integral_expression(st, &pc, 1); 365 | int64_t offset = st->pointer - st->buffer; 366 | if (offset < 0 || (st->buffer_size != 0 && offset > st->buffer_size)) { 367 | luaL_error(st->L, "+ move out of buffer: %d", offset); 368 | } 369 | continue; 370 | } 371 | case '-': 372 | { 373 | st->pointer -= calc_integral_expression(st, &pc, 1); 374 | int64_t offset = st->pointer - st->buffer; 375 | if (offset < 0 || (st->buffer_size != 0 && offset > st->buffer_size)) { 376 | luaL_error(st->L, "- move out of buffer: %d", offset); 377 | } 378 | continue; 379 | } 380 | case '*': 381 | { 382 | if (st->repeat > 1) luaL_error(st->L, "duplicate repeat flag"); 383 | int64_t repeat_count = calc_integral_expression(st, &pc, 0); 384 | if (repeat_count <= 0 || (st->in_tb == 0 && repeat_count > 127)) { 385 | luaL_error(st->L, "invalid repeat times: [%I]", repeat_count); 386 | } 387 | st->repeat = (uint32_t)repeat_count; 388 | continue; 389 | } 390 | 391 | case '@': 392 | case '^': 393 | { 394 | lua_pushinteger(st->L, st->pointer - st->buffer); 395 | if (st->in_tb == 0) { 396 | st->ret++; 397 | CHECK_STACK_SPACE(st); 398 | } else { 399 | lua_rawseti(st->L, -2, st->tb_idx++); 400 | } 401 | continue; 402 | } 403 | 404 | case 'b': read_boolean(st, &pc); break; 405 | case 'i': case 'u': read_integer(st, &pc, c == 'i'); break; 406 | case 'f': read_float32(st); break; 407 | case 'd': read_float64(st); break; 408 | case 's': read_string(st, &pc); break; 409 | case 'c': read_fixed_string(st, &pc); break; 410 | default : 411 | luaL_error(st->L, "unknown read opcode"); 412 | } 413 | 414 | st->repeat = 1; 415 | } 416 | } 417 | 418 | static int buf_read (lua_State *L) { 419 | struct State st = { 420 | .L = L, .repeat = 1, .little = 1, 421 | .stack_space = INIT_LUA_STACK_SPACE, 422 | .create_var = 0, .create_ref = 0, .dont_move = 0, 423 | .ret = 0, .in_tb = 0, .tb_idx = 0, 424 | }; 425 | 426 | if (lua_type(L, 1) == LUA_TNUMBER) { 427 | st.pointer = st.buffer = (void *)(luaL_checkinteger(L, 1)); 428 | st.buffer_size = 0; // unknown buffer size 429 | } else { 430 | st.pointer = st.buffer = luaL_checklstring(L, 1, &st.buffer_size); 431 | st.buffer_end = st.buffer + st.buffer_size; 432 | } 433 | 434 | st.instructions = luaL_checkstring(L, 2); 435 | 436 | luaL_checkstack(L, INIT_LUA_STACK_SPACE, NULL); 437 | run_instructions(&st); 438 | return st.ret; 439 | } 440 | 441 | static const luaL_Reg buffer_lib[] = { 442 | {"read", buf_read}, 443 | {NULL, NULL} 444 | }; 445 | 446 | LUA_API int luaopen_buffer (lua_State *L) { 447 | luaL_checkversion(L); 448 | if (sizeof(lua_Integer) < 8) { 449 | lua_pushstring(L, "incompatible Lua version"); 450 | } else { 451 | luaL_newlib(L, buffer_lib); 452 | } 453 | 454 | return 1; 455 | } 456 | 457 | // vim: tabstop=2 softtabstop=2 shiftwidth=2 smarttab expandtab shiftround 458 | --------------------------------------------------------------------------------