1431 lines
29 KiB
C
1431 lines
29 KiB
C
|
|
#include <stdlib.h>
|
||
|
|
#include <string.h>
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <assert.h>
|
||
|
|
#include "msvcint.h"
|
||
|
|
|
||
|
|
#include "sproto.h"
|
||
|
|
|
||
|
|
#define CHUNK_SIZE 1000
|
||
|
|
#define SIZEOF_LENGTH 4
|
||
|
|
#define SIZEOF_HEADER 2
|
||
|
|
#define SIZEOF_FIELD 2
|
||
|
|
#define SIZEOF_INT64 ((int)sizeof(uint64_t))
|
||
|
|
#define SIZEOF_INT32 ((int)sizeof(uint32_t))
|
||
|
|
|
||
|
|
struct field {
|
||
|
|
int tag;
|
||
|
|
int type;
|
||
|
|
const char * name;
|
||
|
|
struct sproto_type * st;
|
||
|
|
int key;
|
||
|
|
int map; // interpreted two fields struct as map
|
||
|
|
int extra;
|
||
|
|
};
|
||
|
|
|
||
|
|
struct sproto_type {
|
||
|
|
const char * name;
|
||
|
|
int n;
|
||
|
|
int base;
|
||
|
|
int maxn;
|
||
|
|
struct field *f;
|
||
|
|
};
|
||
|
|
|
||
|
|
struct protocol {
|
||
|
|
const char *name;
|
||
|
|
int tag;
|
||
|
|
int confirm; // confirm == 1 where response nil
|
||
|
|
struct sproto_type * p[2];
|
||
|
|
};
|
||
|
|
|
||
|
|
struct chunk {
|
||
|
|
struct chunk * next;
|
||
|
|
};
|
||
|
|
|
||
|
|
struct pool {
|
||
|
|
struct chunk * header;
|
||
|
|
struct chunk * current;
|
||
|
|
int current_used;
|
||
|
|
};
|
||
|
|
|
||
|
|
struct sproto {
|
||
|
|
struct pool memory;
|
||
|
|
int type_n;
|
||
|
|
int protocol_n;
|
||
|
|
struct sproto_type * type;
|
||
|
|
struct protocol * proto;
|
||
|
|
};
|
||
|
|
|
||
|
|
static void
|
||
|
|
pool_init(struct pool *p) {
|
||
|
|
p->header = NULL;
|
||
|
|
p->current = NULL;
|
||
|
|
p->current_used = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void
|
||
|
|
pool_release(struct pool *p) {
|
||
|
|
struct chunk * tmp = p->header;
|
||
|
|
while (tmp) {
|
||
|
|
struct chunk * n = tmp->next;
|
||
|
|
free(tmp);
|
||
|
|
tmp = n;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void *
|
||
|
|
pool_newchunk(struct pool *p, size_t sz) {
|
||
|
|
struct chunk * t = malloc(sz + sizeof(struct chunk));
|
||
|
|
if (t == NULL)
|
||
|
|
return NULL;
|
||
|
|
t->next = p->header;
|
||
|
|
p->header = t;
|
||
|
|
return t+1;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void *
|
||
|
|
pool_alloc(struct pool *p, size_t sz) {
|
||
|
|
// align by 8
|
||
|
|
sz = (sz + 7) & ~7;
|
||
|
|
if (sz >= CHUNK_SIZE) {
|
||
|
|
return pool_newchunk(p, sz);
|
||
|
|
}
|
||
|
|
if (p->current == NULL) {
|
||
|
|
if (pool_newchunk(p, CHUNK_SIZE) == NULL)
|
||
|
|
return NULL;
|
||
|
|
p->current = p->header;
|
||
|
|
}
|
||
|
|
if (sz + p->current_used <= CHUNK_SIZE) {
|
||
|
|
void * ret = (char *)(p->current+1) + p->current_used;
|
||
|
|
p->current_used += sz;
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (sz >= p->current_used) {
|
||
|
|
return pool_newchunk(p, sz);
|
||
|
|
} else {
|
||
|
|
void * ret = pool_newchunk(p, CHUNK_SIZE);
|
||
|
|
p->current = p->header;
|
||
|
|
p->current_used = sz;
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline int
|
||
|
|
toword(const uint8_t * p) {
|
||
|
|
return p[0] | p[1]<<8;
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline uint32_t
|
||
|
|
todword(const uint8_t *p) {
|
||
|
|
return p[0] | p[1]<<8 | p[2]<<16 | p[3]<<24;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int
|
||
|
|
count_array(const uint8_t * stream) {
|
||
|
|
uint32_t length = todword(stream);
|
||
|
|
int n = 0;
|
||
|
|
stream += SIZEOF_LENGTH;
|
||
|
|
while (length > 0) {
|
||
|
|
uint32_t nsz;
|
||
|
|
if (length < SIZEOF_LENGTH)
|
||
|
|
return -1;
|
||
|
|
nsz = todword(stream);
|
||
|
|
nsz += SIZEOF_LENGTH;
|
||
|
|
if (nsz > length)
|
||
|
|
return -1;
|
||
|
|
++n;
|
||
|
|
stream += nsz;
|
||
|
|
length -= nsz;
|
||
|
|
}
|
||
|
|
|
||
|
|
return n;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int
|
||
|
|
struct_field(const uint8_t * stream, size_t sz) {
|
||
|
|
const uint8_t * field;
|
||
|
|
int fn, header, i;
|
||
|
|
if (sz < SIZEOF_LENGTH)
|
||
|
|
return -1;
|
||
|
|
fn = toword(stream);
|
||
|
|
header = SIZEOF_HEADER + SIZEOF_FIELD * fn;
|
||
|
|
if (sz < header)
|
||
|
|
return -1;
|
||
|
|
field = stream + SIZEOF_HEADER;
|
||
|
|
sz -= header;
|
||
|
|
stream += header;
|
||
|
|
for (i=0;i<fn;i++) {
|
||
|
|
int value= toword(field + i * SIZEOF_FIELD);
|
||
|
|
uint32_t dsz;
|
||
|
|
if (value != 0)
|
||
|
|
continue;
|
||
|
|
if (sz < SIZEOF_LENGTH)
|
||
|
|
return -1;
|
||
|
|
dsz = todword(stream);
|
||
|
|
if (sz < SIZEOF_LENGTH + dsz)
|
||
|
|
return -1;
|
||
|
|
stream += SIZEOF_LENGTH + dsz;
|
||
|
|
sz -= SIZEOF_LENGTH + dsz;
|
||
|
|
}
|
||
|
|
|
||
|
|
return fn;
|
||
|
|
}
|
||
|
|
|
||
|
|
static const char *
|
||
|
|
import_string(struct sproto *s, const uint8_t * stream) {
|
||
|
|
uint32_t sz = todword(stream);
|
||
|
|
char * buffer = pool_alloc(&s->memory, sz+1);
|
||
|
|
memcpy(buffer, stream+SIZEOF_LENGTH, sz);
|
||
|
|
buffer[sz] = '\0';
|
||
|
|
return buffer;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int
|
||
|
|
calc_pow(int base, int n) {
|
||
|
|
int r;
|
||
|
|
if (n == 0)
|
||
|
|
return 1;
|
||
|
|
r = calc_pow(base * base , n / 2);
|
||
|
|
if (n&1) {
|
||
|
|
r *= base;
|
||
|
|
}
|
||
|
|
return r;
|
||
|
|
}
|
||
|
|
|
||
|
|
static const uint8_t *
|
||
|
|
import_field(struct sproto *s, struct field *f, const uint8_t * stream) {
|
||
|
|
uint32_t sz;
|
||
|
|
const uint8_t * result;
|
||
|
|
int fn;
|
||
|
|
int i;
|
||
|
|
int array = 0;
|
||
|
|
int tag = -1;
|
||
|
|
f->tag = -1;
|
||
|
|
f->type = -1;
|
||
|
|
f->name = NULL;
|
||
|
|
f->st = NULL;
|
||
|
|
f->key = -1;
|
||
|
|
f->map = -1;
|
||
|
|
f->extra = 0;
|
||
|
|
|
||
|
|
sz = todword(stream);
|
||
|
|
stream += SIZEOF_LENGTH;
|
||
|
|
result = stream + sz;
|
||
|
|
fn = struct_field(stream, sz);
|
||
|
|
if (fn < 0)
|
||
|
|
return NULL;
|
||
|
|
stream += SIZEOF_HEADER;
|
||
|
|
for (i=0;i<fn;i++) {
|
||
|
|
int value;
|
||
|
|
++tag;
|
||
|
|
value = toword(stream + SIZEOF_FIELD * i);
|
||
|
|
if (value & 1) {
|
||
|
|
tag+= value/2;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (tag == 0) { // name
|
||
|
|
if (value != 0)
|
||
|
|
return NULL;
|
||
|
|
f->name = import_string(s, stream + fn * SIZEOF_FIELD);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (value == 0)
|
||
|
|
return NULL;
|
||
|
|
value = value/2 - 1;
|
||
|
|
switch(tag) {
|
||
|
|
case 1: // buildin
|
||
|
|
if (value >= SPROTO_TSTRUCT)
|
||
|
|
return NULL; // invalid buildin type
|
||
|
|
f->type = value;
|
||
|
|
break;
|
||
|
|
case 2: // type index
|
||
|
|
if (f->type == SPROTO_TINTEGER) {
|
||
|
|
f->extra = calc_pow(10, value);
|
||
|
|
} else if (f->type == SPROTO_TSTRING) {
|
||
|
|
f->extra = value; // string if 0 ; binary is 1
|
||
|
|
} else {
|
||
|
|
if (value >= s->type_n)
|
||
|
|
return NULL; // invalid type index
|
||
|
|
if (f->type >= 0)
|
||
|
|
return NULL;
|
||
|
|
f->type = SPROTO_TSTRUCT;
|
||
|
|
f->st = &s->type[value];
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
case 3: // tag
|
||
|
|
f->tag = value;
|
||
|
|
break;
|
||
|
|
case 4: // array
|
||
|
|
if (value)
|
||
|
|
array = SPROTO_TARRAY;
|
||
|
|
break;
|
||
|
|
case 5: // key
|
||
|
|
f->key = value;
|
||
|
|
break;
|
||
|
|
case 6: // map
|
||
|
|
if (value)
|
||
|
|
f->map = 1;
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (f->tag < 0 || f->type < 0 || f->name == NULL)
|
||
|
|
return NULL;
|
||
|
|
f->type |= array;
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
.type {
|
||
|
|
.field {
|
||
|
|
name 0 : string
|
||
|
|
buildin 1 : integer
|
||
|
|
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
|
||
|
|
}
|
||
|
|
*/
|
||
|
|
static const uint8_t *
|
||
|
|
import_type(struct sproto *s, struct sproto_type *t, const uint8_t * stream) {
|
||
|
|
const uint8_t * result;
|
||
|
|
uint32_t sz = todword(stream);
|
||
|
|
int i;
|
||
|
|
int fn;
|
||
|
|
int n;
|
||
|
|
int maxn;
|
||
|
|
int last;
|
||
|
|
stream += SIZEOF_LENGTH;
|
||
|
|
result = stream + sz;
|
||
|
|
fn = struct_field(stream, sz);
|
||
|
|
if (fn <= 0 || fn > 2)
|
||
|
|
return NULL;
|
||
|
|
for (i=0;i<fn*SIZEOF_FIELD;i+=SIZEOF_FIELD) {
|
||
|
|
// name and fields must encode to 0
|
||
|
|
int v = toword(stream + SIZEOF_HEADER + i);
|
||
|
|
if (v != 0)
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
memset(t, 0, sizeof(*t));
|
||
|
|
stream += SIZEOF_HEADER + fn * SIZEOF_FIELD;
|
||
|
|
t->name = import_string(s, stream);
|
||
|
|
if (fn == 1) {
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
stream += todword(stream)+SIZEOF_LENGTH; // second data
|
||
|
|
n = count_array(stream);
|
||
|
|
if (n<0)
|
||
|
|
return NULL;
|
||
|
|
stream += SIZEOF_LENGTH;
|
||
|
|
maxn = n;
|
||
|
|
last = -1;
|
||
|
|
t->n = n;
|
||
|
|
t->f = pool_alloc(&s->memory, sizeof(struct field) * n);
|
||
|
|
for (i=0;i<n;i++) {
|
||
|
|
int tag;
|
||
|
|
struct field *f = &t->f[i];
|
||
|
|
stream = import_field(s, f, stream);
|
||
|
|
if (stream == NULL)
|
||
|
|
return NULL;
|
||
|
|
tag = f->tag;
|
||
|
|
if (tag <= last)
|
||
|
|
return NULL; // tag must in ascending order
|
||
|
|
if (tag > last+1) {
|
||
|
|
++maxn;
|
||
|
|
}
|
||
|
|
last = tag;
|
||
|
|
}
|
||
|
|
t->maxn = maxn;
|
||
|
|
t->base = t->f[0].tag;
|
||
|
|
n = t->f[n-1].tag - t->base + 1;
|
||
|
|
if (n != t->n) {
|
||
|
|
t->base = -1;
|
||
|
|
}
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
.protocol {
|
||
|
|
name 0 : string
|
||
|
|
tag 1 : integer
|
||
|
|
request 2 : integer
|
||
|
|
response 3 : integer
|
||
|
|
}
|
||
|
|
*/
|
||
|
|
static const uint8_t *
|
||
|
|
import_protocol(struct sproto *s, struct protocol *p, const uint8_t * stream) {
|
||
|
|
const uint8_t * result;
|
||
|
|
uint32_t sz = todword(stream);
|
||
|
|
int fn;
|
||
|
|
int i;
|
||
|
|
int tag;
|
||
|
|
stream += SIZEOF_LENGTH;
|
||
|
|
result = stream + sz;
|
||
|
|
fn = struct_field(stream, sz);
|
||
|
|
stream += SIZEOF_HEADER;
|
||
|
|
p->name = NULL;
|
||
|
|
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);
|
||
|
|
if (value & 1) {
|
||
|
|
tag += (value-1)/2;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
value = value/2 - 1;
|
||
|
|
switch (i) {
|
||
|
|
case 0: // name
|
||
|
|
if (value != -1) {
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
p->name = import_string(s, stream + SIZEOF_FIELD *fn);
|
||
|
|
break;
|
||
|
|
case 1: // tag
|
||
|
|
if (value < 0) {
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
p->tag = value;
|
||
|
|
break;
|
||
|
|
case 2: // request
|
||
|
|
if (value < 0 || value>=s->type_n)
|
||
|
|
return NULL;
|
||
|
|
p->p[SPROTO_REQUEST] = &s->type[value];
|
||
|
|
break;
|
||
|
|
case 3: // response
|
||
|
|
if (value < 0 || value>=s->type_n)
|
||
|
|
return NULL;
|
||
|
|
p->p[SPROTO_RESPONSE] = &s->type[value];
|
||
|
|
break;
|
||
|
|
case 4: // confirm
|
||
|
|
p->confirm = value;
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (p->name == NULL || p->tag<0) {
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
static struct sproto *
|
||
|
|
create_from_bundle(struct sproto *s, const uint8_t * stream, size_t sz) {
|
||
|
|
const uint8_t * content;
|
||
|
|
const uint8_t * typedata = NULL;
|
||
|
|
const uint8_t * protocoldata = NULL;
|
||
|
|
int fn = struct_field(stream, sz);
|
||
|
|
int i;
|
||
|
|
if (fn < 0 || fn > 2)
|
||
|
|
return NULL;
|
||
|
|
|
||
|
|
stream += SIZEOF_HEADER;
|
||
|
|
content = stream + fn*SIZEOF_FIELD;
|
||
|
|
|
||
|
|
for (i=0;i<fn;i++) {
|
||
|
|
int value = toword(stream + i*SIZEOF_FIELD);
|
||
|
|
int n;
|
||
|
|
if (value != 0)
|
||
|
|
return NULL;
|
||
|
|
n = count_array(content);
|
||
|
|
if (n<0)
|
||
|
|
return NULL;
|
||
|
|
if (i == 0) {
|
||
|
|
typedata = content+SIZEOF_LENGTH;
|
||
|
|
s->type_n = n;
|
||
|
|
s->type = pool_alloc(&s->memory, n * sizeof(*s->type));
|
||
|
|
} else {
|
||
|
|
protocoldata = content+SIZEOF_LENGTH;
|
||
|
|
s->protocol_n = n;
|
||
|
|
s->proto = pool_alloc(&s->memory, n * sizeof(*s->proto));
|
||
|
|
}
|
||
|
|
content += todword(content) + SIZEOF_LENGTH;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (i=0;i<s->type_n;i++) {
|
||
|
|
typedata = import_type(s, &s->type[i], typedata);
|
||
|
|
if (typedata == NULL) {
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
for (i=0;i<s->protocol_n;i++) {
|
||
|
|
protocoldata = import_protocol(s, &s->proto[i], protocoldata);
|
||
|
|
if (protocoldata == NULL) {
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return s;
|
||
|
|
}
|
||
|
|
|
||
|
|
struct sproto *
|
||
|
|
sproto_create(const void * proto, size_t sz) {
|
||
|
|
struct pool mem;
|
||
|
|
struct sproto * s;
|
||
|
|
pool_init(&mem);
|
||
|
|
s = pool_alloc(&mem, sizeof(*s));
|
||
|
|
if (s == NULL)
|
||
|
|
return NULL;
|
||
|
|
memset(s, 0, sizeof(*s));
|
||
|
|
s->memory = mem;
|
||
|
|
if (create_from_bundle(s, proto, sz) == NULL) {
|
||
|
|
pool_release(&s->memory);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
return s;
|
||
|
|
}
|
||
|
|
|
||
|
|
void
|
||
|
|
sproto_release(struct sproto * s) {
|
||
|
|
if (s == NULL)
|
||
|
|
return;
|
||
|
|
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;
|
||
|
|
printf("=== %d types ===\n", s->type_n);
|
||
|
|
for (i=0;i<s->type_n;i++) {
|
||
|
|
struct sproto_type *t = &s->type[i];
|
||
|
|
printf("%s\n", t->name);
|
||
|
|
for (j=0;j<t->n;j++) {
|
||
|
|
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) {
|
||
|
|
container[0] = '*';
|
||
|
|
} else {
|
||
|
|
container[0] = 0;
|
||
|
|
}
|
||
|
|
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(" 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];
|
||
|
|
if (p->p[SPROTO_REQUEST]) {
|
||
|
|
printf("\t%s (%d) request:%s", p->name, p->tag, p->p[SPROTO_REQUEST]->name);
|
||
|
|
} else {
|
||
|
|
printf("\t%s (%d) request:(null)", p->name, p->tag);
|
||
|
|
}
|
||
|
|
if (p->p[SPROTO_RESPONSE]) {
|
||
|
|
printf(" response:%s", p->p[SPROTO_RESPONSE]->name);
|
||
|
|
} else if (p->confirm) {
|
||
|
|
printf(" response nil");
|
||
|
|
}
|
||
|
|
printf("\n");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// query
|
||
|
|
int
|
||
|
|
sproto_prototag(const struct sproto *sp, const char * name) {
|
||
|
|
int i;
|
||
|
|
for (i=0;i<sp->protocol_n;i++) {
|
||
|
|
if (strcmp(name, sp->proto[i].name) == 0) {
|
||
|
|
return sp->proto[i].tag;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
static struct protocol *
|
||
|
|
query_proto(const struct sproto *sp, int tag) {
|
||
|
|
int begin = 0, end = sp->protocol_n;
|
||
|
|
while(begin<end) {
|
||
|
|
int mid = (begin+end)/2;
|
||
|
|
int t = sp->proto[mid].tag;
|
||
|
|
if (t==tag) {
|
||
|
|
return &sp->proto[mid];
|
||
|
|
}
|
||
|
|
if (tag > t) {
|
||
|
|
begin = mid+1;
|
||
|
|
} else {
|
||
|
|
end = mid;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
struct sproto_type *
|
||
|
|
sproto_protoquery(const struct sproto *sp, int proto, int what) {
|
||
|
|
struct protocol * p;
|
||
|
|
if (what <0 || what >1) {
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
p = query_proto(sp, proto);
|
||
|
|
if (p) {
|
||
|
|
return p->p[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);
|
||
|
|
if (p) {
|
||
|
|
return p->name;
|
||
|
|
}
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
struct sproto_type *
|
||
|
|
sproto_type(const struct sproto *sp, const char * type_name) {
|
||
|
|
int i;
|
||
|
|
for (i=0;i<sp->type_n;i++) {
|
||
|
|
if (strcmp(type_name, sp->type[i].name) == 0) {
|
||
|
|
return &sp->type[i];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
const char *
|
||
|
|
sproto_name(struct sproto_type * st) {
|
||
|
|
return st->name;
|
||
|
|
}
|
||
|
|
|
||
|
|
static struct field *
|
||
|
|
findtag(const struct sproto_type *st, int tag) {
|
||
|
|
int begin, end;
|
||
|
|
if (st->base >=0 ) {
|
||
|
|
tag -= st->base;
|
||
|
|
if (tag < 0 || tag >= st->n)
|
||
|
|
return NULL;
|
||
|
|
return &st->f[tag];
|
||
|
|
}
|
||
|
|
begin = 0;
|
||
|
|
end = st->n;
|
||
|
|
while (begin < end) {
|
||
|
|
int mid = (begin+end)/2;
|
||
|
|
struct field *f = &st->f[mid];
|
||
|
|
int t = f->tag;
|
||
|
|
if (t == tag) {
|
||
|
|
return f;
|
||
|
|
}
|
||
|
|
if (tag > t) {
|
||
|
|
begin = mid + 1;
|
||
|
|
} else {
|
||
|
|
end = mid;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
// encode & decode
|
||
|
|
// sproto_callback(void *ud, int tag, int type, struct sproto_type *, void *value, int length)
|
||
|
|
// return size, -1 means error
|
||
|
|
|
||
|
|
static inline int
|
||
|
|
fill_size(uint8_t * data, int sz) {
|
||
|
|
data[0] = sz & 0xff;
|
||
|
|
data[1] = (sz >> 8) & 0xff;
|
||
|
|
data[2] = (sz >> 16) & 0xff;
|
||
|
|
data[3] = (sz >> 24) & 0xff;
|
||
|
|
return sz + SIZEOF_LENGTH;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int
|
||
|
|
encode_integer(uint32_t v, uint8_t * data, int size) {
|
||
|
|
if (size < SIZEOF_LENGTH + sizeof(v))
|
||
|
|
return -1;
|
||
|
|
data[4] = v & 0xff;
|
||
|
|
data[5] = (v >> 8) & 0xff;
|
||
|
|
data[6] = (v >> 16) & 0xff;
|
||
|
|
data[7] = (v >> 24) & 0xff;
|
||
|
|
return fill_size(data, sizeof(v));
|
||
|
|
}
|
||
|
|
|
||
|
|
static int
|
||
|
|
encode_uint64(uint64_t v, uint8_t * data, int size) {
|
||
|
|
if (size < SIZEOF_LENGTH + sizeof(v))
|
||
|
|
return -1;
|
||
|
|
data[4] = v & 0xff;
|
||
|
|
data[5] = (v >> 8) & 0xff;
|
||
|
|
data[6] = (v >> 16) & 0xff;
|
||
|
|
data[7] = (v >> 24) & 0xff;
|
||
|
|
data[8] = (v >> 32) & 0xff;
|
||
|
|
data[9] = (v >> 40) & 0xff;
|
||
|
|
data[10] = (v >> 48) & 0xff;
|
||
|
|
data[11] = (v >> 56) & 0xff;
|
||
|
|
return fill_size(data, sizeof(v));
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
//#define CB(tagname,type,index,subtype,value,length) cb(ud, tagname,type,index,subtype,value,length)
|
||
|
|
|
||
|
|
static int
|
||
|
|
do_cb(sproto_callback cb, void *ud, const char *tagname, int type, int index, struct sproto_type *subtype, void *value, int length) {
|
||
|
|
if (subtype) {
|
||
|
|
if (type >= 0) {
|
||
|
|
printf("callback: tag=%s[%d], subtype[%s]:%d\n",tagname,index, subtype->name, type);
|
||
|
|
} else {
|
||
|
|
printf("callback: tag=%s[%d], subtype[%s]\n",tagname,index, subtype->name);
|
||
|
|
}
|
||
|
|
} else if (index > 0) {
|
||
|
|
printf("callback: tag=%s[%d]\n",tagname,index);
|
||
|
|
} else if (index == 0) {
|
||
|
|
printf("callback: tag=%s\n",tagname);
|
||
|
|
} else {
|
||
|
|
printf("callback: tag=%s [mainkey]\n",tagname);
|
||
|
|
}
|
||
|
|
return cb(ud, tagname,type,index,subtype,value,length);
|
||
|
|
}
|
||
|
|
#define CB(tagname,type,index,subtype,value,length) do_cb(cb,ud, tagname,type,index,subtype,value,length)
|
||
|
|
*/
|
||
|
|
|
||
|
|
static int
|
||
|
|
encode_object(sproto_callback cb, struct sproto_arg *args, uint8_t *data, int size) {
|
||
|
|
int sz;
|
||
|
|
if (size < SIZEOF_LENGTH)
|
||
|
|
return -1;
|
||
|
|
args->value = data+SIZEOF_LENGTH;
|
||
|
|
args->length = size-SIZEOF_LENGTH;
|
||
|
|
sz = cb(args);
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline void
|
||
|
|
uint32_to_uint64(int negative, uint8_t *buffer) {
|
||
|
|
if (negative) {
|
||
|
|
buffer[4] = 0xff;
|
||
|
|
buffer[5] = 0xff;
|
||
|
|
buffer[6] = 0xff;
|
||
|
|
buffer[7] = 0xff;
|
||
|
|
} else {
|
||
|
|
buffer[4] = 0;
|
||
|
|
buffer[5] = 0;
|
||
|
|
buffer[6] = 0;
|
||
|
|
buffer[7] = 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static uint8_t *
|
||
|
|
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;
|
||
|
|
if (size < 1)
|
||
|
|
return NULL;
|
||
|
|
buffer++;
|
||
|
|
size--;
|
||
|
|
intlen = SIZEOF_INT32;
|
||
|
|
index = 1;
|
||
|
|
*noarray = 0;
|
||
|
|
|
||
|
|
for (;;) {
|
||
|
|
int sz;
|
||
|
|
union {
|
||
|
|
uint64_t u64;
|
||
|
|
uint32_t u32;
|
||
|
|
} u;
|
||
|
|
args->value = &u;
|
||
|
|
args->length = sizeof(u);
|
||
|
|
args->index = index;
|
||
|
|
sz = cb(args);
|
||
|
|
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
|
||
|
|
}
|
||
|
|
// notice: sizeof(uint64_t) is size_t (unsigned) , size may be negative. See issue #75
|
||
|
|
// so use MACRO SIZOF_INT64 instead
|
||
|
|
if (size < SIZEOF_INT64)
|
||
|
|
return NULL;
|
||
|
|
if (sz == SIZEOF_INT32) {
|
||
|
|
uint32_t v = u.u32;
|
||
|
|
buffer[0] = v & 0xff;
|
||
|
|
buffer[1] = (v >> 8) & 0xff;
|
||
|
|
buffer[2] = (v >> 16) & 0xff;
|
||
|
|
buffer[3] = (v >> 24) & 0xff;
|
||
|
|
|
||
|
|
if (intlen == SIZEOF_INT64) {
|
||
|
|
uint32_to_uint64(v & 0x80000000, buffer);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
uint64_t v;
|
||
|
|
if (sz != SIZEOF_INT64)
|
||
|
|
return NULL;
|
||
|
|
if (intlen == SIZEOF_INT32) {
|
||
|
|
int i;
|
||
|
|
// rearrange
|
||
|
|
size -= (index-1) * SIZEOF_INT32;
|
||
|
|
if (size < SIZEOF_INT64)
|
||
|
|
return NULL;
|
||
|
|
buffer += (index-1) * SIZEOF_INT32;
|
||
|
|
for (i=index-2;i>=0;i--) {
|
||
|
|
int negative;
|
||
|
|
memcpy(header+1+i*SIZEOF_INT64, header+1+i*SIZEOF_INT32, SIZEOF_INT32);
|
||
|
|
negative = header[1+i*SIZEOF_INT64+3] & 0x80;
|
||
|
|
uint32_to_uint64(negative, header+1+i*SIZEOF_INT64);
|
||
|
|
}
|
||
|
|
intlen = SIZEOF_INT64;
|
||
|
|
}
|
||
|
|
|
||
|
|
v = u.u64;
|
||
|
|
buffer[0] = v & 0xff;
|
||
|
|
buffer[1] = (v >> 8) & 0xff;
|
||
|
|
buffer[2] = (v >> 16) & 0xff;
|
||
|
|
buffer[3] = (v >> 24) & 0xff;
|
||
|
|
buffer[4] = (v >> 32) & 0xff;
|
||
|
|
buffer[5] = (v >> 40) & 0xff;
|
||
|
|
buffer[6] = (v >> 48) & 0xff;
|
||
|
|
buffer[7] = (v >> 56) & 0xff;
|
||
|
|
}
|
||
|
|
|
||
|
|
size -= intlen;
|
||
|
|
buffer += intlen;
|
||
|
|
index++;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (buffer == header + 1) {
|
||
|
|
return header;
|
||
|
|
}
|
||
|
|
*header = (uint8_t)intlen;
|
||
|
|
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;
|
||
|
|
int sz;
|
||
|
|
if (size < SIZEOF_LENGTH)
|
||
|
|
return -1;
|
||
|
|
size -= SIZEOF_LENGTH;
|
||
|
|
buffer = data + SIZEOF_LENGTH;
|
||
|
|
switch (args->type) {
|
||
|
|
case SPROTO_TDOUBLE:
|
||
|
|
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 (;;) {
|
||
|
|
int v = 0;
|
||
|
|
args->value = &v;
|
||
|
|
args->length = sizeof(v);
|
||
|
|
sz = cb(args);
|
||
|
|
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;
|
||
|
|
size -= 1;
|
||
|
|
buffer += 1;
|
||
|
|
++args->index;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
|
||
|
|
int
|
||
|
|
sproto_encode(const struct sproto_type *st, void * buffer, int size, sproto_callback cb, void *ud) {
|
||
|
|
struct sproto_arg args;
|
||
|
|
uint8_t * header = buffer;
|
||
|
|
uint8_t * data;
|
||
|
|
int header_sz = SIZEOF_HEADER + st->maxn * SIZEOF_FIELD;
|
||
|
|
int i;
|
||
|
|
int index;
|
||
|
|
int lasttag;
|
||
|
|
int datasz;
|
||
|
|
if (size < header_sz)
|
||
|
|
return -1;
|
||
|
|
args.ud = ud;
|
||
|
|
data = header + header_sz;
|
||
|
|
size -= header_sz;
|
||
|
|
index = 0;
|
||
|
|
lasttag = -1;
|
||
|
|
for (i=0;i<st->n;i++) {
|
||
|
|
struct field *f = &st->f[i];
|
||
|
|
int type = f->type;
|
||
|
|
int value = 0;
|
||
|
|
int sz = -1;
|
||
|
|
args.tagname = f->name;
|
||
|
|
args.tagid = f->tag;
|
||
|
|
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);
|
||
|
|
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;
|
||
|
|
args.index = 0;
|
||
|
|
switch(type) {
|
||
|
|
case SPROTO_TDOUBLE:
|
||
|
|
case SPROTO_TINTEGER:
|
||
|
|
case SPROTO_TBOOLEAN: {
|
||
|
|
union {
|
||
|
|
uint64_t u64;
|
||
|
|
uint32_t u32;
|
||
|
|
} u;
|
||
|
|
args.value = &u;
|
||
|
|
args.length = sizeof(u);
|
||
|
|
sz = cb(&args);
|
||
|
|
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_INT32) {
|
||
|
|
if (u.u32 < 0x7fff) {
|
||
|
|
value = (u.u32+1) * 2;
|
||
|
|
sz = 2; // sz can be any number > 0
|
||
|
|
} else {
|
||
|
|
sz = encode_integer(u.u32, data, size);
|
||
|
|
}
|
||
|
|
} else if (sz == SIZEOF_INT64) {
|
||
|
|
sz= encode_uint64(u.u64, data, size);
|
||
|
|
} else {
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case SPROTO_TSTRUCT:
|
||
|
|
case SPROTO_TSTRING:
|
||
|
|
sz = encode_object(cb, &args, data, size);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (sz < 0)
|
||
|
|
return -1;
|
||
|
|
if (sz > 0) {
|
||
|
|
uint8_t * record;
|
||
|
|
int tag;
|
||
|
|
if (value == 0) {
|
||
|
|
data += sz;
|
||
|
|
size -= sz;
|
||
|
|
}
|
||
|
|
record = header+SIZEOF_HEADER+SIZEOF_FIELD*index;
|
||
|
|
tag = f->tag - lasttag - 1;
|
||
|
|
if (tag > 0) {
|
||
|
|
// skip tag
|
||
|
|
tag = (tag - 1) * 2 + 1;
|
||
|
|
if (tag > 0xffff)
|
||
|
|
return -1;
|
||
|
|
record[0] = tag & 0xff;
|
||
|
|
record[1] = (tag >> 8) & 0xff;
|
||
|
|
++index;
|
||
|
|
record += SIZEOF_FIELD;
|
||
|
|
}
|
||
|
|
++index;
|
||
|
|
record[0] = value & 0xff;
|
||
|
|
record[1] = (value >> 8) & 0xff;
|
||
|
|
lasttag = f->tag;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
header[0] = index & 0xff;
|
||
|
|
header[1] = (index >> 8) & 0xff;
|
||
|
|
|
||
|
|
datasz = data - (header + header_sz);
|
||
|
|
data = header + header_sz;
|
||
|
|
if (index != st->maxn) {
|
||
|
|
memmove(header + SIZEOF_HEADER + index * SIZEOF_FIELD, data, datasz);
|
||
|
|
}
|
||
|
|
return SIZEOF_HEADER + index * SIZEOF_FIELD + datasz;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int
|
||
|
|
decode_array_object(sproto_callback cb, struct sproto_arg *args, uint8_t * stream, int sz) {
|
||
|
|
uint32_t hsz;
|
||
|
|
int index = 1;
|
||
|
|
while (sz > 0) {
|
||
|
|
if (sz < SIZEOF_LENGTH)
|
||
|
|
return -1;
|
||
|
|
hsz = todword(stream);
|
||
|
|
stream += SIZEOF_LENGTH;
|
||
|
|
sz -= SIZEOF_LENGTH;
|
||
|
|
if (hsz > sz)
|
||
|
|
return -1;
|
||
|
|
args->index = index;
|
||
|
|
args->value = stream;
|
||
|
|
args->length = hsz;
|
||
|
|
if (cb(args))
|
||
|
|
return -1;
|
||
|
|
sz -= hsz;
|
||
|
|
stream += hsz;
|
||
|
|
++index;
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline uint64_t
|
||
|
|
expand64(uint32_t v) {
|
||
|
|
uint64_t value = v;
|
||
|
|
if (value & 0x80000000) {
|
||
|
|
value |= (uint64_t)~0 << 32 ;
|
||
|
|
}
|
||
|
|
return value;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int
|
||
|
|
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_TDOUBLE:
|
||
|
|
case SPROTO_TINTEGER: {
|
||
|
|
int len = *stream;
|
||
|
|
++stream;
|
||
|
|
--sz;
|
||
|
|
if (len == SIZEOF_INT32) {
|
||
|
|
if (sz % SIZEOF_INT32 != 0)
|
||
|
|
return -1;
|
||
|
|
for (i=0;i<sz/SIZEOF_INT32;i++) {
|
||
|
|
uint64_t value = expand64(todword(stream + i*SIZEOF_INT32));
|
||
|
|
args->index = i+1;
|
||
|
|
args->value = &value;
|
||
|
|
args->length = sizeof(value);
|
||
|
|
cb(args);
|
||
|
|
}
|
||
|
|
} else if (len == SIZEOF_INT64) {
|
||
|
|
if (sz % SIZEOF_INT64 != 0)
|
||
|
|
return -1;
|
||
|
|
for (i=0;i<sz/SIZEOF_INT64;i++) {
|
||
|
|
uint64_t low = todword(stream + i*SIZEOF_INT64);
|
||
|
|
uint64_t hi = todword(stream + i*SIZEOF_INT64 + SIZEOF_INT32);
|
||
|
|
uint64_t value = low | hi << 32;
|
||
|
|
args->index = i+1;
|
||
|
|
args->value = &value;
|
||
|
|
args->length = sizeof(value);
|
||
|
|
cb(args);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case SPROTO_TBOOLEAN:
|
||
|
|
for (i=0;i<sz;i++) {
|
||
|
|
uint64_t value = stream[i];
|
||
|
|
args->index = i+1;
|
||
|
|
args->value = &value;
|
||
|
|
args->length = sizeof(value);
|
||
|
|
cb(args);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
case SPROTO_TSTRING:
|
||
|
|
case SPROTO_TSTRUCT:
|
||
|
|
return decode_array_object(cb, args, stream, sz);
|
||
|
|
default:
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
int
|
||
|
|
sproto_decode(const struct sproto_type *st, const void * data, int size, sproto_callback cb, void *ud) {
|
||
|
|
struct sproto_arg args;
|
||
|
|
int total = size;
|
||
|
|
uint8_t * stream;
|
||
|
|
uint8_t * datastream;
|
||
|
|
int fn;
|
||
|
|
int i;
|
||
|
|
int tag;
|
||
|
|
if (size < SIZEOF_HEADER)
|
||
|
|
return -1;
|
||
|
|
// debug print
|
||
|
|
// printf("sproto_decode[%p] (%s)\n", ud, st->name);
|
||
|
|
stream = (void *)data;
|
||
|
|
fn = toword(stream);
|
||
|
|
stream += SIZEOF_HEADER;
|
||
|
|
size -= SIZEOF_HEADER ;
|
||
|
|
if (size < fn * SIZEOF_FIELD)
|
||
|
|
return -1;
|
||
|
|
datastream = stream + fn * SIZEOF_FIELD;
|
||
|
|
size -= fn * SIZEOF_FIELD;
|
||
|
|
args.ud = ud;
|
||
|
|
|
||
|
|
tag = -1;
|
||
|
|
for (i=0;i<fn;i++) {
|
||
|
|
uint8_t * currentdata;
|
||
|
|
struct field * f;
|
||
|
|
int value = toword(stream + i * SIZEOF_FIELD);
|
||
|
|
++ tag;
|
||
|
|
if (value & 1) {
|
||
|
|
tag += value/2;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
value = value/2 - 1;
|
||
|
|
currentdata = datastream;
|
||
|
|
if (value < 0) {
|
||
|
|
uint32_t sz;
|
||
|
|
if (size < SIZEOF_LENGTH)
|
||
|
|
return -1;
|
||
|
|
sz = todword(datastream);
|
||
|
|
if (size < sz + SIZEOF_LENGTH)
|
||
|
|
return -1;
|
||
|
|
datastream += sz+SIZEOF_LENGTH;
|
||
|
|
size -= sz+SIZEOF_LENGTH;
|
||
|
|
}
|
||
|
|
f = findtag(st, tag);
|
||
|
|
if (f == NULL)
|
||
|
|
continue;
|
||
|
|
args.tagname = f->name;
|
||
|
|
args.tagid = f->tag;
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
switch (f->type) {
|
||
|
|
case SPROTO_TDOUBLE:
|
||
|
|
case SPROTO_TINTEGER: {
|
||
|
|
uint32_t sz = todword(currentdata);
|
||
|
|
if (sz == SIZEOF_INT32) {
|
||
|
|
uint64_t v = expand64(todword(currentdata + SIZEOF_LENGTH));
|
||
|
|
args.value = &v;
|
||
|
|
args.length = sizeof(v);
|
||
|
|
cb(&args);
|
||
|
|
} else if (sz != SIZEOF_INT64) {
|
||
|
|
return -1;
|
||
|
|
} else {
|
||
|
|
uint32_t low = todword(currentdata + SIZEOF_LENGTH);
|
||
|
|
uint32_t hi = todword(currentdata + SIZEOF_LENGTH + SIZEOF_INT32);
|
||
|
|
uint64_t v = (uint64_t)low | (uint64_t) hi << 32;
|
||
|
|
args.value = &v;
|
||
|
|
args.length = sizeof(v);
|
||
|
|
cb(&args);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case SPROTO_TSTRING:
|
||
|
|
case SPROTO_TSTRUCT: {
|
||
|
|
uint32_t sz = todword(currentdata);
|
||
|
|
args.value = currentdata+SIZEOF_LENGTH;
|
||
|
|
args.length = sz;
|
||
|
|
if (cb(&args))
|
||
|
|
return -1;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
default:
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} else if (f->type != SPROTO_TINTEGER && f->type != SPROTO_TBOOLEAN) {
|
||
|
|
return -1;
|
||
|
|
} else {
|
||
|
|
uint64_t v = value;
|
||
|
|
args.value = &v;
|
||
|
|
args.length = sizeof(v);
|
||
|
|
cb(&args);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return total - size;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 0 pack
|
||
|
|
|
||
|
|
static int
|
||
|
|
pack_seg(const uint8_t *src, uint8_t * buffer, int sz, int n) {
|
||
|
|
uint8_t header = 0;
|
||
|
|
int notzero = 0;
|
||
|
|
int i;
|
||
|
|
uint8_t * obuffer = buffer;
|
||
|
|
++buffer;
|
||
|
|
--sz;
|
||
|
|
if (sz < 0)
|
||
|
|
obuffer = NULL;
|
||
|
|
|
||
|
|
for (i=0;i<8;i++) {
|
||
|
|
if (src[i] != 0) {
|
||
|
|
notzero++;
|
||
|
|
header |= 1<<i;
|
||
|
|
if (sz > 0) {
|
||
|
|
*buffer = src[i];
|
||
|
|
++buffer;
|
||
|
|
--sz;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if ((notzero == 7 || notzero == 6) && n > 0) {
|
||
|
|
notzero = 8;
|
||
|
|
}
|
||
|
|
if (notzero == 8) {
|
||
|
|
if (n > 0) {
|
||
|
|
return 8;
|
||
|
|
} else {
|
||
|
|
return 10;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (obuffer) {
|
||
|
|
*obuffer = header;
|
||
|
|
}
|
||
|
|
return notzero + 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline void
|
||
|
|
write_ff(const uint8_t * src, const uint8_t * src_end, uint8_t * des, int n) {
|
||
|
|
des[0] = 0xff;
|
||
|
|
des[1] = n - 1;
|
||
|
|
if (src + n * 8 <= src_end) {
|
||
|
|
memcpy(des+2, src, n*8);
|
||
|
|
} else {
|
||
|
|
int sz = (int)(src_end - src);
|
||
|
|
memcpy(des+2, src, sz);
|
||
|
|
memset(des+2+sz, 0, n*8-sz);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
int
|
||
|
|
sproto_pack(const void * srcv, int srcsz, void * bufferv, int bufsz) {
|
||
|
|
uint8_t tmp[8];
|
||
|
|
int i;
|
||
|
|
const uint8_t * ff_srcstart = NULL;
|
||
|
|
uint8_t * ff_desstart = NULL;
|
||
|
|
int ff_n = 0;
|
||
|
|
int size = 0;
|
||
|
|
const uint8_t * src = srcv;
|
||
|
|
const uint8_t * src_end = (uint8_t *)srcv + srcsz;
|
||
|
|
uint8_t * buffer = bufferv;
|
||
|
|
for (i=0;i<srcsz;i+=8) {
|
||
|
|
int n;
|
||
|
|
int padding = i+8 - srcsz;
|
||
|
|
if (padding > 0) {
|
||
|
|
int j;
|
||
|
|
memcpy(tmp, src, 8-padding);
|
||
|
|
for (j=0;j<padding;j++) {
|
||
|
|
tmp[7-j] = 0;
|
||
|
|
}
|
||
|
|
src = tmp;
|
||
|
|
}
|
||
|
|
n = pack_seg(src, buffer, bufsz, ff_n);
|
||
|
|
bufsz -= n;
|
||
|
|
if (n == 10) {
|
||
|
|
// first FF
|
||
|
|
ff_srcstart = src;
|
||
|
|
ff_desstart = buffer;
|
||
|
|
ff_n = 1;
|
||
|
|
} else if (n==8 && ff_n>0) {
|
||
|
|
++ff_n;
|
||
|
|
if (ff_n == 256) {
|
||
|
|
if (bufsz >= 0) {
|
||
|
|
write_ff(ff_srcstart, src_end, ff_desstart, 256);
|
||
|
|
}
|
||
|
|
ff_n = 0;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
if (ff_n > 0) {
|
||
|
|
if (bufsz >= 0) {
|
||
|
|
write_ff(ff_srcstart, src_end, ff_desstart, ff_n);
|
||
|
|
}
|
||
|
|
ff_n = 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
src += 8;
|
||
|
|
buffer += n;
|
||
|
|
size += n;
|
||
|
|
}
|
||
|
|
if(bufsz >= 0 && ff_n > 0) {
|
||
|
|
write_ff(ff_srcstart, src_end, ff_desstart, ff_n);
|
||
|
|
}
|
||
|
|
return size;
|
||
|
|
}
|
||
|
|
|
||
|
|
int
|
||
|
|
sproto_unpack(const void * srcv, int srcsz, void * bufferv, int bufsz) {
|
||
|
|
const uint8_t * src = srcv;
|
||
|
|
uint8_t * buffer = bufferv;
|
||
|
|
int size = 0;
|
||
|
|
while (srcsz > 0) {
|
||
|
|
uint8_t header = src[0];
|
||
|
|
--srcsz;
|
||
|
|
++src;
|
||
|
|
if (header == 0xff) {
|
||
|
|
int n;
|
||
|
|
if (srcsz < 0) {
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
n = (src[0] + 1) * 8;
|
||
|
|
if (srcsz < n + 1)
|
||
|
|
return -1;
|
||
|
|
srcsz -= n + 1;
|
||
|
|
++src;
|
||
|
|
if (bufsz >= n) {
|
||
|
|
memcpy(buffer, src, n);
|
||
|
|
}
|
||
|
|
bufsz -= n;
|
||
|
|
buffer += n;
|
||
|
|
src += n;
|
||
|
|
size += n;
|
||
|
|
} else {
|
||
|
|
int i;
|
||
|
|
for (i=0;i<8;i++) {
|
||
|
|
int nz = (header >> i) & 1;
|
||
|
|
if (nz) {
|
||
|
|
if (srcsz < 0)
|
||
|
|
return -1;
|
||
|
|
if (bufsz > 0) {
|
||
|
|
*buffer = *src;
|
||
|
|
--bufsz;
|
||
|
|
++buffer;
|
||
|
|
}
|
||
|
|
++src;
|
||
|
|
--srcsz;
|
||
|
|
} else {
|
||
|
|
if (bufsz > 0) {
|
||
|
|
*buffer = 0;
|
||
|
|
--bufsz;
|
||
|
|
++buffer;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
++size;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return size;
|
||
|
|
}
|