Giter Site home page Giter Site logo

sproto's Introduction

Introduction

Sproto is an efficient serialization library for C, and focuses on lua binding. It's like Google protocol buffers, but much faster.

The design is simple. It only supports a few types that lua supports. It can be easily bound to other dynamic languages, or be used directly in C.

In my i5-2500 @3.3GHz CPU, the benchmark is below:

The schema in sproto:

.Person {
    name 0 : string
    id 1 : integer
    email 2 : string

    .PhoneNumber {
        number 0 : string
        type 1 : integer
    }

    phone 3 : *PhoneNumber
}

.AddressBook {
    person 0 : *Person
}

It's equal to:

message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;

  message PhoneNumber {
    required string number = 1;
    optional int32 type = 2 ;
  }

  repeated PhoneNumber phone = 4;
}

message AddressBook {
  repeated Person person = 1;
}

Use the data:

local ab = {
    person = {
        {
            name = "Alice",
            id = 10000,
            phone = {
                { number = "123456789" , type = 1 },
                { number = "87654321" , type = 2 },
            }
        },
        {
            name = "Bob",
            id = 20000,
            phone = {
                { number = "01234567890" , type = 3 },
            }
        }
    }
}
library encode 1M times decode 1M times size
sproto 2.15s 7.84s 83 bytes
sproto (nopack) 1.58s 6.93s 130 bytes
pbc-lua 6.94s 16.9s 69 bytes
lua-cjson 4.92s 8.30s 183 bytes

Parser

local parser = require "sprotoparser"
  • parser.parse parses a sproto schema to a binary string.

The parser is needed for parsing the sproto schema. You can use it to generate binary string offline. The schema text and the parser is not needed when your program is running.

Lua API

local sproto = require "sproto"
local sprotocore = require "sproto.core" -- optional
  • sproto.new(spbin) creates a sproto object by a schema binary string (generates by parser).
  • sprotocore.newproto(spbin) creates a sproto c object by a schema binary string (generates by parser).
  • sproto.sharenew(spbin) share a sproto object from a sproto c object (generates by sprotocore.newproto).
  • sproto.parse(schema) creates a sproto object by a schema text string (by calling parser.parse)
  • sproto:exist_type(typename) detect whether a type exist in sproto object.
  • sproto:encode(typename, luatable) encodes a lua table with typename into a binary string.
  • sproto:decode(typename, blob [,sz]) decodes a binary string generated by sproto.encode with typename. If blob is a lightuserdata (C ptr), sz (integer) is needed.
  • sproto:pencode(typename, luatable) The same with sproto:encode, but pack (compress) the results.
  • sproto:pdecode(typename, blob [,sz]) The same with sproto.decode, but unpack the blob (generated by sproto:pencode) first.
  • sproto:default(typename, type) Create a table with default values of typename. Type can be nil , "REQUEST", or "RESPONSE".

RPC API

There is a lua wrapper for the core API for RPC .

sproto:host([packagename]) creates a host object to deliver the rpc message.

host:dispatch(blob [,sz]) unpack and decode (sproto:pdecode) the binary string with type the host created (packagename).

If .type is exist, it's a REQUEST message with .type , returns "REQUEST", protoname, message, responser, .ud. The responser is a function for encode the response message. The responser will be nil when .session is not exist.

If .type is not exist, it's a RESPONSE message for .session . Returns "RESPONSE", .session, message, .ud .

host:attach(sprotoobj) creates a function(protoname, message, session, ud) to pack and encode request message with sprotoobj.

If you don't want to use host object, you can also use these following apis to encode and decode the rpc message:

sproto:request_encode(protoname, tbl) encode a request message with protoname.

sproto:response_encode(protoname, tbl) encode a response message with protoname.

sproto:request_decode(protoname, blob [,sz]) decode a request message with protoname.

sproto:response_decode(protoname, blob [,sz] decode a response message with protoname.

Read testrpc.lua for detail.

Schema Language

Like Protocol Buffers (but unlike json), sproto messages are strongly-typed and are not self-describing. You must define your message structure in a special language.

You can use sprotoparser library to parse the schema text to a binary string, so that the sproto library can use it. You can parse them offline and save the string, or you can parse them during your program running.

The schema text is like this:

# This is a comment.

.Person {	# . means a user defined type 
    name 0 : string	# string is a build-in type.
    id 1 : integer
    email 2 : string

    .PhoneNumber {	# user defined type can be nest.
        number 0 : string
        type 1 : integer
    }

    phone 3 : *PhoneNumber	# *PhoneNumber means an array of PhoneNumber.
    height 4 : integer(2)	# (2) means a 1/100 fixed-point number.
    data 5 : binary		# Some binary data
    weight 6 : double   # floating number
}

.AddressBook {
    person 0 : *Person(id)	# (id) is optional, means Person.id is main index.
}

foobar 1 {	# define a new protocol (for RPC used) with tag 1
    request Person	# Associate the type Person with foobar.request
    response {	# define the foobar.response type
        ok 0 : boolean
    }
}

A schema text can be self-described by the sproto schema language.

.type {
    .field {
        name 0 : string
        buildin	1 : integer
        type 2 : integer	# type is fixed-point number precision when buildin is SPROTO_TINTEGER; When buildin is SPROTO_TSTRING, it means binary string when type is 1.
        tag 3 : integer
        array 4	: boolean
        key 5 : integer # If key exists, array must be true, and it's a map.
    }
    name 0 : string
    fields 1 : *field
}

.protocol {
    name 0 : string
    tag 1 : integer
    request 2 : integer # index
    response 3 : integer # index
    confirm 4 : boolean # response nil where confirm == true
}

.group {
    type 0 : *type
    protocol 1 : *protocol
}

Types

  • string : string
  • binary : binary string (it's a sub type of string)
  • integer : integer, the max length of an integer is signed 64bit. It can be a fixed-point number with specified precision.
  • double : double precision floating-point number, satisfy the IEEE 754 standard.
  • boolean : true or false

You can add * before the typename to declare an array.

You can also specify a main index with the syntax likes *array(id), the array would be encode as an unordered map with the id field as key.

For empty main index likes *array(), the array would be encoded as an unordered map with the first field as key and the second field as value.

User defined type can be any name in alphanumeric characters except the build-in typenames, and nested types are supported.

  • Where are double or real types?

I have been using Google protocol buffers for many years in many projects, and I found the real types were seldom used. If you really need it, you can use string to serialize the double numbers. When you need decimal, you can specify the fixed-point precision.

NOTE : double is supported now.

  • Where is enum?

In lua, enum types are not very useful. You can use integer to define an enum table in lua.

Wire protocol

Each integer number must be serialized in little-endian format.

The sproto message must be a user defined type struct, and a struct is encoded in three parts. The header, the field part, and the data part. The tag and small integer or boolean will be encoded in field part, and others are in data part.

All the fields must be encoded in ascending order (by tag, base 0). The tags of fields can be discontinuous, if a field is nil. (default value in lua), don't encode it in message.

The header is a 16bit integer. It is the number of fields.

Each field in field part is a 16bit integer (n). If n is zero, that means the field data is encoded in data part ;

If n is even (and not zero), the value of this field is n/2-1 , and the tag increases 1;

If n is odd, that means the tags is not continuous, and we should add current tag by (n+1)/2 .

Arrays are always encode in data part, 4 bytes header for the size, and the following bytes is the contents. See the example 2 for the struct array; example 3/4 for the integer array ; example 5 for the boolean array.

For integer array, an additional byte (4 or 8) to indicate the value is 32bit or 64bit.

Read the examples below to see more details.

Notice: If the tag is not declared in schema, the decoder will simply ignore the field for protocol version compatibility. Notice more: all examples are tested in test_wire_protocol.lua, update test_wire_protocol.lua when update examples.

.Person {
    name 0 : string
    age 1 : integer
    marital 2 : boolean
    children 3 : *Person
}

.Data {
	numbers 0 : *integer
	bools 1 : *boolean
	number 2 : integer
	bignumber 3 : integer
 	double 4 : double
 	doubles 5 : *double
 	fpn 6 : integer(2)
}

Example 1:

person { name = "Alice" ,  age = 13, marital = false } 

03 00 (fn = 3)
00 00 (id = 0, value in data part)
1C 00 (id = 1, value = 13)
02 00 (id = 2, value = false)
05 00 00 00 (sizeof "Alice")
41 6C 69 63 65 ("Alice")

Example 2:

person {
    name = "Bob",
    age = 40,
    children = {
        { name = "Alice" ,  age = 13 },
        { name = "Carol" ,  age = 5 },
    }
}

04 00 (fn = 4)
00 00 (id = 0, value in data part)
52 00 (id = 1, value = 40)
01 00 (skip id = 2)
00 00 (id = 3, value in data part)

03 00 00 00 (sizeof "Bob")
42 6F 62 ("Bob")

26 00 00 00 (sizeof children)

0F 00 00 00 (sizeof child 1)
02 00 (fn = 2)
00 00 (id = 0, value in data part)
1C 00 (id = 1, value = 13)
05 00 00 00 (sizeof "Alice")
41 6C 69 63 65 ("Alice")

0F 00 00 00 (sizeof child 2)
02 00 (fn = 2)
00 00 (id = 0, value in data part)
0C 00 (id = 1, value = 5)
05 00 00 00 (sizeof "Carol")
43 61 72 6F 6C ("Carol")

Example 3:

data {
    numbers = { 1,2,3,4,5 }
}

01 00 (fn = 1)
00 00 (id = 0, value in data part)

15 00 00 00 (sizeof numbers)
04 ( sizeof int32 )
01 00 00 00 (1)
02 00 00 00 (2)
03 00 00 00 (3)
04 00 00 00 (4)
05 00 00 00 (5)

Example 4:

data {
    numbers = {
        (1<<32)+1,
        (1<<32)+2,
        (1<<32)+3,
    }
}

01 00 (fn = 1)
00 00 (id = 0, value in data part)

19 00 00 00 (sizeof numbers)
08 ( sizeof int64 )
01 00 00 00 01 00 00 00 ( (1<32) + 1)
02 00 00 00 01 00 00 00 ( (1<32) + 2)
03 00 00 00 01 00 00 00 ( (1<32) + 3)

Example 5:

data {
    bools = { false, true, false }
}

02 00 (fn = 2)
01 00 (skip id = 0)
00 00 (id = 1, value in data part)

03 00 00 00 (sizeof bools)
00 (false)
01 (true)
00 (false)

Example 6:

data {
    number = 100000,
    bignumber = -10000000000,
}

03 00 (fn = 3)
03 00 (skip id = 1)
00 00 (id = 2, value in data part)
00 00 (id = 3, value in data part)

04 00 00 00 (sizeof number, data part)
A0 86 01 00 (100000, 32bit integer)

08 00 00 00 (sizeof bignumber, data part)
00 1C F4 AB FD FF FF FF (-10000000000, 64bit integer)

Example 7:

data {
    double = 0.01171875,
    doubles = {0.01171875, 23, 4}
}

03 00 (fn = 3)
07 00 (skip id = 3)
00 00 (id = 4, value in data part)
00 00 (id = 5, value in data part)

08 00 00 00 (sizeof number, data part)
00 00 00 00 00 00 88 3f (0.01171875, 64bit double)

19 00 00 00 (sizeof doubles)
08 (sizeof double)
00 00 00 00 00 00 88 3f (0.01171875, 64bit double)
00 00 00 00 00 00 37 40 (23, 64bit double)
00 00 00 00 00 00 10 40 (4, 64bit double)

Example 8:

data {
    fpn = 1.82,
}

02 00 (fn = 2)
0b 00 (skip id = 5)
6e 01 (id = 6, value = 0x16e/2 - 1 = 182)

0 Packing

The algorithm is very similar to Cap'n proto, but 0x00 is not treated specially.

In packed format, the message is padding to 8. Each 8 byte is reduced to a tag byte followed by zero to eight content bytes. The bits of the tag byte correspond to the bytes of the unpacked word, with the least-significant bit corresponding to the first byte. Each zero bit indicates that the corresponding byte is zero. The non-zero bytes are packed following the tag.

For example:

unpacked (hex):  08 00 00 00 03 00 02 00   19 00 00 00 aa 01 00 00
packed (hex):  51 08 03 02   31 19 aa 01

Tag 0xff is treated specially. A number N is following the 0xff tag. N means (N+1)*8 bytes should be copied directly. The bytes may or may not contain zeros. Because of this rule, the worst-case space overhead of packing is 2 bytes per 2 KiB of input.

For example:

unpacked (hex):  8a (x 30 bytes)
packed (hex):  ff 03 8a (x 30 bytes) 00 00

C API

struct sproto * sproto_create(const void * proto, size_t sz);

Create a sproto object with a schema string encoded by sprotoparser:

void sproto_release(struct sproto *);

Release the sproto object:

int sproto_prototag(struct sproto *, const char * name);
const char * sproto_protoname(struct sproto *, int proto);
// SPROTO_REQUEST(0) : request, SPROTO_RESPONSE(1): response
struct sproto_type * sproto_protoquery(struct sproto *, int proto, int what);

Convert between tag and name of a protocol, and query the type object of it:

struct sproto_type * sproto_type(struct sproto *, const char * typename);

Query the type object from a sproto object:

struct sproto_arg {
	void *ud;
	const char *tagname;
	int tagid;
	int type;
	struct sproto_type *subtype;
	void *value;
	int length;
	int index;	// array base 1
	int mainindex;	// for map
	int extra; // SPROTO_TINTEGER: fixed-point presision ; SPROTO_TSTRING 0:utf8 string 1:binary
};

typedef int (*sproto_callback)(const struct sproto_arg *args);

int sproto_decode(struct sproto_type *, const void * data, int size, sproto_callback cb, void *ud);
int sproto_encode(struct sproto_type *, void * buffer, int size, sproto_callback cb, void *ud);

encode and decode the sproto message with a user defined callback function. Read the implementation of lsproto.c for more details.

int sproto_pack(const void * src, int srcsz, void * buffer, int bufsz);
int sproto_unpack(const void * src, int srcsz, void * buffer, int bufsz);

pack and unpack the message with the 0 packing algorithm.

Other Implementions and bindings

See Wiki https://github.com/cloudwu/sproto/wiki

Question?

sproto's People

Contributors

anonymou3 avatar cenwucn avatar chenbinhi avatar cloudwu avatar dalange avatar elgs avatar funny-code avatar hqwrong avatar isaf avatar kevin1sme avatar keyring avatar lalawue avatar lvzixun avatar spin6lock avatar t0350 avatar xebecnan avatar xjdrew avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sproto's Issues

about the sproto:pdecode

in sproto.lua, maybe unpack first then decode ?

function sproto:pdecode(typename, bin)
local st = querytype(self, typename)
return core.unpack(core.decode(st, bin))
end

lua5.1的移植

请问该库会支持lua5.1吗?如果官方不准备支持的话,我需要移植哪些地方以适应lua5.1。谢谢。

请问有计划支持float嘛?

打算在新的项目里用这个协议,想了解一下。
另外有没有一完整的支持类型的关键字列表呢?
谢谢!

Why not directly use Cap'n Proto?

sproto seems very close to Cap'n Proto. Actually, Cap'n Proto is even mentioned at the description of packing.

Why should I choose sproto over Cap'n Proto?

数据压缩部分缓冲区预测

在pack函数中,对0压缩后的缓冲区大小预测为每2k多算2个byte的字节开销,如果数据流是如0x78(x8 byte)0x0 0x78(x7 byte)这样压缩算下来,每16byte就需要两个额外字节开销,如果理解有误请见谅

encode buffer overflow

Assertion failed: (sz <= size-SIZEOF_LENGTH), function encode_object, file lualib-src/sproto/sproto.c, line 733.

如果先 encode 其它的,在 encode 这个有问题的就没问题。怀疑 expand_buffer 没起作用,可能是 data 刚好写满触发了哪里的临界条件。

负数encode的问题

测试: -8 解析出来变成 -7.99, 研究代码,改成下面就好了:
lsproto.c 175 : v = (int64_t)(vn * args->extra + 0.5) => v = (int64_t)(vn * args->extra + (vn<0 ? -0.5 : 0.5))

sproto.c文件内,这些是否是笔误

sproto.c文件里的 pool_realese 函数里,不应该是 free(n) ,应该是 free(tmp) 吧。


还有在 import_string 函数中,buffer[sz] = 0 是否应为 buffer[sz] = '\0'

在32位的机器上发送一个比较大的浮点数异常。

发现问题的代码在
lsproto.c -> static int encode(const struct sproto_arg *args) 中对SPROTO_TINTEGER的处理

lua_Integer v;
lua_Integer vh;
int isnum;
if (args->extra) {
// It's decimal.
lua_Number vn = lua_tonumber(L, -1);
v = (lua_Integer)(vn * args->extra + 0.5);

这样比如我用integer(2)发送一个80,000,000, 会导致v最终是一个负数。
建议将v改为int64_t类型,这样兼容的范围更宽泛,只要不要用什么integer(6) 发送上亿的数据...

int64_t v;
v = (int64_t)(vn * args->extra + 0.5);

Recursive structures supported?

Lua doesn't support recursive types to my knowledge. sproto builds around Lua types. Does it mean recursive structures are not supported by sproto?

My goal is to get rid of protocol buffers and switch to sproto, but recursive structures are a must have (I'd like to represent Dao types which support recursion).

Readme中貌似没有对tag的递增步长详细说明

If n is odd, that means the tags is not continuous, and we should add current tag by (n+1)/2 .

  1. 从这句可以看出n为偶数时tag是连续的,步长为1,但不明显。
    blog里有一句很重要: 字段的 tag 从 0 开始累加,每处理一个字段,将 tag 加一。应该写进readme。

2.别人也不知道这种奇数行的作用究竟是什么,只是从example2 中看出会被忽略,不知道是否是这样。
01 00 (skip id = 2).

初学者的一点建议,如果问的不对,请多多包涵。

使用定点数时,若为负数,decode出来的数不正确

测试版本号:
SHA-1: db7ea80
date 2017/6/15 15:18:54

test case:
testall.lua 59行 i = { 1,2.1,3.21, -4.321 },
运行结果:
+i+1 [1.0]
+2 [2.1]
+3 [3.21]
+4 [1.844674407371e+17]
期望结果:
+i+1 [1.0]
+2 [2.1]
+3 [3.21]
+4 [-4.32]
造成原因:
lsproto.c 347行 , 转换成uint64丢失了符号
修正方法参考:
int64_t v = (int64_t)args->value;

+i+1 [1.0]
| +2 [2.1]
| +3 [3.21]
| +4 [-4.31]

int 编码错误

在手机设备上 2947483648数值溢出了。显示为-1347483648 。pc版本正常显示。

关于sproto二进制8字节对齐

云风您好!关于压缩sproto二进制一连串0的问题,我按照你这个blog上的方法压缩了,http://blog.codingnow.com/2014/07/ejoyproto.html
后来才发现,sproto序列化成二进制的时候默认没有按照8字节对齐,比如有内字符串,并且长度不是8字节的倍数的时候。这样解压后的二进制末尾就会多几个字节的0,虽然测试了一下并不影响反序列化,但是不知道会不会存在潜在的影响。请问,是否能加个参数,在sproto序列化的时候选择是否按8字节对齐?谢谢。

这是我按照您的方法写的压缩0的代码:
https://github.com/korialuo/skynet/blob/master/lualib-src/lua-lzc.c

似乎编码还是有点问题,空表没有打进去

https://gist.github.com/1fd2e1f09d5622667705028da514f531
还原出来obj里面的元素都不见了,期待是见到这几个元素都是空表。

update:
python这边比较吊诡,其中一个空表打进去了,其他的没有。参见spin6lock/python-sproto#9 这个PR的test_basic.py,只有f打印出来了。看日志,sproto_decode传过来的tagname就只有f,但是python版encode函数里,f和其他空表一样,都是返回SPROTO_CB_NIL的

当协议越来越多时候如何分模块管理?

1、.package的结构体是否固定需要?里面的type和session有何作用,是否可变?
2、如”get 1 {}",这里的1是否有上限?get命名标准是什么?
3、如“result 0 : *datastruct(id)",这里的key字段一定要是结构体datastruct里面的字段吗?
4、当协议越来越多,规模越来越大,如何分模块管理?有没有什么好的建议?
我以前没接触过protobuf这类型的协议,云大能给点小小建议吗?非常感谢!

review sprotoparser.lua 源码 if ptype then 这个逻辑分支理论上应该一直不会执行

local function checktype(types, ptype, t)
        if buildin_types[t] then
               return t
	end
	local fullname = ptype .. "." .. t
	if types[fullname] then
		return fullname
	else
		ptype = ptype:match "(.+)%..+$"
		if ptype then
			return checktype(types, ptype, t)
		elseif types[t] then
			return t
		end
	end
end

if ptype then 这个逻辑分支理论上应该一直不会执行,因为按照逻辑所有 ptype 都不会匹配"(.+)%..+$"

关于sproto解析器的问题

HI,云风

C API中sproto_create如何使用sprotoparser.lua解析出来的字符流数据,还是无法直接使用,需要自己编写一个c版本的parser来做?

能否在sproto里面提供命名空间的概念

如:

.login {
   .PlayerInfo {
      nickname 0 : string
  }

  weixinLogin 100 {
    request {
     account 0 : string
   }
   response {
    info 0 : PlayerInfo
  }
 }
}

login.PlayerInfo的结构体是能用的,不过方法login.weixinLogin却不能被解析, 能否稍作修改

tag的作用和map的实现上面不太理解

感谢你的建议我也看了你5.3的中文文档,对我这个初学者很有帮助
另外sproto源码我看了然后昨天做了一次改版,里面有几个不太理解
1.tag的作用?除了实现option的功能外,是否还有其他用途?
2.map支持的局限性,是否考虑增加通用类型map?
目前我是改版支持任意类型的map结构不一定依赖于struct,以及里面的key定义(我们的项目中很多定义是 key 到 基础类型 的map)
另外修改lpeg使用C的struct定义,这样我觉得更能表示他和c的关系(其实也是为了偷懒,复制粘贴二进制的协议定义,支持字节对齐1,2,4,8,string,vt和map)
主要了解下map的以后发展,另外如果可以的话是否这样实现的通用类型map是否会有其他问题
encode{
...
if (args->index > 0) {
...
if (args->m_pMapKeyType > 0) {
...
lua_pushvalue(L, self->iter_index);
--根据类型读取key值
lua_pop(L, 1);
}
}
...
}

decode{
...
if (args->index != 0) {
...
if (args->m_pMapKeyType > 0){
//map,先压入key
lua_Integer v = *args->m_pMapKeyValue;
lua_pushinteger(L, v);
}
}
...
if (args->index > 0) {
if (args->m_pMapKeyType > 0)
{
//map
lua_settable(L, self->array_index);
}
else
{
//array
lua_seti(L, self->array_index, args->index);
}
} else {
lua_setfield(L, self->result_index, args->tagname);
}
}
主要是lua这里实现是否会引发其他问题?

readme中的疑问

.AddressBook {
    person 0 : *Person(id)  # (id) is optional, means Person.id is main index.
}

这个

 means Person.id is main index

是什么意思?什么场景下用这个

是否有人已经有人做了自定义增加所有消息的公共部分?

所谓自定义增加所有消息的公共部分是指,在skynet示例中,您加入了session的设计。这个可以在socket通信中的sprotol部分的外面,但是是否也可以作为一个公共的基础字段添加到sproto中。
比如这样解析
.package {
type 0 : integer
session 1 : integer
session_user_define 2: integer # 自定义增加
}
免得在socket发送的二进制流中自己拼接控制部分。
如果已经有人做了。我就不重复做轮子了,如果这样的设计不好,也请回复。谢谢

pack bug

static int
lpack(lua_State *L) {
size_t sz=0;
const void * buffer = getbuffer(L, 1, &sz);
// the worst-case space overhead of packing is 2 bytes per 2 KiB of input (256 words = 2KiB).
//size_t maxsz = (sz + 2047) / 2048 * 2 + sz;
//if (sz % 8) == 6 then sz = sz + 2 end if (sz%8) == 7 then sz = sz + 1 end
size_t maxsz = (sz + 2047) / 2048 * 2 + ((sz + 7) & (~7));
void * output = lua_touserdata(L, lua_upvalueindex(1));
int bytes;
int osz = lua_tointeger(L, lua_upvalueindex(2));
if (osz < maxsz) {
output = expand_buffer(L, osz, maxsz);
}
bytes = sproto_pack(buffer, sz, output, maxsz);
if (bytes > maxsz) {
return luaL_error(L, "packing error, return size = %d", bytes);
}
lua_pushlstring(L, output, bytes);

return 1;

}

maxsz计算错误。
unpacked (hex): 8a (x 30 bytes)
packed (hex): ff 03 8a (x 30 bytes) 00 00

没有计算padding数据的大小

// the worst-case space overhead of packing is 2 bytes per 2 KiB of input (256 words = 2KiB).
压缩格式:
ff 1byte xx ...
1byte 最大值255, (255+1)*8 = 2Kib, 每2Kib需要额外的两个字节空间 ff ff。

(sz + 2047) / 2048 * 2 计算 the worst-case space overhead of packing is 2 bytes per 2 KiB of input (256 words = 2KiB)这个值, ((sz + 7) & (~7)) 8字节对齐,更精确的值是这样的 if (sz % 8) == 6 then sz = sz + 2 end if (sz%8) == 7 then sz = sz + 1 end。

sproto中关于user_struct(id)的使用,index是动态的string,value的类型必须是table么?

local sproto = require "sproto"
local sprotoparser = require "sprotoparser"

local function test()
    -- this is ok, if the value type is a table

    -- local proto = sproto.parse [[
    --  .entry {
    --      id 0 : string
    --      value 1 : integer
    --  }

    --  .info {
    --      entry 0 : *entry(id)
    --  }
    -- ]]

    -- local data = {
    --  entry = {
    --      ["1001"] = {id = "1001", value = 1},
    --      ["1002"] = {id = "1002", value = 2},
    --      ["1003"] = {id = "1003", value = 3},
    --  }
    -- }
    -- local encoded = proto:encode('info', data)
    -- local decoded = proto:decode('info', encoded)
    -- print_r(decoded)

    -- what if my current data structure is, how to write the proto?
    local data_2 = {
        entry = {
            "1001" = 1,
            "1002" = 2,
            "1003" = 3,
        }
    }
end

return test

data_2 这种结构应该怎么写proto呢?

lpeg这个文件怎么没有,运行报错了

我下载sproto工程,make Linux 后,运行test.lua文件报错
lua: ./sprotoparser.lua:1: module 'lpeg' not found:
no field package.preload['lpeg']
no file '/usr/local/share/lua/5.3/lpeg.lua'
no file '/usr/local/share/lua/5.3/lpeg/init.lua'
no file '/usr/local/lib/lua/5.3/lpeg.lua'
no file '/usr/local/lib/lua/5.3/lpeg/init.lua'
no file './lpeg.lua'
no file './lpeg/init.lua'
no file '/usr/local/lib/lua/5.3/lpeg.so'
no file '/usr/local/lib/lua/5.3/loadall.so'
no file './lpeg.so'

sproto 打包的后的二进制数据存数据库问题

需求是 想将存档数据直接通过sproto打包成二进制存入mysql 数据库

但是在存入的时候需要先处理mysql转义字符, 但是取出后数据不能还原了

我不知道是不能这样做呢。还是需要其他处理。

local sp = sproto.parse [[
.build_object{
	build_index 0 : integer
	build_id 1 : integer
	grid_id 2 : integer
	flip 3 : integer
}

.build_data{
	build_objects 0 : *build_object
}
]]

-- 处理mysql转义字符  
local mysqlEscapeMode = "[%z\'\"\\\26\b\n\r\t]"
local mysqlEscapeReplace = {  
    ['\0']='\\0',  
    ['\''] = '\\\'',  
    ['\"'] = '\\\"',  
    ['\\'] = '\\\\',  
    ['\26'] = '\\z',  
    ['\b'] = '\\b',  
    ['\n'] = '\\n',  
    ['\r'] = '\\r',  
    ['\t'] = '\\t',  
    }
  
local function mysqlEscapeString(s)  
    return string.gsub(s, mysqlEscapeMode, mysqlEscapeReplace) 
end 

build_data = {
        {1001,1001001,2515,1},
        {8004,0,2267,1},
        {8010,0,2268,1},
        {8010,0,2269,1},
        {8014,0,3316,1},
        {4001,4001001,2027,1},
         '''''''
}

local build_dump = {}
build_dump.build_objects = {}
for k,v in pairs(build_data) do
	local build_object = {}
	build_object.build_index = v[1]
	build_object.build_id = v[2]
	build_object.grid_id = v[3]
	build_object.flip = v[4]
	table.insert(build_dump.build_objects,build_object)
end

local code = sp:encode("build_data", build_dump)
code = mysqlEscapeString(code)
local data = sp:decode("build_data", code)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.