LCOV - code coverage report
Current view: top level - oneof_callback - decode_oneof.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 57 66 86.4 %
Date: 2023-02-14 20:10:26 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /* Decode a message using callbacks inside oneof fields */
       2             : 
       3             : #include <stdio.h>
       4             : #include <stdlib.h>
       5             : #include <string.h>
       6             : #include <pb_decode.h>
       7             : #include <assert.h>
       8             : #include "oneof.pb.h"
       9             : #include "test_helpers.h"
      10             : #include "unittests.h"
      11             : 
      12             : /* This is a nanopb-0.4 style global callback, that is referred by function name
      13             :  * and does not have to be bound separately to the message. It also allows defining
      14             :  * a custom data type for the field in the structure.
      15             :  */
      16           1 : bool SubMsg3_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field)
      17             : {
      18           1 :     if (istream && field->tag == SubMsg3_strvalue_tag)
      19             :     {
      20             :         /* We could e.g. malloc some memory and assign it to our custom datatype
      21             :          * in the message structure here, accessible by field->pData. But in
      22             :          * this example we just print the string directly.
      23             :          */
      24             : 
      25             :         uint8_t buffer[64];
      26           1 :         int strlen = istream->bytes_left;
      27             : 
      28           1 :         if (strlen > sizeof(buffer) - 1)
      29           0 :             return false;
      30             : 
      31           1 :         buffer[strlen] = '\0';
      32             : 
      33           1 :         if (!pb_read(istream, buffer, strlen))
      34           0 :             return false;
      35             : 
      36           1 :         printf("  strvalue: \"%s\"\n", buffer);
      37             :     }
      38             : 
      39           1 :     return true;
      40             : }
      41             : 
      42             : /* The two callbacks below are traditional callbacks that use function pointers
      43             :  * defined in pb_callback_t.
      44             :  */
      45          15 : bool print_int32(pb_istream_t *stream, const pb_field_t *field, void **arg)
      46             : {
      47             :     uint64_t value;
      48          15 :     if (!pb_decode_varint(stream, &value))
      49           0 :         return false;
      50             : 
      51          15 :     printf((char*)*arg, (int)value);
      52          15 :     return true;
      53             : }
      54             : 
      55           1 : bool print_string(pb_istream_t *stream, const pb_field_t *field, void **arg)
      56             : {
      57             :     uint8_t buffer[64];
      58           1 :     int strlen = stream->bytes_left;
      59             : 
      60           1 :     if (strlen > sizeof(buffer) - 1)
      61           0 :         return false;
      62             : 
      63           1 :     buffer[strlen] = '\0';
      64             : 
      65           1 :     if (!pb_read(stream, buffer, strlen))
      66           0 :         return false;
      67             : 
      68             :     /* Print the string, in format comparable with protoc --decode.
      69             :      * Format comes from the arg defined in main().
      70             :      */
      71           1 :     printf((char*)*arg, buffer);
      72           1 :     return true;
      73             : }
      74             : 
      75             : /* The callback below is a message-level callback which is called before each
      76             :  * submessage is encoded. It is used to set the pb_callback_t callbacks inside
      77             :  * the submessage. The reason we need this is that different submessages share
      78             :  * storage inside oneof union, and before we know the message type we can't set
      79             :  * the callbacks without overwriting each other.
      80             :  */
      81           4 : bool msg_callback(pb_istream_t *stream, const pb_field_t *field, void **arg)
      82             : {
      83             :     /* Print the prefix field before the submessages.
      84             :      * This also demonstrates how to access the top level message fields
      85             :      * from callbacks.
      86             :      */
      87           4 :     OneOfMessage *topmsg = field->message;
      88           4 :     printf("prefix: %d\n", (int)topmsg->prefix);
      89             : 
      90           4 :     if (field->tag == OneOfMessage_submsg1_tag)
      91             :     {
      92           1 :         SubMsg1 *msg = field->pData;
      93           1 :         printf("submsg1 {\n");
      94           1 :         msg->array.funcs.decode = print_int32;
      95           1 :         msg->array.arg = "  array: %d\n";
      96             :     }
      97           3 :     else if (field->tag == OneOfMessage_submsg2_tag)
      98             :     {
      99           1 :         SubMsg2 *msg = field->pData;
     100           1 :         printf("submsg2 {\n");
     101           1 :         msg->strvalue.funcs.decode = print_string;
     102           1 :         msg->strvalue.arg = "  strvalue: \"%s\"\n";
     103             :     }
     104           2 :     else if (field->tag == OneOfMessage_submsg3_tag)
     105             :     {
     106             :         /* Because SubMsg3 callback is bound by function name, we do not
     107             :          * need to initialize anything here. But we just print a string
     108             :          * to get protoc-equivalent formatted output from the testcase.
     109             :          */
     110           2 :         printf("submsg3 {\n");
     111             :     }
     112             : 
     113             :     /* Once we return true, pb_dec_submessage() will go on to decode the
     114             :      * submessage contents. But if we want, we can also decode it ourselves
     115             :      * above and leave stream->bytes_left at 0 value, inhibiting automatic
     116             :      * decoding.
     117             :      */
     118           4 :     return true;
     119             : }
     120             : 
     121           6 : int main(int argc, char **argv)
     122             : {
     123             :     uint8_t buffer[256];
     124           6 :     OneOfMessage msg = OneOfMessage_init_zero;
     125             :     pb_istream_t stream;
     126             :     size_t count;
     127             : 
     128             :     SET_BINARY_MODE(stdin);
     129           6 :     count = fread(buffer, 1, sizeof(buffer), stdin);
     130             : 
     131           6 :     if (!feof(stdin))
     132             :     {
     133           0 :         fprintf(stderr, "Message does not fit in buffer\n");
     134           0 :         return 1;
     135             :     }
     136             : 
     137             :     /* Set up the cb_values callback, which will in turn set up the callbacks
     138             :      * for each oneof field once the field tag is known. */
     139           6 :     msg.cb_values.funcs.decode = msg_callback;
     140           6 :     stream = pb_istream_from_buffer(buffer, count);
     141           6 :     if (!pb_decode(&stream, OneOfMessage_fields, &msg))
     142             :     {
     143           0 :         fprintf(stderr, "Decoding failed: %s\n", PB_GET_ERROR(&stream));
     144           0 :         return 1;
     145             :     }
     146             : 
     147             :     /* This is just printing for the test case logic */
     148           6 :     if (msg.which_values == OneOfMessage_intvalue_tag)
     149             :     {
     150           1 :         printf("prefix: %d\n", (int)msg.prefix);
     151           1 :         printf("intvalue: %d\n", (int)msg.values.intvalue);
     152             :     }
     153           5 :     else if (msg.which_values == OneOfMessage_strvalue_tag)
     154             :     {
     155           1 :         printf("prefix: %d\n", (int)msg.prefix);
     156           1 :         printf("strvalue: \"%s\"\n", msg.values.strvalue);
     157             :     }
     158           4 :     else if (msg.which_values == OneOfMessage_submsg3_tag &&
     159           2 :              msg.values.submsg3.which_values == SubMsg3_intvalue_tag)
     160             :     {
     161           1 :         printf("  intvalue: %d\n", (int)msg.values.submsg3.values.intvalue);
     162           1 :         printf("}\n");
     163             :     }
     164             :     else
     165             :     {
     166           3 :         printf("}\n");
     167             :     }
     168           6 :     printf("suffix: %d\n", (int)msg.suffix);
     169             : 
     170           6 :     assert(msg.prefix == 123);
     171           6 :     assert(msg.suffix == 321);
     172             : 
     173           6 :     return 0;
     174             : }

Generated by: LCOV version 1.14