diff --git a/lualib-src/sproto/README.md b/lualib-src/sproto/README.md index ea5bc7be..67366a46 100644 --- a/lualib-src/sproto/README.md +++ b/lualib-src/sproto/README.md @@ -210,13 +210,13 @@ 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). The tags of fields can be discontinuous, if a field is nil. (default value in lua), don't encode it in message. +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 ; +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 . diff --git a/lualib-src/sproto/lsproto.c b/lualib-src/sproto/lsproto.c index 6214fdc9..3a70a6d4 100644 --- a/lualib-src/sproto/lsproto.c +++ b/lualib-src/sproto/lsproto.c @@ -126,7 +126,7 @@ encode(const struct sproto_arg *args) { lua_replace(L, self->array_index); } self->array_index = 0; - return 0; + return SPROTO_CB_NOARRAY; } if (!lua_istable(L, -1)) { return luaL_error(L, ".*%s(%d) should be a table (Is a %s)", @@ -147,7 +147,7 @@ encode(const struct sproto_arg *args) { // iterate end lua_pushnil(L); lua_replace(L, self->iter_index); - return 0; + return SPROTO_CB_NIL; } lua_insert(L, -2); lua_replace(L, self->iter_index); @@ -159,7 +159,7 @@ encode(const struct sproto_arg *args) { } if (lua_isnil(L, -1)) { lua_pop(L,1); - return 0; + return SPROTO_CB_NIL; } switch (args->type) { case SPROTO_TINTEGER: { @@ -203,10 +203,10 @@ encode(const struct sproto_arg *args) { str = lua_tolstring(L, -1, &sz); } if (sz > args->length) - return -1; + return SPROTO_CB_ERROR; memcpy(args->value, str, sz); lua_pop(L,1); - return sz + 1; // The length of empty string is 1. + return sz; } case SPROTO_TSTRUCT: { struct encode_ud sub; @@ -226,6 +226,8 @@ encode(const struct sproto_arg *args) { sub.iter_index = sub.tbl_index + 1; r = sproto_encode(args->subtype, args->value, args->length, encode, &sub); lua_settop(L, top-1); // pop the value + if (r < 0) + return SPROTO_CB_ERROR; return r; } default: @@ -309,7 +311,7 @@ decode(const struct sproto_arg *args) { lua_State *L = self->L; if (self->deep >= ENCODE_DEEPLEVEL) return luaL_error(L, "The table is too deep"); - if (args->index > 0) { + if (args->index != 0) { // It's array if (args->tagname != self->array_tag) { self->array_tag = args->tagname; @@ -321,6 +323,10 @@ decode(const struct sproto_arg *args) { } else { self->array_index = lua_gettop(L); } + if (args->index < 0) { + // It's a empty array, return now. + return 0; + } } } switch (args->type) { @@ -355,9 +361,10 @@ decode(const struct sproto_arg *args) { sub.key_index = lua_gettop(L); r = sproto_decode(args->subtype, args->value, args->length, decode, &sub); - if (r < 0 || r != args->length) + if (r < 0) + return SPROTO_CB_ERROR; + if (r != args->length) return r; - // assert(args->index > 0); 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); @@ -370,7 +377,9 @@ decode(const struct sproto_arg *args) { sub.mainindex_tag = -1; sub.key_index = 0; r = sproto_decode(args->subtype, args->value, args->length, decode, &sub); - if (r < 0 || r != args->length) + if (r < 0) + return SPROTO_CB_ERROR; + if (r != args->length) return r; lua_settop(L, sub.result_index); break; diff --git a/lualib-src/sproto/sproto.c b/lualib-src/sproto/sproto.c index 68ccd047..9b23af49 100644 --- a/lualib-src/sproto/sproto.c +++ b/lualib-src/sproto/sproto.c @@ -677,10 +677,10 @@ encode_object(sproto_callback cb, struct sproto_arg *args, uint8_t *data, int si args->value = data+SIZEOF_LENGTH; args->length = size-SIZEOF_LENGTH; sz = cb(args); - if (sz <= 0) - return sz; - if (args->type == SPROTO_TSTRING) { - --sz; // the length of null string is 1 + if (sz < 0) { + if (sz == SPROTO_CB_NIL) + return 0; + return -1; // sz == SPROTO_CB_ERROR } assert(sz <= size-SIZEOF_LENGTH); // verify buffer overflow return fill_size(data, sz); @@ -702,7 +702,7 @@ uint32_to_uint64(int negative, uint8_t *buffer) { } static uint8_t * -encode_integer_array(sproto_callback cb, struct sproto_arg *args, uint8_t *buffer, int size) { +encode_integer_array(sproto_callback cb, struct sproto_arg *args, uint8_t *buffer, int size, int *noarray) { uint8_t * header = buffer; int intlen; int index; @@ -712,6 +712,8 @@ encode_integer_array(sproto_callback cb, struct sproto_arg *args, uint8_t *buffe size--; intlen = sizeof(uint32_t); index = 1; + *noarray = 0; + for (;;) { int sz; union { @@ -722,10 +724,15 @@ encode_integer_array(sproto_callback cb, struct sproto_arg *args, uint8_t *buffe args->length = sizeof(u); args->index = index; sz = cb(args); - if (sz < 0) - return NULL; - if (sz == 0) // nil object, end of array - break; + if (sz <= 0) { + if (sz == SPROTO_CB_NIL) // nil object, end of array + break; + if (sz == SPROTO_CB_NOARRAY) { // no array, don't encode it + *noarray = 1; + break; + } + return NULL; // sz == SPROTO_CB_ERROR + } if (size < sizeof(uint64_t)) return NULL; if (sz == sizeof(uint32_t)) { @@ -790,11 +797,17 @@ encode_array(sproto_callback cb, struct sproto_arg *args, uint8_t *data, int siz size -= SIZEOF_LENGTH; buffer = data + SIZEOF_LENGTH; switch (args->type) { - case SPROTO_TINTEGER: - buffer = encode_integer_array(cb,args,buffer,size); + case SPROTO_TINTEGER: { + int noarray; + buffer = encode_integer_array(cb,args,buffer,size, &noarray); if (buffer == NULL) return -1; + + if (noarray) { + return 0; + } break; + } case SPROTO_TBOOLEAN: args->index = 1; for (;;) { @@ -802,10 +815,13 @@ encode_array(sproto_callback cb, struct sproto_arg *args, uint8_t *data, int siz args->value = &v; args->length = sizeof(v); sz = cb(args); - if (sz < 0) - return -1; - if (sz == 0) // nil object , end of array - break; + if (sz < 0) { + if (sz == SPROTO_CB_NIL) // nil object , end of array + break; + if (sz == SPROTO_CB_NOARRAY) // no array, don't encode it + return 0; + return -1; // sz == SPROTO_CB_ERROR + } if (size < 1) return -1; buffer[0] = v ? 1: 0; @@ -823,12 +839,13 @@ encode_array(sproto_callback cb, struct sproto_arg *args, uint8_t *data, int siz args->value = buffer+SIZEOF_LENGTH; args->length = size; sz = cb(args); - if (sz == 0) - break; - if (sz < 0) - return -1; - if (args->type == SPROTO_TSTRING) { - --sz; + 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; @@ -838,8 +855,6 @@ encode_array(sproto_callback cb, struct sproto_arg *args, uint8_t *data, int siz break; } sz = buffer - (data + SIZEOF_LENGTH); - if (sz == 0) // empty array - return 0; return fill_size(data, sz); } @@ -885,10 +900,13 @@ sproto_encode(const struct sproto_type *st, void * buffer, int size, sproto_call args.value = &u; args.length = sizeof(u); sz = cb(&args); - if (sz < 0) - return -1; - if (sz == 0) // nil object - continue; + if (sz < 0) { + if (sz == SPROTO_CB_NIL) + continue; + if (sz == SPROTO_CB_NOARRAY) // no array, don't encode it + return 0; + return -1; // sz == SPROTO_CB_ERROR + } if (sz == sizeof(uint32_t)) { if (u.u32 < 0x7fff) { value = (u.u32+1) * 2; @@ -985,13 +1003,18 @@ decode_array(sproto_callback cb, struct sproto_arg *args, uint8_t * stream) { uint32_t sz = todword(stream); int type = args->type; int i; + if (sz == 0) { + // It's empty array, call cb with index == -1 to create the empty array. + args->index = -1; + args->value = NULL; + args->length = 0; + cb(args); + return 0; + } stream += SIZEOF_LENGTH; switch (type) { case SPROTO_TINTEGER: { - int len; - if (sz < 1) - return -1; - len = *stream; + int len = *stream; ++stream; --sz; if (len == sizeof(uint32_t)) { diff --git a/lualib-src/sproto/sproto.h b/lualib-src/sproto/sproto.h index 0a419c86..c38a12c0 100644 --- a/lualib-src/sproto/sproto.h +++ b/lualib-src/sproto/sproto.h @@ -14,6 +14,10 @@ struct sproto_type; #define SPROTO_TSTRING 2 #define SPROTO_TSTRUCT 3 +#define SPROTO_CB_ERROR -1 +#define SPROTO_CB_NIL -2 +#define SPROTO_CB_NOARRAY -3 + struct sproto * sproto_create(const void * proto, size_t sz); void sproto_release(struct sproto *);