static bool checkreturn encode_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData);
static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension);
static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData);
+static void *pb_const_cast(const void *p);
static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
static bool checkreturn pb_enc_uvarint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
static bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src);
static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src);
static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src);
+static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src);
/* --- Function pointers to field encoders ---
* Order in the array must match pb_action_t LTYPE numbering.
&pb_enc_string,
&pb_enc_submessage,
NULL, /* extensions */
- &pb_enc_bytes /* PB_LTYPE_FIXED_LENGTH_BYTES */
+ &pb_enc_fixed_length_bytes
};
/*******************************
static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count)
{
+ size_t i;
pb_byte_t *dest = (pb_byte_t*)stream->state;
stream->state = dest + count;
- while (count--)
- *dest++ = *buf++;
+ for (i = 0; i < count; i++)
+ dest[i] = buf[i];
return true;
}
return true;
}
+/* In proto3, all fields are optional and are only encoded if their value is "non-zero".
+ * This function implements the check for the zero value. */
+static bool pb_check_proto3_default_value(const pb_field_t *field, const void *pData)
+{
+ if (PB_ATYPE(field->type) == PB_ATYPE_STATIC)
+ {
+ if (PB_LTYPE(field->type) == PB_LTYPE_BYTES)
+ {
+ const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)pData;
+ return bytes->size == 0;
+ }
+ else if (PB_LTYPE(field->type) == PB_LTYPE_STRING)
+ {
+ return *(const char*)pData == '\0';
+ }
+ else if (PB_LTYPE(field->type) == PB_LTYPE_FIXED_LENGTH_BYTES)
+ {
+ /* Fixed length bytes is only empty if its length is fixed
+ * as 0. Which would be pretty strange, but we can check
+ * it anyway. */
+ return field->data_size == 0;
+ }
+ else if (PB_LTYPE(field->type) == PB_LTYPE_SUBMESSAGE)
+ {
+ /* Check all fields in the submessage to find if any of them
+ * are non-zero. The comparison cannot be done byte-per-byte
+ * because the C struct may contain padding bytes that must
+ * be skipped.
+ */
+ pb_field_iter_t iter;
+ if (pb_field_iter_begin(&iter, (const pb_field_t*)field->ptr, pb_const_cast(pData)))
+ {
+ do
+ {
+ if (!pb_check_proto3_default_value(iter.pos, iter.pData))
+ {
+ return false;
+ }
+ } while (pb_field_iter_next(&iter));
+ }
+ return true;
+ }
+ }
+
+ {
+ /* Catch-all branch that does byte-per-byte comparison for zero value.
+ *
+ * This is for all pointer fields, and for static PB_LTYPE_VARINT,
+ * UVARINT, SVARINT, FIXED32, FIXED64, EXTENSION fields, and also
+ * callback fields. These all have integer or pointer value which
+ * can be compared with 0.
+ */
+ pb_size_t i;
+ const char *p = (const char*)pData;
+ for (i = 0; i < field->data_size; i++)
+ {
+ if (p[i] != 0)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
+
/* 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;
+ bool implicit_has;
+ const void *pSize = &implicit_has;
func = PB_ENCODERS[PB_LTYPE(field->type)];
if (field->size_offset)
+ {
+ /* Static optional, repeated or oneof field */
pSize = (const char*)pData + field->size_offset;
- else if (!field->size_offset && PB_HTYPE(field->type) == PB_HTYPE_OPTIONAL)
+ }
+ else if (PB_HTYPE(field->type) == PB_HTYPE_OPTIONAL)
{
- /* In proto3 there are optional fields but no has_ flag, do not encode this fields
- * when value is default or empty. */
- if(PB_LTYPE(field->type) == PB_LTYPE_BYTES)
- {
- const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)pData;
- if(bytes->size == 0)
- implicit_has = false;
- }
- else if ((PB_LTYPE(field->type) == PB_LTYPE_STRING && *(const char*)pData == '\0') ||
- (field->data_size == sizeof(uint_least8_t) && *(const uint_least8_t*)pData == 0) ||
- (field->data_size == sizeof(uint_least16_t) && *(const uint_least16_t*)pData == 0) ||
- (field->data_size == sizeof(uint32_t) && *(const uint_least32_t*)pData == 0) ||
- (field->data_size == sizeof(uint64_t) && *(const uint_least64_t*)pData == 0))
- {
- implicit_has = false;
- }
- pSize = &implicit_has;
+ /* Proto3 style field, optional but without explicit has_ field. */
+ implicit_has = !pb_check_proto3_default_value(field, pData);
}
else
- pSize = &implicit_has;
+ {
+ /* Required field, always present */
+ implicit_has = true;
+ }
if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
{
* 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);
}
* Encode all fields *
*********************/
-static void *remove_const(const void *p)
+static void *pb_const_cast(const void *p)
{
/* Note: this casts away const, in order to use the common field iterator
* logic for both encoding and decoding. */
bool checkreturn pb_encode(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct)
{
pb_field_iter_t iter;
- if (!pb_field_iter_begin(&iter, fields, remove_const(src_struct)))
+ if (!pb_field_iter_begin(&iter, fields, pb_const_cast(src_struct)))
return true; /* Empty message type */
do {
{
const pb_bytes_array_t *bytes = NULL;
- if (PB_LTYPE(field->type) == PB_LTYPE_FIXED_LENGTH_BYTES)
- return pb_encode_string(stream, (const pb_byte_t*)src, field->data_size);
-
bytes = (const pb_bytes_array_t*)src;
if (src == NULL)
return pb_encode_submessage(stream, (const pb_field_t*)field->ptr, src);
}
+static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+ return pb_encode_string(stream, (const pb_byte_t*)src, field->data_size);
+}
+