From 0f1d5cca59a2fddcf6bc627bb35e207bf3889547 Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Sat, 16 Jun 2012 14:08:40 +0300 Subject: [PATCH] Added example on how to handle unions. --- example_unions/Makefile | 17 ++++++++ example_unions/decode.c | 92 +++++++++++++++++++++++++++++++++++++++++ example_unions/encode.c | 85 +++++++++++++++++++++++++++++++++++++ example_unions/unionproto.proto | 30 ++++++++++++++ 4 files changed, 224 insertions(+) create mode 100644 example_unions/Makefile create mode 100644 example_unions/decode.c create mode 100644 example_unions/encode.c create mode 100644 example_unions/unionproto.proto diff --git a/example_unions/Makefile b/example_unions/Makefile new file mode 100644 index 00000000..29514ca9 --- /dev/null +++ b/example_unions/Makefile @@ -0,0 +1,17 @@ +CFLAGS=-ansi -Wall -Werror -I .. -g -O0 +DEPS=../pb_decode.c ../pb_decode.h ../pb_encode.c ../pb_encode.h ../pb.h + +all: encode decode + ./encode 1 | ./decode + ./encode 2 | ./decode + ./encode 3 | ./decode + +clean: + rm -f encode unionproto.pb.h unionproto.pb.c + +%: %.c $(DEPS) unionproto.pb.h unionproto.pb.c + $(CC) $(CFLAGS) -o $@ $< ../pb_decode.c ../pb_encode.c unionproto.pb.c + +unionproto.pb.h unionproto.pb.c: unionproto.proto ../generator/nanopb_generator.py + protoc -I. -I../generator -I/usr/include -ounionproto.pb $< + python ../generator/nanopb_generator.py unionproto.pb diff --git a/example_unions/decode.c b/example_unions/decode.c new file mode 100644 index 00000000..b20df84e --- /dev/null +++ b/example_unions/decode.c @@ -0,0 +1,92 @@ +/* This program reads a message from stdin, detects its type and decodes it. + */ + +#include +#include +#include + +#include +#include "unionproto.pb.h" + +/* This function reads manually the first tag from the stream and finds the + * corresponding message type. It doesn't yet decode the actual message. + * + * Returns a pointer to the MsgType_fields array, as an identifier for the + * message type. Returns null if the tag is of unknown type or an error occurs. + */ +const pb_field_t* decode_unionmessage_type(pb_istream_t *stream) +{ + pb_wire_type_t wire_type; + int tag; + bool eof; + + while (pb_decode_tag(stream, &wire_type, &tag, &eof)) + { + if (wire_type == PB_WT_STRING) + { + const pb_field_t *field; + for (field = UnionMessage_fields; field->tag != 0; field++) + { + if (field->tag == tag && (field->type & PB_LTYPE_SUBMESSAGE)) + { + /* Found our field. */ + return field->ptr; + } + } + } + + /* Wasn't our field.. */ + pb_skip_field(stream, wire_type); + } + + return NULL; +} + +bool decode_unionmessage_contents(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct) +{ + pb_field_t field = {}; /* NB: Could get rid of this wrapper by fixing issue #2. */ + field.ptr = fields; + + return pb_dec_submessage(stream, &field, dest_struct); +} + +int main() +{ + /* Read the data into buffer */ + uint8_t buffer[512]; + size_t count = fread(buffer, 1, sizeof(buffer), stdin); + pb_istream_t stream = pb_istream_from_buffer(buffer, count); + + const pb_field_t *type = decode_unionmessage_type(&stream); + bool status = false; + + if (type == MsgType1_fields) + { + MsgType1 msg = {}; + status = decode_unionmessage_contents(&stream, MsgType1_fields, &msg); + printf("Got MsgType1: %d\n", msg.value); + } + else if (type == MsgType2_fields) + { + MsgType2 msg = {}; + status = decode_unionmessage_contents(&stream, MsgType2_fields, &msg); + printf("Got MsgType2: %s\n", msg.value ? "true" : "false"); + } + else if (type == MsgType3_fields) + { + MsgType3 msg = {}; + status = decode_unionmessage_contents(&stream, MsgType3_fields, &msg); + printf("Got MsgType3: %d %d\n", msg.value1, msg.value2); + } + + if (!status) + { + printf("Decoding failed.\n"); + return 1; + } + + return 0; +} + + + diff --git a/example_unions/encode.c b/example_unions/encode.c new file mode 100644 index 00000000..e124bf91 --- /dev/null +++ b/example_unions/encode.c @@ -0,0 +1,85 @@ +/* This program takes a command line argument and encodes a message in + * one of MsgType1, MsgType2 or MsgType3. + */ + +#include +#include +#include + +#include +#include "unionproto.pb.h" + +/* This function is the core of the union encoding process. It handles + * the top-level pb_field_t array manually, in order to encode a correct + * field tag before the message. The pointer to MsgType_fields array is + * used as an unique identifier for the message type. + */ +bool encode_unionmessage(pb_ostream_t *stream, const pb_field_t messagetype[], const void *message) +{ + const pb_field_t *field; + for (field = UnionMessage_fields; field->tag != 0; field++) + { + if (field->ptr == messagetype) + { + /* This is our field, encode the message using it. */ + if (!pb_encode_tag_for_field(stream, field)) + return false; + + return pb_encode_submessage(stream, messagetype, message); + } + } + + /* Didn't find the field for messagetype */ + return false; +} + +int main(int argc, char **argv) +{ + if (argc != 2) + { + fprintf(stderr, "Usage: %s (1|2|3)\n", argv[0]); + return 1; + } + + uint8_t buffer[512]; + pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + + bool status = false; + int msgtype = atoi(argv[1]); + if (msgtype == 1) + { + /* Send message of type 1 */ + MsgType1 msg = {42}; + status = encode_unionmessage(&stream, MsgType1_fields, &msg); + } + else if (msgtype == 2) + { + /* Send message of type 2 */ + MsgType2 msg = {true}; + status = encode_unionmessage(&stream, MsgType2_fields, &msg); + } + else if (msgtype == 3) + { + /* Send message of type 3 */ + MsgType3 msg = {3, 1415}; + status = encode_unionmessage(&stream, MsgType3_fields, &msg); + } + else + { + fprintf(stderr, "Unknown message type: %d\n", msgtype); + return 2; + } + + if (!status) + { + fprintf(stderr, "Encoding failed!\n"); + return 3; + } + else + { + fwrite(buffer, 1, stream.bytes_written, stdout); + return 0; /* Success */ + } +} + + diff --git a/example_unions/unionproto.proto b/example_unions/unionproto.proto new file mode 100644 index 00000000..d7c9de2d --- /dev/null +++ b/example_unions/unionproto.proto @@ -0,0 +1,30 @@ +// This is an example of how to handle 'union' style messages +// with nanopb, without allocating memory for all the message types. +// +// There is no official type in Protocol Buffers for describing unions, +// but they are commonly implemented by filling out exactly one of +// several optional fields. + +message MsgType1 +{ + required int32 value = 1; +} + +message MsgType2 +{ + required bool value = 1; +} + +message MsgType3 +{ + required int32 value1 = 1; + required int32 value2 = 2; +} + +message UnionMessage +{ + optional MsgType1 msg1 = 1; + optional MsgType2 msg2 = 2; + optional MsgType3 msg3 = 3; +} + -- 2.16.6