summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPetteri Aimonen <jpa@git.mail.kapsi.fi>2012-06-16 14:08:40 +0300
committerPetteri Aimonen <jpa@git.mail.kapsi.fi>2012-06-16 14:08:40 +0300
commit0f1d5cca59a2fddcf6bc627bb35e207bf3889547 (patch)
treefc3172c5fd6e2a542e0686e36f5fc0420af23774
parente18352d50678005c9dbb3ac76913555f5317c81c (diff)
Added example on how to handle unions.
-rw-r--r--example_unions/Makefile17
-rw-r--r--example_unions/decode.c92
-rw-r--r--example_unions/encode.c85
-rw-r--r--example_unions/unionproto.proto30
4 files changed, 224 insertions, 0 deletions
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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <pb_decode.h>
+#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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <pb_encode.h>
+#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;
+}
+