summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/concepts.rst2
-rw-r--r--tests/Makefile9
-rw-r--r--tests/callbacks.proto16
-rw-r--r--tests/test_decode1.c4
-rw-r--r--tests/test_decode_callbacks.c44
-rw-r--r--tests/test_encode1.c4
-rw-r--r--tests/test_encode_callbacks.c33
7 files changed, 109 insertions, 3 deletions
diff --git a/docs/concepts.rst b/docs/concepts.rst
index e6076401..c4e5476e 100644
--- a/docs/concepts.rst
+++ b/docs/concepts.rst
@@ -201,7 +201,7 @@ Decoding callbacks
bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void *arg);
-When decoding, the callback receives a length-limited substring that reads the contents of a single field. The field tag has already been read.
+When decoding, the callback receives a length-limited substring that reads the contents of a single field. The field tag has already been read. For *string* and *bytes*, the length value has already been parsed, and is available at *stream->bytes_left*.
The callback will be called multiple times for repeated fields. For packed fields, you can either read multiple values until the stream ends, or leave it to `pb_decode`_ to call your function over and over until all values have been read.
diff --git a/tests/Makefile b/tests/Makefile
index 807da64a..30bce647 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -1,6 +1,6 @@
CFLAGS=-ansi -Wall -Werror -I .. -g -O0 --coverage
LDFLAGS=--coverage
-DEPS=../pb_decode.h ../pb_encode.h ../pb.h person.pb.h unittests.h unittestproto.pb.h
+DEPS=../pb_decode.h ../pb_encode.h ../pb.h person.pb.h callbacks.pb.h unittests.h unittestproto.pb.h
TESTS=test_decode1 test_encode1 decode_unittests encode_unittests
all: $(TESTS) run_unittests breakpoints
@@ -19,6 +19,8 @@ pb_decode.o: ../pb_decode.c $(DEPS)
test_decode1: test_decode1.o pb_decode.o person.pb.o
test_encode1: test_encode1.o pb_encode.o person.pb.o
+test_decode_callbacks: test_decode_callbacks.o pb_decode.o callbacks.pb.o
+test_encode_callbacks: test_encode_callbacks.o pb_encode.o callbacks.pb.o
decode_unittests: decode_unittests.o pb_decode.o unittestproto.pb.o
encode_unittests: encode_unittests.o pb_encode.o unittestproto.pb.o
@@ -35,7 +37,7 @@ coverage: run_unittests
gcov pb_encode.gcda
gcov pb_decode.gcda
-run_unittests: decode_unittests encode_unittests test_encode1 test_decode1
+run_unittests: decode_unittests encode_unittests test_encode1 test_decode1 test_encode_callbacks test_decode_callbacks
rm -f *.gcda
./decode_unittests > /dev/null
@@ -43,6 +45,9 @@ run_unittests: decode_unittests encode_unittests test_encode1 test_decode1
[ "`./test_encode1 | ./test_decode1`" = \
"`./test_encode1 | protoc --decode=Person -I. -I../generator -I/usr/include person.proto`" ]
+
+ [ "`./test_encode_callbacks | ./test_decode_callbacks`" = \
+ "`./test_encode_callbacks | protoc --decode=TestMessage callbacks.proto`" ]
run_fuzztest: test_decode1
bash -c 'I=1; while cat /dev/urandom | ./test_decode1 > /dev/null; do I=$$(($$I+1)); echo -en "\r$$I"; done'
diff --git a/tests/callbacks.proto b/tests/callbacks.proto
new file mode 100644
index 00000000..7bc79002
--- /dev/null
+++ b/tests/callbacks.proto
@@ -0,0 +1,16 @@
+/* Todo: write tests for the rest of these fields, currently only stringvalue
+ * is tested.
+ */
+
+message SubMessage {
+ optional int32 int32value = 1;
+}
+
+message TestMessage {
+ optional string stringvalue = 1;
+ optional int32 int32value = 2;
+ optional fixed32 fixed32value = 3;
+ optional fixed64 fixed64value = 4;
+ optional SubMessage submsg = 5;
+}
+
diff --git a/tests/test_decode1.c b/tests/test_decode1.c
index b46d0d5a..d0cc427a 100644
--- a/tests/test_decode1.c
+++ b/tests/test_decode1.c
@@ -1,3 +1,7 @@
+/* A very simple decoding test case, using person.proto.
+ * Produces output compatible with protoc --decode.
+ */
+
#include <stdio.h>
#include <pb_decode.h>
#include "person.pb.h"
diff --git a/tests/test_decode_callbacks.c b/tests/test_decode_callbacks.c
new file mode 100644
index 00000000..1c8d43a5
--- /dev/null
+++ b/tests/test_decode_callbacks.c
@@ -0,0 +1,44 @@
+/* Decoding testcase for callback fields.
+ * Run e.g. ./test_encode_callbacks | ./test_decode_callbacks
+ */
+
+#include <stdio.h>
+#include <pb_decode.h>
+#include "callbacks.pb.h"
+
+bool print_string(pb_istream_t *stream, const pb_field_t *field, void *arg)
+{
+ uint8_t buffer[1024];
+
+ /* We could read block-by-block to avoid the large buffer... */
+ if (stream->bytes_left > sizeof(buffer))
+ return false;
+
+ if (!pb_read(stream, buffer, stream->bytes_left))
+ return false;
+
+ /* Print the string, in format comparable with protoc --decode. */
+ printf("%s: \"%s\"\n", (char*)arg, buffer);
+ return true;
+}
+
+int main()
+{
+ uint8_t buffer[1024];
+ size_t length = fread(buffer, 1, 1024, stdin);
+ pb_istream_t stream = pb_istream_from_buffer(buffer, length);
+
+ /* Note: empty initializer list initializes the struct with all-0.
+ * This is recommended so that unused callbacks are set to NULL instead
+ * of crashing at runtime.
+ */
+ TestMessage testmessage = {};
+
+ testmessage.stringvalue.funcs.decode = &print_string;
+ testmessage.stringvalue.arg = "stringvalue";
+
+ if (!pb_decode(&stream, TestMessage_fields, &testmessage))
+ return 1;
+
+ return 0;
+} \ No newline at end of file
diff --git a/tests/test_encode1.c b/tests/test_encode1.c
index 412a271e..df1ec4fc 100644
--- a/tests/test_encode1.c
+++ b/tests/test_encode1.c
@@ -1,3 +1,7 @@
+/* A very simple encoding test case using person.proto.
+ * Just puts constant data in the fields.
+ */
+
#include <stdio.h>
#include <pb_encode.h>
#include "person.pb.h"
diff --git a/tests/test_encode_callbacks.c b/tests/test_encode_callbacks.c
new file mode 100644
index 00000000..da2ee28e
--- /dev/null
+++ b/tests/test_encode_callbacks.c
@@ -0,0 +1,33 @@
+/* Encoding testcase for callback fields */
+
+#include <stdio.h>
+#include <string.h>
+#include <pb_encode.h>
+#include "callbacks.pb.h"
+
+bool encode_string(pb_ostream_t *stream, const pb_field_t *field, const void *arg)
+{
+ char *str = "Hello world!";
+
+ if (!pb_encode_tag_for_field(stream, field))
+ return false;
+
+ return pb_encode_string(stream, (uint8_t*)str, strlen(str));
+}
+
+int main()
+{
+ uint8_t buffer[1024];
+ pb_ostream_t stream = pb_ostream_from_buffer(buffer, 1024);
+ TestMessage testmessage = {};
+
+ testmessage.stringvalue.funcs.encode = &encode_string;
+
+ if (!pb_encode(&stream, TestMessage_fields, &testmessage))
+ return 1;
+
+ if (fwrite(buffer, stream.bytes_written, 1, stdout) != 1)
+ return 2;
+
+ return 0;
+}