Return values and error handling
================================
-Most functions in nanopb return bool: *true* means success, *false* means failure. If this is enough for you, skip this section.
+Most functions in nanopb return bool: *true* means success, *false* means failure. There is also some support for error messages for debugging purposes: the error messages go in *stream->errmsg*.
-For simplicity, nanopb doesn't define it's own error codes. This might be added if there is a compelling need for it. You can however deduce something about the error causes:
+The error messages help in guessing what is the underlying cause of the error. The most common error conditions are:
1) Running out of memory. Because everything is allocated from the stack, nanopb can't detect this itself. Encoding or decoding the same type of a message always takes the same amount of stack space. Therefore, if it works once, it works always.
2) Invalid field description. These are usually stored as constants, so if it works under the debugger, it always does.
-3) IO errors in your own stream callbacks. Because encoding/decoding stops at the first error, you can overwrite the *state* field in the struct and store your own error code there.
-4) Errors that happen in your callback functions. You can use the state field in the callback structure.
+3) IO errors in your own stream callbacks.
+4) Errors that happen in your callback functions.
5) Exceeding the max_size or bytes_left of a stream.
6) Exceeding the max_size of a string or array field
-7) Invalid protocol buffers binary message. It's not like you could recover from it anyway, so a simple failure should be enough.
-
-In my opinion, it is enough that 1. and 2. can be resolved using a debugger.
-
-However, you may be interested which of the remaining conditions caused the error. For 3. and 4., you can set and check the state. If you have to detect 5. and 6., you should convert the fields to callback type. Any remaining problem is of type 7.
+7) Invalid protocol buffers binary message.
Increases code size 3 bytes per each field. Compiler error will tell if you need this.
PB_FIELD_32BIT Add support for tag numbers > 65535 and fields larger than 65535 bytes or 65535 array entries.
Increases code size 9 bytes per each field. Compiler error will tell if you need this.
+PB_NO_ERRMSG Disables the support for error messages; only error information is the true/false return value.
+ Decreases the code size by a few hundred bytes.
============================ ================================================================================================
The PB_MAX_REQUIRED_FIELDS, PB_FIELD_16BIT and PB_FIELD_32BIT settings allow raising some datatype limits to suit larger messages.
This function uses `pb_decode_varint`_ to read an integer from the stream. This is interpreted as a number of bytes, and the substream is set up so that its `bytes_left` is initially the same as the length, and its callback function and state the same as the parent stream.
pb_close_string_substream
-------------------------
+-------------------------
Close the substream created with `pb_make_string_substream`_. ::
void pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream);
if (!pb_decode(&input, ListFilesResponse_fields, &response))
{
- fprintf(stderr, "Decoding failed.\n");
+ fprintf(stderr, "Decode failed: %s\n", PB_GET_ERROR(&input));
return false;
}
if (!pb_decode(&input, ListFilesRequest_fields, &request))
{
- printf("Decoding failed.\n");
+ printf("Decode failed: %s\n", PB_GET_ERROR(&input));
return;
}
if (!status)
{
- printf("Decoding failed.\n");
+ printf("Decode failed: %s\n", PB_GET_ERROR(&stream));
return 1;
}
#define pb_delta_end(st, m1, m2) (offsetof(st, m1) - offsetof(st, m2) - pb_membersize(st, m2))
#define PB_LAST_FIELD {0,(pb_type_t) 0,0,0}
+/* These macros are used for giving out error messages.
+ * They are mostly a debugging aid; the main error information
+ * is the true/false return value from functions.
+ * Some code space can be saved by disabling the error
+ * messages if not used.
+ */
+#ifdef PB_NO_ERRMSG
+#define PB_RETURN_ERROR(stream,msg) return false
+#define PB_GET_ERROR(stream) "(errmsg disabled)"
+#else
+#define PB_RETURN_ERROR(stream,msg) \
+ do {\
+ if ((stream)->errmsg == NULL) \
+ (stream)->errmsg = (msg); \
+ return false; \
+ } while(0)
+#define PB_GET_ERROR(stream) ((stream)->errmsg ? (stream)->errmsg : "(none)")
+#endif
#endif
bool checkreturn pb_read(pb_istream_t *stream, uint8_t *buf, size_t count)
{
if (stream->bytes_left < count)
- return false;
+ PB_RETURN_ERROR(stream, "end-of-stream");
if (!stream->callback(stream, buf, count))
- return false;
+ PB_RETURN_ERROR(stream, "io error");
stream->bytes_left -= count;
return true;
return true;
}
- return false;
+ PB_RETURN_ERROR(stream, "varint overflow");
}
bool checkreturn pb_skip_varint(pb_istream_t *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: return false;
+ default: PB_RETURN_ERROR(stream, "invalid wire_type");
}
}
*size = 4;
return pb_read(stream, buf, 4);
- default: return false;
+ default: PB_RETURN_ERROR(stream, "invalid wire_type");
}
}
*substream = *stream;
if (substream->bytes_left < size)
- return false;
+ PB_RETURN_ERROR(stream, "parent stream too short");
substream->bytes_left = size;
stream->bytes_left -= size;
size_t *size = (size_t*)iter->pSize;
void *pItem = (uint8_t*)iter->pData + iter->current->data_size * (*size);
if (*size >= iter->current->array_size)
- return false;
+ PB_RETURN_ERROR(stream, "array overflow");
(*size)++;
return func(stream, iter->current, pItem);
while (substream.bytes_left)
{
if (!pCallback->funcs.decode(&substream, iter->current, pCallback->arg))
- return false;
+ PB_RETURN_ERROR(stream, "callback failed");
}
pb_close_string_substream(stream, &substream);
}
default:
- return false;
+ PB_RETURN_ERROR(stream, "invalid field type");
}
}
iter.required_field_index < PB_MAX_REQUIRED_FIELDS &&
!(fields_seen[iter.required_field_index >> 3] & (1 << (iter.required_field_index & 7))))
{
- return false;
+ PB_RETURN_ERROR(stream, "missing required field");
}
} while (pb_field_next(&iter));
case 2: *(uint16_t*)dest = value; break;
case 4: *(uint32_t*)dest = value; break;
case 8: *(uint64_t*)dest = value; break;
- default: return false;
+ default: PB_RETURN_ERROR(stream, "invalid data_size");
}
return status;
{
case 4: *(int32_t*)dest = value; break;
case 8: *(int64_t*)dest = value; break;
- default: return false;
+ default: PB_RETURN_ERROR(stream, "invalid data_size");
}
return status;
/* Check length, noting the space taken by the size_t header. */
if (x->size > field->data_size - offsetof(pb_bytes_array_t, bytes))
- return false;
+ PB_RETURN_ERROR(stream, "bytes overflow");
return pb_read(stream, x->bytes, x->size);
}
/* Check length, noting the null terminator */
if (size + 1 > field->data_size)
- return false;
+ PB_RETURN_ERROR(stream, "string overflow");
status = pb_read(stream, (uint8_t*)dest, size);
*((uint8_t*)dest + size) = 0;
return false;
if (field->ptr == NULL)
- return false;
+ PB_RETURN_ERROR(stream, "invalid field descriptor");
status = pb_decode(&substream, (pb_field_t*)field->ptr, dest);
pb_close_string_substream(stream, &substream);
bool (*callback)(pb_istream_t *stream, uint8_t *buf, size_t count);
void *state; /* Free field for use by callback implementation */
size_t bytes_left;
+
+#ifndef PB_NO_ERRMSG
+ const char *errmsg;
+#endif
};
pb_istream_t pb_istream_from_buffer(uint8_t *buf, size_t bufsize);
/* Decode and print out the stuff */
if (!print_person(&stream))
{
- printf("Parsing failed.\n");
+ printf("Parsing failed: %s\n", PB_GET_ERROR(&stream));
return 1;
} else {
return 0;
pb_istream_t stream = {&callback, stdin, 10000};
if (!print_person(&stream))
{
- printf("Parsing failed.\n");
+ printf("Parsing failed: %s\n", PB_GET_ERROR(&stream));
return 1;
} else {
return 0;
/* Decode and print out the stuff */
if (!check_alltypes(&stream))
{
- printf("Parsing failed.\n");
+ printf("Parsing failed: %s\n", PB_GET_ERROR(&stream));
return 1;
} else {
return 0;
if (!pb_decode(&stream, MissingField_fields, &msg))
{
- printf("Decode failed.\n");
+ printf("Decode failed: %s\n", PB_GET_ERROR(&stream));
return 2;
}
}