#include "pbc.h" #include "alloc.h" #include "context.h" #include "proto.h" #include "varint.h" #include static const char * TYPENAME[] = { "invalid", // 0 "integer", // 1 "real", // 2 "boolean", // 3 "enum", // 4 "string", // 5 "message", // 6 "fixed64", // 7 "fixed32", // 8 "bytes", // 9 "int64", // 10 "uint", // 11 }; static int call_unknown(pbc_decoder f, void * ud, int id, struct atom *a, uint8_t * start) { union pbc_value v; switch (a->wire_id & 7) { case WT_VARINT: v.i.low = a->v.i.low; v.i.hi = a->v.i.hi; f(ud, PBC_INT, TYPENAME[PBC_INT], &v, id , NULL); break; case WT_BIT64: v.i.low = a->v.i.low; v.i.hi = a->v.i.hi; f(ud, PBC_FIXED64, TYPENAME[PBC_FIXED64], &v, id , NULL); break; case WT_LEND: v.s.buffer = (char*)start + a->v.s.start; v.s.len = a->v.s.end - a->v.s.start; f(ud, PBC_BYTES, TYPENAME[PBC_BYTES], &v, id , NULL); break; case WT_BIT32: v.i.low = a->v.i.low; v.i.hi = 0; f(ud, PBC_FIXED32, TYPENAME[PBC_FIXED32], &v, id , NULL); break; default: return 1; } return 0; } static int call_type(pbc_decoder pd, void * ud, struct _field *f, struct atom *a, uint8_t * start) { union pbc_value v; const char * type_name = NULL; int type = _pbcP_type(f, &type_name); assert(type != 0); if (type_name == NULL) { type_name = TYPENAME[type & ~PBC_REPEATED]; } switch (f->type) { case PTYPE_DOUBLE: CHECK_BIT64(a, -1); v.f = read_double(a); break; case PTYPE_FLOAT: CHECK_BIT32(a, -1); v.f = (double) read_float(a); break; case PTYPE_ENUM: CHECK_VARINT(a, -1); v.e.id = a->v.i.low; v.e.name = (const char *)_pbcM_ip_query(f->type_name.e->id , v.e.id); break; case PTYPE_INT64: case PTYPE_UINT64: CHECK_VARINT(a, -1); v.i.low = a->v.i.low; v.i.hi = a->v.i.hi; break; case PTYPE_FIXED64: case PTYPE_SFIXED64: CHECK_BIT64(a, -1); v.i.low = a->v.i.low; v.i.hi = a->v.i.hi; break; case PTYPE_INT32: case PTYPE_UINT32: case PTYPE_BOOL: CHECK_VARINT(a, -1); v.i.low = a->v.i.low; v.i.hi = 0; break; case PTYPE_FIXED32: case PTYPE_SFIXED32: CHECK_BIT32(a, -1); v.i.low = a->v.i.low; v.i.hi = 0; break; case PTYPE_SINT32: CHECK_VARINT(a, -1); v.i.low = a->v.i.low; v.i.hi = a->v.i.hi; _pbcV_dezigzag32((struct longlong *)&(v.i)); break; case PTYPE_SINT64: CHECK_VARINT(a, -1); v.i.low = a->v.i.low; v.i.hi = a->v.i.hi; _pbcV_dezigzag64((struct longlong *)&(v.i)); break; case PTYPE_STRING: case PTYPE_BYTES: case PTYPE_MESSAGE: CHECK_LEND(a, -1); v.s.buffer = start + a->v.s.start; v.s.len = a->v.s.end - a->v.s.start; break; default: assert(0); break; } pd(ud, type, type_name, &v, f->id, f->name); return 0; } static int call_array(pbc_decoder pd, void * ud, struct _field *f, uint8_t * buffer , int size) { union pbc_value v; const char * type_name = NULL; int type = _pbcP_type(f, &type_name); assert(type != 0); if (type_name == NULL) { type_name = TYPENAME[type & ~PBC_REPEATED]; } v.i.hi = 0; int i; switch(f->type) { case PTYPE_DOUBLE: if (size % 8 != 0) { return -1; } for (i=0;iid, f->name); } return size/8; case PTYPE_FLOAT: if (size % 4 != 0) return -1; for (i=0;iid, f->name); } return size/4; case PTYPE_FIXED32: case PTYPE_SFIXED32: if (size % 4 != 0) return -1; for (i=0;iid, f->name); } return size/4; case PTYPE_FIXED64: case PTYPE_SFIXED64: if (size % 8 != 0) return -1; for (i=0;iid, f->name); } return size/8; case PTYPE_INT64: case PTYPE_UINT64: case PTYPE_INT32: case PTYPE_UINT32: case PTYPE_BOOL: { int n = 0; while (size > 0) { int len; if (size >= 10) { len = _pbcV_decode(buffer, (struct longlong *)&(v.i)); } else { uint8_t temp[10]; memcpy(temp, buffer, size); len = _pbcV_decode(buffer, (struct longlong *)&(v.i)); if (len > size) return -1; } pd(ud, type , type_name, &v, f->id, f->name); buffer += len; size -= len; ++n; } return n; } case PTYPE_ENUM: { int n = 0; while (size > 0) { int len; if (size >= 10) { len = _pbcV_decode(buffer, (struct longlong *)&(v.i)); } else { uint8_t temp[10]; memcpy(temp, buffer, size); len = _pbcV_decode(buffer, (struct longlong *)&(v.i)); if (len > size) return -1; } v.e.id = v.i.low; v.e.name = (const char *)_pbcM_ip_query(f->type_name.e->id , v.i.low); pd(ud, type , type_name, &v, f->id, f->name); buffer += len; size -= len; ++n; } return n; } case PTYPE_SINT32: { int n = 0; while (size > 0) { int len; if (size >= 10) { len = _pbcV_decode(buffer, (struct longlong *)&(v.i)); _pbcV_dezigzag32((struct longlong *)&(v.i)); } else { uint8_t temp[10]; memcpy(temp, buffer, size); len = _pbcV_decode(buffer, (struct longlong *)&(v.i)); if (len > size) return -1; _pbcV_dezigzag32((struct longlong *)&(v.i)); } pd(ud, type , type_name, &v, f->id, f->name); buffer += len; size -= len; ++n; } return n; } case PTYPE_SINT64: { int n = 0; while (size > 0) { int len; if (size >= 10) { len = _pbcV_decode(buffer, (struct longlong *)&(v.i)); _pbcV_dezigzag64((struct longlong *)&(v.i)); } else { uint8_t temp[10]; memcpy(temp, buffer, size); len = _pbcV_decode(buffer, (struct longlong *)&(v.i)); if (len > size) return -1; _pbcV_dezigzag64((struct longlong *)&(v.i)); } pd(ud, type , type_name, &v, f->id, f->name); buffer += len; size -= len; ++n; } return n; } default: return -1; } } int pbc_decode(struct pbc_env * env, const char * type_name , struct pbc_slice * slice, pbc_decoder pd, void *ud) { struct _message * msg = _pbcP_get_message(env, type_name); if (msg == NULL) { env->lasterror = "Proto not found"; return -1; } if (slice->len == 0) { return 0; } pbc_ctx _ctx; int count = _pbcC_open(_ctx,slice->buffer,slice->len); if (count <= 0) { env->lasterror = "decode context error"; _pbcC_close(_ctx); return count - 1; } struct context * ctx = (struct context *)_ctx; uint8_t * start = (uint8_t *)slice->buffer; int i; for (i=0;inumber;i++) { int id = ctx->a[i].wire_id >> 3; struct _field * f = (struct _field *)_pbcM_ip_query(msg->id , id); if (f==NULL) { int err = call_unknown(pd,ud,id,&ctx->a[i],start); if (err) { _pbcC_close(_ctx); return -i-1; } } else if (f->label == LABEL_PACKED) { struct atom * a = &ctx->a[i]; int n = call_array(pd, ud, f , start + a->v.s.start , a->v.s.end - a->v.s.start); if (n < 0) { _pbcC_close(_ctx); return -i-1; } } else { if (call_type(pd,ud,f,&ctx->a[i],start) != 0) { _pbcC_close(_ctx); return -i-1; } } } _pbcC_close(_ctx); return ctx->number; }