sproto support response nil

This commit is contained in:
Cloud Wu
2017-05-02 15:29:48 +08:00
parent fd427843a9
commit d4eeb1ff28
5 changed files with 83 additions and 21 deletions

View File

@@ -82,7 +82,7 @@ lua-cjson | 4.92s | 8.30s | 183 bytes
* pbc-lua is a google protocol buffers library https://github.com/cloudwu/pbc
* lua-cjson is a json library https://github.com/efelix/lua-cjson
Lua API
Parser
=======
```lua
@@ -93,24 +93,50 @@ local parser = require "sprotoparser"
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
=======
```lua
local sproto = require "sproto.core"
local sproto = require "sproto"
local sprotocore = require "sproto.core" -- optional
```
* `sproto.newproto(sp)` creates a sproto object by a schema string (generates by parser).
* `sproto.querytype(sp, typename)` queries a type object from a sproto object by typename.
* `sproto.encode(st, luatable)` encodes a lua table by a type object, and generates a string message.
* `sproto.decode(st, message)` decodes a message string generated by sproto.encode with type.
* `sproto.pack(sprotomessage)` packs a string encoded by sproto.encode to reduce the size.
* `sproto.unpack(packedmessage)` unpacks the string packed by sproto.pack.
The sproto supports protocol tag for RPC. Use `sproto.protocol(tagorname)` to convert the protocol name to the tag id, or convert back from tag id to the name, and returns the request/response message type objects of this protocol.
* `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)` creares 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
@@ -137,6 +163,8 @@ The schema text is like this:
}
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
}
.AddressBook {
@@ -159,7 +187,7 @@ A schema text can be self-described by the sproto schema language.
.field {
name 0 : string
buildin 1 : integer
type 2 : 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.
@@ -173,6 +201,7 @@ A schema text can be self-described by the sproto schema language.
tag 1 : integer
request 2 : integer # index
response 3 : integer # index
confirm 4 : boolean # response nil where confirm == true
}
.group {
@@ -184,8 +213,9 @@ A schema text can be self-described by the sproto schema language.
Types
=======
* **string** : binary string
* **integer** : integer, the max length of an integer is signed 64bit.
* **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.
* **boolean** : true or false
You can add * before the typename to declare an array.
@@ -196,7 +226,7 @@ User defined type can be any name in alphanumeric characters except the build-in
* 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.
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 presision.
* Where is enum?
@@ -436,6 +466,7 @@ struct sproto_arg {
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);

View File

@@ -275,7 +275,9 @@ lencode(lua_State *L) {
int tbl_index = 2;
struct sproto_type * st = lua_touserdata(L, 1);
if (st == NULL) {
return luaL_argerror(L, 1, "Need a sproto_type object");
luaL_checktype(L, tbl_index, LUA_TNIL);
lua_pushstring(L, "");
return 1; // response nil
}
luaL_checktype(L, tbl_index, LUA_TTABLE);
luaL_checkstack(L, ENCODE_DEEPLEVEL*2 + 8, NULL);
@@ -573,7 +575,11 @@ lprotocol(lua_State *L) {
}
response = sproto_protoquery(sp, tag, SPROTO_RESPONSE);
if (response == NULL) {
lua_pushnil(L);
if (sproto_protoresponse(sp, tag)) {
lua_pushlightuserdata(L, NULL); // response nil
} else {
lua_pushnil(L);
}
} else {
lua_pushlightuserdata(L, response);
}

View File

@@ -32,6 +32,7 @@ struct sproto_type {
struct protocol {
const char *name;
int tag;
int confirm; // confirm == 1 where response nil
struct sproto_type * p[2];
};
@@ -364,6 +365,7 @@ import_protocol(struct sproto *s, struct protocol *p, const uint8_t * stream) {
p->tag = -1;
p->p[SPROTO_REQUEST] = NULL;
p->p[SPROTO_RESPONSE] = NULL;
p->confirm = 0;
tag = 0;
for (i=0;i<fn;i++,tag++) {
int value = toword(stream + SIZEOF_FIELD * i);
@@ -395,6 +397,9 @@ import_protocol(struct sproto *s, struct protocol *p, const uint8_t * stream) {
return NULL;
p->p[SPROTO_RESPONSE] = &s->type[value];
break;
case 4: // confirm
p->confirm = value;
break;
default:
return NULL;
}
@@ -542,6 +547,8 @@ sproto_dump(struct sproto *s) {
}
if (p->p[SPROTO_RESPONSE]) {
printf(" response:%s", p->p[SPROTO_RESPONSE]->name);
} else if (p->confirm) {
printf(" response nil");
}
printf("\n");
}
@@ -590,6 +597,12 @@ sproto_protoquery(const struct sproto *sp, int proto, int what) {
return NULL;
}
int
sproto_protoresponse(const struct sproto * sp, int proto) {
struct protocol * p = query_proto(sp, proto);
return (p!=NULL && (p->p[SPROTO_RESPONSE] || p->confirm));
}
const char *
sproto_protoname(const struct sproto *sp, int proto) {
struct protocol * p = query_proto(sp, proto);

View File

@@ -30,6 +30,7 @@ int sproto_prototag(const struct sproto *, const char * name);
const char * sproto_protoname(const struct sproto *, int proto);
// SPROTO_REQUEST(0) : request, SPROTO_RESPONSE(1): response
struct sproto_type * sproto_protoquery(const struct sproto *, int proto, int what);
int sproto_protoresponse(const struct sproto *, int proto);
struct sproto_type * sproto_type(const struct sproto *, const char * type_name);

View File

@@ -104,7 +104,13 @@ function convert.protocol(all, obj)
typename = obj[1] .. "." .. p[1]
all.type[typename] = convert.type(all, { typename, struct })
end
result[p[1]] = typename
if typename == "nil" then
if p[1] == "response" then
result.confirm = true
end
else
result[p[1]] = typename
end
end
return result
end
@@ -259,6 +265,7 @@ end
tag 1 : integer
request 2 : integer # index
response 3 : integer # index
confirm 4 : boolean # true means response nil
}
.group {
@@ -366,18 +373,22 @@ local function packproto(name, p, alltypes)
"\0\0", -- name (id=0, ref=0)
packvalue(p.tag), -- tag (tag=1)
}
if p.request == nil and p.response == nil then
tmp[1] = "\2\0"
if p.request == nil and p.response == nil and p.confirm == nil then
tmp[1] = "\2\0" -- only two fields
else
if p.request then
table.insert(tmp, packvalue(alltypes[p.request].id)) -- request typename (tag=2)
else
table.insert(tmp, "\1\0")
table.insert(tmp, "\1\0") -- skip this field (request)
end
if p.response then
table.insert(tmp, packvalue(alltypes[p.response].id)) -- request typename (tag=3)
elseif p.confirm then
tmp[1] = "\5\0" -- add confirm field
table.insert(tmp, "\1\0") -- skip this field (response)
table.insert(tmp, packvalue(1)) -- confirm = true
else
tmp[1] = "\3\0"
tmp[1] = "\3\0" -- only three fields
end
end