mirror of
https://github.com/cloudwu/skynet.git
synced 2026-07-24 20:23:06 +00:00
Add map for sproto
This commit is contained in:
@@ -165,6 +165,7 @@ 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
|
||||
weight 6 : double # floating number
|
||||
}
|
||||
|
||||
.AddressBook {
|
||||
@@ -216,11 +217,14 @@ 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, floating-point number.
|
||||
* **boolean** : true or false
|
||||
|
||||
You can add * before the typename to declare an array.
|
||||
You can add * before the typename to declare an array.
|
||||
|
||||
You can also specify a main index, the array whould be encode as an unordered map.
|
||||
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.
|
||||
|
||||
@@ -228,6 +232,8 @@ User defined type can be any name in alphanumeric characters except the build-in
|
||||
|
||||
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.
|
||||
|
||||
**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.
|
||||
|
||||
@@ -128,6 +128,7 @@ struct encode_ud {
|
||||
const char * array_tag;
|
||||
int array_index;
|
||||
int deep;
|
||||
int map_entry;
|
||||
int iter_func;
|
||||
int iter_table;
|
||||
int iter_key;
|
||||
@@ -153,13 +154,11 @@ next_list(lua_State *L, struct encode_ud * self) {
|
||||
}
|
||||
|
||||
static int
|
||||
encode(const struct sproto_arg *args) {
|
||||
get_encodefield(const struct sproto_arg *args) {
|
||||
struct encode_ud *self = args->ud;
|
||||
lua_State *L = self->L;
|
||||
luaL_checkstack(L, 12, NULL);
|
||||
if (self->deep >= ENCODE_DEEPLEVEL)
|
||||
return luaL_error(L, "The table is too deep");
|
||||
if (args->index > 0) {
|
||||
int map = args->ktagname != NULL;
|
||||
if (args->tagname != self->array_tag) {
|
||||
// a new array
|
||||
self->array_tag = args->tagname;
|
||||
@@ -177,6 +176,13 @@ encode(const struct sproto_arg *args) {
|
||||
self->array_index = lua_gettop(L);
|
||||
}
|
||||
|
||||
if (map) {
|
||||
if (!self->map_entry) {
|
||||
lua_createtable(L, 0, 2); // key/value entry
|
||||
self->map_entry = lua_gettop(L);
|
||||
}
|
||||
}
|
||||
|
||||
if (luaL_getmetafield(L, self->array_index, "__pairs")) {
|
||||
lua_pushvalue(L, self->array_index);
|
||||
lua_call(L, 1, 3);
|
||||
@@ -194,26 +200,39 @@ encode(const struct sproto_arg *args) {
|
||||
self->iter_key = lua_gettop(L);
|
||||
}
|
||||
}
|
||||
if (args->mainindex >= 0) {
|
||||
if (args->mainindex >= 0) { // *type(mainindex)
|
||||
if (!next_list(L, self)) {
|
||||
// iterate end
|
||||
lua_pushnil(L);
|
||||
lua_replace(L, self->iter_key);
|
||||
return SPROTO_CB_NIL;
|
||||
}
|
||||
lua_insert(L, -2);
|
||||
lua_replace(L, self->iter_key);
|
||||
if (map) {
|
||||
lua_pushvalue(L, -2);
|
||||
lua_replace(L, self->iter_key);
|
||||
lua_setfield(L, self->map_entry, args->vtagname);
|
||||
lua_setfield(L, self->map_entry, args->ktagname);
|
||||
lua_pushvalue(L, self->map_entry);
|
||||
} else {
|
||||
lua_insert(L, -2);
|
||||
lua_replace(L, self->iter_key);
|
||||
}
|
||||
} else {
|
||||
lua_geti(L, self->array_index, args->index);
|
||||
}
|
||||
} else {
|
||||
lua_getfield(L, self->tbl_index, args->tagname);
|
||||
}
|
||||
if (lua_isnil(L, -1)) {
|
||||
lua_pop(L,1);
|
||||
return SPROTO_CB_NIL;
|
||||
}
|
||||
switch (args->type) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int encode(const struct sproto_arg *args);
|
||||
|
||||
static int
|
||||
encode_one(const struct sproto_arg *args, struct encode_ud *self) {
|
||||
lua_State *L = self->L;
|
||||
int type = args->type;
|
||||
switch (type) {
|
||||
case SPROTO_TINTEGER: {
|
||||
int64_t v;
|
||||
lua_Integer vh;
|
||||
@@ -282,6 +301,7 @@ encode(const struct sproto_arg *args) {
|
||||
sub.array_tag = NULL;
|
||||
sub.array_index = 0;
|
||||
sub.deep = self->deep + 1;
|
||||
sub.map_entry = 0;
|
||||
sub.iter_func = 0;
|
||||
sub.iter_table = 0;
|
||||
sub.iter_key = 0;
|
||||
@@ -296,6 +316,25 @@ encode(const struct sproto_arg *args) {
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
encode(const struct sproto_arg *args) {
|
||||
struct encode_ud *self = args->ud;
|
||||
lua_State *L = self->L;
|
||||
int code;
|
||||
luaL_checkstack(L, 12, NULL);
|
||||
if (self->deep >= ENCODE_DEEPLEVEL)
|
||||
return luaL_error(L, "The table is too deep");
|
||||
code = get_encodefield(args);
|
||||
if (code < 0) {
|
||||
return code;
|
||||
}
|
||||
if (lua_isnil(L, -1)) {
|
||||
lua_pop(L,1);
|
||||
return SPROTO_CB_NIL;
|
||||
}
|
||||
return encode_one(args, self);
|
||||
}
|
||||
|
||||
static void *
|
||||
expand_buffer(lua_State *L, int osz, int nsz) {
|
||||
void *output;
|
||||
@@ -342,6 +381,7 @@ lencode(lua_State *L) {
|
||||
self.deep = 0;
|
||||
|
||||
lua_settop(L, tbl_index);
|
||||
self.map_entry = 0;
|
||||
self.iter_func = 0;
|
||||
self.iter_table = 0;
|
||||
self.iter_key = 0;
|
||||
@@ -365,6 +405,7 @@ struct decode_ud {
|
||||
int deep;
|
||||
int mainindex_tag;
|
||||
int key_index;
|
||||
int map_entry;
|
||||
};
|
||||
|
||||
static int
|
||||
@@ -422,14 +463,24 @@ decode(const struct sproto_arg *args) {
|
||||
break;
|
||||
}
|
||||
case SPROTO_TSTRUCT: {
|
||||
int map = args->ktagname != NULL;
|
||||
struct decode_ud sub;
|
||||
int r;
|
||||
lua_newtable(L);
|
||||
sub.L = L;
|
||||
sub.result_index = lua_gettop(L);
|
||||
if (map) {
|
||||
if (!self->map_entry) {
|
||||
lua_newtable(L);
|
||||
self->map_entry = lua_gettop(L);
|
||||
}
|
||||
sub.result_index = self->map_entry;
|
||||
} else {
|
||||
lua_newtable(L);
|
||||
sub.result_index = lua_gettop(L);
|
||||
}
|
||||
sub.deep = self->deep + 1;
|
||||
sub.array_index = 0;
|
||||
sub.array_tag = NULL;
|
||||
sub.map_entry = 0;
|
||||
if (args->mainindex >= 0) {
|
||||
// This struct will set into a map, so mark the main index tag.
|
||||
sub.mainindex_tag = args->mainindex;
|
||||
@@ -441,13 +492,26 @@ decode(const struct sproto_arg *args) {
|
||||
return SPROTO_CB_ERROR;
|
||||
if (r != args->length)
|
||||
return r;
|
||||
lua_pushvalue(L, sub.key_index);
|
||||
if (lua_isnil(L, -1)) {
|
||||
luaL_error(L, "Can't find main index (tag=%d) in [%s]", args->mainindex, args->tagname);
|
||||
if (map) {
|
||||
lua_getfield(L, sub.result_index, args->ktagname);
|
||||
if (lua_isnil(L, -1)) {
|
||||
luaL_error(L, "Can't find key field in [%s]", args->tagname);
|
||||
}
|
||||
lua_getfield(L, sub.result_index, args->vtagname);
|
||||
if (lua_isnil(L, -1)) {
|
||||
luaL_error(L, "Can't find value field in [%s]", args->tagname);
|
||||
}
|
||||
lua_settable(L, self->array_index);
|
||||
lua_settop(L, sub.result_index);
|
||||
} else {
|
||||
lua_pushvalue(L, sub.key_index);
|
||||
if (lua_isnil(L, -1)) {
|
||||
luaL_error(L, "Can't find main index (tag=%d) in [%s]", args->mainindex, args->tagname);
|
||||
}
|
||||
lua_pushvalue(L, sub.result_index);
|
||||
lua_settable(L, self->array_index);
|
||||
lua_settop(L, sub.result_index-1);
|
||||
}
|
||||
lua_pushvalue(L, sub.result_index);
|
||||
lua_settable(L, self->array_index);
|
||||
lua_settop(L, sub.result_index-1);
|
||||
return 0;
|
||||
} else {
|
||||
sub.mainindex_tag = -1;
|
||||
@@ -524,6 +588,7 @@ ldecode(lua_State *L) {
|
||||
self.deep = 0;
|
||||
self.mainindex_tag = -1;
|
||||
self.key_index = 0;
|
||||
self.map_entry = 0;
|
||||
r = sproto_decode(st, buffer, (int)sz, decode, &self);
|
||||
if (r < 0) {
|
||||
return luaL_error(L, "decode error");
|
||||
@@ -679,7 +744,7 @@ lloadproto(lua_State *L) {
|
||||
}
|
||||
|
||||
static void
|
||||
push_default(const struct sproto_arg *args, int array) {
|
||||
push_default(const struct sproto_arg *args, int table) {
|
||||
lua_State *L = args->ud;
|
||||
switch(args->type) {
|
||||
case SPROTO_TINTEGER:
|
||||
@@ -698,7 +763,7 @@ push_default(const struct sproto_arg *args, int array) {
|
||||
lua_pushliteral(L, "");
|
||||
break;
|
||||
case SPROTO_TSTRUCT:
|
||||
if (array) {
|
||||
if (table) {
|
||||
lua_pushstring(L, sproto_name(args->subtype));
|
||||
} else {
|
||||
lua_createtable(L, 0, 1);
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#include "sproto.h"
|
||||
|
||||
#define SPROTO_TARRAY 0x80
|
||||
#define CHUNK_SIZE 1000
|
||||
#define SIZEOF_LENGTH 4
|
||||
#define SIZEOF_HEADER 2
|
||||
@@ -20,6 +19,7 @@ struct field {
|
||||
const char * name;
|
||||
struct sproto_type * st;
|
||||
int key;
|
||||
int map; // interpreted two fields struct as map
|
||||
int extra;
|
||||
};
|
||||
|
||||
@@ -206,6 +206,7 @@ import_field(struct sproto *s, struct field *f, const uint8_t * stream) {
|
||||
f->name = NULL;
|
||||
f->st = NULL;
|
||||
f->key = -1;
|
||||
f->map = -1;
|
||||
f->extra = 0;
|
||||
|
||||
sz = todword(stream);
|
||||
@@ -262,6 +263,10 @@ import_field(struct sproto *s, struct field *f, const uint8_t * stream) {
|
||||
case 5: // key
|
||||
f->key = value;
|
||||
break;
|
||||
case 6: // map
|
||||
if (value)
|
||||
f->map = 1;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
@@ -281,6 +286,8 @@ import_field(struct sproto *s, struct field *f, const uint8_t * stream) {
|
||||
type 2 : integer
|
||||
tag 3 : integer
|
||||
array 4 : boolean
|
||||
key 5 : integer
|
||||
map 6 : boolean // Interpreted two fields struct as map when decoding
|
||||
}
|
||||
name 0 : string
|
||||
fields 1 : *field
|
||||
@@ -487,6 +494,32 @@ sproto_release(struct sproto * s) {
|
||||
pool_release(&s->memory);
|
||||
}
|
||||
|
||||
static const char *
|
||||
get_typename(int type, struct field *f) {
|
||||
if (type == SPROTO_TSTRUCT) {
|
||||
return f->st->name;
|
||||
} else {
|
||||
switch (type) {
|
||||
case SPROTO_TINTEGER:
|
||||
if (f->extra)
|
||||
return "decimal";
|
||||
else
|
||||
return "integer";
|
||||
case SPROTO_TBOOLEAN:
|
||||
return "boolean";
|
||||
case SPROTO_TSTRING:
|
||||
if (f->extra == SPROTO_TSTRING_BINARY)
|
||||
return "binary";
|
||||
else
|
||||
return "string";
|
||||
case SPROTO_TDOUBLE:
|
||||
return "double";
|
||||
default:
|
||||
return "invalid";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sproto_dump(struct sproto *s) {
|
||||
int i,j;
|
||||
@@ -495,50 +528,30 @@ sproto_dump(struct sproto *s) {
|
||||
struct sproto_type *t = &s->type[i];
|
||||
printf("%s\n", t->name);
|
||||
for (j=0;j<t->n;j++) {
|
||||
char array[2] = { 0, 0 };
|
||||
const char * type_name = NULL;
|
||||
char container[2] = { 0, 0 };
|
||||
const char * typename = NULL;
|
||||
struct field *f = &t->f[j];
|
||||
int type = f->type & ~SPROTO_TARRAY;
|
||||
if (f->type & SPROTO_TARRAY) {
|
||||
array[0] = '*';
|
||||
container[0] = '*';
|
||||
} else {
|
||||
array[0] = 0;
|
||||
container[0] = 0;
|
||||
}
|
||||
if (type == SPROTO_TSTRUCT) {
|
||||
type_name = f->st->name;
|
||||
} else {
|
||||
switch(type) {
|
||||
case SPROTO_TINTEGER:
|
||||
if (f->extra) {
|
||||
type_name = "decimal";
|
||||
} else {
|
||||
type_name = "integer";
|
||||
}
|
||||
break;
|
||||
case SPROTO_TBOOLEAN:
|
||||
type_name = "boolean";
|
||||
break;
|
||||
case SPROTO_TSTRING:
|
||||
if (f->extra == SPROTO_TSTRING_BINARY)
|
||||
type_name = "binary";
|
||||
else
|
||||
type_name = "string";
|
||||
break;
|
||||
default:
|
||||
type_name = "invalid";
|
||||
break;
|
||||
}
|
||||
}
|
||||
printf("\t%s (%d) %s%s", f->name, f->tag, array, type_name);
|
||||
typename = get_typename(type, f);
|
||||
printf("\t%s (%d) %s%s", f->name, f->tag, container, typename);
|
||||
if (type == SPROTO_TINTEGER && f->extra > 0) {
|
||||
printf("(%d)", f->extra);
|
||||
}
|
||||
if (f->key >= 0) {
|
||||
printf("[%d]", f->key);
|
||||
printf(" key[%d]", f->key);
|
||||
if (f->map >= 0) {
|
||||
printf(" value[%d]", f->st->f[1].tag);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
printf("=== %d protocol ===\n", s->protocol_n);
|
||||
for (i=0;i<s->protocol_n;i++) {
|
||||
struct protocol *p = &s->proto[i];
|
||||
@@ -840,6 +853,36 @@ encode_integer_array(sproto_callback cb, struct sproto_arg *args, uint8_t *buffe
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static uint8_t *
|
||||
encode_array_object(sproto_callback cb, struct sproto_arg *args, uint8_t *buffer, int size, int *noarray) {
|
||||
int sz;
|
||||
*noarray = 0;
|
||||
args->index = 1;
|
||||
for (;;) {
|
||||
if (size < SIZEOF_LENGTH)
|
||||
return NULL;
|
||||
size -= SIZEOF_LENGTH;
|
||||
args->value = buffer + SIZEOF_LENGTH;
|
||||
args->length = size;
|
||||
sz = cb(args);
|
||||
if (sz < 0) {
|
||||
if (sz == SPROTO_CB_NIL) {
|
||||
break;
|
||||
}
|
||||
if (sz == SPROTO_CB_NOARRAY) { // no array, don't encode it
|
||||
*noarray = 1;
|
||||
break;
|
||||
}
|
||||
return NULL; // sz == SPROTO_CB_ERROR
|
||||
}
|
||||
fill_size(buffer, sz);
|
||||
buffer += SIZEOF_LENGTH+sz;
|
||||
size -= sz;
|
||||
++args->index;
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static int
|
||||
encode_array(sproto_callback cb, struct sproto_arg *args, uint8_t *data, int size) {
|
||||
uint8_t * buffer;
|
||||
@@ -883,30 +926,16 @@ encode_array(sproto_callback cb, struct sproto_arg *args, uint8_t *data, int siz
|
||||
++args->index;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
args->index = 1;
|
||||
for (;;) {
|
||||
if (size < SIZEOF_LENGTH)
|
||||
return -1;
|
||||
size -= SIZEOF_LENGTH;
|
||||
args->value = buffer+SIZEOF_LENGTH;
|
||||
args->length = size;
|
||||
sz = cb(args);
|
||||
if (sz < 0) {
|
||||
if (sz == SPROTO_CB_NIL) {
|
||||
break;
|
||||
}
|
||||
if (sz == SPROTO_CB_NOARRAY) // no array, don't encode it
|
||||
return 0;
|
||||
return -1; // sz == SPROTO_CB_ERROR
|
||||
}
|
||||
fill_size(buffer, sz);
|
||||
buffer += SIZEOF_LENGTH+sz;
|
||||
size -=sz;
|
||||
++args->index;
|
||||
}
|
||||
default: {
|
||||
int noarray;
|
||||
buffer = encode_array_object(cb, args, buffer, size, &noarray);
|
||||
if (buffer == NULL)
|
||||
return -1;
|
||||
if (noarray)
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
sz = buffer - (data + SIZEOF_LENGTH);
|
||||
return fill_size(data, sz);
|
||||
}
|
||||
@@ -938,8 +967,14 @@ sproto_encode(const struct sproto_type *st, void * buffer, int size, sproto_call
|
||||
args.subtype = f->st;
|
||||
args.mainindex = f->key;
|
||||
args.extra = f->extra;
|
||||
args.ktagname = NULL;
|
||||
args.vtagname = NULL;
|
||||
if (type & SPROTO_TARRAY) {
|
||||
args.type = type & ~SPROTO_TARRAY;
|
||||
args.type = type & (~SPROTO_TARRAY);
|
||||
if (f->map > 0) {
|
||||
args.ktagname = f->st->f[0].name;
|
||||
args.vtagname = f->st->f[1].name;
|
||||
}
|
||||
sz = encode_array(cb, &args, data, size);
|
||||
} else {
|
||||
args.type = type;
|
||||
@@ -1168,13 +1203,20 @@ sproto_decode(const struct sproto_type *st, const void * data, int size, sproto_
|
||||
continue;
|
||||
args.tagname = f->name;
|
||||
args.tagid = f->tag;
|
||||
args.type = f->type & ~SPROTO_TARRAY;
|
||||
args.type = f->type;
|
||||
args.subtype = f->st;
|
||||
args.index = 0;
|
||||
args.mainindex = f->key;
|
||||
args.extra = f->extra;
|
||||
args.ktagname = NULL;
|
||||
args.vtagname = NULL;
|
||||
if (value < 0) {
|
||||
if (f->type & SPROTO_TARRAY) {
|
||||
args.type = f->type & (~SPROTO_TARRAY);
|
||||
if (f->map > 0) {
|
||||
args.ktagname = f->st->f[0].name;
|
||||
args.vtagname = f->st->f[1].name;
|
||||
}
|
||||
if (decode_array(cb, &args, currentdata)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@ struct sproto_type;
|
||||
#define SPROTO_TDOUBLE 3
|
||||
#define SPROTO_TSTRUCT 4
|
||||
|
||||
// container type
|
||||
#define SPROTO_TARRAY 0x80
|
||||
|
||||
// sub type of string (sproto_arg.extra)
|
||||
#define SPROTO_TSTRING_STRING 0
|
||||
#define SPROTO_TSTRING_BINARY 1
|
||||
@@ -46,9 +49,13 @@ struct sproto_arg {
|
||||
struct sproto_type *subtype;
|
||||
void *value;
|
||||
int length;
|
||||
int index; // array base 1
|
||||
int index; // array base 1, negative value indicates that it is a empty array
|
||||
int mainindex; // for map
|
||||
int extra; // SPROTO_TINTEGER: decimal ; SPROTO_TSTRING 0:utf8 string 1:binary
|
||||
|
||||
// When interpretd two fields struct as map, the following fields must not be NULL.
|
||||
const char *ktagname;
|
||||
const char *vtagname;
|
||||
};
|
||||
|
||||
typedef int (*sproto_callback)(const struct sproto_arg *args);
|
||||
|
||||
Reference in New Issue
Block a user