mirror of
https://github.com/grumpycoders/pcsx-redux.git
synced 2024-05-16 19:11:04 -04:00
Adding lua-protobuf
This commit is contained in:
parent
71dd4a474f
commit
f506f642f5
|
@ -23,8 +23,8 @@ find a link to the vendored version itself.
|
|||
- [ELFIO](https://github.com/serge1/ELFIO)
|
||||
- [expected](https://github.com/TartanLlama/expected)
|
||||
- [ffi-reflect](https://github.com/corsix/ffi-reflect) ([vendored](https://github.com/grumpycoders/pcsx-redux/tree/main/third_party/ffi-reflect))
|
||||
- [FFmpeg](https://ffmpeg.org)
|
||||
- [flags](https://github.com/sailormoon/flags) ([vendored](https://github.com/grumpycoders/pcsx-redux/blob/main/third_party/flags.h))
|
||||
- [ffmpeg](https://ffmpeg.org)
|
||||
- [fmt](https://github.com/fmtlib/fmt)
|
||||
- [FreeType](https://gitlab.freedesktop.org/freetype/freetype.git)
|
||||
- [gl3w](https://github.com/skaslev/gl3w) ([vendored](https://github.com/grumpycoders/pcsx-redux/tree/main/third_party/gl3w/GL))
|
||||
|
@ -41,6 +41,7 @@ find a link to the vendored version itself.
|
|||
- [json](https://github.com/nlohmann/json) ([vendored](https://github.com/grumpycoders/pcsx-redux/blob/main/third_party/json.hpp))
|
||||
- [libcester](https://github.com/exoticlibraries/libcester)
|
||||
- [libuv](https://github.com/libuv/libuv)
|
||||
- [lua-protobuf](https://github.com/starwing/lua-protobuf) ([vendored](https://github.com/grumpycoders/pcsx-redux/tree/main/third_party/lua-protobuf))
|
||||
- [LuaJIT](https://github.com/LuaJIT/LuaJIT) ([vendored](https://github.com/grumpycoders/LuaJIT/tree/vendored-clib-virtual))
|
||||
- [luv](https://github.com/luvit/luv)
|
||||
- [magic_enum](https://github.com/Neargye/magic_enum)
|
||||
|
|
1
Makefile
1
Makefile
|
@ -123,6 +123,7 @@ SRCS += third_party/imgui/misc/freetype/imgui_freetype.cpp
|
|||
SRCS += third_party/imgui_lua_bindings/imgui_lua_bindings.cpp
|
||||
SRCS += third_party/imgui_md/imgui_md.cpp
|
||||
SRCS += third_party/imgui_memory_editor/imgui_memory_editor.cpp
|
||||
SRCS += third_party/lua-protobuf/pb.c
|
||||
SRCS += third_party/luv/src/luv.c
|
||||
SRCS += third_party/md4c/src/md4c.c
|
||||
SRCS += third_party/multipart-parser-c/multipart_parser.c
|
||||
|
|
|
@ -23,4 +23,5 @@
|
|||
-Ithird_party/xbyak/xbyak
|
||||
-DIMGUI_IMPL_OPENGL_LOADER_GL3W
|
||||
-DIMGUI_ENABLE_FREETYPE
|
||||
-DLUAJIT_ENABLE_LUA52COMPAT
|
||||
-DZEP_FEATURE_CPP_FILE_SYSTEM
|
||||
|
|
|
@ -19,16 +19,58 @@
|
|||
|
||||
#include "lua/extra.h"
|
||||
|
||||
#include "lua-protobuf/pb.h"
|
||||
#include "lua/luawrapper.h"
|
||||
|
||||
void PCSX::LuaFFI::open_extra(Lua L) {
|
||||
L.getfieldtable("_LOADED", LUA_REGISTRYINDEX);
|
||||
luaopen_pb(L.getState());
|
||||
L.setfield("pb", -3);
|
||||
L.pop();
|
||||
luaopen_pb_io(L.getState());
|
||||
L.setfield("pb.io");
|
||||
luaopen_pb_conv(L.getState());
|
||||
L.setfield("pb.conv");
|
||||
luaopen_pb_slice(L.getState());
|
||||
L.setfield("pb.slice");
|
||||
luaopen_pb_buffer(L.getState());
|
||||
L.setfield("pb.buffer");
|
||||
luaopen_pb_unsafe(L.getState());
|
||||
L.setfield("pb.unsafe");
|
||||
|
||||
static int lualoader = 1;
|
||||
static const char* pprint = (
|
||||
#include "pprint.lua/pprint.lua"
|
||||
);
|
||||
static const char* reflectFFI = (
|
||||
#include "ffi-reflect/reflect.lua"
|
||||
);
|
||||
static const char* protobufLexer = (
|
||||
#include "lua-protobuf/lexer.lua"
|
||||
);
|
||||
static const char* protobufTopLevel = (
|
||||
#include "lua-protobuf/toplevel.lua"
|
||||
);
|
||||
static const char* descriptorPB = (
|
||||
#include "lua-protobuf/descriptor.pb.lua"
|
||||
);
|
||||
static const char* protoc = (
|
||||
#include "lua-protobuf/protoc.lua"
|
||||
);
|
||||
L.load(pprint, "internal:pprinter.lua/pprint.lua");
|
||||
L.load(reflectFFI, "internal:ffi-reflect/reflect.lua");
|
||||
|
||||
L.load(protobufLexer, "internal:lua-protobuf/lexer.lua");
|
||||
L.setfield("pb.Lexer");
|
||||
|
||||
L.load(protobufTopLevel, "internal:lua-protobuf/toplevel.lua");
|
||||
L.setfield("pb.TopLevel");
|
||||
|
||||
L.load(descriptorPB, "internal:lua-protobuf/descriptor.pb.lua");
|
||||
L.setfield("pb.Descriptor");
|
||||
|
||||
L.load(protoc, "internal:lua-protobuf/protoc.lua");
|
||||
L.setfield("protoc");
|
||||
|
||||
L.pop();
|
||||
}
|
||||
|
|
24
third_party/lua-protobuf/.gitignore
vendored
Normal file
24
third_party/lua-protobuf/.gitignore
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
*.dSYM
|
||||
*.dll
|
||||
*.dll.*
|
||||
*.exp
|
||||
*.gcda
|
||||
*.gcno
|
||||
*.gcov
|
||||
*.iobj
|
||||
*.ipdb
|
||||
*.lib
|
||||
*.natvis
|
||||
*.obj
|
||||
*.out
|
||||
*.pb
|
||||
*.pdb
|
||||
*.rock
|
||||
*.so
|
||||
*.o
|
||||
*.gc*
|
||||
google
|
||||
lua*/
|
||||
out*
|
||||
test*
|
||||
test*.lua
|
45
third_party/lua-protobuf/.travis.yml
vendored
Normal file
45
third_party/lua-protobuf/.travis.yml
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
language: c
|
||||
os: linux
|
||||
dist: xenial
|
||||
|
||||
env:
|
||||
global:
|
||||
- ROCKSPEC=rockspecs/lua-protobuf-scm-1.rockspec
|
||||
jobs:
|
||||
- LUA="lua 5.1"
|
||||
- LUA="lua 5.2"
|
||||
- LUA="lua 5.3"
|
||||
- LUA="lua 5.4"
|
||||
- LUA="luajit 2.0"
|
||||
- LUA="luajit 2.1"
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- lua54
|
||||
|
||||
before_install:
|
||||
- pip install --user urllib3[secure] cpp-coveralls
|
||||
- curl -O https://raw.githubusercontent.com/luarocks/hererocks/master/hererocks.py
|
||||
- python hererocks.py env --$LUA -rlatest
|
||||
- source env/bin/activate
|
||||
|
||||
install:
|
||||
# - sudo luarocks make $ROCKSPEC CFLAGS="-O2 -fPIC -ftest-coverage -fprofile-arcs" LIBFLAG="-shared --coverage"
|
||||
- luarocks make $ROCKSPEC CFLAGS="-O3 -fPIC -Wall -Wextra --coverage" LIBFLAG="-shared --coverage"
|
||||
|
||||
script:
|
||||
- lua test.lua
|
||||
# - lunit.sh test.lua
|
||||
|
||||
after_success:
|
||||
- coveralls
|
||||
# - coveralls -b .. -r .. --dump c.report.json
|
||||
# - luacov-coveralls -j c.report.json -v
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_success: change
|
||||
on_failure: always
|
||||
|
||||
# vim: ft=yaml nu et sw=2 fdc=2 fdm=syntax
|
21
third_party/lua-protobuf/LICENSE
vendored
Normal file
21
third_party/lua-protobuf/LICENSE
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2018 Xavier Wang
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
517
third_party/lua-protobuf/README.md
vendored
Normal file
517
third_party/lua-protobuf/README.md
vendored
Normal file
|
@ -0,0 +1,517 @@
|
|||
# Google protobuf support for Lua
|
||||
|
||||
[![Build Status](https://img.shields.io/github/workflow/status/starwing/lua-protobuf/CI)](https://github.com/starwing/lua-protobuf/actions?query=branch%3Amaster)[![Coverage Status](https://img.shields.io/coveralls/github/starwing/lua-protobuf)](https://coveralls.io/github/starwing/lua-protobuf?branch=master)
|
||||
|
||||
English | [中文](https://github.com/starwing/lua-protobuf/blob/master/README.zh.md)
|
||||
|
||||
---
|
||||
|
||||
This project offers a C module for Lua (5.1, 5.2, 5.3, 5.4 and LuaJIT) manipulating Google's protobuf protocol, both for version 2 and 3 syntax and semantics. It splits to the lower-level and the high-level parts for different goals.
|
||||
|
||||
For converting between binary protobuf data with Lua tables, using `pb.load()` loads the compiled protobuf schema content (`*.pb` file) generated by Google protobuf's compiler named `protoc` and call `pb.encode()`/`pb.decode()`.
|
||||
|
||||
Or use these modules to manipulate the raw wire format in lower-level way:
|
||||
|
||||
- `pb.slice`: a wire format decoding module.
|
||||
- `pb.buffer`: a buffer implement that use to encode basic types into protobuf's wire format. It can be used to support streaming decode protobuf data.
|
||||
- `pb.conv`: a module converting integers in the protobuf wire format.
|
||||
- `pb.io`: a module access `stdin/stdout` or other files in binary mode.
|
||||
|
||||
If you don't want to depend Google's protobuf compiler, `protoc.lua` is a pure Lua module translating text-based protobuf schema content into the `*.pb` binary format.
|
||||
|
||||
## Install
|
||||
|
||||
To install, you could just use `luarocks`:
|
||||
|
||||
```shell
|
||||
luarocks install lua-protobuf
|
||||
```
|
||||
|
||||
If you want to build it from source, just clone the repo and use luarocks:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/starwing/lua-protobuf
|
||||
luarocks make rockspecs/lua-protobuf-scm-1.rockspec
|
||||
```
|
||||
|
||||
If you don't have luarocks, use `hererocks` to install Lua and luarocks:
|
||||
|
||||
```shell
|
||||
pip install hererocks
|
||||
git clone https://github.com/starwing/lua-protobuf
|
||||
hererocks -j 2.0 -rlatest .
|
||||
bin/luarocks make lua-protobuf/rockspecs/lua-protobuf-scm-1.rockspec CFLAGS="-fPIC -Wall -Wextra" LIBFLAGS="-shared"
|
||||
cp protoc.lua pb.so ..
|
||||
```
|
||||
|
||||
Or you can build it by hand, it only has a pure Lua module `protoc.lua` and a pair of C source: `pb.h` and `pb.c`. *Notice* that in order to build the `pb` C module, you need Lua header file and/or libary file installed. replace `$LUA_HEADERS` and `$LUA_LIBS` below to real install locations.
|
||||
|
||||
To build it on macOS, use your favor compiler:
|
||||
|
||||
```shell
|
||||
gcc -O2 -shared -undefined dynamic_lookup -I "$LUA_HEADERS" pb.c -o pb.so
|
||||
```
|
||||
|
||||
On Linux, use the nearly same command:
|
||||
|
||||
```shell
|
||||
gcc -O2 -shared -fPIC -I "$LUA_HEADERS" pb.c -o pb.so
|
||||
```
|
||||
|
||||
On Windows, you could use MinGW or MSVC, create a `*.sln` project or build it on the command line (notice the `Lua_BUILD_AS_DLL` flag):
|
||||
|
||||
```shell
|
||||
cl /O2 /LD /Fepb.dll /I "$LUA_HEADERS" /DLUA_BUILD_AS_DLL pb.c "$LUA_LIBS"
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```lua
|
||||
local pb = require "pb"
|
||||
local protoc = require "protoc"
|
||||
|
||||
-- load schema from text (just for demo, use protoc.new() in real world)
|
||||
assert(protoc:load [[
|
||||
message Phone {
|
||||
optional string name = 1;
|
||||
optional int64 phonenumber = 2;
|
||||
}
|
||||
message Person {
|
||||
optional string name = 1;
|
||||
optional int32 age = 2;
|
||||
optional string address = 3;
|
||||
repeated Phone contacts = 4;
|
||||
} ]])
|
||||
|
||||
-- lua table data
|
||||
local data = {
|
||||
name = "ilse",
|
||||
age = 18,
|
||||
contacts = {
|
||||
{ name = "alice", phonenumber = 12312341234 },
|
||||
{ name = "bob", phonenumber = 45645674567 }
|
||||
}
|
||||
}
|
||||
|
||||
-- encode lua table data into binary format in lua string and return
|
||||
local bytes = assert(pb.encode("Person", data))
|
||||
print(pb.tohex(bytes))
|
||||
|
||||
-- and decode the binary data back into lua table
|
||||
local data2 = assert(pb.decode("Person", bytes))
|
||||
print(require "serpent".block(data2))
|
||||
|
||||
```
|
||||
|
||||
## Use case
|
||||
|
||||
[![零境交错](https://img.tapimg.com/market/images/e59627dc9039ff22ba7d000b5c9fe7f6.jpg?imageView2/2/h/560/q/40/format/jpg/interlace/1/ignore-error/1)](http://djwk.qq.com)
|
||||
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
### `protoc` Module
|
||||
|
||||
| Function | Returns | Descriptions |
|
||||
| ------------------- | ------------- | ---------------------------------------------------- |
|
||||
| `protoc.new()` | Proroc object | create a new compiler instance |
|
||||
| `protoc.reload()` | true | reload all google standard messages into `pb` module |
|
||||
| `p:parse(string)` | table | transform schema to `DescriptorProto` table |
|
||||
| `p:compile(string)` | string | transform schema to binary *.pb format data |
|
||||
| `p:load(string)` | true | load schema into `pb` module |
|
||||
| `p.loaded` | table | contains all parsed `DescriptorProto` table |
|
||||
| `p.unknown_import` | see below | handle schema import error |
|
||||
| `p.unknown_type` | see below | handle unknown type in schema |
|
||||
| `p.include_imports` | bool | auto load imported proto |
|
||||
|
||||
To parse a text schema content, create a compiler instance first:
|
||||
|
||||
```lua
|
||||
local p = protoc.new()
|
||||
```
|
||||
|
||||
Then, set some options to the compiler, e.g. the unknown handlers:
|
||||
|
||||
```lua
|
||||
-- set some hooks
|
||||
p.unknown_import = function(self, module_name) ... end
|
||||
p.unknown_type = function(self, type_name) ... end
|
||||
-- ... and options
|
||||
p.include_imports = true
|
||||
```
|
||||
|
||||
The `unknown_import` and `unknown_type` handle could be `true`, string or a function. Seting it to `true` means all *non-exist* modules and types are given a default value without triggering an error; A string means a Lua pattern that indicates whether an unknown module or type should raise an error, e.g.
|
||||
|
||||
```lua
|
||||
p.unknown_type = "Foo.*"
|
||||
```
|
||||
|
||||
means all types prefixed by `Foo` will be treat as existing type and do not trigger errors.
|
||||
|
||||
If these are functions, the unknown type and module name will be passed to functions. For module handler, it should return a `DescriptorProto` Table produced by `p:load()` functions, for type handler, it should return a type name and type, such as `message` or `enum`, e.g.
|
||||
|
||||
```lua
|
||||
function p:unknown_import(name)
|
||||
-- if can not find "foo.proto", load "my_foo.proto" instead
|
||||
return p:parsefile("my_"..name)
|
||||
end
|
||||
|
||||
function p:unknown_type(name)
|
||||
-- if cannot find "Type", treat it as ".MyType" and is a message type return ".My"..name, "message"
|
||||
end
|
||||
```
|
||||
|
||||
After setting options, use `load()` or `compile()` or `parse()` function to get result.
|
||||
|
||||
### `pb` Module
|
||||
|
||||
`pb` module has high-level routines to manipulate protobuf messages.
|
||||
|
||||
In below table of functions, we have several types that have special means:
|
||||
|
||||
- `type`: a string that indicates the protobuf message type, `".Foo"` means the type in a proto definition that has not `package` statement declared. `"foo.Foo"` means the type in a proto definition that declared `package foo;`
|
||||
|
||||
- `data`: could be string, `pb.Slice` value or `pb.Buffer` value.
|
||||
|
||||
- `iterator`: a function that can use in Lua `for in` statement, e.g.
|
||||
|
||||
```lua
|
||||
for name in pb.types() do
|
||||
print(name)
|
||||
end
|
||||
```
|
||||
|
||||
**NOTICE**: Only `pb.load()` returns error on failure, *do check* the result it returns. Other routines raise a error when failure for convenience.
|
||||
|
||||
| Function | Returns | Description |
|
||||
| ------------------------------ | --------------- | ------------------------------------------------------- |
|
||||
| `pb.clear()` | None | clear all types |
|
||||
| `pb.clear(type)` | None | delete specific type |
|
||||
| `pb.load(data)` | boolean,integer | load a binary schema data into `pb` module |
|
||||
| `pb.encode(type, table)` | string | encode a message table into binary form |
|
||||
| `pb.encode(type, table, b)` | buffer | encode a message table into binary form to buffer |
|
||||
| `pb.decode(type, data)` | table | decode a binary message into Lua table |
|
||||
| `pb.decode(type, data, table)` | table | decode a binary message into a given Lua table |
|
||||
| `pb.pack(fmt, ...)` | string | same as `buffer.pack()` but return string |
|
||||
| `pb.unpack(data, fmt, ...)` | values... | same as `slice.unpack()` but accept data |
|
||||
| `pb.types()` | iterator | iterate all types in `pb` module |
|
||||
| `pb.type(type)` | see below | return informations for specific type |
|
||||
| `pb.fields(type)` | iterator | iterate all fields in a message |
|
||||
| `pb.field(type, string)` | see below | return informations for specific field of type |
|
||||
| `pb.typefmt(type)` | String | transform type name of field into pack/unpack formatter |
|
||||
| `pb.enum(type, string)` | number | get the value of a enum by name |
|
||||
| `pb.enum(type, number)` | string | get the name of a enum by value |
|
||||
| `pb.defaults(type[, table])` | table | get the default table of type |
|
||||
| `pb.hook(type[, function])` | function | get or set hook functions |
|
||||
| `pb.option(string)` | string | set options to decoder/encoder |
|
||||
| `pb.state()` | `pb.State` | retrieve current pb state |
|
||||
| `pb.state(newstate \| nil)` | `pb.State` | set new pb state and retrieve the old one |
|
||||
|
||||
#### Schema loading
|
||||
|
||||
`pb.load()` accepts the schema binary data and returns a boolean indicates the result of loading, success or failure, and a offset reading in schema so far that is useful to figure out the reason of failure.
|
||||
|
||||
#### Type mapping
|
||||
|
||||
| Protobuf Types | Lua Types |
|
||||
| -------------------------------------------------- | ------------------------------------------------------------ |
|
||||
| `double`, `float` | `number` |
|
||||
| `int32`, `uint32`, `fixed32`, `sfixed32`, `sint32` | `number` or `integer` in Lua 5.3+ |
|
||||
| `int64`, `uint64`, `fixed64`, `sfixed64`, `sint64` | `number` or `"#"` prefixed `string` or `integer` in Lua 5.3+ |
|
||||
| `bool` | `boolean` |
|
||||
| `string`, `bytes` | `string` |
|
||||
| `message` | `table` |
|
||||
| `enum` | `string` or `number` |
|
||||
|
||||
#### Type Information
|
||||
|
||||
Using `pb.(type|field)[s]()` functions retrieve type information for loaded messages.
|
||||
|
||||
`pb.type()` returns multiple informations for specified type:
|
||||
|
||||
- name : the full qualifier name of type, e.g. ".package.TypeName"
|
||||
- basename: the type name without package prefix, e.g. "TypeName"
|
||||
- `"map"` | `"enum"` | `"message"`: whether the type is a map_entry type, enum type or message type.
|
||||
|
||||
`pb.types()` returns a iterators, behavior like call `pb.type()` on every types of all messages.
|
||||
|
||||
```lua
|
||||
print(pb.type "MyType")
|
||||
|
||||
-- list all types that loaded into pb
|
||||
for name, basename, type in pb.types() do
|
||||
print(name, basename, type)
|
||||
end
|
||||
```
|
||||
|
||||
`pb.field()` returns information of the specified field for one type:
|
||||
|
||||
- name: the name of the field
|
||||
- number: number of field in the schema
|
||||
- type: field type
|
||||
- default value: if no default value, nil
|
||||
- `"packed"`|`"repeated"`| `"optional"`: label of the field, optional or repeated, required is not supported
|
||||
- [oneof_name, oneof_index]: if this is a `oneof` field, this is the `oneof` name and index
|
||||
|
||||
And `pb.fields()` iterates all fields in a message:
|
||||
|
||||
```lua
|
||||
print(pb.field("MyType", "the_first_field"))
|
||||
|
||||
-- notice that you needn't receive all return values from iterator
|
||||
for name, number, type in pb.fields "MyType" do
|
||||
print(name, number, type)
|
||||
end
|
||||
```
|
||||
|
||||
`pb.enum()` maps from enum name and value:
|
||||
|
||||
```lua
|
||||
protoc:load [[
|
||||
enum Color { Red = 1; Green = 2; Blue = 3 }
|
||||
]]
|
||||
print(pb.enum("Color", "Red")) --> 1
|
||||
print(pb.enum("Color", 2)) --> "Green"
|
||||
```
|
||||
|
||||
#### Default Values
|
||||
|
||||
Using `pb.defaults()` to get a table with all default values from a message. this table will be used as the metatable of the corresponding decoded message table when setting `use_default_metatable` option.
|
||||
|
||||
```lua
|
||||
check_load [[
|
||||
message TestDefault {
|
||||
optional int32 defaulted_int = 10 [ default = 777 ];
|
||||
optional bool defaulted_bool = 11 [ default = true ];
|
||||
optional string defaulted_str = 12 [ default = "foo" ];
|
||||
optional float defaulted_num = 13 [ default = 0.125 ];
|
||||
} ]]
|
||||
print(require "serpent".block(pb.defaults "TestDefault"))
|
||||
-- output:
|
||||
-- {
|
||||
-- defaulted_bool = true,
|
||||
-- defaulted_int = 777,
|
||||
-- defaulted_num = 0.125,
|
||||
-- defaulted_str = "foo"
|
||||
-- } --[[table: 0x7f8c1e52b050]]
|
||||
|
||||
```
|
||||
|
||||
#### Hooks
|
||||
|
||||
If set `pb.option "enable_hooks"`, the hook function will be enabled. you could use `pb.hook()` and `pb.encode_hook` to set or get a decode or encode hook function, respectively: call it with type name directly get current setted hook; call it with two arguments to set a hook; and call it with `nil` as the second argument to remove the hook. in all case, the original one will be returned.
|
||||
|
||||
After the hook function setted and hook enabled, the decode function will be called *after* a message get decoded and encode functions will be called *before* the message is encoded. So you could get all values in the table passed to hook function. That's the only argument of hook.
|
||||
|
||||
If you need type name in hook functions, use this helper:
|
||||
|
||||
```lua
|
||||
local function make_hook(name, func)
|
||||
return pb.hook(name, function(t)
|
||||
return func(name, t)
|
||||
end)
|
||||
end
|
||||
```
|
||||
|
||||
#### Options
|
||||
|
||||
Setting options to change the behavior of other routines.
|
||||
These options are supported currently:
|
||||
|
||||
| Option | Description |
|
||||
| ----------------------- | ------------------------------------------------------------ |
|
||||
| `enum_as_name` | set value to enum name when decode a enum **(default)** |
|
||||
| `enum_as_value` | set value to enum value when decode a enum |
|
||||
| `int64_as_number` | set value to integer when it fit into uint32, otherwise return a number **(default)** |
|
||||
| `int64_as_string` | same as above, but return a string instead |
|
||||
| `int64_as_hexstring` | same as above, but return a hexadigit string instead |
|
||||
| `auto_default_values` | act as `use_default_values` for proto3 and act as `no_default_values` for the others **(default)** |
|
||||
| `no_default_values` | do not default values for decoded message table |
|
||||
| `use_default_values` | set default values by copy values from default table before decode |
|
||||
| `use_default_metatable` | set default values by set table from `pb.default()` as the metatable |
|
||||
| `enable_hooks` | `pb.decode` will call `pb.hooks()` hook functions |
|
||||
| `disable_hooks` | `pb.decode` do not call hooks **(default)** |
|
||||
| `encode_default_values` | default values also encode |
|
||||
| `no_encode_default_values` | do not encode default values **(default)** |
|
||||
| `decode_default_array` | work with `no_default_values`,decode null to empty table for array |
|
||||
| `no_decode_default_array` | work with `no_default_values`,decode null to nil for array **(default)** |
|
||||
| `encode_order` | guarantees the same message will be encoded into the same result with the same schema and the same data (but the order itself is not specified) |
|
||||
| `no_encode_order` | do not have guarantees about encode orders **(default)** |
|
||||
| `decode_default_message` | decode null message to default message table |
|
||||
| `no_decode_default_message` | decode null message to null **(default)** |
|
||||
|
||||
*Note*: The string returned by `int64_as_string` or `int64_as_hexstring` will prefix a `'#'` character. Because Lua may convert between string with number, prefix a `'#'` makes Lua return the string as-is.
|
||||
|
||||
all routines in all module accepts `'#'` prefix `string`/`hex string` as arguments regardless of the option setting.
|
||||
|
||||
#### Multiple State
|
||||
|
||||
`pb` module support multiple states. A state is a database that contains all type information of registered messages. You can retrieve current state by `pb.state()`, or set new state by `pb.state(newstate)`.
|
||||
|
||||
Use `pb.state(nil)` to discard current state, but not to set a new one (the following routines call that use the state will create a new default state automatedly). Use `pb.state()` to retrieve current state without setting a new one. e.g.
|
||||
|
||||
```lua
|
||||
local old = pb.state(nil)
|
||||
-- if you use protoc.lua, call protoc.reload() here.
|
||||
assert(pb.load(...))
|
||||
-- do someting ...
|
||||
pb.state(old)
|
||||
```
|
||||
|
||||
Notice that if you use `protoc.lua` module, it will register some message to the state, so you should call `proto.reload()` after setting a new state.
|
||||
|
||||
|
||||
|
||||
### `pb.io` Module
|
||||
|
||||
`pb.io` module reads binary data from a file or `stdin`/`stdout`, `pb.io.read()` reads binary data from a file, or `stdin` if no file name given as the first parameter.
|
||||
|
||||
`pb.io.write()` and `pb.io.dump()` are same as Lua's `io.write()` except they write binary data. the former writes data to `stdout`, and the latter writes data to a file specified by the first parameter as the file name.
|
||||
|
||||
All these functions return a true value when success, and return `nil, errmsg` when an error occurs.
|
||||
|
||||
| Function | Returns | Description |
|
||||
| ---------------------- | ------- | ----------------------------------- |
|
||||
| `io.read()` | string | read all binary data from `stdin` |
|
||||
| `io.read(string)` | string | read all binary data from file name |
|
||||
| `io.write(...)` | true | write binary data to `stdout` |
|
||||
| `io.dump(string, ...)` | string | write binary data to file name |
|
||||
|
||||
|
||||
|
||||
### `pb.conv` Module
|
||||
|
||||
`pb.conv` provide functions to convert between numbers.
|
||||
|
||||
| Encode Function | Decode Function |
|
||||
| ---------------------- | ---------------------- |
|
||||
| `conv.encode_int32()` | `conv.decode_int32()` |
|
||||
| `conv.encode_uint32()` | `conv.decode_uint32()` |
|
||||
| `conv.encode_sint32()` | `conv.decode_sint32()` |
|
||||
| `conv.encode_sint64()` | `conv.decode_sint64()` |
|
||||
| `conv.encode_float()` | `conv.decode_float()` |
|
||||
| `conv.encode_double()` | `conv.decode_double()` |
|
||||
|
||||
|
||||
|
||||
### `pb.slice` Module
|
||||
|
||||
Slice object parse binary protobuf data in a low-level way. Use `slice.new()` to create a slice object, with the optional offset `i` and `j` to access a subpart of the original data (named a *view*).
|
||||
|
||||
As protobuf usually nest sub message with in a range of slice, a slice object has a stack itself to support this. Calling `s:enter(i, j)` saves current position and enters next level with the optional offset `i` and `j` just as `slice.new()`. calling `s:leave()` restore the prior view. `s:level()` returns the current level, and `s:level(n)` returns the current position, the start and the end position information of the `n`th level. calling `s:enter()` without parameter will read a length delimited type value from the slice and enter the view in reading value. Using `#a` to get the count of bytes remains in current view.
|
||||
|
||||
```lua
|
||||
local s = slice.new("<data here>")
|
||||
local tag = s:unpack "v"
|
||||
if tag%8 == 2 then -- tag has a type of string/bytes? maybe it's a sub-message.
|
||||
s:enter() -- read following bytes value, and enter the view of bytes value.
|
||||
-- do something with bytes value, e.g. reads a lot of fixed32 integers from bytes.
|
||||
local t = {}
|
||||
while #s > 0 do
|
||||
t[#t+1] = s:unpack "d"
|
||||
end
|
||||
s:leave() -- after done, leave bytes value and ready to read next value.
|
||||
end
|
||||
```
|
||||
|
||||
To read values from slice, use `slice.unpack()`, it use a format string to control how to read into a slice as below table (same format character are also used in `buffer.pack()`). Notice that you can use `pb.typefmt()` to convert between format and protobuf type names (returned from `pb.field()`).
|
||||
|
||||
| Format | Description |
|
||||
| ------ | ------------------------------------------------------------ |
|
||||
| v | variable Int value |
|
||||
| d | 4 bytes fixed32 value |
|
||||
| q | 8 bytes fixed64 value |
|
||||
| s | length delimited value, usually a `string`, `bytes` or `message` in protobuf. |
|
||||
| c | receive a extra number parameter `count` after the format, and reads `count` bytes in slice. |
|
||||
| b | variable int value as a Lua `boolean` value. |
|
||||
| f | 4 bytes `fixed32` value as floating point `number` value. |
|
||||
| F | 8 bytes `fixed64` value as floating point `number` value. |
|
||||
| i | variable int value as signed int value, i.e. `int32` |
|
||||
| j | variable int value as zig-zad encoded signed int value, i.e.`sint32` |
|
||||
| u | variable int value as unsigned int value, i.e. `uint32` |
|
||||
| x | 4 bytes fixed32 value as unsigned fixed32 value, i.e.`fixed32` |
|
||||
| y | 4 bytes fixed32 value as signed fixed32 value, i.e. `sfixed32` |
|
||||
| I | variable int value as signed int value, i.e.`int64` |
|
||||
| J | variable int value as zig-zad encoded signed int value, i.e. `sint64` |
|
||||
| U | variable int value and treat it as `uint64` |
|
||||
| X | 8 bytes fixed64 value as unsigned fixed64 value, i.e. `fixed64` |
|
||||
| Y | 8 bytes fixed64 value as signed fixed64 value, i.e. `sfixed64` |
|
||||
|
||||
And extra format can be used to control the read cursor in one `slice.unpack()` process:
|
||||
|
||||
| Format | Description |
|
||||
| ------ | ------------------------------------------------------------ |
|
||||
| @ | returns current cursor position in the slice, related with the beginning of the current view. |
|
||||
| * | set the current cursor position to the extra parameter after format string. |
|
||||
| + | set the relate cursor position, i.e. add the extra parameter to the current position. |
|
||||
|
||||
e.g. If you want to read a `varint` value twice, you can write it as:
|
||||
|
||||
```lua
|
||||
local v1, v2 = s:unpack("v*v", 1)
|
||||
-- v: reads a `varint` value
|
||||
-- *: receive the second parameter 1 and set it to the current cursor position, i.e. restore the cursor to the head of the view
|
||||
-- v: reads the first `varint` value again
|
||||
```
|
||||
|
||||
All routines in `pb.slice` module:
|
||||
|
||||
| Function | Returns | Description |
|
||||
| ------------------------- | ------------ | ------------------------------------------------------------ |
|
||||
| `slice.new(data[,i[,j]])` | Slice object | create a new slice object |
|
||||
| `s:delete()` | none | same as `s:reset()`, free it's content |
|
||||
| `tostring(s)` | string | return the string repr of the object |
|
||||
| `#s` | number | returns the count of bytes can read in current view |
|
||||
| `s:result([i[, j]])` | String | return the remaining bytes in current view |
|
||||
| `s:reset([...])` | self | reset object to another data |
|
||||
| `s:level()` | number | returns the count of stored state |
|
||||
| `s:level(number)` | p, i, j | returns the informations of the `n`th stored state |
|
||||
| `s:enter()` | self | reads a bytes value, and enter it's view |
|
||||
| `s:enter(i[, j])` | self | enter a view start at `i` and ends at `j`, includes |
|
||||
| `s:leave([number])` | self, n | leave the number count of level (default 1) and return current level |
|
||||
| `s:unpack(fmt, ...)` | values... | reads values of current view from slice |
|
||||
|
||||
|
||||
|
||||
### `pb.buffer` Module
|
||||
|
||||
Buffer module used to construct a protobuf data format stream in a low-level way. It's just a bytes data buffer. using `buffer.pack()` to append values to the buffer, and `buffer.result()` to get the encoded raw data, or `buffer.tohex()` to get the human-readable hex digit value of data.
|
||||
|
||||
`buffer.pack()` use the same format syntax with `slice.unpack()`, and support `'()'` format means the inner value will be encoded as a length delimited value, i.e. a message value encoded format.
|
||||
|
||||
parenthesis can be nested.
|
||||
|
||||
e.g.
|
||||
|
||||
```lua
|
||||
b:pack("(vvv)", 1, 2, 3) -- get a bytes value that contains three varint value.
|
||||
```
|
||||
|
||||
|
||||
|
||||
`buffer.pack()` also support '#' format, it means prepends a length into the buffer.
|
||||
|
||||
e.g.
|
||||
|
||||
```lua
|
||||
b:pack("#", 5) -- prepends a varint length #b-5+1 at offset 5
|
||||
```
|
||||
|
||||
All routines in `pb.buffer` module:
|
||||
|
||||
| Function | Returns | Description |
|
||||
| ------------------- | ------------- | ------------------------------------------------------------ |
|
||||
| `buffer.new([...])` | Buffer object | create a new buffer object, extra args will passed to `b:reset()` |
|
||||
| `b:delete()` | none | same as `b:reset()`, free it's content |
|
||||
| `tostring(b)` | string | returns the string repr of the object |
|
||||
| `#b` | number | returns the encoded count of bytes in buffer |
|
||||
| `b:reset()` | self | reset to a empty buffer |
|
||||
| `b:reset([...])` | self | resets the buffer and set its content as the concat of it's args |
|
||||
| `b:tohex([i[, j]])` | string | return the string of hexadigit represent of the data, `i` and `j` are ranges in encoded data, includes. Omit it means the whole range |
|
||||
| `b:result([i[,j]])` | string | return the raw data, `i` and `j` are ranges in encoded data, includes,. Omit it means the whole range |
|
||||
| `b:pack(fmt, ...)` | self | encode the values passed to `b:pack()`, use `fmt` to indicate how to encode value |
|
||||
|
||||
|
||||
|
||||
|
545
third_party/lua-protobuf/README.zh.md
vendored
Normal file
545
third_party/lua-protobuf/README.zh.md
vendored
Normal file
|
@ -0,0 +1,545 @@
|
|||
# 在Lua中操作Google protobuf格式数据
|
||||
|
||||
[![Build Status](https://img.shields.io/github/workflow/status/starwing/lua-protobuf/CI)](https://github.com/starwing/lua-protobuf/actions?query=branch%3Amaster)[![Coverage Status](https://img.shields.io/coveralls/github/starwing/lua-protobuf)](https://coveralls.io/github/starwing/lua-protobuf?branch=master)
|
||||
|
||||
[English](https://github.com/starwing/lua-protobuf/blob/master/README.md) | 中文
|
||||
|
||||
---
|
||||
|
||||
Urho3d集成说明:https://note.youdao.com/ynoteshare1/index.html?id=20d06649fab669371140256abd7a362b&type=note
|
||||
|
||||
Unreal SLua集成:https://github.com/zengdelang/slua-unreal-pb
|
||||
|
||||
Unreal UnLua集成:https://github.com/hxhb/unlua-pb
|
||||
|
||||
ToLua集成说明:[链接](http://changxianjie.gitee.io/unitypartner/2019/10/01/tolua中使用protobuf3—集成lua-protobuf/)
|
||||
|
||||
xlua集成:[链接](https://github.com/91Act/build_xlua_with_libs)
|
||||
|
||||
QQ群:485016061 [![lua-protobuf1交流群](https://pub.idqqimg.com/wpa/images/group.png)](https://shang.qq.com/wpa/qunwpa?idkey=d7e2973604a723c4f77d0a837df39be26e15be2c2ec29d5ebfdb64f94e74e6ae)
|
||||
|
||||
本项目提供在 Lua 全版本(5.1+、LuaJIT)下的protobuf 2/3 版本支持。提供了高级的消息编码/解码接口以及底层的protobuf wireformat 二进制数据操作接口。
|
||||
|
||||
使用高级接口,你需要通过 `pb.load()` 接口导入 protobuf 的消息描述文件(schema文件,.proto后缀名)的二进制版本(本质上是schema文件通过官方的 `FileDescriptorSet` 编码得到的二进制pb消息),导入的信息被存储在称为“state”的内存数据库中供消息编码/解码使用。你也可以通过`pb`模块提供的一系列接口来读取这个数据库里的内容。
|
||||
|
||||
要使用底层接口,你需要使用下面几个库提供的功能:
|
||||
|
||||
- `pb.slice`:读取二进制的wireformat信息。
|
||||
- `pb.buffer`:写入二进制wireformat信息。
|
||||
- `pb.conv`:在Lua的数字类型和protobuf提供的一众数字数据类型之间转换。
|
||||
- `pb.io`:用于将`pb`模块用于工具当中使用:通过标准输入输出流读取写入二进制消息。
|
||||
|
||||
另外,为了得到schema文件的二进制版本(一般后缀名是.pb的文件),你需要官方protobuf项目提供的schema编译器二进制`protoc.exe`工具,如果在你的平台下获得这个工具太麻烦,或者你希望能有一个小体积的protobuf编译工具,你可以使用项目自带的另一个独立的纯 Lua库:`protoc.lua`文件。该文件提供了纯Lua实现的schema文件编译器,并集成了通过调用`pb.load()`载入编译结果的方便接口。但是要注意因为这个库是纯Lua实现的,它编译的速度会非常慢,如果你的schema文件太大或者编译的时候遇到了性能瓶颈。还是推荐你通过`protoc.exe`或者在开发时利用`pb`库自己写脚本将schema编译成.pb文件供生产环境使用。
|
||||
|
||||
## 安装
|
||||
|
||||
**注意**:`lua-prootbuf`毕竟是个纯C的Lua库,而Lua库的编译安装是**有门槛**的。如果遇到了问题,建议询问**有Lua的C模块使用经验**的人,或者参阅《Lua程序设计》里的相关内容,预先学习相关知识。
|
||||
|
||||
另外,Lua的C模块是通用的,任何使用Lua的环境下都可以使用,然而XLua等C#环境通常有自己的一套规则,需要一些额外的集成操作。请确认**自己对这些环境集成C模块足够了解**,或者**咨询足够了解的人**获得帮助。这里只提供通用C模块的安装方法。
|
||||
|
||||
最简单的安装方法是使用Lua生态的包管理器`luarocks`进行安装(注意,这样**依然需要你的电脑上装有C编译器**,如果安装失败,你应该首先检查你的`luarocks`能否正常工作,即,能否正常安装其他Lua C模块):
|
||||
|
||||
```shell
|
||||
luarocks install lua-protobuf
|
||||
```
|
||||
|
||||
你也可以使用`luarocks`从源码安装(这样装的版本会更新一些):
|
||||
|
||||
```shell
|
||||
git clone https://github.com/starwing/lua-protobuf
|
||||
luarocks make rockspecs/lua-protobuf-scm-1.rockspec
|
||||
```
|
||||
|
||||
如果你没有/不会配置`luarocks`,有一个Python写的方便脚本可以在你的电脑上安装`luarocks`——还是需要你有能正常运行的C编译器——当然,也需要你有Python。
|
||||
|
||||
```shell
|
||||
pip install hererocks
|
||||
git clone https://github.com/starwing/lua-protobuf
|
||||
hererocks -j 2.0 -rlatest .
|
||||
bin/luarocks make lua-protobuf/rockspecs/lua-protobuf-scm-1.rockspec CFLAGS="-fPIC -Wall -Wextra" LIBFLAGS="-shared"
|
||||
cp protoc.lua pb.so ..
|
||||
```
|
||||
|
||||
如果你想尝试Hard模式的话,自己手动编译也是可行的。
|
||||
|
||||
这是macOS的编译命令行:
|
||||
|
||||
```shell
|
||||
gcc -O2 -shared -undefined dynamic_lookup pb.c -o pb.so
|
||||
```
|
||||
|
||||
Linux的:
|
||||
|
||||
```shell
|
||||
gcc -O2 -shared -fPIC pb.c -o pb.so
|
||||
```
|
||||
|
||||
Windows的(注意那个Lua_BUILD_AS_DLL!在Windows上必须带这个预处理宏编译):
|
||||
|
||||
```shell
|
||||
cl /O2 /LD /Fepb.dll /I Lua53\include /DLUA_BUILD_AS_DLL pb.c Lua53\lib\lua53.lib
|
||||
```
|
||||
|
||||
## 样例
|
||||
|
||||
```lua
|
||||
local pb = require "pb"
|
||||
local protoc = require "protoc"
|
||||
|
||||
-- 直接载入schema (这么写只是方便, 生产环境推荐使用 protoc.new() 接口)
|
||||
assert(protoc:load [[
|
||||
message Phone {
|
||||
optional string name = 1;
|
||||
optional int64 phonenumber = 2;
|
||||
}
|
||||
message Person {
|
||||
optional string name = 1;
|
||||
optional int32 age = 2;
|
||||
optional string address = 3;
|
||||
repeated Phone contacts = 4;
|
||||
} ]])
|
||||
|
||||
-- lua 表数据
|
||||
local data = {
|
||||
name = "ilse",
|
||||
age = 18,
|
||||
contacts = {
|
||||
{ name = "alice", phonenumber = 12312341234 },
|
||||
{ name = "bob", phonenumber = 45645674567 }
|
||||
}
|
||||
}
|
||||
|
||||
-- 将Lua表编码为二进制数据
|
||||
local bytes = assert(pb.encode("Person", data))
|
||||
print(pb.tohex(bytes))
|
||||
|
||||
-- 再解码回Lua表
|
||||
local data2 = assert(pb.decode("Person", bytes))
|
||||
print(require "serpent".block(data2))
|
||||
|
||||
```
|
||||
|
||||
## 使用案例
|
||||
|
||||
[![零境交错](https://img.tapimg.com/market/images/e59627dc9039ff22ba7d000b5c9fe7f6.jpg?imageView2/2/h/560/q/40/format/jpg/interlace/1/ignore-error/1)](http://djwk.qq.com)
|
||||
|
||||
|
||||
|
||||
## 接口文档
|
||||
|
||||
请注意接口文档有些是`.`有些是`:`!`.`代表这是静态函数,直接调用即可,`:`代表这是一个方法,需要在一个对象上调用。
|
||||
|
||||
### `protoc` 模块
|
||||
|
||||
| 接口 | 返回 | 描述 |
|
||||
| --------------- | ------- | --------------------------------------------- |
|
||||
| `protoc.new()` | 编译器对象 | 创建一个新的编译器对象 |
|
||||
| `protoc.reload()` | `true` | 重新载入谷歌标准的schema信息(编译需要用到) |
|
||||
| `p:parse(string[, filename])` | table | 将文本schema信息转换成 `DescriptorProto` 消息的Lua表 |
|
||||
| `p:compile(string[, filename])` | string | 将文本schema信息转换成二进制.pb文件数据 |
|
||||
| `p:load(string[, filename])` | `true` | 将文本schema信息转换后,调用`pb.load()`载入内存数据库 |
|
||||
| `p.loaded` | table | 一个包含了所有已载入的 `DescriptorProto` 表的缓存表 |
|
||||
| `p.unknown_import` | 详情见下 | 处理schema里`import`语句找不到文件的回调 |
|
||||
| `p.unknown_type` | 详情见下 | 处理schema里未知类型的回调 |
|
||||
| `p.include_imports` | bool | 编译结果中包含所有`import`的文件 |
|
||||
|
||||
要编译一个文本的schema信息,首先,生成一个编译器实例。一个编译器实例会记住你用它编译的每一个scehma文件,从而能够正确处理schema之间的import关系:
|
||||
|
||||
```lua
|
||||
local p = protoc.new()
|
||||
```
|
||||
|
||||
生成了编译器实例之后,可以给编译器实例设置一些选项或者回调:
|
||||
|
||||
```lua
|
||||
-- 设置回调……
|
||||
p.unknown_import = function(self, module_name) ... end
|
||||
p.unknown_type = function(self, type_name) ... end
|
||||
-- ……和选项
|
||||
p.include_imports = true
|
||||
```
|
||||
|
||||
`unknown_import`和`unknown_type`可以被设置成多种类型的值:如果被设置成`true`,则所有不存在的模块或者消息类型会自动生成一个默认值(空模块/空消息)而不会报错。`pb.load()`会自动处理空消息的合并,因此这样载入信息也不会出错。如果设置一个字符串值,那么这个字符串是一个Lua的正则表达式,满足正则表达式的模块或者消息类型会被设置成默认值,而不满足的则会报错:
|
||||
|
||||
```lua
|
||||
p.unknown_type = "Foo.*"
|
||||
```
|
||||
|
||||
上面的选项意味着所有`Foo`包里的消息会被当作好像已经载入了,即使没找到也不会报错。
|
||||
|
||||
如果这些回调被设置成一个函数,这个函数会在找不到模块/消息的时候被调用。调用的时候会传入找不到的模块/消息的名字,你需要返回一个`DescriptorProto`数据表(对模块而言),或者一个类型的名字和这个类型的实际分类,比如说`message`或者`enum`,如下所示:
|
||||
|
||||
```lua
|
||||
function p:unknown_import(name)
|
||||
-- 如果找不到 "foo.proto" 文件而调用了这个函数,那就自己手动载入 "my_foo.proto" 文件并返回信息
|
||||
return p:parsefile("my_"..name)
|
||||
end
|
||||
|
||||
function p:unknown_type(name)
|
||||
-- 如果找不到 "Type", 那就把它当 "MyType" 消息编译好了,注意前面那个“.”,那是包名。
|
||||
return ".My"..name, "message"
|
||||
end
|
||||
```
|
||||
|
||||
设置好这些选项或者回调以后,使用`load()`或`compile()`或`parse()`来按照你的需求得到想要的结果。这些函数都需要直接传入scehma的文本内容作为参数,可以可选地多传入当前schema对应的文件名,用于方便schema之间的`import`指令找到对应的文件,但是除非设置了`include_imports`,否则`import`指令即使找到了对应的文件也不会编译/加载对应文件,只是会检查类型引入是否正确。即使没有设置`include_imports`,也可以手动按照拓扑顺序依次load对应文件从而加载所有schema。
|
||||
|
||||
### `pb` 模块
|
||||
|
||||
`pb`模块提供了编码/解码信息的高级接口、内存schema数据库的载入和读取接口,以及其他的一些方便的工具函数。
|
||||
|
||||
下面的表格里给出了`pb`模块里所有的函数,注意表格中的中“返回”一栏中的一些特殊返回值,这些返回值有一些约定俗成的含义:
|
||||
|
||||
- `type`:这个返回值代表返回的是一个代表类型的字符串。`".Foo"`代表没有包名的proto文件里的Foo消息,而`"foo.Foo"`代表写了`package foo;`包名的proto文件里的Foo消息。
|
||||
|
||||
- `data`:一个字符串,或者`pb.Slice`对象或者`pb.Buffer`对象,反正是个能表示二进制数据的东西
|
||||
|
||||
- `iterator`:返回一个能在for in语句里使用的迭代器对象,比如说:
|
||||
|
||||
```lua
|
||||
for name in pb.types() do
|
||||
print(name)
|
||||
end
|
||||
```
|
||||
|
||||
**注意**:只有`pb.load()`通过返回值返回是否出错,你要用`assert(pb.load(...))`去调用这个函数!其他的函数会直接扔一个错误异常,不需要你对返回值调用`assert()`函数。
|
||||
|
||||
| 接口 | 返回 | 描述 |
|
||||
| --------------- | ------- | --------------------------------------------- |
|
||||
| `pb.clear()` | None | 清除所有类型 |
|
||||
| `pb.clear(type)` | None | 清除特定类型 |
|
||||
| `pb.load(data)` | boolean,integer | 将一个二进制schema信息载入内存数据库 |
|
||||
| `pb.encode(type, table)` | string | 将table按照type消息类型进行编码 |
|
||||
| `pb.encode(type, table, b)` | buffer | 同上,但是编码进额外提供的buffer对象里并返回 |
|
||||
| `pb.decode(type, data)` | table | 将二进制data按照type消息类型解码为一个表 |
|
||||
| `pb.decode(type, data, table)` | table | 同上,但是解码到你提供的表里 |
|
||||
| `pb.pack(fmt, ...)` | string | 同 `buffer.pack()` ,但直接用字符串返回二进制数据 |
|
||||
| `pb.unpack(data, fmt, ...)` | values... | 同 `slice.unpack()` 但是接受任何二进制类型数据 |
|
||||
| `pb.types()` | iterator | 遍历内存数据库里所有的消息类型,返回具体信息 |
|
||||
| `pb.type(type)` | 详情见下 | 返回内存数据库特定消息类型的具体信息 |
|
||||
| `pb.fields(type)` | iterator | 遍历特定消息里所有的域,返回具体信息 |
|
||||
| `pb.field(type, string)` | 详情见下 | 返回特定消息里特定域的具体信息 |
|
||||
| `pb.field(type, number)` | 详情见下 | 返回特定消息里特定域的具体信息 |
|
||||
| `pb.typefmt(type)` | String | 得到 protobuf 数据类型名对应的 pack/unpack 的格式字符串 |
|
||||
| `pb.enum(type, string)` | number | 提供特定枚举里的名字,返回枚举数字 |
|
||||
| `pb.enum(type, number)` | string | 提供特定枚举里的数字,返回枚举名字 |
|
||||
| `pb.defaults(type[, boolean])` | table | 获得或设置特定消息类型的默认表 |
|
||||
| `pb.hook(type[, function])` | function | 获得或设置特定消息类型的解码钩子 |
|
||||
| `pb.option(string)` | string | 设置编码或解码的具体选项 |
|
||||
| `pb.state()` | `pb.State` | 返回当前的内存数据库 |
|
||||
| `pb.state(newstate \| nil)` | `pb.State` | 设置或删除当前的内存数据库,返回旧的内存数据库 |
|
||||
|
||||
#### 内存数据库载入 Schema 信息
|
||||
|
||||
`pb.load()` 接受一个二进制的schema数据,并将其载入到内存数据库中。如果载入成功则返回`true`,否则返回`false`,无论成功与否,都会返回读取的二进制数据的字节数。如果载入失败,你可以检查在这个字节位置周围是否有数据错误的情况,比如被`NUL`字符截断等等的问题。
|
||||
|
||||
二进制流中是什么样的schema,就会载入什么样的schema。通常只能载入一个文件。如果需要同时载入多个文件(比如包括import后的文件,或者多个不相干文件),可以通过在使用`protoc.exe`或者`protoc.lua`编译二进制schema的时候编译多个文件,或者使用`include_imports`在二进制数据中包含多个文件的内容实现。注意根据protobuf的特性,直接将多个schema二进制数据连接在一起载入也是可行的。
|
||||
|
||||
|
||||
#### 类型映射
|
||||
|
||||
| Protobuf 类型 | Lua 类型 |
|
||||
| ------------------------------------------------- | ----------------------------------------------------------- |
|
||||
| `double`, `float` | `number` |
|
||||
| `int32`, `uint32`, `fixed32`, `sfixed32`, `sint32` | `number` 或 `integer` (Lua 5.3+) |
|
||||
| `int64`, `uint64`, `fixed64`, `sfixed64`, `sint64` | `number` 或 `"#"` 打头的 `string` 或 `integer` (Lua 5.3+) |
|
||||
| `bool` | `boolean` |
|
||||
| `string`, `bytes` | `string` |
|
||||
| `message` | `table` |
|
||||
| `enum` | `string` 或 `number` |
|
||||
|
||||
#### 内存数据库信息获取
|
||||
|
||||
可以使用`pb.type()`、`pb.types()`、`pb.field()`、`pb.fields()`系列函数获取内存数据库内的消息类型信息。
|
||||
|
||||
内存数据库存储了可以编码/解码的所有的消息类型信息,如果内存数据库中无法查询到对应信息,则编码/解码可能失败。使用*限定后的消息类型*名字就可以获取对应的类型信息。比如`foo` 包里的`Foo`消息的*限定消息类型名字*是`".foo.Foo"`,如果没有包名,则直接在消息名前面加`"."`,比如`".Foo"`就是没有包名的`Foo`消息的限定名称。
|
||||
|
||||
通过调用`pb.type()`,你可以获得下面的信息:
|
||||
|
||||
- name:即限定的消息类型名称(如`".package.TypeName"`)
|
||||
- basename:即去除了包名的消息类型名称(如`"TypeName"`)
|
||||
- type:`"map"` | `"enum"` | `"message"`,消息的实际类型——`MapEntry`类型,或者枚举,或者消息。
|
||||
|
||||
`pb.types()`返回了一个迭代器,就好像对内存数据库里存储的每个类型调用 `pb.type()`一样:
|
||||
|
||||
```lua
|
||||
-- 打印出 MyType 消息的详细信息
|
||||
print(pb.type "MyType")
|
||||
|
||||
-- 列出内存数据库里存储的所有消息类型的信息
|
||||
for name, basename, type in pb.types() do
|
||||
print(name, basename, type)
|
||||
end
|
||||
```
|
||||
|
||||
`pb.field()` 返回了一个特定的消息里的一个域的详细信息:
|
||||
|
||||
- name: 域名
|
||||
- number: schema中该域的对应数字(序号)
|
||||
- type: 域类型
|
||||
- default value: 域的默认值,没有的话是`nil`
|
||||
- `"packed"`|`"repeated"`| `"optional"`:域的标签,注意并不支持`required`,会被当作`optional`
|
||||
- oneof_name:域所属的oneof块的名字,可选
|
||||
- , oneof_index:域所属的oneof块的索引,可选
|
||||
|
||||
`pb.fields()` 会返回一个好像对消息类型里的每个域调用`pb.field()`一样的迭代器对象:
|
||||
|
||||
```lua
|
||||
-- 打印 MyType 消息类型里 the_first_field 域的详细信息
|
||||
print(pb.field("MyType", "the_first_field"))
|
||||
|
||||
-- 遍历 MyType 消息类型里所有的域,注意你并不需要写全所有的详细信息(后面的信息会被忽略掉)
|
||||
for name, number, type in pb.fields "MyType" do
|
||||
print(name, number, type) -- 只需要打印这三个
|
||||
end
|
||||
```
|
||||
|
||||
`pb.enum()` 转换枚举的名字和值:
|
||||
|
||||
```lua
|
||||
protoc:load [[
|
||||
enum Color { Red = 1; Green = 2; Blue = 3 }
|
||||
]]
|
||||
print(pb.enum("Color", "Red")) --> 1
|
||||
print(pb.enum("Color", 2)) --> "Green"
|
||||
```
|
||||
|
||||
其实枚举本身就是一种特殊的“消息类型”,在内存数据库里,枚举和消息其实没什么区别,因此使用`pb.field()`也能做到枚举的名字和值之间的转换,`pb.enum()`这个名字更多的只是语义上的区别,另外因为只返回一个值,可能会相对快一些。
|
||||
|
||||
#### 默认值
|
||||
|
||||
你可以调用`pb.defaults()` 函数得到对应一个消息类型的一张Lua表,这张表存储了该消息类型所有域的默认值。
|
||||
|
||||
`pb.defaults()`函数的第一个参数是指定的消息类型名称,如果可选的第二个参数为true,那么该缓存的默认表会被从内存数据库中清除。
|
||||
|
||||
其实通过`pb.decode("Type")`本身就能得到一张填充了默认值的Lua表。这个函数的目的是,它提供的表会被内存数据库记住,如果你设置了`use_default_metatable`这个选项,那么这个默认值表就会成为对应类型被解码时被自动设置的原表——也就是说,可以支持解码一个空表,但是你能通过元表取得所有域的默认值,示例如下:
|
||||
|
||||
```lua
|
||||
check_load [[
|
||||
message TestDefault {
|
||||
optional int32 defaulted_int = 10 [ default = 777 ];
|
||||
optional bool defaulted_bool = 11 [ default = true ];
|
||||
optional string defaulted_str = 12 [ default = "foo" ];
|
||||
optional float defaulted_num = 13 [ default = 0.125 ];
|
||||
} ]]
|
||||
print(require "serpent".block(pb.defaults "TestDefault"))
|
||||
-- output:
|
||||
-- {
|
||||
-- defaulted_bool = true,
|
||||
-- defaulted_int = 777,
|
||||
-- defaulted_num = 0.125,
|
||||
-- defaulted_str = "foo"
|
||||
-- } --[[table: 0x7f8c1e52b050]]
|
||||
|
||||
```
|
||||
|
||||
#### 钩子
|
||||
|
||||
如果通过`pb.option "enable_hooks"`启用了钩子功能,那么你可以通过`pb.hook()`函数为指定的消息类型设置一个解码钩子。一个钩子是一个会在该消息类型所有的域都被读取完毕之后调用的一个函数。你可以在这个时候对这个已经读取完毕的消息表做任何事。比如设置上一节提到的元表。
|
||||
|
||||
`pb.hook()`的第一个参数是指定的消息类型名称,第二个参数就是钩子函数了。如果第二个参数是`nil`,则这个类型的钩子函数会被清除;任何情况下,`pb.hook()`函数都会返回之前设置过的钩子函数(或者`nil`)。
|
||||
|
||||
钩子函数只有一个参数,即当前已经处理完的消息表。如果你同时还需要消息类型名称,那么可以使用以下工具函数:
|
||||
|
||||
```lua
|
||||
local function make_hook(name, func)
|
||||
return pb.hook(name, function(t)
|
||||
return func(name, t)
|
||||
end)
|
||||
end
|
||||
```
|
||||
|
||||
#### 选项
|
||||
|
||||
你可以通过调用`pb.option()`函数设置选项来改变编码/解码时的行为。
|
||||
|
||||
目前支持的选项如下:
|
||||
|
||||
| 选项 | 描述 |
|
||||
| --------------------- | ----------------------------------------------------- |
|
||||
| `enum_as_name` | 解码枚举的时候,设置值为枚举名 **(默认)** |
|
||||
| `enum_as_value` | 解码枚举的时候,设置值为枚举值数字 |
|
||||
| `int64_as_number` | 如果值的大小小于uint32允许的最大值,则存储整数,否则存储Lua浮点数类型($\le$ Lua 5.2,可能会导致不精确)或者64位整数类型($\ge$ Lua 5.3,这个版本开始才支持64位整数类型) **(默认)** |
|
||||
| `int64_as_string` | 同上,但返回一个前缀`"#"`的字符串以避免精度损失 |
|
||||
| `int64_as_hexstring` | 同上,但返回一个16进制的字符串 |
|
||||
| `auto_default_values` | 对于 proto3,采取 `use_default_values` 的设置;对于其他 protobuf 格式,则采取 `no_default_values` 的设置 **(默认)** |
|
||||
| `no_default_values` | 忽略默认值设置 |
|
||||
| `use_default_values` | 将默认值表复制到解码目标表中来 |
|
||||
| `use_default_metatable` | 将默认值表作为解码目标表的元表使用 |
|
||||
| `enable_hooks` | `pb.decode` 启用钩子功能 |
|
||||
| `disable_hooks` | `pb.decode` 禁用钩子功能 **(默认)** |
|
||||
| `encode_default_values` | 默认值也参与编码 |
|
||||
| `no_encode_default_values` | 默认值不参与编码 **(默认)** |
|
||||
| `decode_default_array` | 配合`no_default_values`选项,对于数组,将空值解码为空表 |
|
||||
| `no_decode_default_array` | 配合`no_default_values`选项,对于数组,将空值解码为nil **(默认)** |
|
||||
| `encode_order` | 保证对相同的schema和data,`pb.encode`编码出的结果一致。注意这个选项会损失效率 |
|
||||
| `no_encode_order` | 不保证对相同输入,`pb.encode`编码出的结果一致。**(默认)** |
|
||||
| `decode_default_message` | 将空子消息解析成默认值表 |
|
||||
| `no_decode_default_message` | 将空子消息解析成 `nil` **(default)** |
|
||||
|
||||
|
||||
*注意*: `int64_as_string` 或 `int64_as_hexstring` 返回的字符串会带一个 `'#'` 字符前缀,因为Lua会自动把数字表示的字符串当作数字使用,从而导致精度损失。带一个前缀会让Lua认为这个字符串并不是数字,从而避免了Lua的自动转换。
|
||||
|
||||
本模块中所有接受数字参数的函数都支持使用带`'#'`前缀的字符串用于表示数字,无论是否开启了相关的选项都是如此。如果需要表格中提供的数字,也同样支持使用前缀字符串指定。
|
||||
|
||||
#### 多内存数据库
|
||||
|
||||
`pb` 模块支持同时存在多个内存数据库,但是你每次只能使用其中的一个。内存数据库仅仅存储所有的类型。默认值表、选项等等不受影响。你可以通过`pb.state()`函数来获得/设置内存数据库。
|
||||
|
||||
如果要新建一个内存数据库,调用 `pb.state(nil)` 函数清除当前内存数据库(会在返回值中返回),如果在调用`pb.load()`函数载入消息的类型的时候发现当前没有内存数据库,那么载入器会自动创建一个新的内存数据库。从而支持多个内存数据库同时存在。你可以同样使用`pb.state()` 函数来切换这多个内存数据库。
|
||||
|
||||
下面的示例能让你在独立的内存数据库中完成某些操作:
|
||||
|
||||
```lua
|
||||
local old = pb.state(nil) -- 清空当前内存数据库
|
||||
-- 如果要使用 protoc.lua, 这里还需要调用 protoc.reload() 函数
|
||||
assert(pb.load(...)) -- 载入新的消息类型信息
|
||||
-- 开始编码/解码 ...
|
||||
pb.state(old) -- 恢复旧的内存数据库(并丢弃刚才新建的那个)
|
||||
```
|
||||
|
||||
需要注意的是 `protoc.Lua` 模块会注册一些Google标准消息类型到内存数据库中,因此一定要记得在创建新的内存数据库之后,调用 `proto.reload()` 函数恢复这些信息。
|
||||
|
||||
### `pb.io` 模块
|
||||
|
||||
`pb.io` 模块从文件或者 `stdin`/`stdout`中读取或者写入二进制数据。提供这个模块的目的是在Windows下,Lua没有二进制读写标准输入输出的能力。然而要实现一个官方的`protoc`插件则必须能够读写二进制的标准输入输出流。因为官方的`protoc`找到插件以后会用插件启动新进程,然后把读取编译好的proto文件的内容用二进制的`FileDescriptorSet`消息的格式发给新进程的`stdin`。所以提供了这个插件,才可以用纯Lua写官方的插件。
|
||||
|
||||
`pb.io.read(filename)` 负责从提供的文件名指定的文件里读取二进制数据,如果不提供文件名,那么就直接从 `stdin` 读取。
|
||||
|
||||
`pb.io.write()` 和 `pb.io.dump()` 和Lua标准库里的 `io.write()` 是一样的,只是会写二进制数据。前者写`stdout`,而后者写到第一个参数提供的文件名所指定的文件中。
|
||||
|
||||
这些函数执行成功的时候都会返回`true`,执行失败的时候会返回 `nil, errmsg`,所以调用的时候记得用`assert()`包住以捕获错误。
|
||||
|
||||
| 接口 | 返回 | 描述 |
|
||||
| --------------- | ------- | --------------------------------------------- |
|
||||
| `io.read()` | string | 从 `stdin`读取所有二进制数据 |
|
||||
| `io.read(string)` | string | 从文件中读取所有二进制数据 |
|
||||
| `io.write(...)` | true | 将二进制数据写入 `stdout` |
|
||||
| `io.dump(string, ...)` | string | write binary data to file name |
|
||||
|
||||
### `pb.conv` 模块
|
||||
|
||||
`pb.conv` 能够在Lua和protobuf提供的各种数字类型之间进行转换。如果你要使用底层接口,那么这个模块就会很有用。
|
||||
|
||||
| Encode Function | Decode Function |
|
||||
| ---------------------- | ---------------------- |
|
||||
| `conv.encode_int32()` | `conv.decode_int32()` |
|
||||
| `conv.encode_uint32()` | `conv.decode_uint32()` |
|
||||
| `conv.encode_sint32()` | `conv.decode_sint32()` |
|
||||
| `conv.encode_sint64()` | `conv.decode_sint64()` |
|
||||
| `conv.encode_float()` | `conv.decode_float()` |
|
||||
| `conv.encode_double()` | `conv.decode_double()` |
|
||||
|
||||
### `pb.slice` 模块
|
||||
|
||||
“Slice”是一种类似于“视图”的对象,它代表某个二进制数据的一部分。使用`slice.new()`可以创建一个slice视图,它会自动关联你传给new函数的那个对象,并且在它之上获取一个指针用于读取二进制的底层wireformat信息。
|
||||
|
||||
slice对象最重要的方法是`slice:unpack()`,它的第一个参数是一个格式字符串,每个格式字符代表需要解码的一个类型。具体的格式字符下面会用表格的形式给出,这些格式字符也可以使用`pb.typefmt()`函数从protobuf的基础类型的名字转换而来。请注意,`pb.buffer`模块的重要方法`buffer:pack()`使用的是同一套格式字符:
|
||||
|
||||
| 格式字符 | 描述 |
|
||||
| ------ | ------------------------------------------------------------ |
|
||||
| v | 基础类型,变长的整数类型,1到10个字节(`varint`) |
|
||||
| d | 基础类型,4 字节数字类型 |
|
||||
| q | 基础类型,8 字节数字类型 |
|
||||
| s | 基础类型,带长度数据通常是 `string`, `bytes` 或者 `message` 类型的数据 |
|
||||
| c | fmt之后额外接受一个数字参数 `count`,直接读取接下来的 `count` 个字节 |
|
||||
| b | 布尔类型:`bool` |
|
||||
| f | 4 字节浮点数类型:`float` |
|
||||
| F | 8 字节浮点数类型:`double` |
|
||||
| i | `varint`表示的32位有符号整数:`int32` |
|
||||
| j | `varint`表示的zig-zad 编码的有符号32位整数:`sint32` |
|
||||
| u | `varint`表示的32位无符号整数:`uint32` |
|
||||
| x | 4 字节的无符号32位整数:`fixed32` |
|
||||
| y | 4 字节的有符号32位整数:`sfixed32` |
|
||||
| I | `varint`表示的64位有符号整数:`int64` |
|
||||
| J | `varint`表示的zig-zad 编码的有符号64位整数:`sint64` |
|
||||
| U | `varint`表示的64位无符号整数:`uint64` |
|
||||
| X | 4 字节的无符号32位整数:`fixed32` |
|
||||
| Y | 4 字节的有符号32位整数:`sfixed32` |
|
||||
|
||||
slice对象内部会维护“当前读到哪儿”的位置信息。每当使用unpack读取的时候,会自动指向下一个待读取的偏移,可以使用`#slice` 方法得知“还剩下多少字节的数据没有读”。下面的表给出了操控“当前位置”的“格式字符”——注意,这些格式字符只能用在`unpack`函数里,`pack`是不给用的:
|
||||
|
||||
| 格式字符 | 描述 |
|
||||
| ------ | ------------------------------------------------------------ |
|
||||
| @ | 返回1开始的当前读取位置偏移,以当前视图开始为1 |
|
||||
| * | fmt参数后提供一个额外参数,直接设置偏移为这个参数的值 |
|
||||
| + | fmt参数后提供一个额外参数,设置偏移为加上这个参数以后的值 |
|
||||
|
||||
下面是一个“将一个 `varint` 类型的值读取两次”的例子:
|
||||
```lua
|
||||
local v1, v2 = s:unpack("v*v", 1)
|
||||
-- v: 读取一个 varint
|
||||
-- *: 接受下一个参数(这里是1),将其设置为当前位置:现在读取位置回到一开始了
|
||||
-- v: 现在,把上一个 varint 再读一遍
|
||||
```
|
||||
|
||||
除了读取位置以外,slice还支持“进入”和“退出”视图,也就是视图栈的功能。比如说,你的协议里是一个消息A,套一个消息B,再套一个消息C,你用`slice:new()`可以得到整个消息A的视图,那么在发现消息B出现的时候,你可以通过`s:enter()`先读取一个带长度数据(这个数据读取后被跳过),然后将视图缩小到这个带长度数据内部:也就是消息B的内容。同理可以处理消息C。当处理完之后,调用`s:leave()`可以回到原视图了,注意在读取带长度数据的时候这个数据就被跳过了,这时回到原视图读取位置正好是带长度数据之后,就可以继续处理后续的消息了。
|
||||
|
||||
`s:enter()`还支持接受两个参数i和j直接给出偏移量位置。注意这种情况下,`s:leave()`就不会修改读取位置了——因为并没有读取操作。
|
||||
|
||||
下面是一个使用底层接口读取一个消息的示例:
|
||||
|
||||
```lua
|
||||
local s = slice.new("<data here>")
|
||||
local tag = s:unpack "v"
|
||||
if tag%8 == 2 then -- tag 是 string/bytes 类型?这可能就是个子消息
|
||||
s:enter() -- 读取这个带长度数据,进入数据本身
|
||||
-- 现在可以对这个带长度数据做任何事儿了:比如说,读取一堆的fixed32数据
|
||||
local t = {}
|
||||
while #s > 0 do
|
||||
t[#t+1] = s:unpack "d"
|
||||
end
|
||||
s:leave() -- 搞定了?回到上级视图继续读取接下来的数据
|
||||
end
|
||||
```
|
||||
|
||||
以下是`pb.slice`模块里的所有接口:
|
||||
|
||||
| 接口 | 返回 | 描述 |
|
||||
| --------------- | ------- | --------------------------------------------- |
|
||||
| `slice.new(data[,i[,j]])` | Slice object | 创建一个新的 slice 对象 |
|
||||
| `s:delete()` | none | 和 `s:reset()`相同,重置并释放slice对象引用的内存 |
|
||||
| `tostring(s)` | string | 返回slice的字符串表示信息 |
|
||||
| `#s` | number | 得到当前视图还未读取的字节数 |
|
||||
| `s:result([i[, j]])` | String | 得到当前视图的二进制数据 |
|
||||
| `s:reset([data[,i[,j]]])` | self | 将slice对象重置绑定另一个数据源 |
|
||||
| `s:level()` | number | 返回当前视图栈的深度 |
|
||||
| `s:level(number)` | p, i, j | 返回第n层视图栈的信息(读取位置、视图偏移) |
|
||||
| `s:enter()` | self | 读取一个带长度数据,并将其视图推入视图栈 |
|
||||
| `s:enter(i[, j])` | self | 将[i,j]字节范围的数据推入视图栈 |
|
||||
| `s:leave([number])` | self, n | 离开n层的视图栈(默认离开一层),返回当前视图栈深度 |
|
||||
| `s:unpack(fmt, ...)` | values... | 利用fmt和额外参数,读取当前视图内的信息 |
|
||||
|
||||
### `pb.buffer` 模块
|
||||
|
||||
Buffer模块本质上其实就是一个内存缓存,类似Java的“StringBuilder”的东西。他是一个构建wireformat数据的底层接口。使用`buffer:pack()`可以向缓存里新增数据,使用`buffer:result()`可以得到编码后的二进制数据结构。或者使用`buffer:tohex()`获取人类可读的16进制编码字符串。
|
||||
|
||||
`buffer.pack()` 使用和 `slice.unpack()`相同的格式字符,请参见`pb.slice`的文档获取详细的格式字符的表格。除此以外,pack还支持 `'()'`格式字符,用于支持编码嵌套的带长度数据——也就是嵌套消息的结构。括号格式字符是可以嵌套的。下面是一个例子:
|
||||
|
||||
```lua
|
||||
b:pack("(vvv)", 1, 2, 3) -- 获得一个编码了3个varint的带长度数据
|
||||
```
|
||||
|
||||
`buffer.pack()` 也支持 '#' 格式字符:该格式字符会额外读取一个长度参数`oldlen`,并将`buffer`从`oldlen`开始到结束的所有字节数据重新编码为一个带长度数据:
|
||||
|
||||
```lua
|
||||
b:pack("#", 5) -- 将b里从第五个字节开始的所有数据编码为一个带长度数据类型数据
|
||||
```
|
||||
|
||||
这个功能可以用来更方便地编码长度不清楚的嵌套子消息:先读取一个当前长度,然后直接将子消息编码进buffer,一旦编码结束,就编码一个`"#"`格式,这样之前编码的所有信息就自动成为了一个带长度数据——即合法的子消息类型数据。
|
||||
|
||||
下面是 `pb.buffer` 模块里所有的接口:
|
||||
|
||||
| 接口 | 返回 | 描述 |
|
||||
| --------------- | ------- | --------------------------------------------- |
|
||||
| `buffer.new([...])` | Buffer object | 创建一个新的buffer对象,额外参数会传递给`b:reset(...)`函数 |
|
||||
| `b:delete()` | none | 即`b:reset()`,释放buffer使用的内存 |
|
||||
| `tostring(b)` | string | 返回buffer的字符串表示信息 |
|
||||
| `#b` | number | 返回buffer中已经完成编码的字节数 |
|
||||
| `b:reset()` | self | 清空buffer中的所有数据 |
|
||||
| `b:reset([...])` | self | 清空buffer,并将其数据设置为所有的参数,如同`io.write()`一样处理其参数 |
|
||||
| `b:tohex([i[, j]])` | string | 返回可选范围(默认是全部)的数据的16进制表示 |
|
||||
| `b:result([i[,j]])` | string | 返回编码后二进制数据。允许只返回一部分。默认返回全部 |
|
||||
| `b:pack(fmt, ...)` | self | 利用fmt和额外参数,将参数里提供的数据编码到buffer中 |
|
||||
|
||||
---
|
||||
|
151
third_party/lua-protobuf/descriptor.pb.lua
vendored
Normal file
151
third_party/lua-protobuf/descriptor.pb.lua
vendored
Normal file
|
@ -0,0 +1,151 @@
|
|||
--lualoader, R"EOF(--
|
||||
|
||||
return
|
||||
"\10\179;\10\16descriptor.proto\18\15google.protobuf\"M\10\17FileDescrip"..
|
||||
"torSet\0188\10\4file\24\1 \3(\0112$.google.protobuf.FileDescriptorProto"..
|
||||
"R\4file\"\228\4\10\19FileDescriptorProto\18\18\10\4name\24\1 \1(\9R\4na"..
|
||||
"me\18\24\10\7package\24\2 \1(\9R\7package\18\30\10\10dependency\24\3 \3"..
|
||||
"(\9R\10dependency\18+\10\17public_dependency\24\10 \3(\5R\16publicDepen"..
|
||||
"dency\18'\10\15weak_dependency\24\11 \3(\5R\14weakDependency\18C\10\12m"..
|
||||
"essage_type\24\4 \3(\0112 .google.protobuf.DescriptorProtoR\11messageTy"..
|
||||
"pe\18A\10\9enum_type\24\5 \3(\0112$.google.protobuf.EnumDescriptorProto"..
|
||||
"R\8enumType\18A\10\7service\24\6 \3(\0112'.google.protobuf.ServiceDescr"..
|
||||
"iptorProtoR\7service\18C\10\9extension\24\7 \3(\0112%.google.protobuf.F"..
|
||||
"ieldDescriptorProtoR\9extension\0186\10\7options\24\8 \1(\0112\28.googl"..
|
||||
"e.protobuf.FileOptionsR\7options\18I\10\16source_code_info\24\9 \1(\011"..
|
||||
"2\31.google.protobuf.SourceCodeInfoR\14sourceCodeInfo\18\22\10\6syntax"..
|
||||
"\24\12 \1(\9R\6syntax\"\185\6\10\15DescriptorProto\18\18\10\4name\24\1 "..
|
||||
"\1(\9R\4name\18;\10\5field\24\2 \3(\0112%.google.protobuf.FieldDescript"..
|
||||
"orProtoR\5field\18C\10\9extension\24\6 \3(\0112%.google.protobuf.FieldD"..
|
||||
"escriptorProtoR\9extension\18A\10\11nested_type\24\3 \3(\0112 .google.p"..
|
||||
"rotobuf.DescriptorProtoR\10nestedType\18A\10\9enum_type\24\4 \3(\0112$."..
|
||||
"google.protobuf.EnumDescriptorProtoR\8enumType\18X\10\15extension_range"..
|
||||
"\24\5 \3(\0112/.google.protobuf.DescriptorProto.ExtensionRangeR\14exten"..
|
||||
"sionRange\18D\10\10oneof_decl\24\8 \3(\0112%.google.protobuf.OneofDescr"..
|
||||
"iptorProtoR\9oneofDecl\0189\10\7options\24\7 \1(\0112\31.google.protobu"..
|
||||
"f.MessageOptionsR\7options\18U\10\14reserved_range\24\9 \3(\0112..googl"..
|
||||
"e.protobuf.DescriptorProto.ReservedRangeR\13reservedRange\18#\10\13rese"..
|
||||
"rved_name\24\10 \3(\9R\12reservedName\26z\10\14ExtensionRange\18\20\10"..
|
||||
"\5start\24\1 \1(\5R\5start\18\16\10\3end\24\2 \1(\5R\3end\18@\10\7optio"..
|
||||
"ns\24\3 \1(\0112&.google.protobuf.ExtensionRangeOptionsR\7options\0267"..
|
||||
"\10\13ReservedRange\18\20\10\5start\24\1 \1(\5R\5start\18\16\10\3end\24"..
|
||||
"\2 \1(\5R\3end\"|\10\21ExtensionRangeOptions\18X\10\20uninterpreted_opt"..
|
||||
"ion\24\231\7 \3(\0112$.google.protobuf.UninterpretedOptionR\19uninterpr"..
|
||||
"etedOption*\9\8\232\7\16\128\128\128\128\2\"\193\6\10\20FieldDescriptor"..
|
||||
"Proto\18\18\10\4name\24\1 \1(\9R\4name\18\22\10\6number\24\3 \1(\5R\6nu"..
|
||||
"mber\18A\10\5label\24\4 \1(\0142+.google.protobuf.FieldDescriptorProto."..
|
||||
"LabelR\5label\18>\10\4type\24\5 \1(\0142*.google.protobuf.FieldDescript"..
|
||||
"orProto.TypeR\4type\18\27\10\9type_name\24\6 \1(\9R\8typeName\18\26\10"..
|
||||
"\8extendee\24\2 \1(\9R\8extendee\18#\10\13default_value\24\7 \1(\9R\12d"..
|
||||
"efaultValue\18\31\10\11oneof_index\24\9 \1(\5R\10oneofIndex\18\27\10\9j"..
|
||||
"son_name\24\10 \1(\9R\8jsonName\0187\10\7options\24\8 \1(\0112\29.googl"..
|
||||
"e.protobuf.FieldOptionsR\7options\18'\10\15proto3_optional\24\17 \1(\8R"..
|
||||
"\14proto3Optional\"\182\2\10\4Type\18\15\10\11TYPE_DOUBLE\16\1\18\14\10"..
|
||||
"\10TYPE_FLOAT\16\2\18\14\10\10TYPE_INT64\16\3\18\15\10\11TYPE_UINT64\16"..
|
||||
"\4\18\14\10\10TYPE_INT32\16\5\18\16\10\12TYPE_FIXED64\16\6\18\16\10\12T"..
|
||||
"YPE_FIXED32\16\7\18\13\10\9TYPE_BOOL\16\8\18\15\10\11TYPE_STRING\16\9"..
|
||||
"\18\14\10\10TYPE_GROUP\16\10\18\16\10\12TYPE_MESSAGE\16\11\18\14\10\10T"..
|
||||
"YPE_BYTES\16\12\18\15\10\11TYPE_UINT32\16\13\18\13\10\9TYPE_ENUM\16\14"..
|
||||
"\18\17\10\13TYPE_SFIXED32\16\15\18\17\10\13TYPE_SFIXED64\16\16\18\15\10"..
|
||||
"\11TYPE_SINT32\16\17\18\15\10\11TYPE_SINT64\16\18\"C\10\5Label\18\18\10"..
|
||||
"\14LABEL_OPTIONAL\16\1\18\18\10\14LABEL_REQUIRED\16\2\18\18\10\14LABEL_"..
|
||||
"REPEATED\16\3\"c\10\20OneofDescriptorProto\18\18\10\4name\24\1 \1(\9R\4"..
|
||||
"name\0187\10\7options\24\2 \1(\0112\29.google.protobuf.OneofOptionsR\7o"..
|
||||
"ptions\"\227\2\10\19EnumDescriptorProto\18\18\10\4name\24\1 \1(\9R\4nam"..
|
||||
"e\18?\10\5value\24\2 \3(\0112).google.protobuf.EnumValueDescriptorProto"..
|
||||
"R\5value\0186\10\7options\24\3 \1(\0112\28.google.protobuf.EnumOptionsR"..
|
||||
"\7options\18]\10\14reserved_range\24\4 \3(\01126.google.protobuf.EnumDe"..
|
||||
"scriptorProto.EnumReservedRangeR\13reservedRange\18#\10\13reserved_name"..
|
||||
"\24\5 \3(\9R\12reservedName\26;\10\17EnumReservedRange\18\20\10\5start"..
|
||||
"\24\1 \1(\5R\5start\18\16\10\3end\24\2 \1(\5R\3end\"\131\1\10\24EnumVal"..
|
||||
"ueDescriptorProto\18\18\10\4name\24\1 \1(\9R\4name\18\22\10\6number\24"..
|
||||
"\2 \1(\5R\6number\18;\10\7options\24\3 \1(\0112!.google.protobuf.EnumVa"..
|
||||
"lueOptionsR\7options\"\167\1\10\22ServiceDescriptorProto\18\18\10\4name"..
|
||||
"\24\1 \1(\9R\4name\18>\10\6method\24\2 \3(\0112&.google.protobuf.Method"..
|
||||
"DescriptorProtoR\6method\0189\10\7options\24\3 \1(\0112\31.google.proto"..
|
||||
"buf.ServiceOptionsR\7options\"\137\2\10\21MethodDescriptorProto\18\18"..
|
||||
"\10\4name\24\1 \1(\9R\4name\18\29\10\10input_type\24\2 \1(\9R\9inputTyp"..
|
||||
"e\18\31\10\11output_type\24\3 \1(\9R\10outputType\0188\10\7options\24\4"..
|
||||
" \1(\0112\30.google.protobuf.MethodOptionsR\7options\0180\10\16client_s"..
|
||||
"treaming\24\5 \1(\8:\5falseR\15clientStreaming\0180\10\16server_streami"..
|
||||
"ng\24\6 \1(\8:\5falseR\15serverStreaming\"\145\9\10\11FileOptions\18!"..
|
||||
"\10\12java_package\24\1 \1(\9R\11javaPackage\0180\10\20java_outer_class"..
|
||||
"name\24\8 \1(\9R\18javaOuterClassname\0185\10\19java_multiple_files\24"..
|
||||
"\10 \1(\8:\5falseR\17javaMultipleFiles\18D\10\29java_generate_equals_an"..
|
||||
"d_hash\24\20 \1(\8B\2\24\1R\25javaGenerateEqualsAndHash\18:\10\22java_s"..
|
||||
"tring_check_utf8\24\27 \1(\8:\5falseR\19javaStringCheckUtf8\18S\10\12op"..
|
||||
"timize_for\24\9 \1(\0142).google.protobuf.FileOptions.OptimizeMode:\5SP"..
|
||||
"EEDR\11optimizeFor\18\29\10\10go_package\24\11 \1(\9R\9goPackage\0185"..
|
||||
"\10\19cc_generic_services\24\16 \1(\8:\5falseR\17ccGenericServices\0189"..
|
||||
"\10\21java_generic_services\24\17 \1(\8:\5falseR\19javaGenericServices"..
|
||||
"\0185\10\19py_generic_services\24\18 \1(\8:\5falseR\17pyGenericServices"..
|
||||
"\0187\10\20php_generic_services\24* \1(\8:\5falseR\18phpGenericServices"..
|
||||
"\18%\10\10deprecated\24\23 \1(\8:\5falseR\10deprecated\18.\10\16cc_enab"..
|
||||
"le_arenas\24\31 \1(\8:\4trueR\14ccEnableArenas\18*\10\17objc_class_pref"..
|
||||
"ix\24$ \1(\9R\15objcClassPrefix\18)\10\16csharp_namespace\24% \1(\9R\15"..
|
||||
"csharpNamespace\18!\10\12swift_prefix\24' \1(\9R\11swiftPrefix\18(\10"..
|
||||
"\16php_class_prefix\24( \1(\9R\14phpClassPrefix\18#\10\13php_namespace"..
|
||||
"\24) \1(\9R\12phpNamespace\0184\10\22php_metadata_namespace\24, \1(\9R"..
|
||||
"\20phpMetadataNamespace\18!\10\12ruby_package\24- \1(\9R\11rubyPackage"..
|
||||
"\18X\10\20uninterpreted_option\24\231\7 \3(\0112$.google.protobuf.Unint"..
|
||||
"erpretedOptionR\19uninterpretedOption\":\10\12OptimizeMode\18\9\10\5SPE"..
|
||||
"ED\16\1\18\13\10\9CODE_SIZE\16\2\18\16\10\12LITE_RUNTIME\16\3*\9\8\232"..
|
||||
"\7\16\128\128\128\128\2J\4\8&\16'\"\227\2\10\14MessageOptions\18<\10\23"..
|
||||
"message_set_wire_format\24\1 \1(\8:\5falseR\20messageSetWireFormat\18L"..
|
||||
"\10\31no_standard_descriptor_accessor\24\2 \1(\8:\5falseR\28noStandardD"..
|
||||
"escriptorAccessor\18%\10\10deprecated\24\3 \1(\8:\5falseR\10deprecated"..
|
||||
"\18\27\10\9map_entry\24\7 \1(\8R\8mapEntry\18X\10\20uninterpreted_optio"..
|
||||
"n\24\231\7 \3(\0112$.google.protobuf.UninterpretedOptionR\19uninterpret"..
|
||||
"edOption*\9\8\232\7\16\128\128\128\128\2J\4\8\4\16\5J\4\8\5\16\6J\4\8\6"..
|
||||
"\16\7J\4\8\8\16\9J\4\8\9\16\10\"\226\3\10\12FieldOptions\18A\10\5ctype"..
|
||||
"\24\1 \1(\0142#.google.protobuf.FieldOptions.CType:\6STRINGR\5ctype\18"..
|
||||
"\22\10\6packed\24\2 \1(\8R\6packed\18G\10\6jstype\24\6 \1(\0142$.google"..
|
||||
".protobuf.FieldOptions.JSType:\9JS_NORMALR\6jstype\18\25\10\4lazy\24\5 "..
|
||||
"\1(\8:\5falseR\4lazy\18%\10\10deprecated\24\3 \1(\8:\5falseR\10deprecat"..
|
||||
"ed\18\25\10\4weak\24\10 \1(\8:\5falseR\4weak\18X\10\20uninterpreted_opt"..
|
||||
"ion\24\231\7 \3(\0112$.google.protobuf.UninterpretedOptionR\19uninterpr"..
|
||||
"etedOption\"/\10\5CType\18\10\10\6STRING\16\0\18\8\10\4CORD\16\1\18\16"..
|
||||
"\10\12STRING_PIECE\16\2\"5\10\6JSType\18\13\10\9JS_NORMAL\16\0\18\13\10"..
|
||||
"\9JS_STRING\16\1\18\13\10\9JS_NUMBER\16\2*\9\8\232\7\16\128\128\128\128"..
|
||||
"\2J\4\8\4\16\5\"s\10\12OneofOptions\18X\10\20uninterpreted_option\24"..
|
||||
"\231\7 \3(\0112$.google.protobuf.UninterpretedOptionR\19uninterpretedOp"..
|
||||
"tion*\9\8\232\7\16\128\128\128\128\2\"\192\1\10\11EnumOptions\18\31\10"..
|
||||
"\11allow_alias\24\2 \1(\8R\10allowAlias\18%\10\10deprecated\24\3 \1(\8:"..
|
||||
"\5falseR\10deprecated\18X\10\20uninterpreted_option\24\231\7 \3(\0112$."..
|
||||
"google.protobuf.UninterpretedOptionR\19uninterpretedOption*\9\8\232\7"..
|
||||
"\16\128\128\128\128\2J\4\8\5\16\6\"\158\1\10\16EnumValueOptions\18%\10"..
|
||||
"\10deprecated\24\1 \1(\8:\5falseR\10deprecated\18X\10\20uninterpreted_o"..
|
||||
"ption\24\231\7 \3(\0112$.google.protobuf.UninterpretedOptionR\19uninter"..
|
||||
"pretedOption*\9\8\232\7\16\128\128\128\128\2\"\156\1\10\14ServiceOption"..
|
||||
"s\18%\10\10deprecated\24! \1(\8:\5falseR\10deprecated\18X\10\20uninterp"..
|
||||
"reted_option\24\231\7 \3(\0112$.google.protobuf.UninterpretedOptionR\19"..
|
||||
"uninterpretedOption*\9\8\232\7\16\128\128\128\128\2\"\224\2\10\13Method"..
|
||||
"Options\18%\10\10deprecated\24! \1(\8:\5falseR\10deprecated\18q\10\17id"..
|
||||
"empotency_level\24\" \1(\0142/.google.protobuf.MethodOptions.Idempotenc"..
|
||||
"yLevel:\19IDEMPOTENCY_UNKNOWNR\16idempotencyLevel\18X\10\20uninterprete"..
|
||||
"d_option\24\231\7 \3(\0112$.google.protobuf.UninterpretedOptionR\19unin"..
|
||||
"terpretedOption\"P\10\16IdempotencyLevel\18\23\10\19IDEMPOTENCY_UNKNOWN"..
|
||||
"\16\0\18\19\10\15NO_SIDE_EFFECTS\16\1\18\14\10\10IDEMPOTENT\16\2*\9\8"..
|
||||
"\232\7\16\128\128\128\128\2\"\154\3\10\19UninterpretedOption\18A\10\4na"..
|
||||
"me\24\2 \3(\0112-.google.protobuf.UninterpretedOption.NamePartR\4name"..
|
||||
"\18)\10\16identifier_value\24\3 \1(\9R\15identifierValue\18,\10\18posit"..
|
||||
"ive_int_value\24\4 \1(\4R\16positiveIntValue\18,\10\18negative_int_valu"..
|
||||
"e\24\5 \1(\3R\16negativeIntValue\18!\10\12double_value\24\6 \1(\1R\11do"..
|
||||
"ubleValue\18!\10\12string_value\24\7 \1(\12R\11stringValue\18'\10\15agg"..
|
||||
"regate_value\24\8 \1(\9R\14aggregateValue\26J\10\8NamePart\18\27\10\9na"..
|
||||
"me_part\24\1 \2(\9R\8namePart\18!\10\12is_extension\24\2 \2(\8R\11isExt"..
|
||||
"ension\"\167\2\10\14SourceCodeInfo\18D\10\8location\24\1 \3(\0112(.goog"..
|
||||
"le.protobuf.SourceCodeInfo.LocationR\8location\26\206\1\10\8Location\18"..
|
||||
"\22\10\4path\24\1 \3(\5B\2\16\1R\4path\18\22\10\4span\24\2 \3(\5B\2\16"..
|
||||
"\1R\4span\18)\10\16leading_comments\24\3 \1(\9R\15leadingComments\18+"..
|
||||
"\10\17trailing_comments\24\4 \1(\9R\16trailingComments\18:\10\25leading"..
|
||||
"_detached_comments\24\6 \3(\9R\23leadingDetachedComments\"\209\1\10\17G"..
|
||||
"eneratedCodeInfo\18M\10\10annotation\24\1 \3(\0112-.google.protobuf.Gen"..
|
||||
"eratedCodeInfo.AnnotationR\10annotation\26m\10\10Annotation\18\22\10\4p"..
|
||||
"ath\24\1 \3(\5B\2\16\1R\4path\18\31\10\11source_file\24\2 \1(\9R\10sour"..
|
||||
"ceFile\18\20\10\5begin\24\3 \1(\5R\5begin\18\16\10\3end\24\4 \1(\5R\3en"..
|
||||
"dB~\10\19com.google.protobufB\16DescriptorProtosH\1Z-google.golang.org/"..
|
||||
"protobuf/types/descriptorpb\248\1\1\162\2\3GPB\170\2\26Google.Protobuf."..
|
||||
"Reflection"
|
||||
|
||||
-- )EOF"
|
297
third_party/lua-protobuf/lexer.lua
vendored
Normal file
297
third_party/lua-protobuf/lexer.lua
vendored
Normal file
|
@ -0,0 +1,297 @@
|
|||
--lualoader, R"EOF(--
|
||||
|
||||
local string = string
|
||||
local tonumber = tonumber
|
||||
local setmetatable = setmetatable
|
||||
local error = error
|
||||
local ipairs = ipairs
|
||||
local io = io
|
||||
local table = table
|
||||
local math = math
|
||||
local assert = assert
|
||||
local tostring = tostring
|
||||
local type = type
|
||||
local insert_tab = table.insert
|
||||
|
||||
local function meta(name, t)
|
||||
t = t or {}
|
||||
t.__name = name
|
||||
t.__index = t
|
||||
return t
|
||||
end
|
||||
|
||||
local function default(t, k, def)
|
||||
local v = t[k]
|
||||
if not v then
|
||||
v = def or {}
|
||||
t[k] = v
|
||||
end
|
||||
return v
|
||||
end
|
||||
|
||||
local Lexer = meta "Lexer" do
|
||||
|
||||
local escape = {
|
||||
a = "\a", b = "\b", f = "\f", n = "\n",
|
||||
r = "\r", t = "\t", v = "\v"
|
||||
}
|
||||
|
||||
local function tohex(x) return string.byte(tonumber(x, 16)) end
|
||||
local function todec(x) return string.byte(tonumber(x, 10)) end
|
||||
local function toesc(x) return escape[x] or x end
|
||||
|
||||
function Lexer.new(name, src)
|
||||
local self = {
|
||||
name = name,
|
||||
src = src,
|
||||
pos = 1
|
||||
}
|
||||
return setmetatable(self, Lexer)
|
||||
end
|
||||
|
||||
function Lexer:__call(patt, pos)
|
||||
return self.src:match(patt, pos or self.pos)
|
||||
end
|
||||
|
||||
function Lexer:test(patt)
|
||||
self:whitespace()
|
||||
local pos = self('^'..patt..'%s*()')
|
||||
if not pos then return false end
|
||||
self.pos = pos
|
||||
return true
|
||||
end
|
||||
|
||||
function Lexer:expected(patt, name)
|
||||
if not self:test(patt) then
|
||||
return self:error((name or ("'"..patt.."'")).." expected")
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function Lexer:pos2loc(pos)
|
||||
local linenr = 1
|
||||
pos = pos or self.pos
|
||||
for start, stop in self.src:gmatch "()[^\n]*()\n?" do
|
||||
if start <= pos and pos <= stop then
|
||||
return linenr, pos - start + 1
|
||||
end
|
||||
linenr = linenr + 1
|
||||
end
|
||||
end
|
||||
|
||||
function Lexer:error(fmt, ...)
|
||||
local ln, co = self:pos2loc()
|
||||
return error(("%s:%d:%d: "..fmt):format(self.name, ln, co, ...))
|
||||
end
|
||||
|
||||
function Lexer:opterror(opt, msg)
|
||||
if not opt then return self:error(msg) end
|
||||
return nil
|
||||
end
|
||||
|
||||
function Lexer:whitespace()
|
||||
local pos, c = self "^%s*()(%/?)"
|
||||
self.pos = pos
|
||||
if c == '' then return self end
|
||||
return self:comment()
|
||||
end
|
||||
|
||||
function Lexer:comment()
|
||||
local pos = self "^%/%/[^\n]*\n?()"
|
||||
if not pos then
|
||||
if self "^%/%*" then
|
||||
pos = self "^%/%*.-%*%/()"
|
||||
if not pos then
|
||||
self:error "unfinished comment"
|
||||
end
|
||||
end
|
||||
end
|
||||
if not pos then return self end
|
||||
self.pos = pos
|
||||
return self:whitespace()
|
||||
end
|
||||
|
||||
function Lexer:line_end(opt)
|
||||
self:whitespace()
|
||||
local pos = self '^[%s;]*%s*()'
|
||||
if not pos then
|
||||
return self:opterror(opt, "';' expected")
|
||||
end
|
||||
self.pos = pos
|
||||
return pos
|
||||
end
|
||||
|
||||
function Lexer:eof()
|
||||
self:whitespace()
|
||||
return self.pos > #self.src
|
||||
end
|
||||
|
||||
function Lexer:keyword(kw, opt)
|
||||
self:whitespace()
|
||||
local ident, pos = self "^([%a_][%w_]*)%s*()"
|
||||
if not ident or ident ~= kw then
|
||||
return self:opterror(opt, "''"..kw..'" expected')
|
||||
end
|
||||
self.pos = pos
|
||||
return kw
|
||||
end
|
||||
|
||||
function Lexer:ident(name, opt)
|
||||
self:whitespace()
|
||||
local b, ident, pos = self "^()([%a_][%w_]*)%s*()"
|
||||
if not ident then
|
||||
return self:opterror(opt, (name or 'name')..' expected')
|
||||
end
|
||||
self.pos = pos
|
||||
return ident, b
|
||||
end
|
||||
|
||||
function Lexer:full_ident(name, opt)
|
||||
self:whitespace()
|
||||
local b, ident, pos = self "^()([%a_][%w_.]*)%s*()"
|
||||
if not ident or ident:match "%.%.+" then
|
||||
return self:opterror(opt, (name or 'name')..' expected')
|
||||
end
|
||||
self.pos = pos
|
||||
return ident, b
|
||||
end
|
||||
|
||||
function Lexer:integer(opt)
|
||||
self:whitespace()
|
||||
local ns, oct, hex, s, pos =
|
||||
self "^([+-]?)(0?)([xX]?)([0-9a-fA-F]+)%s*()"
|
||||
local n
|
||||
if oct == '0' and hex == '' then
|
||||
n = tonumber(s, 8)
|
||||
elseif oct == '' and hex == '' then
|
||||
n = tonumber(s, 10)
|
||||
elseif oct == '0' and hex ~= '' then
|
||||
n = tonumber(s, 16)
|
||||
end
|
||||
if not n then
|
||||
return self:opterror(opt, 'integer expected')
|
||||
end
|
||||
self.pos = pos
|
||||
return ns == '-' and -n or n
|
||||
end
|
||||
|
||||
function Lexer:number(opt)
|
||||
self:whitespace()
|
||||
if self:test "nan%f[%A]" then
|
||||
return 0.0/0.0
|
||||
elseif self:test "inf%f[%A]" then
|
||||
return 1.0/0.0
|
||||
end
|
||||
local ns, d1, s, d2, s2, pos = self "^([+-]?)(%.?)([0-9]+)(%.?)([0-9]*)()"
|
||||
if not ns then
|
||||
return self:opterror(opt, 'floating-point number expected')
|
||||
end
|
||||
local es, pos2 = self("(^[eE][+-]?[0-9]+)%s*()", pos)
|
||||
if d1 == "." and d2 == "." then
|
||||
return self:error "malformed floating-point number"
|
||||
end
|
||||
self.pos = pos2 or pos
|
||||
local n = tonumber(d1..s..d2..s2..(es or ""))
|
||||
return ns == '-' and -n or n
|
||||
end
|
||||
|
||||
function Lexer:quote(opt)
|
||||
self:whitespace()
|
||||
local q, start = self '^(["\'])()'
|
||||
if not start then
|
||||
return self:opterror(opt, 'string expected')
|
||||
end
|
||||
self.pos = start
|
||||
local patt = '()(\\?'..q..')%s*()'
|
||||
while true do
|
||||
local stop, s, pos = self(patt)
|
||||
if not stop then
|
||||
self.pos = start-1
|
||||
return self:error "unfinished string"
|
||||
end
|
||||
self.pos = pos
|
||||
if s == q then
|
||||
return self.src:sub(start, stop-1)
|
||||
:gsub("\\x(%x+)", tohex)
|
||||
:gsub("\\(%d+)", todec)
|
||||
:gsub("\\(.)", toesc)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Lexer:structure(opt)
|
||||
self:whitespace()
|
||||
if not self:test "{" then
|
||||
return self:opterror(opt, 'opening curly brace expected')
|
||||
end
|
||||
local t = {}
|
||||
while not self:test "}" do
|
||||
local pos, name, npos = self "^%s*()(%b[])()"
|
||||
if not pos then
|
||||
name = self:full_ident "field name"
|
||||
else
|
||||
self.pos = npos
|
||||
end
|
||||
self:test ":"
|
||||
local value = self:constant()
|
||||
self:test ","
|
||||
self:line_end "opt"
|
||||
t[name] = value
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
function Lexer:array(opt)
|
||||
self:whitespace()
|
||||
if not self:test "%[" then
|
||||
return self:opterror(opt, 'opening square bracket expected')
|
||||
end
|
||||
local t = {}
|
||||
while not self:test "]" do
|
||||
local value = self:constant()
|
||||
self:test ","
|
||||
t[#t + 1] = value
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
function Lexer:constant(opt)
|
||||
local c = self:full_ident('constant', 'opt') or
|
||||
self:number('opt') or
|
||||
self:quote('opt') or
|
||||
self:structure('opt') or
|
||||
self:array('opt')
|
||||
if not c and not opt then
|
||||
return self:error "constant expected"
|
||||
end
|
||||
return c
|
||||
end
|
||||
|
||||
function Lexer:option_name()
|
||||
local ident
|
||||
if self:test "%(" then
|
||||
ident = self:full_ident "option name"
|
||||
self:expected "%)"
|
||||
else
|
||||
ident = self:ident "option name"
|
||||
end
|
||||
while self:test "%." do
|
||||
ident = ident .. "." .. self:ident()
|
||||
end
|
||||
return ident
|
||||
end
|
||||
|
||||
function Lexer:type_name()
|
||||
if self:test "%." then
|
||||
local id, pos = self:full_ident "type name"
|
||||
return "."..id, pos
|
||||
else
|
||||
return self:full_ident "type name"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return Lexer
|
||||
|
||||
-- )EOF"
|
2971
third_party/lua-protobuf/luaunit.lua
vendored
Normal file
2971
third_party/lua-protobuf/luaunit.lua
vendored
Normal file
File diff suppressed because it is too large
Load diff
2125
third_party/lua-protobuf/pb.c
vendored
Normal file
2125
third_party/lua-protobuf/pb.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
1771
third_party/lua-protobuf/pb.h
vendored
Normal file
1771
third_party/lua-protobuf/pb.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
341
third_party/lua-protobuf/protoc.lua
vendored
Normal file
341
third_party/lua-protobuf/protoc.lua
vendored
Normal file
|
@ -0,0 +1,341 @@
|
|||
--lualoader, R"EOF(--
|
||||
|
||||
local string = string
|
||||
local tonumber = tonumber
|
||||
local setmetatable = setmetatable
|
||||
local error = error
|
||||
local ipairs = ipairs
|
||||
local io = io
|
||||
local table = table
|
||||
local math = math
|
||||
local assert = assert
|
||||
local tostring = tostring
|
||||
local type = type
|
||||
local insert_tab = table.insert
|
||||
|
||||
local function meta(name, t)
|
||||
t = t or {}
|
||||
t.__name = name
|
||||
t.__index = t
|
||||
return t
|
||||
end
|
||||
|
||||
local function default(t, k, def)
|
||||
local v = t[k]
|
||||
if not v then
|
||||
v = def or {}
|
||||
t[k] = v
|
||||
end
|
||||
return v
|
||||
end
|
||||
|
||||
local Lexer = require 'pb.Lexer'
|
||||
|
||||
local Parser = meta "Parser" do
|
||||
Parser.typemap = {}
|
||||
Parser.loaded = {}
|
||||
Parser.paths = { "", "." }
|
||||
|
||||
function Parser.new()
|
||||
local self = {}
|
||||
self.typemap = {}
|
||||
self.loaded = {}
|
||||
self.paths = { "", "." }
|
||||
return setmetatable(self, Parser)
|
||||
end
|
||||
|
||||
function Parser:reset()
|
||||
self.typemap = {}
|
||||
self.loaded = {}
|
||||
return self
|
||||
end
|
||||
|
||||
function Parser:error(msg)
|
||||
return self.lex:error(msg)
|
||||
end
|
||||
|
||||
function Parser:addpath(path)
|
||||
insert_tab(self.paths, path)
|
||||
end
|
||||
|
||||
function Parser:parsefile(name)
|
||||
local info = self.loaded[name]
|
||||
if info then return info end
|
||||
local errors = {}
|
||||
for _, path in ipairs(self.paths) do
|
||||
local fn = path ~= "" and path.."/"..name or name
|
||||
local fh, err = io.open(fn)
|
||||
if fh then
|
||||
local content = fh:read "*a"
|
||||
info = self:parse(content, name)
|
||||
fh:close()
|
||||
return info
|
||||
end
|
||||
insert_tab(errors, err or fn..": ".."unknown error")
|
||||
end
|
||||
local import_fallback = self.unknown_import
|
||||
if import_fallback == true then
|
||||
info = import_fallback
|
||||
elseif import_fallback then
|
||||
info = import_fallback(self, name)
|
||||
end
|
||||
if not info then
|
||||
error("module load error: "..name.."\n\t"..table.concat(errors, "\n\t"))
|
||||
end
|
||||
return info
|
||||
end
|
||||
|
||||
-- parser
|
||||
|
||||
local toplevel = require 'pb.TopLevel'
|
||||
|
||||
local function make_context(self, lex)
|
||||
local ctx = {
|
||||
syntax = "proto2";
|
||||
locmap = {};
|
||||
prefix = ".";
|
||||
lex = lex;
|
||||
}
|
||||
ctx.loaded = self.loaded
|
||||
ctx.typemap = self.typemap
|
||||
ctx.paths = self.paths
|
||||
ctx.proto3_optional =
|
||||
self.proto3_optional or self.experimental_allow_proto3_optional
|
||||
ctx.unknown_type = self.unknown_type
|
||||
ctx.unknown_import = self.unknown_import
|
||||
ctx.on_import = self.on_import
|
||||
|
||||
return setmetatable(ctx, Parser)
|
||||
end
|
||||
|
||||
function Parser:parse(src, name)
|
||||
local loaded = self.loaded[name]
|
||||
if loaded then
|
||||
if loaded == true then
|
||||
error("loop loaded: "..name)
|
||||
end
|
||||
return loaded
|
||||
end
|
||||
|
||||
name = name or "<input>"
|
||||
self.loaded[name] = true
|
||||
local lex = Lexer.new(name, src)
|
||||
local ctx = make_context(self, lex)
|
||||
local info = { name = lex.name, syntax = ctx.syntax }
|
||||
|
||||
local syntax = lex:keyword('syntax', 'opt')
|
||||
if syntax then
|
||||
info.syntax = lex:expected '=' :quote()
|
||||
ctx.syntax = info.syntax
|
||||
lex:line_end()
|
||||
end
|
||||
|
||||
while not lex:eof() do
|
||||
local ident = lex:ident()
|
||||
local top_parser = toplevel[ident]
|
||||
if top_parser then
|
||||
top_parser(ctx, lex, info)
|
||||
else
|
||||
lex:error("unknown keyword '"..ident.."'")
|
||||
end
|
||||
lex:line_end "opt"
|
||||
end
|
||||
self.loaded[name] = name ~= "<input>" and info or nil
|
||||
return ctx:resolve(lex, info)
|
||||
end
|
||||
|
||||
-- resolver
|
||||
|
||||
local function empty() end
|
||||
|
||||
local function iter(t, k)
|
||||
local v = t[k]
|
||||
if v then return ipairs(v) end
|
||||
return empty
|
||||
end
|
||||
|
||||
local function check_dup(self, lex, typ, map, k, v)
|
||||
local old = map[v[k]]
|
||||
if old then
|
||||
local ln, co = lex:pos2loc(self.locmap[old])
|
||||
lex:error("%s '%s' exists, previous at %d:%d",
|
||||
typ, v[k], ln, co)
|
||||
end
|
||||
map[v[k]] = v
|
||||
end
|
||||
|
||||
local function check_type(self, lex, tname)
|
||||
if tname:match "^%." then
|
||||
local t = self.typemap[tname]
|
||||
if not t then
|
||||
return lex:error("unknown type '%s'", tname)
|
||||
end
|
||||
return t, tname
|
||||
end
|
||||
local prefix = self.prefix
|
||||
for i = #prefix+1, 1, -1 do
|
||||
local op = prefix[i]
|
||||
prefix[i] = tname
|
||||
local tn = table.concat(prefix, ".", 1, i)
|
||||
prefix[i] = op
|
||||
local t = self.typemap[tn]
|
||||
if t then return t, tn end
|
||||
end
|
||||
local tn, t
|
||||
local type_fallback = self.unknown_type
|
||||
if type_fallback then
|
||||
if type_fallback == true then
|
||||
tn = true
|
||||
elseif type(type_fallback) == 'string' then
|
||||
tn = tname:match(type_fallback) and true
|
||||
else
|
||||
tn = type_fallback(self, tname)
|
||||
end
|
||||
end
|
||||
if tn then
|
||||
t = types[t or "message"]
|
||||
if tn == true then tn = "."..tname end
|
||||
return t, tn
|
||||
end
|
||||
return lex:error("unknown type '%s'", tname)
|
||||
end
|
||||
|
||||
local function check_field(self, lex, info)
|
||||
if info.extendee then
|
||||
local t, tn = check_type(self, lex, info.extendee)
|
||||
if t ~= types.message then
|
||||
lex:error("message type expected in extension")
|
||||
end
|
||||
info.extendee = tn
|
||||
end
|
||||
if info.type_name then
|
||||
local t, tn = check_type(self, lex, info.type_name)
|
||||
info.type = t
|
||||
info.type_name = tn
|
||||
end
|
||||
end
|
||||
|
||||
local function check_enum(self, lex, info)
|
||||
local names, numbers = {}, {}
|
||||
for _, v in iter(info, 'value') do
|
||||
lex.pos = assert(self.locmap[v])
|
||||
check_dup(self, lex, 'enum name', names, 'name', v)
|
||||
if not (info.options and info.options.allow_alias) then
|
||||
check_dup(self, lex, 'enum number', numbers, 'number', v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function check_message(self, lex, info)
|
||||
insert_tab(self.prefix, info.name)
|
||||
local names, numbers = {}, {}
|
||||
for _, v in iter(info, 'field') do
|
||||
lex.pos = assert(self.locmap[v])
|
||||
check_dup(self, lex, 'field name', names, 'name', v)
|
||||
check_dup(self, lex, 'field number', numbers, 'number', v)
|
||||
check_field(self, lex, v)
|
||||
end
|
||||
for _, v in iter(info, 'nested_type') do
|
||||
check_message(self, lex, v)
|
||||
end
|
||||
for _, v in iter(info, 'extension') do
|
||||
lex.pos = assert(self.locmap[v])
|
||||
check_field(self, lex, v)
|
||||
end
|
||||
self.prefix[#self.prefix] = nil
|
||||
end
|
||||
|
||||
local function check_service(self, lex, info)
|
||||
local names = {}
|
||||
for _, v in iter(info, 'method') do
|
||||
lex.pos = self.locmap[v]
|
||||
check_dup(self, lex, 'rpc name', names, 'name', v)
|
||||
local t, tn = check_type(self, lex, v.input_type)
|
||||
v.input_type = tn
|
||||
if t ~= types.message then
|
||||
lex:error "message type expected in parameter"
|
||||
end
|
||||
t, tn = check_type(self, lex, v.output_type)
|
||||
v.output_type = tn
|
||||
if t ~= types.message then
|
||||
lex:error "message type expected in return"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Parser:resolve(lex, info)
|
||||
self.prefix = { "", info.package }
|
||||
for _, v in iter(info, 'message_type') do
|
||||
check_message(self, lex, v)
|
||||
end
|
||||
for _, v in iter(info, 'enum_type') do
|
||||
check_enum(self, lex, v)
|
||||
end
|
||||
for _, v in iter(info, 'service') do
|
||||
check_service(self, lex, v)
|
||||
end
|
||||
for _, v in iter(info, 'extension') do
|
||||
lex.pos = assert(self.locmap[v])
|
||||
check_field(self, lex, v)
|
||||
end
|
||||
self.prefix = nil
|
||||
return info
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local has_pb, pb = pcall(require, "pb") do
|
||||
if has_pb then
|
||||
function Parser.reload()
|
||||
assert(pb.load(require 'pb.Descriptor'), "load descriptor msg failed")
|
||||
end
|
||||
|
||||
local function do_compile(self, f, ...)
|
||||
if self.include_imports then
|
||||
local old = self.on_import
|
||||
local infos = {}
|
||||
function self.on_import(info)
|
||||
insert_tab(infos, info)
|
||||
end
|
||||
local r = f(...)
|
||||
insert_tab(infos, r)
|
||||
self.on_import = old
|
||||
return { file = infos }
|
||||
end
|
||||
return { file = { f(...) } }
|
||||
end
|
||||
|
||||
function Parser:compile(s, name)
|
||||
if self == Parser then self = Parser.new() end
|
||||
local set = do_compile(self, self.parse, self, s, name)
|
||||
return pb.encode('.google.protobuf.FileDescriptorSet', set)
|
||||
end
|
||||
|
||||
function Parser:compilefile(fn)
|
||||
if self == Parser then self = Parser.new() end
|
||||
local set = do_compile(self, self.parsefile, self, fn)
|
||||
return pb.encode('.google.protobuf.FileDescriptorSet', set)
|
||||
end
|
||||
|
||||
function Parser:load(s, name)
|
||||
if self == Parser then self = Parser.new() end
|
||||
local ret, pos = pb.load(self:compile(s, name))
|
||||
if ret then return ret, pos end
|
||||
error("load failed at offset "..pos)
|
||||
end
|
||||
|
||||
function Parser:loadfile(fn)
|
||||
if self == Parser then self = Parser.new() end
|
||||
local ret, pos = pb.load(self:compilefile(fn))
|
||||
if ret then return ret, pos end
|
||||
error("load failed at offset "..pos)
|
||||
end
|
||||
|
||||
Parser.reload()
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
return Parser
|
||||
|
||||
-- )EOF"
|
24
third_party/lua-protobuf/rockspecs/lua-protobuf-0.4.0-1.rockspec
vendored
Normal file
24
third_party/lua-protobuf/rockspecs/lua-protobuf-0.4.0-1.rockspec
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
package = "lua-protobuf"
|
||||
version = "0.4.0-1"
|
||||
source = {
|
||||
url = "git+https://github.com/starwing/lua-protobuf.git",
|
||||
tag = "0.4.0"
|
||||
}
|
||||
description = {
|
||||
summary = "protobuf data support for Lua",
|
||||
detailed = [[
|
||||
This project offers a simple C library for basic protobuf wire format encode/decode.
|
||||
]],
|
||||
homepage = "https://github.com/starwing/lua-protobuf",
|
||||
license = "MIT"
|
||||
}
|
||||
dependencies = {
|
||||
"lua >= 5.1"
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
pb = "pb.c",
|
||||
protoc = "protoc.lua"
|
||||
}
|
||||
}
|
27
third_party/lua-protobuf/rockspecs/lua-protobuf-scm-1.rockspec
vendored
Normal file
27
third_party/lua-protobuf/rockspecs/lua-protobuf-scm-1.rockspec
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
package = "lua-protobuf"
|
||||
version = "scm-1"
|
||||
|
||||
source = {
|
||||
url = "git+https://github.com/starwing/lua-protobuf.git",
|
||||
}
|
||||
|
||||
description = {
|
||||
summary = "protobuf data support for Lua",
|
||||
detailed = [[
|
||||
This project offers a simple C library for basic protobuf wire format encode/decode.
|
||||
]],
|
||||
homepage = "https://github.com/starwing/lua-protobuf",
|
||||
license = "MIT",
|
||||
}
|
||||
|
||||
dependencies = {
|
||||
"lua >= 5.1"
|
||||
}
|
||||
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
pb = "pb.c";
|
||||
protoc = "protoc.lua";
|
||||
}
|
||||
}
|
140
third_party/lua-protobuf/serpent.lua
vendored
Normal file
140
third_party/lua-protobuf/serpent.lua
vendored
Normal file
|
@ -0,0 +1,140 @@
|
|||
local n, v = "serpent", "0.30" -- (C) 2012-17 Paul Kulchenko; MIT License
|
||||
local c, d = "Paul Kulchenko", "Lua serializer and pretty printer"
|
||||
local snum = {[tostring(1/0)]='1/0 --[[math.huge]]',[tostring(-1/0)]='-1/0 --[[-math.huge]]',[tostring(0/0)]='0/0'}
|
||||
local badtype = {thread = true, userdata = true, cdata = true}
|
||||
local getmetatable = debug and debug.getmetatable or getmetatable
|
||||
local pairs = function(t) return next, t end -- avoid using __pairs in Lua 5.2+
|
||||
local keyword, globals, G = {}, {}, (_G or _ENV)
|
||||
for _,k in ipairs({'and', 'break', 'do', 'else', 'elseif', 'end', 'false',
|
||||
'for', 'function', 'goto', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat',
|
||||
'return', 'then', 'true', 'until', 'while'}) do keyword[k] = true end
|
||||
for k,v in pairs(G) do globals[v] = k end -- build func to name mapping
|
||||
for _,g in ipairs({'coroutine', 'debug', 'io', 'math', 'string', 'table', 'os'}) do
|
||||
for k,v in pairs(type(G[g]) == 'table' and G[g] or {}) do globals[v] = g..'.'..k end end
|
||||
|
||||
local function s(t, opts)
|
||||
local name, indent, fatal, maxnum = opts.name, opts.indent, opts.fatal, opts.maxnum
|
||||
local sparse, custom, huge = opts.sparse, opts.custom, not opts.nohuge
|
||||
local space, maxl = (opts.compact and '' or ' '), (opts.maxlevel or math.huge)
|
||||
local maxlen, metatostring = tonumber(opts.maxlength), opts.metatostring
|
||||
local iname, comm = '_'..(name or ''), opts.comment and (tonumber(opts.comment) or math.huge)
|
||||
local numformat = opts.numformat or "%.17g"
|
||||
local seen, sref, syms, symn = {}, {'local '..iname..'={}'}, {}, 0
|
||||
local function gensym(val) return '_'..(tostring(tostring(val)):gsub("[^%w]",""):gsub("(%d%w+)",
|
||||
-- tostring(val) is needed because __tostring may return a non-string value
|
||||
function(s) if not syms[s] then symn = symn+1; syms[s] = symn end return tostring(syms[s]) end)) end
|
||||
local function safestr(s) return type(s) == "number" and tostring(huge and snum[tostring(s)] or numformat:format(s))
|
||||
or type(s) ~= "string" and tostring(s) -- escape NEWLINE/010 and EOF/026
|
||||
or ("%q"):format(s):gsub("\010","n"):gsub("\026","\\026") end
|
||||
local function comment(s,l) return comm and (l or 0) < comm and ' --[['..select(2, pcall(tostring, s))..']]' or '' end
|
||||
local function globerr(s,l) return globals[s] and globals[s]..comment(s,l) or not fatal
|
||||
and safestr(select(2, pcall(tostring, s))) or error("Can't serialize "..tostring(s)) end
|
||||
local function safename(path, name) -- generates foo.bar, foo[3], or foo['b a r']
|
||||
local n = name == nil and '' or name
|
||||
local plain = type(n) == "string" and n:match("^[%l%u_][%w_]*$") and not keyword[n]
|
||||
local safe = plain and n or '['..safestr(n)..']'
|
||||
return (path or '')..(plain and path and '.' or '')..safe, safe end
|
||||
local alphanumsort = type(opts.sortkeys) == 'function' and opts.sortkeys or function(k, o, n) -- k=keys, o=originaltable, n=padding
|
||||
local maxn, to = tonumber(n) or 12, {number = 'a', string = 'b'}
|
||||
local function padnum(d) return ("%0"..tostring(maxn).."d"):format(tonumber(d)) end
|
||||
table.sort(k, function(a,b)
|
||||
-- sort numeric keys first: k[key] is not nil for numerical keys
|
||||
return (k[a] ~= nil and 0 or to[type(a)] or 'z')..(tostring(a):gsub("%d+",padnum))
|
||||
< (k[b] ~= nil and 0 or to[type(b)] or 'z')..(tostring(b):gsub("%d+",padnum)) end) end
|
||||
local function val2str(t, name, indent, insref, path, plainindex, level)
|
||||
local ttype, level, mt = type(t), (level or 0), getmetatable(t)
|
||||
local spath, sname = safename(path, name)
|
||||
local tag = plainindex and
|
||||
((type(name) == "number") and '' or name..space..'='..space) or
|
||||
(name ~= nil and sname..space..'='..space or '')
|
||||
if seen[t] then -- already seen this element
|
||||
sref[#sref+1] = spath..space..'='..space..seen[t]
|
||||
return tag..'nil'..comment('ref', level) end
|
||||
-- protect from those cases where __tostring may fail
|
||||
if type(mt) == 'table' then
|
||||
local to, tr = pcall(function() return mt.__tostring(t) end)
|
||||
local so, sr = pcall(function() return mt.__serialize(t) end)
|
||||
if (opts.metatostring ~= false and to or so) then -- knows how to serialize itself
|
||||
seen[t] = insref or spath
|
||||
t = so and sr or tr
|
||||
ttype = type(t)
|
||||
end -- new value falls through to be serialized
|
||||
end
|
||||
if ttype == "table" then
|
||||
if level >= maxl then return tag..'{}'..comment('maxlvl', level) end
|
||||
seen[t] = insref or spath
|
||||
if next(t) == nil then return tag..'{}'..comment(t, level) end -- table empty
|
||||
if maxlen and maxlen < 0 then return tag..'{}'..comment('maxlen', level) end
|
||||
local maxn, o, out = math.min(#t, maxnum or #t), {}, {}
|
||||
for key = 1, maxn do o[key] = key end
|
||||
if not maxnum or #o < maxnum then
|
||||
local n = #o -- n = n + 1; o[n] is much faster than o[#o+1] on large tables
|
||||
for key in pairs(t) do if o[key] ~= key then n = n + 1; o[n] = key end end end
|
||||
if maxnum and #o > maxnum then o[maxnum+1] = nil end
|
||||
if opts.sortkeys and #o > maxn then alphanumsort(o, t, opts.sortkeys) end
|
||||
local sparse = sparse and #o > maxn -- disable sparsness if only numeric keys (shorter output)
|
||||
for n, key in ipairs(o) do
|
||||
local value, ktype, plainindex = t[key], type(key), n <= maxn and not sparse
|
||||
if opts.valignore and opts.valignore[value] -- skip ignored values; do nothing
|
||||
or opts.keyallow and not opts.keyallow[key]
|
||||
or opts.keyignore and opts.keyignore[key]
|
||||
or opts.valtypeignore and opts.valtypeignore[type(value)] -- skipping ignored value types
|
||||
or sparse and value == nil then -- skipping nils; do nothing
|
||||
elseif ktype == 'table' or ktype == 'function' or badtype[ktype] then
|
||||
if not seen[key] and not globals[key] then
|
||||
sref[#sref+1] = 'placeholder'
|
||||
local sname = safename(iname, gensym(key)) -- iname is table for local variables
|
||||
sref[#sref] = val2str(key,sname,indent,sname,iname,true) end
|
||||
sref[#sref+1] = 'placeholder'
|
||||
local path = seen[t]..'['..tostring(seen[key] or globals[key] or gensym(key))..']'
|
||||
sref[#sref] = path..space..'='..space..tostring(seen[value] or val2str(value,nil,indent,path))
|
||||
else
|
||||
out[#out+1] = val2str(value,key,indent,insref,seen[t],plainindex,level+1)
|
||||
if maxlen then
|
||||
maxlen = maxlen - #out[#out]
|
||||
if maxlen < 0 then break end
|
||||
end
|
||||
end
|
||||
end
|
||||
local prefix = string.rep(indent or '', level)
|
||||
local head = indent and '{\n'..prefix..indent or '{'
|
||||
local body = table.concat(out, ','..(indent and '\n'..prefix..indent or space))
|
||||
local tail = indent and "\n"..prefix..'}' or '}'
|
||||
return (custom and custom(tag,head,body,tail,level) or tag..head..body..tail)..comment(t, level)
|
||||
elseif badtype[ttype] then
|
||||
seen[t] = insref or spath
|
||||
return tag..globerr(t, level)
|
||||
elseif ttype == 'function' then
|
||||
seen[t] = insref or spath
|
||||
if opts.nocode then return tag.."function() --[[..skipped..]] end"..comment(t, level) end
|
||||
local ok, res = pcall(string.dump, t)
|
||||
local func = ok and "((loadstring or load)("..safestr(res)..",'@serialized'))"..comment(t, level)
|
||||
return tag..(func or globerr(t, level))
|
||||
else return tag..safestr(t) end -- handle all other types
|
||||
end
|
||||
local sepr = indent and "\n" or ";"..space
|
||||
local body = val2str(t, name, indent) -- this call also populates sref
|
||||
local tail = #sref>1 and table.concat(sref, sepr)..sepr or ''
|
||||
local warn = opts.comment and #sref>1 and space.."--[[incomplete output with shared/self-references skipped]]" or ''
|
||||
return not name and body..warn or "do local "..body..sepr..tail.."return "..name..sepr.."end"
|
||||
end
|
||||
|
||||
local function deserialize(data, opts)
|
||||
local env = (opts and opts.safe == false) and G
|
||||
or setmetatable({}, {
|
||||
__index = function(t,k) return t end,
|
||||
__call = function(t,...) error("cannot call functions") end
|
||||
})
|
||||
local f, res = (loadstring or load)('return '..data, nil, nil, env)
|
||||
if not f then f, res = (loadstring or load)(data, nil, nil, env) end
|
||||
if not f then return f, res end
|
||||
if setfenv then setfenv(f, env) end
|
||||
return pcall(f)
|
||||
end
|
||||
|
||||
local function merge(a, b) if b then for k,v in pairs(b) do a[k] = v end end; return a; end
|
||||
return { _NAME = n, _COPYRIGHT = c, _DESCRIPTION = d, _VERSION = v, serialize = s,
|
||||
load = deserialize,
|
||||
dump = function(a, opts) return s(a, merge({name = '_', compact = true, sparse = true}, opts)) end,
|
||||
line = function(a, opts) return s(a, merge({sortkeys = true, comment = true}, opts)) end,
|
||||
block = function(a, opts) return s(a, merge({indent = ' ', sortkeys = true, comment = true}, opts)) end }
|
478
third_party/lua-protobuf/toplevel.lua
vendored
Normal file
478
third_party/lua-protobuf/toplevel.lua
vendored
Normal file
|
@ -0,0 +1,478 @@
|
|||
--lualoader, R"EOF(--
|
||||
|
||||
local string = string
|
||||
local tonumber = tonumber
|
||||
local setmetatable = setmetatable
|
||||
local error = error
|
||||
local ipairs = ipairs
|
||||
local io = io
|
||||
local table = table
|
||||
local math = math
|
||||
local assert = assert
|
||||
local tostring = tostring
|
||||
local type = type
|
||||
local insert_tab = table.insert
|
||||
|
||||
local function meta(name, t)
|
||||
t = t or {}
|
||||
t.__name = name
|
||||
t.__index = t
|
||||
return t
|
||||
end
|
||||
|
||||
local function default(t, k, def)
|
||||
local v = t[k]
|
||||
if not v then
|
||||
v = def or {}
|
||||
t[k] = v
|
||||
end
|
||||
return v
|
||||
end
|
||||
|
||||
local toplevel = {} do
|
||||
|
||||
local labels = { optional = 1; required = 2; repeated = 3 }
|
||||
|
||||
local key_types = {
|
||||
int32 = 5; int64 = 3; uint32 = 13;
|
||||
uint64 = 4; sint32 = 17; sint64 = 18;
|
||||
fixed32 = 7; fixed64 = 6; sfixed32 = 15;
|
||||
sfixed64 = 16; bool = 8; string = 9;
|
||||
}
|
||||
|
||||
local com_types = {
|
||||
group = 10; message = 11; enum = 14;
|
||||
}
|
||||
|
||||
local types = {
|
||||
double = 1; float = 2; int32 = 5;
|
||||
int64 = 3; uint32 = 13; uint64 = 4;
|
||||
sint32 = 17; sint64 = 18; fixed32 = 7;
|
||||
fixed64 = 6; sfixed32 = 15; sfixed64 = 16;
|
||||
bool = 8; string = 9; bytes = 12;
|
||||
group = 10; message = 11; enum = 14;
|
||||
}
|
||||
|
||||
local function register_type(self, lex, tname, typ)
|
||||
if not tname:match "%."then
|
||||
tname = self.prefix..tname
|
||||
end
|
||||
if self.typemap[tname] then
|
||||
return lex:error("type %s already defined", tname)
|
||||
end
|
||||
self.typemap[tname] = typ
|
||||
end
|
||||
|
||||
local function type_info(lex, tname)
|
||||
local tenum = types[tname]
|
||||
if com_types[tname] then
|
||||
return lex:error("invalid type name: "..tname)
|
||||
elseif tenum then
|
||||
tname = nil
|
||||
end
|
||||
return tenum, tname
|
||||
end
|
||||
|
||||
local function map_info(lex)
|
||||
local keyt = lex:ident "key type"
|
||||
if not key_types[keyt] then
|
||||
return lex:error("invalid key type: "..keyt)
|
||||
end
|
||||
local valt = lex:expected "," :type_name()
|
||||
local name = lex:expected ">" :ident()
|
||||
local ident = name:gsub("^%a", string.upper)
|
||||
:gsub("_(%a)", string.upper).."Entry"
|
||||
local kt, ktn = type_info(lex, keyt)
|
||||
local vt, vtn = type_info(lex, valt)
|
||||
return name, types.message, ident, {
|
||||
name = ident,
|
||||
field = {
|
||||
{
|
||||
name = "key",
|
||||
number = 1;
|
||||
label = labels.optional,
|
||||
type = kt,
|
||||
type_name = ktn
|
||||
},
|
||||
{
|
||||
name = "value",
|
||||
number = 2;
|
||||
label = labels.optional,
|
||||
type = vt,
|
||||
type_name = vtn
|
||||
},
|
||||
},
|
||||
options = { map_entry = true }
|
||||
}
|
||||
end
|
||||
|
||||
local function inline_option(lex, info)
|
||||
if lex:test "%[" then
|
||||
info = info or {}
|
||||
while true do
|
||||
local name = lex:option_name()
|
||||
local value = lex:expected '=' :constant()
|
||||
info[name] = value
|
||||
if lex:test "%]" then
|
||||
return info
|
||||
end
|
||||
lex:expected ','
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function field(self, lex, ident)
|
||||
local name, typ, type_name, map_entry
|
||||
if ident == "map" and lex:test "%<" then
|
||||
name, typ, type_name, map_entry = map_info(lex)
|
||||
self.locmap[map_entry.field[1]] = lex.pos
|
||||
self.locmap[map_entry.field[2]] = lex.pos
|
||||
register_type(self, lex, type_name, types.message)
|
||||
else
|
||||
typ, type_name = type_info(lex, ident)
|
||||
name = lex:ident()
|
||||
end
|
||||
local info = {
|
||||
name = name,
|
||||
number = lex:expected "=":integer(),
|
||||
label = ident == "map" and labels.repeated or labels.optional,
|
||||
type = typ,
|
||||
type_name = type_name
|
||||
}
|
||||
local options = inline_option(lex)
|
||||
if options then
|
||||
info.default_value, options.default = tostring(options.default), nil
|
||||
info.json_name, options.json_name = options.json_name, nil
|
||||
if options.packed and options.packed == "false" then
|
||||
options.packed = false
|
||||
end
|
||||
info.options = options
|
||||
end
|
||||
if info.number <= 0 then
|
||||
lex:error("invalid tag number: "..info.number)
|
||||
end
|
||||
return info, map_entry
|
||||
end
|
||||
|
||||
local function label_field(self, lex, ident, parent)
|
||||
local label = labels[ident]
|
||||
local info, map_entry
|
||||
if not label then
|
||||
if self.syntax == "proto2" and ident ~= "map" then
|
||||
return lex:error("proto2 disallow missing label")
|
||||
end
|
||||
return field(self, lex, ident)
|
||||
end
|
||||
local proto3_optional = label == labels.optional and self.syntax == "proto3"
|
||||
if proto3_optional and not (self.proto3_optional and parent) then
|
||||
return lex:error("proto3 disallow 'optional' label")
|
||||
end
|
||||
info, map_entry = field(self, lex, lex:type_name())
|
||||
if proto3_optional then
|
||||
local ot = default(parent, "oneof_decl")
|
||||
info.oneof_index = #ot
|
||||
ot[#ot+1] = { name = "optional_" .. info.name }
|
||||
else
|
||||
info.label = label
|
||||
end
|
||||
return info, map_entry
|
||||
end
|
||||
|
||||
function toplevel:package(lex, info)
|
||||
local package = lex:full_ident 'package name'
|
||||
lex:line_end()
|
||||
info.package = package
|
||||
self.prefix = "."..package.."."
|
||||
return self
|
||||
end
|
||||
|
||||
function toplevel:import(lex, info)
|
||||
local mode = lex:ident('"weak" or "public"', 'opt') or "public"
|
||||
if mode ~= 'weak' and mode ~= 'public' then
|
||||
return lex:error '"weak or "public" expected'
|
||||
end
|
||||
local name = lex:quote()
|
||||
lex:line_end()
|
||||
local result = self:parsefile(name)
|
||||
if self.on_import then
|
||||
self.on_import(result)
|
||||
end
|
||||
local dep = default(info, 'dependency')
|
||||
local index = #dep
|
||||
dep[index+1] = name
|
||||
if mode == "public" then
|
||||
local it = default(info, 'public_dependency')
|
||||
insert_tab(it, index)
|
||||
else
|
||||
local it = default(info, 'weak_dependency')
|
||||
insert_tab(it, index)
|
||||
end
|
||||
end
|
||||
|
||||
local msgbody = {} do
|
||||
|
||||
function msgbody:message(lex, info)
|
||||
local nested_type = default(info, 'nested_type')
|
||||
insert_tab(nested_type, toplevel.message(self, lex))
|
||||
return self
|
||||
end
|
||||
|
||||
function msgbody:enum(lex, info)
|
||||
local nested_type = default(info, 'enum_type')
|
||||
insert_tab(nested_type, toplevel.enum(self, lex))
|
||||
return self
|
||||
end
|
||||
|
||||
function msgbody:extend(lex, info)
|
||||
local extension = default(info, 'extension')
|
||||
local nested_type = default(info, 'nested_type')
|
||||
local ft, mt = toplevel.extend(self, lex, {})
|
||||
for _, v in ipairs(ft) do
|
||||
insert_tab(extension, v)
|
||||
end
|
||||
for _, v in ipairs(mt) do
|
||||
insert_tab(nested_type, v)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function msgbody:extensions(lex, info)
|
||||
local rt = default(info, 'extension_range')
|
||||
local idx = #rt
|
||||
repeat
|
||||
local start = lex:integer "field number range"
|
||||
local stop = math.floor(2^29)
|
||||
if lex:keyword('to', 'opt') then
|
||||
if not lex:keyword('max', 'opt') then
|
||||
stop = lex:integer "field number range end or 'max'"
|
||||
end
|
||||
insert_tab(rt, { start = start, ['end'] = stop })
|
||||
else
|
||||
insert_tab(rt, { start = start, ['end'] = start })
|
||||
end
|
||||
until not lex:test ','
|
||||
rt[idx+1].options = inline_option(lex)
|
||||
lex:line_end()
|
||||
return self
|
||||
end
|
||||
|
||||
function msgbody:reserved(lex, info)
|
||||
lex:whitespace()
|
||||
if not lex '^%d' then
|
||||
local rt = default(info, 'reserved_name')
|
||||
repeat
|
||||
insert_tab(rt, (lex:quote()))
|
||||
until not lex:test ','
|
||||
else
|
||||
local rt = default(info, 'reserved_range')
|
||||
local first = true
|
||||
repeat
|
||||
local start = lex:integer(first and 'field name or number range'
|
||||
or 'field number range')
|
||||
if lex:keyword('to', 'opt') then
|
||||
if lex:keyword('max', 'opt') then
|
||||
insert_tab(rt, { start = start, ['end'] = 2^29-1 })
|
||||
else
|
||||
local stop = lex:integer 'field number range end'
|
||||
insert_tab(rt, { start = start, ['end'] = stop })
|
||||
end
|
||||
else
|
||||
insert_tab(rt, { start = start, ['end'] = start })
|
||||
end
|
||||
first = false
|
||||
until not lex:test ','
|
||||
end
|
||||
lex:line_end()
|
||||
return self
|
||||
end
|
||||
|
||||
function msgbody:oneof(lex, info)
|
||||
local fs = default(info, "field")
|
||||
local ts = default(info, "nested_type")
|
||||
local ot = default(info, "oneof_decl")
|
||||
local index = #ot + 1
|
||||
local oneof = { name = lex:ident() }
|
||||
lex:expected "{"
|
||||
while not lex:test "}" do
|
||||
local ident = lex:type_name()
|
||||
if ident == "option" then
|
||||
toplevel.option(self, lex, oneof)
|
||||
else
|
||||
local f, t = field(self, lex, ident)
|
||||
self.locmap[f] = lex.pos
|
||||
if t then insert_tab(ts, t) end
|
||||
f.oneof_index = index - 1
|
||||
insert_tab(fs, f)
|
||||
end
|
||||
lex:line_end 'opt'
|
||||
end
|
||||
ot[index] = oneof
|
||||
end
|
||||
|
||||
function msgbody:option(lex, info)
|
||||
toplevel.option(self, lex, info)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function toplevel:message(lex, info)
|
||||
local name = lex:ident 'message name'
|
||||
local typ = { name = name }
|
||||
register_type(self, lex, name, types.message)
|
||||
local prefix = self.prefix
|
||||
self.prefix = prefix..name.."."
|
||||
lex:expected "{"
|
||||
while not lex:test "}" do
|
||||
local ident, pos = lex:type_name()
|
||||
local body_parser = msgbody[ident]
|
||||
if body_parser then
|
||||
body_parser(self, lex, typ)
|
||||
else
|
||||
local fs = default(typ, 'field')
|
||||
local f, t = label_field(self, lex, ident, typ)
|
||||
self.locmap[f] = pos
|
||||
insert_tab(fs, f)
|
||||
if t then
|
||||
local ts = default(typ, 'nested_type')
|
||||
insert_tab(ts, t)
|
||||
end
|
||||
end
|
||||
lex:line_end 'opt'
|
||||
end
|
||||
lex:line_end 'opt'
|
||||
if info then
|
||||
info = default(info, 'message_type')
|
||||
insert_tab(info, typ)
|
||||
end
|
||||
self.prefix = prefix
|
||||
return typ
|
||||
end
|
||||
|
||||
function toplevel:enum(lex, info)
|
||||
local name, pos = lex:ident 'enum name'
|
||||
local enum = { name = name }
|
||||
self.locmap[enum] = pos
|
||||
register_type(self, lex, name, types.enum)
|
||||
lex:expected "{"
|
||||
while not lex:test "}" do
|
||||
local ident, pos = lex:ident 'enum constant name'
|
||||
if ident == 'option' then
|
||||
toplevel.option(self, lex, enum)
|
||||
elseif ident == 'reserved' then
|
||||
msgbody.reserved(self, lex, enum)
|
||||
else
|
||||
local values = default(enum, 'value')
|
||||
local number = lex:expected '=' :integer()
|
||||
local value = {
|
||||
name = ident,
|
||||
number = number,
|
||||
options = inline_option(lex)
|
||||
}
|
||||
self.locmap[value] = pos
|
||||
insert_tab(values, value)
|
||||
end
|
||||
lex:line_end 'opt'
|
||||
end
|
||||
lex:line_end 'opt'
|
||||
if info then
|
||||
info = default(info, 'enum_type')
|
||||
insert_tab(info, enum)
|
||||
end
|
||||
return enum
|
||||
end
|
||||
|
||||
function toplevel:option(lex, info)
|
||||
local ident = lex:option_name()
|
||||
lex:expected "="
|
||||
local value = lex:constant()
|
||||
lex:line_end()
|
||||
local options = info and default(info, 'options') or {}
|
||||
options[ident] = value
|
||||
return options, self
|
||||
end
|
||||
|
||||
function toplevel:extend(lex, info)
|
||||
local name = lex:type_name()
|
||||
local ft = info and default(info, 'extension') or {}
|
||||
local mt = info and default(info, 'message_type') or {}
|
||||
lex:expected "{"
|
||||
while not lex:test "}" do
|
||||
local ident, pos = lex:type_name()
|
||||
local f, t = label_field(self, lex, ident)
|
||||
self.locmap[f] = pos
|
||||
f.extendee = name
|
||||
insert_tab(ft, f)
|
||||
insert_tab(mt, t)
|
||||
lex:line_end 'opt'
|
||||
end
|
||||
return ft, mt
|
||||
end
|
||||
|
||||
local svr_body = {} do
|
||||
|
||||
function svr_body:rpc(lex, info)
|
||||
local name, pos = lex:ident "rpc name"
|
||||
local rpc = { name = name }
|
||||
self.locmap[rpc] = pos
|
||||
local _, tn
|
||||
lex:expected "%("
|
||||
rpc.client_streaming = lex:keyword("stream", "opt")
|
||||
_, tn = type_info(lex, lex:type_name())
|
||||
if not tn then return lex:error "rpc input type must by message" end
|
||||
rpc.input_type = tn
|
||||
lex:expected "%)" :expected "returns" :expected "%("
|
||||
rpc.server_streaming = lex:keyword("stream", "opt")
|
||||
_, tn = type_info(lex, lex:type_name())
|
||||
if not tn then return lex:error "rpc output type must by message" end
|
||||
rpc.output_type = tn
|
||||
lex:expected "%)"
|
||||
if lex:test "{" then
|
||||
while not lex:test "}" do
|
||||
lex:line_end "opt"
|
||||
lex:keyword "option"
|
||||
toplevel.option(self, lex, rpc)
|
||||
end
|
||||
end
|
||||
lex:line_end "opt"
|
||||
local t = default(info, "method")
|
||||
insert_tab(t, rpc)
|
||||
end
|
||||
|
||||
function svr_body:option(lex, info)
|
||||
return toplevel.option(self, lex, info)
|
||||
end
|
||||
|
||||
function svr_body.stream(_, lex)
|
||||
lex:error "stream not implement yet"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function toplevel:service(lex, info)
|
||||
local name, pos = lex:ident 'service name'
|
||||
local svr = { name = name }
|
||||
self.locmap[svr] = pos
|
||||
lex:expected "{"
|
||||
while not lex:test "}" do
|
||||
local ident = lex:type_name()
|
||||
local body_parser = svr_body[ident]
|
||||
if body_parser then
|
||||
body_parser(self, lex, svr)
|
||||
else
|
||||
return lex:error "expected 'rpc' or 'option' in service body"
|
||||
end
|
||||
lex:line_end 'opt'
|
||||
end
|
||||
lex:line_end 'opt'
|
||||
if info then
|
||||
info = default(info, 'service')
|
||||
insert_tab(info, svr)
|
||||
end
|
||||
return svr
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return toplevel
|
||||
|
||||
-- )EOF"
|
|
@ -40,6 +40,7 @@
|
|||
<ClCompile Include="..\..\src\lua\luafile.cc" />
|
||||
<ClCompile Include="..\..\src\lua\luawrapper.cc" />
|
||||
<ClCompile Include="..\..\src\lua\zlibffi.cc" />
|
||||
<ClCompile Include="..\..\third_party\lua-protobuf\pb.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\src\lua\extra.h" />
|
||||
|
@ -47,6 +48,7 @@
|
|||
<ClInclude Include="..\..\src\lua\luafile.h" />
|
||||
<ClInclude Include="..\..\src\lua\luawrapper.h" />
|
||||
<ClInclude Include="..\..\src\lua\zlibffi.h" />
|
||||
<ClInclude Include="..\..\third_party\lua-protobuf\pb.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\src\lua\fileffi.lua" />
|
||||
|
@ -59,6 +61,10 @@
|
|||
<None Include="..\..\src\lua\glffi-enum3.lua" />
|
||||
<None Include="..\..\src\lua\glffi-typedefs.lua" />
|
||||
<None Include="..\..\third_party\ffi-reflect\reflect.lua" />
|
||||
<None Include="..\..\third_party\lua-protobuf\descriptor.pb.lua" />
|
||||
<None Include="..\..\third_party\lua-protobuf\lexer.lua" />
|
||||
<None Include="..\..\third_party\lua-protobuf\protoc.lua" />
|
||||
<None Include="..\..\third_party\lua-protobuf\toplevel.lua" />
|
||||
<None Include="..\..\third_party\pprint.lua\pprint.lua" />
|
||||
<None Include="..\..\third_party\zlibffi\zlibffi.lua" />
|
||||
<None Include="packages.config" />
|
||||
|
|
|
@ -30,6 +30,9 @@
|
|||
<ClCompile Include="..\..\src\lua\extra.cc">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\third_party\lua-protobuf\pb.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\src\lua\luawrapper.h">
|
||||
|
@ -47,6 +50,9 @@
|
|||
<ClInclude Include="..\..\src\lua\extra.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\third_party\lua-protobuf\pb.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\src\lua\glffi-enum2.lua">
|
||||
|
@ -86,5 +92,17 @@
|
|||
<Filter>Source Files</Filter>
|
||||
</None>
|
||||
<None Include="packages.config" />
|
||||
<None Include="..\..\third_party\lua-protobuf\protoc.lua">
|
||||
<Filter>Source Files</Filter>
|
||||
</None>
|
||||
<None Include="..\..\third_party\lua-protobuf\lexer.lua">
|
||||
<Filter>Source Files</Filter>
|
||||
</None>
|
||||
<None Include="..\..\third_party\lua-protobuf\toplevel.lua">
|
||||
<Filter>Source Files</Filter>
|
||||
</None>
|
||||
<None Include="..\..\third_party\lua-protobuf\descriptor.pb.lua">
|
||||
<Filter>Source Files</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
Loading…
Reference in a new issue