#ifndef Plan9#include <inttypes.h>#include <stdarg.h>#include <stdint.h>#include <stdio.h>#include <string.h>#endif#include "pb.h" #define MAX(a, b) (a > b ? a : b)#define MIN(a, b) (a < b ? a : b) static int bytes_size(const struct pb_field *f, const void *data, const struct pb_msg *msg); static intis_default(const uint8_t *o, const struct pb_field *f, const struct pb_data *pb, const struct pb_msg *msg){ const struct pb_default *dflt; const void *fin; int i, type, sizebytes; if((f->flags & PB_FLAG_REQUIRED) != 0) return 0; if(f->num_at >= 0) return *(int*)(o + f->num_at) == 0; dflt = f->dfltidx > 0 ? &pb->dflts[f->dfltidx-1] : NULL; type = f->flags & PB_FLAG_MASK_TYPE; fin = o + f->offset; if(f->flags & PB_FLAG_POINTER) fin = *(const void**)fin; if(type == PB_TYPE_STRING){ if(dflt != NULL) return fin != NULL && strcmp(fin, dflt->pointer) == 0; return fin == NULL || *(const char*)fin == 0; } if(type == PB_TYPE_INTEGER && (f->flags & PB_FLAG_POINTER) == 0 && dflt != NULL){ if(f->size == 4) return *(int32_t*)fin == (int32_t)dflt->integer; if(f->size == 8) return *(int64_t*)fin == (int64_t)dflt->integer; if(f->size == 2) return *(int16_t*)fin == (int16_t)dflt->integer; return *(int8_t*)fin == (int8_t)dflt->integer; } if(type == PB_TYPE_MSG){ for(i = 0; fin != NULL && i < pb->msgs[f->msgidx].num_fields; i++){ if(!is_default(fin, &pb->msgs[f->msgidx].fields[i], pb, msg)) return 0; } return 1; } if(f->flags & PB_FLAG_POINTER) return fin == NULL; sizebytes = f->size; if(type == PB_TYPE_BYTES && (sizebytes = bytes_size(f, o, msg)) < 0) return 0; // it could be -1, but we do this check later for(i = 0; i < sizebytes; i++) if(((uint8_t*)fin)[i] != 0) return 0; return 1;} static void *errorf(const struct pb_msg *, const char *, int, const char *, ...) PB_GNUC_ATTR(format(printf, 4, 5)); #ifndef PB_PRINTERS#define fname(f) "?"#else#define fname(f) (f)->name char *seprintf(char *s, char *e, char *fmt, ...){ va_list arg; int n; va_start(arg, fmt); n = vsnprintf(s, e-s, fmt, arg); va_end(arg); return e-s > n ? s+n : e;} static char *print_single(char *s, char *e, const uint8_t *fin, const struct pb_field *f, int left, int indent, const void *msg, const struct pb_data *pb, const struct pb_msg *pbmsg){ int i, sz, type; type = f->flags & PB_FLAG_MASK_TYPE; if(f->seprint != NULL){ s = f->seprint(fin, s, e, left, indent, f); }else if(type == PB_TYPE_MSG){ s = pb_seprint(fin, s, e, &pb->msgs[f->msgidx], pb, left, indent); }else if(type == PB_TYPE_INTEGER){ if((f->flags & PB_FLAG_SIGNED) != 0 || (f->flags & PB_FLAG_TYPE_UNSIGNED) == 0){ if(f->size == 4) s = seprintf(s, e, "%d", *(int32_t*)fin); else if(f->size == 8) s = seprintf(s, e, "%"PRId64, *(int64_t*)fin); else if(f->size == 2) s = seprintf(s, e, "%d", *(int16_t*)fin); else s = seprintf(s, e, "%d", *(int8_t*)fin); }else{ if(f->size == 4) s = seprintf(s, e, "%u", *(uint32_t*)fin); else if(f->size == 8) s = seprintf(s, e, "%"PRIu64, *(uint64_t*)fin); else if(f->size == 2) s = seprintf(s, e, "%u", *(uint16_t*)fin); else s = seprintf(s, e, "%u", *(uint8_t*)fin); } }else if(type == PB_TYPE_STRING){ s = seprintf(s, e, "\"%s\"", (char*)fin); }else if(type == PB_TYPE_BYTES){ if((sz = bytes_size(f, msg, pbmsg)) < 0) return NULL; s = seprintf(s, e, "0x"); for(i = 0; i < sz; i++) s = seprintf(s, e, "%02x", fin[i]); s = seprintf(s, e, " (%d byte%s)", sz, sz == 1 ? "" : "s"); } return s;} char *pb_seprint(const void *in, char *s, char *e, const struct pb_msg *msg, const struct pb_data *pb, int left, int indent){ const struct pb_field *f; const uint8_t *o, *fin; char *old; int i, j, num, sz, type; o = in; old = s = seprintf(s, e, "{\n"); for(i = 0; i < msg->num_fields; i++){ f = &msg->fields[msg->indices[i]]; if(f->flags & PB_FLAG_NO_PRINT) continue; if(is_default(o, f, pb, msg)) continue; type = f->flags & PB_FLAG_MASK_TYPE; if(f->num_at >= 0 && type != PB_TYPE_BYTES){ s = seprintf(s, e, "%*s%s = [", left+indent, "", fname(f)); if(f->index > 0) s = seprintf(s, e, " // #%d", f->index); s = seprintf(s, e, "\n"); num = *(int*)(o + f->num_at); for(j = 0; j < num && s != NULL; j++){ fin = o + f->offset; if(f->flags & PB_FLAG_POINTER) fin = *(uint8_t**)fin; fin += j*f->size; s = seprintf(s, e, "%*s", left+indent+indent, ""); s = print_single(s, e, fin, f, left+indent+indent, indent, o, pb, msg); s = seprintf(s, e, ",\n"); } s = seprintf(s, e, "%*s] (%d item%s)", left+indent, "", num, num == 1 ? "" : "s"); }else{ fin = o + f->offset; if(f->flags & PB_FLAG_POINTER) fin = *(uint8_t**)fin; if(type == PB_TYPE_BYTES){ if((sz = bytes_size(f, in, msg)) < 0) return NULL; if(sz < 1) continue; } s = seprintf(s, e, "%*s%s = ", left+indent, "", fname(f)); s = print_single(s, e, fin, f, left+indent, indent, o, pb, msg); } s = seprintf(s, e, ","); if(f->index > 0) s = seprintf(s, e, " // #%d", f->index); s = seprintf(s, e, "\n"); } if(old != s) s = seprintf(s, e, "%*s}", left, ""); else s = seprintf(old-1, e, "}"); return s;}#endif static void *errorf(const struct pb_msg *msg, const char *f, int findex, const char *fmt, ...){ va_list ap; va_start(ap, fmt);#ifdef PB_PRINTERS fprintf(stderr, "pb: %s.%s (#%d): ", msg->name, f, findex);#else (void)msg; (void)f; fprintf(stderr, "pb: #%d: ", findex);#endif vfprintf(stderr, fmt, ap); va_end(ap); return NULL;} static intbytes_size(const struct pb_field *f, const void *data, const struct pb_msg *msg){ int sz; if(f->flags & PB_FLAG_CONST_SIZE) return f->size; if(f->num_at < 0) return f->num; sz = *(int*)((uint8_t*)data + f->num_at); if(sz < 0 || ((f->flags & PB_FLAG_POINTER) == 0 && sz > f->num)){ errorf(msg, fname(f), f->index, "invalid size %d\n", sz); return -1; } return sz;} static voidset_default(void *out, const struct pb_field *f, const struct pb_data *pb){ const struct pb_default *dflt; uint8_t *o; int i, type, sizebytes; o = out; type = f->flags & PB_FLAG_MASK_TYPE; dflt = (pb->dflts != NULL && f->dfltidx > 0) ? &pb->dflts[f->dfltidx-1] : NULL; if(f->flags & PB_FLAG_POINTER){ *(void**)(o + f->offset) = NULL; }else if(type == PB_TYPE_MSG){ i = 0; do{ pb_reset(o + f->offset + i*f->size, &pb->msgs[f->msgidx], pb); }while(++i < f->num); }else if(type == PB_TYPE_INTEGER && dflt != NULL){ if(f->size == 4) *(int32_t*)(o + f->offset) = dflt->integer; else if(f->size == 8) *(int64_t*)(o + f->offset) = dflt->integer; else if(f->size == 2) *(int16_t*)(o + f->offset) = dflt->integer; else *(uint8_t*)(o + f->offset) = dflt->integer; }else if(dflt != NULL && dflt->size > 0){ memmove(o + f->offset, dflt->pointer, MIN(dflt->size, f->size)); }else{ sizebytes = f->size; if(type == PB_TYPE_BYTES && (f->flags & PB_FLAG_CONST_SIZE) == 0) sizebytes = f->num; memset(o + f->offset, 0, sizebytes); } if(f->num_at >= 0) *(int*)(o + f->num_at) = 0;} voidpb_reset(void *out, const struct pb_msg *msg, const struct pb_data *pb){ const struct pb_field *f; uint8_t *o; int i; for(i = 0, f = msg->fields; i < msg->num_fields; i++, f++) set_default(out, f, pb); o = out; if(msg->rest_at >= 0) memset(o + msg->rest_at, 0, 2); if(msg->buffer_at >= 0) memset(o + msg->buffer_at, 0, 2);} static const uint8_t *varint_read(uint64_t *out, const struct pb_msg *msg, const uint8_t *b, const uint8_t *e, const char *what){ int varint_bits; for(varint_bits = 0, *out = 0; varint_bits < 64; varint_bits += 7, b++){ if(b == e) return errorf(msg, what, -1, "short read: need 1, have 0\n"); *out |= (uint64_t)(*b & 0x7f) << varint_bits; if((*b & 0x80) == 0){ b++; break; } } if(varint_bits >= 64 || (varint_bits == 63 && b[-1] > 1)) return errorf(msg, what, -1, "too many bits in a varint\n"); return b;} static intvarint_outofrange(const struct pb_field *f, int64_t varint){ if((f->flags & PB_FLAG_SIGNED) != 0 || (f->flags & PB_FLAG_TYPE_UNSIGNED) == 0){ if(f->size == 4) return varint < -0x80000000LL || varint > 0x7fffffffLL; else if(f->size == 2) return varint < -0x8000LL || varint > 0x7fffLL; else if(f->size == 1) return varint < -0x80LL || varint > 0x7fLL; }else{ if(f->size == 4) return varint < 0LL || varint > 0xffffffffLL; else if(f->size == 2) return varint < 0LL || varint > 0xffffLL; else if(f->size == 1) return varint < 0LL || varint > 0xffLL; } return 0;} static intru16(const uint8_t *b){ return (int)b[0] | (int)b[1]<<8;} static voidwu16(uint8_t *b, uint32_t x){ b[0] = x; b[1] = x>>8;} const uint8_t *pb_decode(void *out, const uint8_t *s, const uint8_t *e, const struct pb_msg *msg, const struct pb_data *pb){ uint64_t varint; const struct pb_field *f; uint8_t *o, *oldvalue, *value, **assignvalue; const uint8_t *b; const void *cb; uint64_t fields_set[2]; int fsetidx, fsetshift, pbtype; int i, res, datasz, max_bytes_size, raw_size, rest_left, rest_sz, written; uint8_t type, fieldn; o = out; b = s; if(msg->rest_at >= 0) memset(o + msg->rest_at, 0, 2); if(msg->buffer_at >= 0) memset(o + msg->buffer_at, 0, 2); fields_set[0] = 1; /* index=0 is non-protobuf fields, don't reset them after parsing */ fields_set[1] = 0; for(; b != e;){ s = b; if((b = varint_read(&varint, msg, b, e, "?tag")) == NULL) return NULL; type = varint & 7; varint >>= 3; fieldn = varint; if(type != 0 && type != 2) /* no, we don't need it in the "rest" either */ return errorf(msg, "?", fieldn, "unsupported protobuf type %d\n", type); fsetidx = fieldn/64; fsetshift = fieldn%64; if((b = varint_read(&varint, msg, b, e, "?sz")) == NULL) return NULL; datasz = 0; if(type == 2){ /* string, bytes, message, packed repeated */ datasz = varint; if(datasz < 0) return errorf(msg, "?sz", fieldn, "invalid data size: %d\n", datasz); if(datasz > e-b) return errorf(msg, "?sz", fieldn, "short read: need %d, have %d\n", datasz, (int)(e-b)); } /* search for the field */ for(f = msg->fields, i = 0; i < msg->num_fields && (f->index == 0 || f->index != fieldn); i++, f++); if(i >= msg->num_fields){ /* wasn't found */ if(msg->rest_at >= 0){ raw_size = b - s + datasz; rest_sz = ru16(o + msg->rest_at); rest_left = msg->rest_size - rest_sz - 2; if(rest_left < raw_size) return errorf(msg, "?", fieldn, "no space in \"rest\": need %d, have %d\n", raw_size, rest_left); memmove(o + msg->rest_at + 2 + rest_sz, s, raw_size); wu16(o + msg->rest_at, rest_sz + raw_size); } b += datasz; continue; } cb = (f->cbidx > 0 && pb->cbs != NULL) ? pb->cbs[f->cbidx-1] : NULL; value = o + f->offset; if(f->num_at >= 0 && f->num > 0){ /* if it's an array of constant size */ if((fields_set[fsetidx] & (1ULL<<fsetshift)) == 0) /* reset "num" the first time we see the field */ *(int*)(o + f->num_at) = 0; if(*(int*)(o + f->num_at) >= f->num){ /* nowhere to save */ b += datasz; continue; } value += *(int*)(o + f->num_at) * f->size; }else if(f->num_at < 0 && (fields_set[fsetidx] & (1ULL<<fsetshift)) != 0){ /* repeated value, but the field isn't array and is already set, so just keep the first value */ b += datasz; continue; } pbtype = f->flags & PB_FLAG_MASK_TYPE; max_bytes_size = 0; if(f->flags & PB_FLAG_POINTER){ assignvalue = (void*)value; value = *(uint8_t**)value; if(f->flags & PB_FLAG_IS_CONST){ max_bytes_size = 0x1000000; /* arbitrary limit, randomly picked */ }else if(pbtype == PB_TYPE_STRING || pbtype == PB_TYPE_BYTES){ max_bytes_size = MAX(0, msg->buffer_size - ru16(o + msg->buffer_at) - 2); value = o + msg->buffer_at + 2 + ru16(o + msg->buffer_at); }else if(value == NULL){ b += datasz; continue; } }else{ assignvalue = NULL; max_bytes_size = f->size; if(pbtype == PB_TYPE_BYTES && (f->flags & PB_FLAG_CONST_SIZE) == 0) max_bytes_size = f->num; } if(pbtype == PB_TYPE_STRING) /* needs to be null-terminated */ max_bytes_size--; if(type == 0){ /* varint */ if(pbtype != PB_TYPE_INTEGER) return errorf(msg, fname(f), f->index, "expected type %d, got varint instead\n", pbtype); if(f->flags & PB_FLAG_SIGNED) varint = varint>>1 ^ -(varint&1); if(varint_outofrange(f, varint)){ return errorf(msg, fname(f), f->index, "varint out of range: can't fit %"PRId64" in %d bits (%ssigned)\n", varint, f->size*8, (f->flags & PB_FLAG_SIGNED) ? "" : "un"); } if(f->size == 4) *(int32_t*)value = varint; else if(f->size == 8) *(int64_t*)value = varint; else if(f->size == 2) *(int16_t*)value = varint; else *(uint8_t*)value = varint; res = cb == NULL ? PB_CALLBACK_OK : ((int (*)(void*, void*))cb)(out, value); if(res < 0) return NULL; if(res == PB_CALLBACK_THROW_AWAY){ b += datasz; continue; } fields_set[fsetidx] |= 1ULL<<fsetshift; }else if(type == 2){ if(pbtype == PB_TYPE_INTEGER){ /* packed integers */ oldvalue = value; for(s = b; b != s+datasz;){ if((b = varint_read(&varint, msg, b, e, "?int")) == NULL) return NULL; if(f->flags & PB_FLAG_SIGNED) varint = varint>>1 ^ -(varint&1); if(varint_outofrange(f, varint)){ return errorf(msg, fname(f), f->index, "varint out of range: can't fit %"PRId64" in %d bits (%ssigned)\n", varint, f->size*8, (f->flags & PB_FLAG_SIGNED) ? "" : "un"); } if(f->size == 4) *(int32_t*)value = varint; else if(f->size == 8) *(int64_t*)value = varint; else if(f->size == 2) *(int16_t*)value = varint; else *(uint8_t*)value = varint; res = cb == NULL ? PB_CALLBACK_OK : ((int (*)(void*, void*))cb)(out, value); if(res < 0) return NULL; if(res != PB_CALLBACK_THROW_AWAY) value += f->size; if(f->num_at < 0 || ++(*(int*)(o + f->num_at)) >= f->num) break; } b = s + datasz; if(oldvalue != value) fields_set[fsetidx] |= 1ULL<<fsetshift; continue; } if(pbtype == PB_TYPE_MSG){ if(pb_decode(value, b, b+datasz, &pb->msgs[f->msgidx], pb) == NULL) return NULL; res = cb == NULL ? PB_CALLBACK_OK : ((int (*)(void*, void*))cb)(out, value); if(res < 0) return NULL; if(res == PB_CALLBACK_THROW_AWAY){ b += datasz; continue; } fields_set[fsetidx] |= 1ULL<<fsetshift; }else{ if((datasz > max_bytes_size && (f->flags & PB_FLAG_TRUNCATE) == 0) || (datasz != max_bytes_size && (f->flags & PB_FLAG_CONST_SIZE) != 0)){ return errorf(msg, fname(f), f->index, "unexpected size: should be %s%d, have %d\n", (f->flags & PB_FLAG_CONST_SIZE) ? "==" : "<=", max_bytes_size, datasz); } written = MAX(0, MIN(datasz, max_bytes_size)); res = PB_CALLBACK_OK; if(written > 0){ if(f->flags & PB_FLAG_IS_CONST){ *assignvalue = value = (void*)b; }else{ memmove(value, b, written); if(pbtype == PB_TYPE_STRING) value[written++] = 0; if((f->flags & PB_FLAG_POINTER) != 0 && msg->buffer_at >= 0){ *assignvalue = value; wu16(o + msg->buffer_at, ru16(o + msg->buffer_at)+written);Lines 1–500 of 672. Load more
#ifndef Plan9 #include <inttypes.h> #include <stdarg.h> #include <stdint.h> #include <stdio.h> #include <string.h> #endif #include "pb.h" #define MAX(a, b) (a > b ? a : b) #define MIN(a, b) (a < b ? a : b) static int bytes_size(const struct pb_field *f, const void *data, const struct pb_msg *msg); static int is_default(const uint8_t *o, const struct pb_field *f, const struct pb_data *pb, const struct pb_msg *msg) { const struct pb_default *dflt; const void *fin; int i, type, sizebytes; if((f->flags & PB_FLAG_REQUIRED) != 0) return 0; if(f->num_at >= 0) return *(int*)(o + f->num_at) == 0; dflt = f->dfltidx > 0 ? &pb->dflts[f->dfltidx-1] : NULL; type = f->flags & PB_FLAG_MASK_TYPE; fin = o + f->offset; if(f->flags & PB_FLAG_POINTER) fin = *(const void**)fin; if(type == PB_TYPE_STRING){ if(dflt != NULL) return fin != NULL && strcmp(fin, dflt->pointer) == 0; return fin == NULL || *(const char*)fin == 0; } if(type == PB_TYPE_INTEGER && (f->flags & PB_FLAG_POINTER) == 0 && dflt != NULL){ if(f->size == 4) return *(int32_t*)fin == (int32_t)dflt->integer; if(f->size == 8) return *(int64_t*)fin == (int64_t)dflt->integer; if(f->size == 2) return *(int16_t*)fin == (int16_t)dflt->integer; return *(int8_t*)fin == (int8_t)dflt->integer; } if(type == PB_TYPE_MSG){ for(i = 0; fin != NULL && i < pb->msgs[f->msgidx].num_fields; i++){ if(!is_default(fin, &pb->msgs[f->msgidx].fields[i], pb, msg)) return 0; } return 1; } if(f->flags & PB_FLAG_POINTER) return fin == NULL; sizebytes = f->size; if(type == PB_TYPE_BYTES && (sizebytes = bytes_size(f, o, msg)) < 0) return 0; // it could be -1, but we do this check later for(i = 0; i < sizebytes; i++) if(((uint8_t*)fin)[i] != 0) return 0; return 1; } static void *errorf(const struct pb_msg *, const char *, int, const char *, ...) PB_GNUC_ATTR(format(printf, 4, 5)); #ifndef PB_PRINTERS #define fname(f) "?" #else #define fname(f) (f)->name char * seprintf(char *s, char *e, char *fmt, ...) { va_list arg; int n; va_start(arg, fmt); n = vsnprintf(s, e-s, fmt, arg); va_end(arg); return e-s > n ? s+n : e; } static char * print_single(char *s, char *e, const uint8_t *fin, const struct pb_field *f, int left, int indent, const void *msg, const struct pb_data *pb, const struct pb_msg *pbmsg) { int i, sz, type; type = f->flags & PB_FLAG_MASK_TYPE; if(f->seprint != NULL){ s = f->seprint(fin, s, e, left, indent, f); }else if(type == PB_TYPE_MSG){ s = pb_seprint(fin, s, e, &pb->msgs[f->msgidx], pb, left, indent); }else if(type == PB_TYPE_INTEGER){ if((f->flags & PB_FLAG_SIGNED) != 0 || (f->flags & PB_FLAG_TYPE_UNSIGNED) == 0){ if(f->size == 4) s = seprintf(s, e, "%d", *(int32_t*)fin); else if(f->size == 8) s = seprintf(s, e, "%"PRId64, *(int64_t*)fin); else if(f->size == 2) s = seprintf(s, e, "%d", *(int16_t*)fin); else s = seprintf(s, e, "%d", *(int8_t*)fin); }else{ if(f->size == 4) s = seprintf(s, e, "%u", *(uint32_t*)fin); else if(f->size == 8) s = seprintf(s, e, "%"PRIu64, *(uint64_t*)fin); else if(f->size == 2) s = seprintf(s, e, "%u", *(uint16_t*)fin); else s = seprintf(s, e, "%u", *(uint8_t*)fin); } }else if(type == PB_TYPE_STRING){ s = seprintf(s, e, "\"%s\"", (char*)fin); }else if(type == PB_TYPE_BYTES){ if((sz = bytes_size(f, msg, pbmsg)) < 0) return NULL; s = seprintf(s, e, "0x"); for(i = 0; i < sz; i++) s = seprintf(s, e, "%02x", fin[i]); s = seprintf(s, e, " (%d byte%s)", sz, sz == 1 ? "" : "s"); } return s; } char * pb_seprint(const void *in, char *s, char *e, const struct pb_msg *msg, const struct pb_data *pb, int left, int indent) { const struct pb_field *f; const uint8_t *o, *fin; char *old; int i, j, num, sz, type; o = in; old = s = seprintf(s, e, "{\n"); for(i = 0; i < msg->num_fields; i++){ f = &msg->fields[msg->indices[i]]; if(f->flags & PB_FLAG_NO_PRINT) continue; if(is_default(o, f, pb, msg)) continue; type = f->flags & PB_FLAG_MASK_TYPE; if(f->num_at >= 0 && type != PB_TYPE_BYTES){ s = seprintf(s, e, "%*s%s = [", left+indent, "", fname(f)); if(f->index > 0) s = seprintf(s, e, " // #%d", f->index); s = seprintf(s, e, "\n"); num = *(int*)(o + f->num_at); for(j = 0; j < num && s != NULL; j++){ fin = o + f->offset; if(f->flags & PB_FLAG_POINTER) fin = *(uint8_t**)fin; fin += j*f->size; s = seprintf(s, e, "%*s", left+indent+indent, ""); s = print_single(s, e, fin, f, left+indent+indent, indent, o, pb, msg); s = seprintf(s, e, ",\n"); } s = seprintf(s, e, "%*s] (%d item%s)", left+indent, "", num, num == 1 ? "" : "s"); }else{ fin = o + f->offset; if(f->flags & PB_FLAG_POINTER) fin = *(uint8_t**)fin; if(type == PB_TYPE_BYTES){ if((sz = bytes_size(f, in, msg)) < 0) return NULL; if(sz < 1) continue; } s = seprintf(s, e, "%*s%s = ", left+indent, "", fname(f)); s = print_single(s, e, fin, f, left+indent, indent, o, pb, msg); } s = seprintf(s, e, ","); if(f->index > 0) s = seprintf(s, e, " // #%d", f->index); s = seprintf(s, e, "\n"); } if(old != s) s = seprintf(s, e, "%*s}", left, ""); else s = seprintf(old-1, e, "}"); return s; } #endif static void * errorf(const struct pb_msg *msg, const char *f, int findex, const char *fmt, ...) { va_list ap; va_start(ap, fmt); #ifdef PB_PRINTERS fprintf(stderr, "pb: %s.%s (#%d): ", msg->name, f, findex); #else (void)msg; (void)f; fprintf(stderr, "pb: #%d: ", findex); #endif vfprintf(stderr, fmt, ap); va_end(ap); return NULL; } static int bytes_size(const struct pb_field *f, const void *data, const struct pb_msg *msg) { int sz; if(f->flags & PB_FLAG_CONST_SIZE) return f->size; if(f->num_at < 0) return f->num; sz = *(int*)((uint8_t*)data + f->num_at); if(sz < 0 || ((f->flags & PB_FLAG_POINTER) == 0 && sz > f->num)){ errorf(msg, fname(f), f->index, "invalid size %d\n", sz); return -1; } return sz; } static void set_default(void *out, const struct pb_field *f, const struct pb_data *pb) { const struct pb_default *dflt; uint8_t *o; int i, type, sizebytes; o = out; type = f->flags & PB_FLAG_MASK_TYPE; dflt = (pb->dflts != NULL && f->dfltidx > 0) ? &pb->dflts[f->dfltidx-1] : NULL; if(f->flags & PB_FLAG_POINTER){ *(void**)(o + f->offset) = NULL; }else if(type == PB_TYPE_MSG){ i = 0; do{ pb_reset(o + f->offset + i*f->size, &pb->msgs[f->msgidx], pb); }while(++i < f->num); }else if(type == PB_TYPE_INTEGER && dflt != NULL){ if(f->size == 4) *(int32_t*)(o + f->offset) = dflt->integer; else if(f->size == 8) *(int64_t*)(o + f->offset) = dflt->integer; else if(f->size == 2) *(int16_t*)(o + f->offset) = dflt->integer; else *(uint8_t*)(o + f->offset) = dflt->integer; }else if(dflt != NULL && dflt->size > 0){ memmove(o + f->offset, dflt->pointer, MIN(dflt->size, f->size)); }else{ sizebytes = f->size; if(type == PB_TYPE_BYTES && (f->flags & PB_FLAG_CONST_SIZE) == 0) sizebytes = f->num; memset(o + f->offset, 0, sizebytes); } if(f->num_at >= 0) *(int*)(o + f->num_at) = 0; } void pb_reset(void *out, const struct pb_msg *msg, const struct pb_data *pb) { const struct pb_field *f; uint8_t *o; int i; for(i = 0, f = msg->fields; i < msg->num_fields; i++, f++) set_default(out, f, pb); o = out; if(msg->rest_at >= 0) memset(o + msg->rest_at, 0, 2); if(msg->buffer_at >= 0) memset(o + msg->buffer_at, 0, 2); } static const uint8_t * varint_read(uint64_t *out, const struct pb_msg *msg, const uint8_t *b, const uint8_t *e, const char *what) { int varint_bits; for(varint_bits = 0, *out = 0; varint_bits < 64; varint_bits += 7, b++){ if(b == e) return errorf(msg, what, -1, "short read: need 1, have 0\n"); *out |= (uint64_t)(*b & 0x7f) << varint_bits; if((*b & 0x80) == 0){ b++; break; } } if(varint_bits >= 64 || (varint_bits == 63 && b[-1] > 1)) return errorf(msg, what, -1, "too many bits in a varint\n"); return b; } static int varint_outofrange(const struct pb_field *f, int64_t varint) { if((f->flags & PB_FLAG_SIGNED) != 0 || (f->flags & PB_FLAG_TYPE_UNSIGNED) == 0){ if(f->size == 4) return varint < -0x80000000LL || varint > 0x7fffffffLL; else if(f->size == 2) return varint < -0x8000LL || varint > 0x7fffLL; else if(f->size == 1) return varint < -0x80LL || varint > 0x7fLL; }else{ if(f->size == 4) return varint < 0LL || varint > 0xffffffffLL; else if(f->size == 2) return varint < 0LL || varint > 0xffffLL; else if(f->size == 1) return varint < 0LL || varint > 0xffLL; } return 0; } static int ru16(const uint8_t *b) { return (int)b[0] | (int)b[1]<<8; } static void wu16(uint8_t *b, uint32_t x) { b[0] = x; b[1] = x>>8; } const uint8_t * pb_decode(void *out, const uint8_t *s, const uint8_t *e, const struct pb_msg *msg, const struct pb_data *pb) { uint64_t varint; const struct pb_field *f; uint8_t *o, *oldvalue, *value, **assignvalue; const uint8_t *b; const void *cb; uint64_t fields_set[2]; int fsetidx, fsetshift, pbtype; int i, res, datasz, max_bytes_size, raw_size, rest_left, rest_sz, written; uint8_t type, fieldn; o = out; b = s; if(msg->rest_at >= 0) memset(o + msg->rest_at, 0, 2); if(msg->buffer_at >= 0) memset(o + msg->buffer_at, 0, 2); fields_set[0] = 1; /* index=0 is non-protobuf fields, don't reset them after parsing */ fields_set[1] = 0; for(; b != e;){ s = b; if((b = varint_read(&varint, msg, b, e, "?tag")) == NULL) return NULL; type = varint & 7; varint >>= 3; fieldn = varint; if(type != 0 && type != 2) /* no, we don't need it in the "rest" either */ return errorf(msg, "?", fieldn, "unsupported protobuf type %d\n", type); fsetidx = fieldn/64; fsetshift = fieldn%64; if((b = varint_read(&varint, msg, b, e, "?sz")) == NULL) return NULL; datasz = 0; if(type == 2){ /* string, bytes, message, packed repeated */ datasz = varint; if(datasz < 0) return errorf(msg, "?sz", fieldn, "invalid data size: %d\n", datasz); if(datasz > e-b) return errorf(msg, "?sz", fieldn, "short read: need %d, have %d\n", datasz, (int)(e-b)); } /* search for the field */ for(f = msg->fields, i = 0; i < msg->num_fields && (f->index == 0 || f->index != fieldn); i++, f++); if(i >= msg->num_fields){ /* wasn't found */ if(msg->rest_at >= 0){ raw_size = b - s + datasz; rest_sz = ru16(o + msg->rest_at); rest_left = msg->rest_size - rest_sz - 2; if(rest_left < raw_size) return errorf(msg, "?", fieldn, "no space in \"rest\": need %d, have %d\n", raw_size, rest_left); memmove(o + msg->rest_at + 2 + rest_sz, s, raw_size); wu16(o + msg->rest_at, rest_sz + raw_size); } b += datasz; continue; } cb = (f->cbidx > 0 && pb->cbs != NULL) ? pb->cbs[f->cbidx-1] : NULL; value = o + f->offset; if(f->num_at >= 0 && f->num > 0){ /* if it's an array of constant size */ if((fields_set[fsetidx] & (1ULL<<fsetshift)) == 0) /* reset "num" the first time we see the field */ *(int*)(o + f->num_at) = 0; if(*(int*)(o + f->num_at) >= f->num){ /* nowhere to save */ b += datasz; continue; } value += *(int*)(o + f->num_at) * f->size; }else if(f->num_at < 0 && (fields_set[fsetidx] & (1ULL<<fsetshift)) != 0){ /* repeated value, but the field isn't array and is already set, so just keep the first value */ b += datasz; continue; } pbtype = f->flags & PB_FLAG_MASK_TYPE; max_bytes_size = 0; if(f->flags & PB_FLAG_POINTER){ assignvalue = (void*)value; value = *(uint8_t**)value; if(f->flags & PB_FLAG_IS_CONST){ max_bytes_size = 0x1000000; /* arbitrary limit, randomly picked */ }else if(pbtype == PB_TYPE_STRING || pbtype == PB_TYPE_BYTES){ max_bytes_size = MAX(0, msg->buffer_size - ru16(o + msg->buffer_at) - 2); value = o + msg->buffer_at + 2 + ru16(o + msg->buffer_at); }else if(value == NULL){ b += datasz; continue; } }else{ assignvalue = NULL; max_bytes_size = f->size; if(pbtype == PB_TYPE_BYTES && (f->flags & PB_FLAG_CONST_SIZE) == 0) max_bytes_size = f->num; } if(pbtype == PB_TYPE_STRING) /* needs to be null-terminated */ max_bytes_size--; if(type == 0){ /* varint */ if(pbtype != PB_TYPE_INTEGER) return errorf(msg, fname(f), f->index, "expected type %d, got varint instead\n", pbtype); if(f->flags & PB_FLAG_SIGNED) varint = varint>>1 ^ -(varint&1); if(varint_outofrange(f, varint)){ return errorf(msg, fname(f), f->index, "varint out of range: can't fit %"PRId64" in %d bits (%ssigned)\n", varint, f->size*8, (f->flags & PB_FLAG_SIGNED) ? "" : "un"); } if(f->size == 4) *(int32_t*)value = varint; else if(f->size == 8) *(int64_t*)value = varint; else if(f->size == 2) *(int16_t*)value = varint; else *(uint8_t*)value = varint; res = cb == NULL ? PB_CALLBACK_OK : ((int (*)(void*, void*))cb)(out, value); if(res < 0) return NULL; if(res == PB_CALLBACK_THROW_AWAY){ b += datasz; continue; } fields_set[fsetidx] |= 1ULL<<fsetshift; }else if(type == 2){ if(pbtype == PB_TYPE_INTEGER){ /* packed integers */ oldvalue = value; for(s = b; b != s+datasz;){ if((b = varint_read(&varint, msg, b, e, "?int")) == NULL) return NULL; if(f->flags & PB_FLAG_SIGNED) varint = varint>>1 ^ -(varint&1); if(varint_outofrange(f, varint)){ return errorf(msg, fname(f), f->index, "varint out of range: can't fit %"PRId64" in %d bits (%ssigned)\n", varint, f->size*8, (f->flags & PB_FLAG_SIGNED) ? "" : "un"); } if(f->size == 4) *(int32_t*)value = varint; else if(f->size == 8) *(int64_t*)value = varint; else if(f->size == 2) *(int16_t*)value = varint; else *(uint8_t*)value = varint; res = cb == NULL ? PB_CALLBACK_OK : ((int (*)(void*, void*))cb)(out, value); if(res < 0) return NULL; if(res != PB_CALLBACK_THROW_AWAY) value += f->size; if(f->num_at < 0 || ++(*(int*)(o + f->num_at)) >= f->num) break; } b = s + datasz; if(oldvalue != value) fields_set[fsetidx] |= 1ULL<<fsetshift; continue; } if(pbtype == PB_TYPE_MSG){ if(pb_decode(value, b, b+datasz, &pb->msgs[f->msgidx], pb) == NULL) return NULL; res = cb == NULL ? PB_CALLBACK_OK : ((int (*)(void*, void*))cb)(out, value); if(res < 0) return NULL; if(res == PB_CALLBACK_THROW_AWAY){ b += datasz; continue; } fields_set[fsetidx] |= 1ULL<<fsetshift; }else{ if((datasz > max_bytes_size && (f->flags & PB_FLAG_TRUNCATE) == 0) || (datasz != max_bytes_size && (f->flags & PB_FLAG_CONST_SIZE) != 0)){ return errorf(msg, fname(f), f->index, "unexpected size: should be %s%d, have %d\n", (f->flags & PB_FLAG_CONST_SIZE) ? "==" : "<=", max_bytes_size, datasz); } written = MAX(0, MIN(datasz, max_bytes_size)); res = PB_CALLBACK_OK; if(written > 0){ if(f->flags & PB_FLAG_IS_CONST){ *assignvalue = value = (void*)b; }else{ memmove(value, b, written); if(pbtype == PB_TYPE_STRING) value[written++] = 0; if((f->flags & PB_FLAG_POINTER) != 0 && msg->buffer_at >= 0){ *assignvalue = value; wu16(o + msg->buffer_at, ru16(o + msg->buffer_at)+written);