bool checkreturn pb_read(pb_istream_t *stream, uint8_t *buf, size_t count) { #ifndef PB_BUFFER_ONLY if (buf == NULL && stream->callback != buf_read) { /* Skip input bytes */ uint8_t tmp[16]; while (count > 16) { if (!pb_read(stream, tmp, 16)) return false; count -= 16; } return pb_read(stream, tmp, count); } #endif if (stream->bytes_left < count) PB_RETURN_ERROR(stream, "end-of-stream"); #ifndef PB_BUFFER_ONLY if (!stream->callback(stream, buf, count)) PB_RETURN_ERROR(stream, "io error"); #else if (!buf_read(stream, buf, count)) return false; #endif stream->bytes_left -= count; return true; }
static inline int pb_decode_tag(pb_field_t *f) { if (pb_decode_varint(f)) { if(f->left == 0) f->eom = 1; // signal end-of-message return -1; } if (f->val.i64 == 0) { f->eom = 1; // Allow 0-terminated messages. PB_RETURN_ERROR(f, -2, "0-terminated msg"); } f->tag = f->val.i64 >> 3; f->type = (pb_wire_type_t)(f->val.i32 & 7); DBGOUT("tag %u, type %u\n", f->tag, f->type); switch (f->type) { case PB_WT_VARINT: return pb_decode_varint(f); case PB_WT_FIXED64: return pb_decode_fixed64(f); case PB_WT_STRING: return pb_decode_string(f); case PB_WT_FIXED32: return pb_decode_fixed32(f); default: PB_RETURN_ERROR(f, -3, "invalid wire_type"); } return 0; }
/* Encode a field with static or pointer allocation, i.e. one whose data * is available to the encoder directly. */ static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData) { pb_encoder_t func; const void *pSize; bool implicit_has = true; func = PB_ENCODERS[PB_LTYPE(field->type)]; if (field->size_offset) pSize = (const char*)pData + field->size_offset; else pSize = &implicit_has; if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) { /* pData is a pointer to the field, which contains pointer to * the data. If the 2nd pointer is NULL, it is interpreted as if * the has_field was false. */ pData = *(const void* const*)pData; implicit_has = (pData != NULL); } switch (PB_HTYPE(field->type)) { case PB_HTYPE_REQUIRED: if (!pData) PB_RETURN_ERROR(stream, "missing required field"); if (!pb_encode_tag_for_field(stream, field)) return false; if (!func(stream, field, pData)) return false; break; case PB_HTYPE_OPTIONAL: if (*(const bool*)pSize) { if (!pb_encode_tag_for_field(stream, field)) return false; if (!func(stream, field, pData)) return false; } break; case PB_HTYPE_REPEATED: if (!encode_array(stream, field, pData, *(const size_t*)pSize, func)) return false; break; default: PB_RETURN_ERROR(stream, "invalid field type"); } return true; }
bool checkreturn pb_read(pb_istream_t *stream, uint8_t *buf, size_t count) { if (stream->bytes_left < count) PB_RETURN_ERROR(stream, "end-of-stream"); if (!stream->callback(stream, buf, count)) PB_RETURN_ERROR(stream, "io error"); stream->bytes_left -= count; return true; }
bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct) { /* First calculate the message size using a non-writing substream. */ pb_ostream_t substream = PB_OSTREAM_SIZING; size_t size; bool status; if (!pb_encode(&substream, fields, src_struct)) { #ifndef PB_NO_ERRMSG stream->errmsg = substream.errmsg; #endif return false; } size = substream.bytes_written; if (!pb_encode_varint(stream, (uint64_t)size)) return false; if (stream->callback == NULL) return pb_write(stream, NULL, size); /* Just sizing */ if (stream->bytes_written + size > stream->max_size) PB_RETURN_ERROR(stream, "stream full"); /* Use a substream to verify that a callback doesn't write more than * what it did the first time. */ substream.callback = stream->callback; substream.state = stream->state; substream.max_size = size; substream.bytes_written = 0; #ifndef PB_NO_ERRMSG substream.errmsg = NULL; #endif status = pb_encode(&substream, fields, src_struct); stream->bytes_written += substream.bytes_written; stream->state = substream.state; #ifndef PB_NO_ERRMSG stream->errmsg = substream.errmsg; #endif if (substream.bytes_written != size) PB_RETURN_ERROR(stream, "submsg size changed"); return status; }
static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src) { if (field->ptr == NULL) PB_RETURN_ERROR(stream, "invalid field descriptor"); return pb_encode_submessage(stream, (const pb_field_t*)field->ptr, src); }
bool checkreturn pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t *field) { pb_wire_type_t wiretype; switch (PB_LTYPE(field->type)) { case PB_LTYPE_VARINT: case PB_LTYPE_UVARINT: case PB_LTYPE_SVARINT: wiretype = PB_WT_VARINT; break; case PB_LTYPE_FIXED32: wiretype = PB_WT_32BIT; break; case PB_LTYPE_FIXED64: wiretype = PB_WT_64BIT; break; case PB_LTYPE_BYTES: case PB_LTYPE_STRING: case PB_LTYPE_SUBMESSAGE: wiretype = PB_WT_STRING; break; default: PB_RETURN_ERROR(stream, "invalid field type"); } return pb_encode_tag(stream, wiretype, field->tag); }
static bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest) { uint8_t byte; uint32_t result; if (!pb_read(stream, &byte, 1)) return false; if (!(byte & 0x80)) { /* Quick case, 1 byte value */ result = byte; } else { /* Multibyte case */ uint8_t bitpos = 7; result = byte & 0x7F; do { if (bitpos >= 32) PB_RETURN_ERROR(stream, "varint overflow"); if (!pb_read(stream, &byte, 1)) return false; result |= (uint32_t)(byte & 0x7F) << bitpos; bitpos = (uint8_t)(bitpos + 7); } while (byte & 0x80); } *dest = result; return true; }
/* Read a raw value to buffer, for the purpose of passing it to callback as * a substream. Size is maximum size on call, and actual size on return. */ static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, uint8_t *buf, size_t *size) { size_t max_size = *size; switch (wire_type) { case PB_WT_VARINT: *size = 0; do { (*size)++; if (*size > max_size) return false; if (!pb_read(stream, buf, 1)) return false; } while (*buf++ & 0x80); return true; case PB_WT_64BIT: *size = 8; return pb_read(stream, buf, 8); case PB_WT_32BIT: *size = 4; return pb_read(stream, buf, 4); default: PB_RETURN_ERROR(stream, "invalid wire_type"); } }
/* Parse a dictionary in format {'name': value} and encode it directly to protobuf */ static bool encode_dictionary(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) { int textlen; char *p = (char*)*arg; if (*p == '{') p++; while (*p != '}') { KeyValuePair pair = KeyValuePair_init_zero; if (*p != '\'') PB_RETURN_ERROR(stream, "invalid key, missing quote"); p++; /* Starting quote of key */ textlen = strchr(p, '\'') - p; strncpy(pair.key, p, textlen); pair.key[textlen] = 0; p += textlen + 2; while (*p == ' ') p++; if (*p == '[') { /* Value is a tree */ pair.treeValue.funcs.encode = encode_tree; pair.treeValue.arg = p; } else if (*p == '\'') { /* Value is a string */ pair.has_stringValue = true; p++; textlen = strchr(p, '\'') - p; strncpy(pair.stringValue, p, textlen); pair.stringValue[textlen] = 0; } else if (*p == '{') { /* Value is a dictionary */ pair.has_dictValue = true; pair.dictValue.dictItem.funcs.encode = encode_dictionary; pair.dictValue.dictItem.arg = p; } else { /* Value is integer */ pair.has_intValue = true; pair.intValue = atoi(p); } p = find_end_of_item(p); if (!pb_encode_tag_for_field(stream, field)) return false; if (!pb_encode_submessage(stream, KeyValuePair_fields, &pair)) return false; } return true; }
/* Read a single byte from input stream. buf may not be NULL. * This is an optimization for the varint decoding. */ static bool checkreturn pb_readbyte(pb_istream_t *stream, uint8_t *buf) { if (!stream->bytes_left) PB_RETURN_ERROR(stream, "end-of-stream"); #ifndef PB_BUFFER_ONLY if (!stream->callback(stream, buf, 1)) PB_RETURN_ERROR(stream, "io error"); #else *buf = *(uint8_t*)stream->state; stream->state = (uint8_t*)stream->state + 1; #endif stream->bytes_left--; return true; }
bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src) { const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)src; if (bytes->size + offsetof(pb_bytes_array_t, bytes) > field->data_size) PB_RETURN_ERROR(stream, "bytes size exceeded"); return pb_encode_string(stream, bytes->bytes, bytes->size); }
bool checkreturn pb_write(pb_ostream_t *stream, const uint8_t *buf, size_t count) { if (stream->callback != NULL) { if (stream->bytes_written + count > stream->max_size) PB_RETURN_ERROR(stream, "stream full"); #ifdef PB_BUFFER_ONLY if (!buf_write(stream, buf, count)) PB_RETURN_ERROR(stream, "io error"); #else if (!stream->callback(stream, buf, count)) PB_RETURN_ERROR(stream, "io error"); #endif } stream->bytes_written += count; return true; }
static inline int pb_get_byte(pb_field_t *f, uint8_t *buf) { if (f->left < 1) PB_RETURN_ERROR(f, -1, "end-of-buffer"); if(buf) *buf = *(uint8_t*)f->ptr; f->ptr += 1; f->left -= 1; return 0; }
static inline int pb_get_ddword(pb_field_t *f, uint64_t *buf) { if (f->left < 8) PB_RETURN_ERROR(f, -1, "end-of-buffer"); if(buf) *buf = *(uint64_t*)f->ptr; f->ptr += 8; f->left -= 8; return 0; }
int pb_skip_field(pb_field_t *f, pb_wire_type_t wire_type) { switch (wire_type) { case PB_WT_VARINT: return pb_skip_varint(f); case PB_WT_FIXED64: return pb_get_ddword(f, NULL); case PB_WT_STRING: return pb_skip_string(f); case PB_WT_FIXED32: return pb_get_dword(f, NULL); default: PB_RETURN_ERROR(f, -1, "invalid wire_type"); } }
bool checkreturn pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type) { switch (wire_type) { case PB_WT_VARINT: return pb_skip_varint(stream); case PB_WT_64BIT: return pb_read(stream, NULL, 8); case PB_WT_STRING: return pb_skip_string(stream); case PB_WT_32BIT: return pb_read(stream, NULL, 4); default: PB_RETURN_ERROR(stream, "invalid wire_type"); } }
static inline int pb_get(pb_field_t *f, uint8_t *buf, size_t count) { if ((size_t )f->left < count) PB_RETURN_ERROR(f, -1, "end-of-buffer"); if(buf) memcpy(buf, f->ptr, count); f->ptr += count; f->left -= count; return 0; }
static inline int pb_add_ddword(pb_buff_t *b, uint64_t val) { if(b->left < 8) PB_RETURN_ERROR(b, -1, "buf overflow"); *(uint64_t*)b->ptr = val; DBGOUT("add_ddword: %02x %02x %02x %02x %02x %02x %02x %02x\n", b->ptr[0], b->ptr[1], b->ptr[2], b->ptr[3], b->ptr[4], b->ptr[5], b->ptr[6], b->ptr[7]); b->ptr += 8; b->left -= 8; return 0; }
/* static inline int pb_add_byte(pb_buff_t *b, const uint8_t *buf) { if(b->left < 1) PB_RETURN_ERROR(b, -1, "buf overflow"); *(uint8_t*)b->ptr = *buf; b->ptr += 1; b->left -= 1; DBGOUT("add_byte: %02x\n", *buf); return 0; } */ static inline int pb_add_dword(pb_buff_t *b, uint32_t val) { if(b->left < 4) PB_RETURN_ERROR(b, -1, "buf overflow"); *(uint32_t*)b->ptr = val; DBGOUT("add_dword: %02x %02x %02x %02x\n", b->ptr[0], b->ptr[1], b->ptr[2], b->ptr[3]); b->ptr += 4; b->left -= 4; return 0; }
bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src) { int64_t value = 0; switch (field->data_size) { case 4: value = *(const int32_t*)src; break; case 8: value = *(const int64_t*)src; break; default: PB_RETURN_ERROR(stream, "invalid data_size"); } return pb_encode_svarint(stream, value); }
bool write_callback(pb_ostream_t *stream, const uint8_t *buf, size_t count) { faulty_stream_t *state = stream->state; while (count--) { if (state->fail_after == 0) PB_RETURN_ERROR(stream, "simulated"); state->fail_after--; *state->buffer++ = *buf++; } return true; }
/* Decode string length from stream and return a substream with limited length. * Remember to close the substream using pb_close_string_substream(). */ bool checkreturn pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream) { uint32_t size; if (!pb_decode_varint32(stream, &size)) return false; *substream = *stream; if (substream->bytes_left < size) PB_RETURN_ERROR(stream, "parent stream too short"); substream->bytes_left = size; stream->bytes_left -= size; return true; }
static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iterator_t *iter) { switch (PB_ATYPE(iter->current->type)) { case PB_ATYPE_STATIC: return decode_static_field(stream, wire_type, iter); case PB_ATYPE_CALLBACK: return decode_callback_field(stream, wire_type, iter); default: PB_RETURN_ERROR(stream, "invalid field type"); } }
/* Encode a single field of any callback or static type. */ static bool checkreturn encode_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData) { switch (PB_ATYPE(field->type)) { case PB_ATYPE_STATIC: return encode_static_field(stream, field, pData); case PB_ATYPE_CALLBACK: return encode_callback_field(stream, field, pData); default: PB_RETURN_ERROR(stream, "invalid field type"); } }
int pb_add_field_val(pb_buff_t *b, uint32_t tag, pb_wire_type_t type, const pb_field_val_t *val) { DBGOUT("add_field_val:"); int ret = pb_add_tag(b, type, tag); if(ret) return -1; switch (type) { case PB_WT_VARINT: return pb_add_vint(b, val->i64); case PB_WT_FIXED64: return pb_add_ddword(b, val->i64); case PB_WT_STRING: if(pb_add_vint(b, val->str.len)) return -2; return pb_add(b, val->str.data, val->str.len); case PB_WT_FIXED32: return pb_add_dword(b, val->i32); default: PB_RETURN_ERROR(b, -2, "invalid wire_type"); } }
static inline int pb_add(pb_buff_t *b, const void *buf, size_t count) { if((size_t)b->left < count) PB_RETURN_ERROR(b, -1, "buf overflow"); DBGOUT("add(len=%u):", count); // int i; // for(i=0; i<count; i++) DBGOUT(" %02x", ((uint8_t*)buf)[i]); DBGOUT("\n"); memcpy(b->ptr, buf, count); b->ptr += count; b->left -= count; return 0; }
static inline int pb_decode_string(pb_field_t *f) { uint32_t sz; if (pb_decode_varint(f)) return -1; if (f->val.i64 > (uint64_t)f->left) PB_RETURN_ERROR(f, -2, "string overflow"); sz = f->val.i64; f->val.str.len = sz; f->val.str.data = (char*)f->ptr; f->ptr += sz; f->left -= sz; return 0; }
pb_field_t *pb_begin_delimited(pb_field_t *f, const void *data, int dlen) { f->start = f->ptr = (char *)data; f->size = f->left = dlen; f->eom = 0; if (pb_decode_varint(f)) return NULL; if(f->val.i64 > (uint64_t)f->left) PB_RETURN_ERROR(f, NULL, "delimited overflow"); f->left = f->val.i64; if(pb_decode_tag(f)) return NULL; return f; }
bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest) { uint8_t byte; int bitpos = 0; *dest = 0; while (bitpos < 64 && pb_read(stream, &byte, 1)) { *dest |= (uint64_t)(byte & 0x7F) << bitpos; bitpos += 7; if (!(byte & 0x80)) return true; } PB_RETURN_ERROR(stream, "varint overflow"); }