Import of the watch repository from Pebble

This commit is contained in:
Matthieu Jeanson 2024-12-12 16:43:03 -08:00 committed by Katharine Berry
commit 3b92768480
10334 changed files with 2564465 additions and 0 deletions

View file

@ -0,0 +1,20 @@
# Include the nanopb provided Makefile rules
include ../../extra/nanopb.mk
# Compiler flags to enable all warnings & debug info
CFLAGS = -ansi -Wall -Werror -g -O0
CFLAGS += -I$(NANOPB_DIR)
all: encode decode
./encode 1 | ./decode
./encode 2 | ./decode
./encode 3 | ./decode
.SUFFIXES:
clean:
rm -f encode unionproto.pb.h unionproto.pb.c
%: %.c unionproto.pb.c
$(CC) $(CFLAGS) -o $@ $^ $(NANOPB_CORE)

View file

@ -0,0 +1,55 @@
Nanopb example "using_union_messages"
=====================================
Union messages is a common technique in Google Protocol Buffers used to
represent a group of messages, only one of which is passed at a time.
It is described in Google's documentation:
https://developers.google.com/protocol-buffers/docs/techniques#union
This directory contains an example on how to encode and decode union messages
with minimal memory usage. Usually, nanopb would allocate space to store
all of the possible messages at the same time, even though at most one of
them will be used at a time.
By using some of the lower level nanopb APIs, we can manually generate the
top level message, so that we only need to allocate the one submessage that
we actually want. Similarly when decoding, we can manually read the tag of
the top level message, and only then allocate the memory for the submessage
after we already know its type.
NOTE: There is a newer protobuf feature called `oneof` that is also supported
by nanopb. It might be a better option for new code.
Example usage
-------------
Type `make` to run the example. It will build it and run commands like
following:
./encode 1 | ./decode
Got MsgType1: 42
./encode 2 | ./decode
Got MsgType2: true
./encode 3 | ./decode
Got MsgType3: 3 1415
This simply demonstrates that the "decode" program has correctly identified
the type of the received message, and managed to decode it.
Details of implementation
-------------------------
unionproto.proto contains the protocol used in the example. It consists of
three messages: MsgType1, MsgType2 and MsgType3, which are collected together
into UnionMessage.
encode.c takes one command line argument, which should be a number 1-3. It
then fills in and encodes the corresponding message, and writes it to stdout.
decode.c reads a UnionMessage from stdin. Then it calls the function
decode_unionmessage_type() to determine the type of the message. After that,
the corresponding message is decoded and the contents of it printed to the
screen.

View file

@ -0,0 +1,95 @@
/* 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 <pb_common.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_msgdesc_t* decode_unionmessage_type(pb_istream_t *stream)
{
pb_wire_type_t wire_type;
uint32_t tag;
bool eof;
while (pb_decode_tag(stream, &wire_type, &tag, &eof))
{
if (wire_type == PB_WT_STRING)
{
pb_field_iter_t iter;
if (pb_field_iter_begin(&iter, UnionMessage_fields, NULL) &&
pb_field_iter_find(&iter, tag))
{
/* Found our field. */
return iter.submsg_desc;
}
}
/* Wasn't our field.. */
pb_skip_field(stream, wire_type);
}
return NULL;
}
bool decode_unionmessage_contents(pb_istream_t *stream, const pb_msgdesc_t *messagetype, void *dest_struct)
{
pb_istream_t substream;
bool status;
if (!pb_make_string_substream(stream, &substream))
return false;
status = pb_decode(&substream, messagetype, dest_struct);
pb_close_string_substream(stream, &substream);
return status;
}
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_msgdesc_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("Decode failed: %s\n", PB_GET_ERROR(&stream));
return 1;
}
return 0;
}

View file

@ -0,0 +1,90 @@
/* 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 <pb_common.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_msgdesc_t *messagetype, void *message)
{
pb_field_iter_t iter;
if (!pb_field_iter_begin(&iter, UnionMessage_fields, message))
return false;
do
{
if (iter.submsg_desc == messagetype)
{
/* This is our field, encode the message using it. */
if (!pb_encode_tag_for_field(stream, &iter))
return false;
return pb_encode_submessage(stream, messagetype, message);
}
} while (pb_field_iter_next(&iter));
/* 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 */
}
}

View file

@ -0,0 +1,32 @@
// 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.
syntax = "proto2";
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;
}