summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPetteri Aimonen <jpa@git.mail.kapsi.fi>2017-04-14 11:46:18 +0300
committerPetteri Aimonen <jpa@git.mail.kapsi.fi>2017-04-14 11:46:18 +0300
commit278ffb890e3d8722e4c7d824baaf221a1e375fc4 (patch)
tree17cc07decab75fbd04f4d5f68d59d218034db84a
parente75d20b659aced782a0017244836e415164e2d0e (diff)
Fix bugs in proto3 mode encoding of submessages (#256)
pb_check_proto3_default_value() recurses into submessages, but it didn't handle other than singular fields correctly. This caused it to sometimes skip submessages with only repeated or oneof fields present.
-rw-r--r--pb_encode.c36
1 files changed, 31 insertions, 5 deletions
diff --git a/pb_encode.c b/pb_encode.c
index 30f60d83..05d691df 100644
--- a/pb_encode.c
+++ b/pb_encode.c
@@ -204,25 +204,51 @@ static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *fie
* 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)
+ pb_type_t type = field->type;
+ const void *pSize = (const char*)pData + field->size_offset;
+
+ if (PB_HTYPE(type) == PB_HTYPE_REQUIRED)
+ {
+ /* Required proto2 fields inside proto3 submessage, pretty rare case */
+ return false;
+ }
+ else if (PB_HTYPE(type) == PB_HTYPE_REPEATED)
+ {
+ /* Repeated fields inside proto3 submessage: present if count != 0 */
+ return *(const pb_size_t*)pSize == 0;
+ }
+ else if (PB_HTYPE(type) == PB_HTYPE_ONEOF)
+ {
+ /* Oneof fields */
+ return *(const pb_size_t*)pSize == 0;
+ }
+ else if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->size_offset)
+ {
+ /* Proto2 optional fields inside proto3 submessage */
+ return *(const bool*)pSize == false;
+ }
+
+ /* Rest is proto3 singular fields */
+
+ if (PB_ATYPE(type) == PB_ATYPE_STATIC)
{
- if (PB_LTYPE(field->type) == PB_LTYPE_BYTES)
+ if (PB_LTYPE(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)
+ else if (PB_LTYPE(type) == PB_LTYPE_STRING)
{
return *(const char*)pData == '\0';
}
- else if (PB_LTYPE(field->type) == PB_LTYPE_FIXED_LENGTH_BYTES)
+ else if (PB_LTYPE(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)
+ else if (PB_LTYPE(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