summaryrefslogtreecommitdiff
path: root/tests/pb
diff options
context:
space:
mode:
authorJosh Haberman <jhaberman@gmail.com>2014-01-21 18:38:49 -0800
committerJosh Haberman <jhaberman@gmail.com>2014-01-21 18:38:49 -0800
commit0fd2f830882402979a83010e89650e7245960d39 (patch)
tree0968ca9424c5fb2433047519cbd54d3dd8d0b863 /tests/pb
parentce9bba3cb5409844f8f3d7dcc235a9ea30cad090 (diff)
Sync to internal Google development.
Diffstat (limited to 'tests/pb')
-rw-r--r--tests/pb/test_decoder.cc951
-rw-r--r--tests/pb/test_decoder_schema.proto64
-rw-r--r--tests/pb/test_varint.c255
3 files changed, 1270 insertions, 0 deletions
diff --git a/tests/pb/test_decoder.cc b/tests/pb/test_decoder.cc
new file mode 100644
index 0000000..eb95580
--- /dev/null
+++ b/tests/pb/test_decoder.cc
@@ -0,0 +1,951 @@
+/*
+ * upb - a minimalist implementation of protocol buffers.
+ *
+ * Copyright (c) 2011 Google Inc. See LICENSE for details.
+ *
+ * An exhaustive set of tests for parsing both valid and invalid protobuf
+ * input, with buffer breaks in arbitrary places.
+ *
+ * Tests to add:
+ * - string/bytes
+ * - unknown field handler called appropriately
+ * - unknown fields can be inserted in random places
+ * - fuzzing of valid input
+ * - resource limits (max stack depth, max string len)
+ * - testing of groups
+ * - more throrough testing of sequences
+ * - test skipping of submessages
+ * - test suspending the decoder
+ * - buffers that are close enough to the end of the address space that
+ * pointers overflow (this might be difficult).
+ * - a few "kitchen sink" examples (one proto that uses all types, lots
+ * of submsg/sequences, etc.
+ * - test different handlers at every level and whether handlers fire at
+ * the correct field path.
+ * - test skips that extend past the end of current buffer (where decoder
+ * returns value greater than the size param).
+ */
+
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS // For PRIuS, etc.
+#endif
+
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tests/upb_test.h"
+#include "third_party/upb/tests/pb/test_decoder_schema.upb.h"
+#include "upb/handlers.h"
+#include "upb/pb/decoder.h"
+#include "upb/pb/varint.int.h"
+#include "upb/upb.h"
+
+#undef PRINT_FAILURE
+#define PRINT_FAILURE(expr) \
+ fprintf(stderr, "Assertion failed: %s:%d\n", __FILE__, __LINE__); \
+ fprintf(stderr, "expr: %s\n", #expr); \
+ if (testhash) { \
+ fprintf(stderr, "assertion failed running test %x. " \
+ "Run with the arg %x to run only this test.\n", \
+ testhash, testhash); \
+ fprintf(stderr, "Failed at %02.2f%% through tests.\n", \
+ (float)completed * 100 / total); \
+ }
+
+uint32_t filter_hash = 0;
+double completed;
+double total;
+double *count;
+bool count_only;
+upb::BufferHandle global_handle;
+
+// Copied from decoder.c, since this is not a public interface.
+typedef struct {
+ uint8_t native_wire_type;
+ bool is_numeric;
+} upb_decoder_typeinfo;
+
+static const upb_decoder_typeinfo upb_decoder_types[] = {
+ {UPB_WIRE_TYPE_END_GROUP, false}, // ENDGROUP
+ {UPB_WIRE_TYPE_64BIT, true}, // DOUBLE
+ {UPB_WIRE_TYPE_32BIT, true}, // FLOAT
+ {UPB_WIRE_TYPE_VARINT, true}, // INT64
+ {UPB_WIRE_TYPE_VARINT, true}, // UINT64
+ {UPB_WIRE_TYPE_VARINT, true}, // INT32
+ {UPB_WIRE_TYPE_64BIT, true}, // FIXED64
+ {UPB_WIRE_TYPE_32BIT, true}, // FIXED32
+ {UPB_WIRE_TYPE_VARINT, true}, // BOOL
+ {UPB_WIRE_TYPE_DELIMITED, false}, // STRING
+ {UPB_WIRE_TYPE_START_GROUP, false}, // GROUP
+ {UPB_WIRE_TYPE_DELIMITED, false}, // MESSAGE
+ {UPB_WIRE_TYPE_DELIMITED, false}, // BYTES
+ {UPB_WIRE_TYPE_VARINT, true}, // UINT32
+ {UPB_WIRE_TYPE_VARINT, true}, // ENUM
+ {UPB_WIRE_TYPE_32BIT, true}, // SFIXED32
+ {UPB_WIRE_TYPE_64BIT, true}, // SFIXED64
+ {UPB_WIRE_TYPE_VARINT, true}, // SINT32
+ {UPB_WIRE_TYPE_VARINT, true}, // SINT64
+};
+
+
+class buffer {
+ public:
+ buffer(const void *data, size_t len) : len_(0) { append(data, len); }
+ explicit buffer(const char *data) : len_(0) { append(data); }
+ explicit buffer(size_t len) : len_(len) { memset(buf_, 0, len); }
+ buffer(const buffer& buf) : len_(0) { append(buf); }
+ buffer() : len_(0) {}
+
+ void append(const void *data, size_t len) {
+ ASSERT_NOCOUNT(len + len_ < sizeof(buf_));
+ memcpy(buf_ + len_, data, len);
+ len_ += len;
+ buf_[len_] = NULL;
+ }
+
+ void append(const buffer& buf) {
+ append(buf.buf_, buf.len_);
+ }
+
+ void append(const char *str) {
+ append(str, strlen(str));
+ }
+
+ void vappendf(const char *fmt, va_list args) {
+ size_t avail = sizeof(buf_) - len_;
+ size_t size = vsnprintf(buf_ + len_, avail, fmt, args);
+ ASSERT_NOCOUNT(avail > size);
+ len_ += size;
+ }
+
+ void appendf(const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ vappendf(fmt, args);
+ va_end(args);
+ }
+
+ void assign(const buffer& buf) {
+ clear();
+ append(buf);
+ }
+
+ bool eql(const buffer& other) const {
+ return len_ == other.len_ && memcmp(buf_, other.buf_, len_) == 0;
+ }
+
+ void clear() { len_ = 0; }
+ size_t len() const { return len_; }
+ const char *buf() const { return buf_; }
+
+ private:
+ // Has to be big enough for the largest string used in the test.
+ char buf_[32768];
+ size_t len_;
+};
+
+
+/* Routines for building arbitrary protos *************************************/
+
+const buffer empty;
+
+buffer cat(const buffer& a, const buffer& b,
+ const buffer& c = empty,
+ const buffer& d = empty,
+ const buffer& e = empty,
+ const buffer& f = empty) {
+ buffer ret;
+ ret.append(a);
+ ret.append(b);
+ ret.append(c);
+ ret.append(d);
+ ret.append(e);
+ ret.append(f);
+ return ret;
+}
+
+buffer varint(uint64_t x) {
+ char buf[UPB_PB_VARINT_MAX_LEN];
+ size_t len = upb_vencode64(x, buf);
+ return buffer(buf, len);
+}
+
+// TODO: proper byte-swapping for big-endian machines.
+buffer fixed32(void *data) { return buffer(data, 4); }
+buffer fixed64(void *data) { return buffer(data, 8); }
+
+buffer delim(const buffer& buf) { return cat(varint(buf.len()), buf); }
+buffer uint32(uint32_t u32) { return fixed32(&u32); }
+buffer uint64(uint64_t u64) { return fixed64(&u64); }
+buffer flt(float f) { return fixed32(&f); }
+buffer dbl(double d) { return fixed64(&d); }
+buffer zz32(int32_t x) { return varint(upb_zzenc_32(x)); }
+buffer zz64(int64_t x) { return varint(upb_zzenc_64(x)); }
+
+buffer tag(uint32_t fieldnum, char wire_type) {
+ return varint((fieldnum << 3) | wire_type);
+}
+
+buffer submsg(uint32_t fn, const buffer& buf) {
+ return cat( tag(fn, UPB_WIRE_TYPE_DELIMITED), delim(buf) );
+}
+
+
+/* A set of handlers that covers all .proto types *****************************/
+
+// The handlers simply append to a string indicating what handlers were called.
+// This string is similar to protobuf text format but fields are referred to by
+// number instead of name and sequences are explicitly delimited. We indent
+// using the closure depth to test that the stack of closures is properly
+// handled.
+
+int closures[UPB_DECODER_MAX_NESTING];
+buffer output;
+
+void indentbuf(buffer *buf, int depth) {
+ for (int i = 0; i < depth; i++)
+ buf->append(" ", 2);
+}
+
+#define NUMERIC_VALUE_HANDLER(member, ctype, fmt) \
+ bool value_ ## member(int* depth, const uint32_t* num, ctype val) { \
+ indentbuf(&output, *depth); \
+ output.appendf("%" PRIu32 ":%" fmt "\n", *num, val); \
+ return true; \
+ }
+
+NUMERIC_VALUE_HANDLER(uint32, uint32_t, PRIu32)
+NUMERIC_VALUE_HANDLER(uint64, uint64_t, PRIu64)
+NUMERIC_VALUE_HANDLER(int32, int32_t, PRId32)
+NUMERIC_VALUE_HANDLER(int64, int64_t, PRId64)
+NUMERIC_VALUE_HANDLER(float, float, "g")
+NUMERIC_VALUE_HANDLER(double, double, "g")
+
+bool value_bool(int* depth, const uint32_t* num, bool val) {
+ indentbuf(&output, *depth);
+ output.appendf("%" PRIu32 ":%s\n", *num, val ? "true" : "false");
+ return true;
+}
+
+int* startstr(int* depth, const uint32_t* num, size_t size_hint) {
+ indentbuf(&output, *depth);
+ output.appendf("%" PRIu32 ":(%zu)\"", *num, size_hint);
+ return depth + 1;
+}
+
+size_t value_string(int* depth, const uint32_t* num, const char* buf,
+ size_t n, const upb::BufferHandle* handle) {
+ UPB_UNUSED(num);
+ output.append(buf, n);
+ ASSERT(handle == &global_handle);
+ return n;
+}
+
+bool endstr(int* depth, const uint32_t* num) {
+ UPB_UNUSED(depth);
+ UPB_UNUSED(num);
+ output.append("\"\n");
+ return true;
+}
+
+int* startsubmsg(int* depth, const uint32_t* num) {
+ indentbuf(&output, *depth);
+ output.appendf("%" PRIu32 ":{\n", *num);
+ return depth + 1;
+}
+
+bool endsubmsg(int* depth, const uint32_t* num) {
+ UPB_UNUSED(num);
+ indentbuf(&output, *depth);
+ output.append("}\n");
+ return true;
+}
+
+int* startseq(int* depth, const uint32_t* num) {
+ indentbuf(&output, *depth);
+ output.appendf("%" PRIu32 ":[\n", *num);
+ return depth + 1;
+}
+
+bool endseq(int* depth, const uint32_t* num) {
+ UPB_UNUSED(num);
+ indentbuf(&output, *depth);
+ output.append("]\n");
+ return true;
+}
+
+bool startmsg(int* depth) {
+ indentbuf(&output, *depth);
+ output.append("<\n");
+ return true;
+}
+
+bool endmsg(int* depth, upb_status* status) {
+ indentbuf(&output, *depth);
+ output.append(">\n");
+ return true;
+}
+
+void free_uint32(void *val) {
+ uint32_t *u32 = static_cast<uint32_t*>(val);
+ delete u32;
+}
+
+template<class T, bool F(int*, const uint32_t*, T)>
+void doreg(upb_handlers *h, uint32_t num) {
+ const upb_fielddef *f = upb_msgdef_itof(upb_handlers_msgdef(h), num);
+ ASSERT(f);
+ ASSERT(h->SetValueHandler<T>(f, UpbBindT(F, new uint32_t(num))));
+ if (f->IsSequence()) {
+ ASSERT(h->SetStartSequenceHandler(f, UpbBind(startseq, new uint32_t(num))));
+ ASSERT(h->SetEndSequenceHandler(f, UpbBind(endseq, new uint32_t(num))));
+ }
+}
+
+// The repeated field number to correspond to the given non-repeated field
+// number.
+uint32_t rep_fn(uint32_t fn) {
+ return (UPB_MAX_FIELDNUMBER - 1000) + fn;
+}
+
+#define NOP_FIELD 40
+#define UNKNOWN_FIELD 666
+
+template <class T, bool F(int*, const uint32_t*, T)>
+void reg(upb_handlers *h, upb_descriptortype_t type) {
+ // We register both a repeated and a non-repeated field for every type.
+ // For the non-repeated field we make the field number the same as the
+ // type. For the repeated field we make it a function of the type.
+ doreg<T, F>(h, type);
+ doreg<T, F>(h, rep_fn(type));
+}
+
+void regseq(upb::Handlers* h, const upb::FieldDef* f, uint32_t num) {
+ ASSERT(h->SetStartSequenceHandler(f, UpbBind(startseq, new uint32_t(num))));
+ ASSERT(h->SetEndSequenceHandler(f, UpbBind(endseq, new uint32_t(num))));
+}
+
+void reg_subm(upb_handlers *h, uint32_t num) {
+ const upb_fielddef *f = upb_msgdef_itof(upb_handlers_msgdef(h), num);
+ ASSERT(f);
+ if (f->IsSequence()) regseq(h, f, num);
+ ASSERT(
+ h->SetStartSubMessageHandler(f, UpbBind(startsubmsg, new uint32_t(num))));
+ ASSERT(h->SetEndSubMessageHandler(f, UpbBind(endsubmsg, new uint32_t(num))));
+ ASSERT(upb_handlers_setsubhandlers(h, f, h));
+}
+
+void reg_str(upb_handlers *h, uint32_t num) {
+ const upb_fielddef *f = upb_msgdef_itof(upb_handlers_msgdef(h), num);
+ ASSERT(f);
+ if (f->IsSequence()) regseq(h, f, num);
+ ASSERT(h->SetStartStringHandler(f, UpbBind(startstr, new uint32_t(num))));
+ ASSERT(h->SetEndStringHandler(f, UpbBind(endstr, new uint32_t(num))));
+ ASSERT(h->SetStringHandler(f, UpbBind(value_string, new uint32_t(num))));
+}
+
+upb::reffed_ptr<const upb::Handlers> NewHandlers() {
+ upb::reffed_ptr<upb::Handlers> h(
+ upb::Handlers::New(UPB_TEST_DECODER_DECODERTEST));
+
+ h->SetStartMessageHandler(UpbMakeHandler(startmsg));
+ h->SetEndMessageHandler(UpbMakeHandler(endmsg));
+
+ // Register handlers for each type.
+ reg<double, value_double>(h.get(), UPB_DESCRIPTOR_TYPE_DOUBLE);
+ reg<float, value_float> (h.get(), UPB_DESCRIPTOR_TYPE_FLOAT);
+ reg<int64_t, value_int64> (h.get(), UPB_DESCRIPTOR_TYPE_INT64);
+ reg<uint64_t, value_uint64>(h.get(), UPB_DESCRIPTOR_TYPE_UINT64);
+ reg<int32_t, value_int32> (h.get(), UPB_DESCRIPTOR_TYPE_INT32);
+ reg<uint64_t, value_uint64>(h.get(), UPB_DESCRIPTOR_TYPE_FIXED64);
+ reg<uint32_t, value_uint32>(h.get(), UPB_DESCRIPTOR_TYPE_FIXED32);
+ reg<bool, value_bool> (h.get(), UPB_DESCRIPTOR_TYPE_BOOL);
+ reg<uint32_t, value_uint32>(h.get(), UPB_DESCRIPTOR_TYPE_UINT32);
+ reg<int32_t, value_int32> (h.get(), UPB_DESCRIPTOR_TYPE_ENUM);
+ reg<int32_t, value_int32> (h.get(), UPB_DESCRIPTOR_TYPE_SFIXED32);
+ reg<int64_t, value_int64> (h.get(), UPB_DESCRIPTOR_TYPE_SFIXED64);
+ reg<int32_t, value_int32> (h.get(), UPB_DESCRIPTOR_TYPE_SINT32);
+ reg<int64_t, value_int64> (h.get(), UPB_DESCRIPTOR_TYPE_SINT64);
+
+ reg_str(h.get(), UPB_DESCRIPTOR_TYPE_STRING);
+ reg_str(h.get(), UPB_DESCRIPTOR_TYPE_BYTES);
+ reg_str(h.get(), rep_fn(UPB_DESCRIPTOR_TYPE_STRING));
+ reg_str(h.get(), rep_fn(UPB_DESCRIPTOR_TYPE_BYTES));
+
+ // Register submessage/group handlers that are self-recursive
+ // to this type, eg: message M { optional M m = 1; }
+ reg_subm(h.get(), UPB_DESCRIPTOR_TYPE_MESSAGE);
+ reg_subm(h.get(), rep_fn(UPB_DESCRIPTOR_TYPE_MESSAGE));
+
+ // For NOP_FIELD we register no handlers, so we can pad a proto freely without
+ // changing the output.
+
+ bool ok = h->Freeze(NULL);
+ ASSERT(ok);
+
+ return h;
+}
+
+
+/* Running of test cases ******************************************************/
+
+const upb::Handlers *global_handlers;
+const upb::pb::DecoderMethod *global_method;
+
+uint32_t Hash(const buffer& proto, const buffer* expected_output, size_t seam1,
+ size_t seam2) {
+ uint32_t hash = MurmurHash2(proto.buf(), proto.len(), 0);
+ if (expected_output)
+ hash = MurmurHash2(expected_output->buf(), expected_output->len(), hash);
+ hash = MurmurHash2(&seam1, sizeof(seam1), hash);
+ hash = MurmurHash2(&seam2, sizeof(seam2), hash);
+ return hash;
+}
+
+bool parse(upb::BytesSink* s, void* subc, const char* buf, size_t start,
+ size_t end, size_t* ofs, upb::Status* status) {
+ start = UPB_MAX(start, *ofs);
+ if (start <= end) {
+ size_t len = end - start;
+ size_t parsed = s->PutBuffer(subc, buf + start, len, &global_handle);
+ if (status->ok() != (parsed >= len)) {
+ fprintf(stderr, "Status: %s, parsed=%zu, len=%zu\n",
+ status->error_message(), parsed, len);
+ ASSERT(false);
+ }
+ if (!status->ok())
+ return false;
+ *ofs += parsed;
+ }
+ return true;
+}
+
+#define LINE(x) x "\n"
+void run_decoder(const buffer& proto, const buffer* expected_output) {
+ upb::Status status;
+ upb::pb::Decoder decoder(global_method, &status);
+ upb::Sink sink(global_handlers, &closures[0]);
+ decoder.ResetOutput(&sink);
+ for (size_t i = 0; i < proto.len(); i++) {
+ for (size_t j = i; j < UPB_MIN(proto.len(), i + 5); j++) {
+ testhash = Hash(proto, expected_output, i, j);
+ if (filter_hash && testhash != filter_hash) continue;
+ if (!count_only) {
+ decoder.Reset();
+ output.clear();
+ status.Clear();
+ size_t ofs = 0;
+ upb::BytesSink* input = decoder.input();
+ void *sub;
+ bool ok =
+ input->Start(proto.len(), &sub) &&
+ parse(input, sub, proto.buf(), 0, i, &ofs, &status) &&
+ parse(input, sub, proto.buf(), i, j, &ofs, &status) &&
+ parse(input, sub, proto.buf(), j, proto.len(), &ofs, &status) &&
+ ofs == proto.len() &&
+ input->End();
+ if (expected_output) {
+ if (!output.eql(*expected_output)) {
+ fprintf(stderr, "Text mismatch: '%s' vs '%s'\n",
+ output.buf(), expected_output->buf());
+ }
+ if (!ok) {
+ fprintf(stderr, "Failed: %s\n", status.error_message());
+ }
+ ASSERT(ok);
+ ASSERT(output.eql(*expected_output));
+ } else {
+ if (ok) {
+ fprintf(stderr, "Didn't expect ok result, but got output: '%s'\n",
+ output.buf());
+ }
+ ASSERT(!ok);
+ }
+ }
+ (*count)++;
+ }
+ }
+ testhash = 0;
+}
+
+const static buffer thirty_byte_nop = buffer(cat(
+ tag(NOP_FIELD, UPB_WIRE_TYPE_DELIMITED), delim(buffer(30)) ));
+
+void assert_successful_parse(const buffer& proto,
+ const char *expected_fmt, ...) {
+ buffer expected_text;
+ va_list args;
+ va_start(args, expected_fmt);
+ expected_text.vappendf(expected_fmt, args);
+ va_end(args);
+ // To test both middle-of-buffer and end-of-buffer code paths,
+ // repeat once with no-op padding data at the end of buffer.
+ run_decoder(proto, &expected_text);
+ run_decoder(cat( proto, thirty_byte_nop ), &expected_text);
+}
+
+void assert_does_not_parse_at_eof(const buffer& proto) {
+ run_decoder(proto, NULL);
+}
+
+void assert_does_not_parse(const buffer& proto) {
+ // Test that the error is caught both at end-of-buffer and middle-of-buffer.
+ assert_does_not_parse_at_eof(proto);
+ assert_does_not_parse_at_eof(cat( proto, thirty_byte_nop ));
+}
+
+
+/* The actual tests ***********************************************************/
+
+void test_premature_eof_for_type(upb_descriptortype_t type) {
+ // Incomplete values for each wire type.
+ static const buffer incompletes[6] = {
+ buffer("\x80"), // UPB_WIRE_TYPE_VARINT
+ buffer("abcdefg"), // UPB_WIRE_TYPE_64BIT
+ buffer("\x80"), // UPB_WIRE_TYPE_DELIMITED (partial length)
+ buffer(), // UPB_WIRE_TYPE_START_GROUP (no value required)
+ buffer(), // UPB_WIRE_TYPE_END_GROUP (no value required)
+ buffer("abc") // UPB_WIRE_TYPE_32BIT
+ };
+
+ uint32_t fieldnum = type;
+ uint32_t rep_fieldnum = rep_fn(type);
+ int wire_type = upb_decoder_types[type].native_wire_type;
+ const buffer& incomplete = incompletes[wire_type];
+
+ // EOF before a known non-repeated value.
+ assert_does_not_parse_at_eof(tag(fieldnum, wire_type));
+
+ // EOF before a known repeated value.
+ assert_does_not_parse_at_eof(tag(rep_fieldnum, wire_type));
+
+ // EOF before an unknown value.
+ assert_does_not_parse_at_eof(tag(UNKNOWN_FIELD, wire_type));
+
+ // EOF inside a known non-repeated value.
+ assert_does_not_parse_at_eof(
+ cat( tag(fieldnum, wire_type), incomplete ));
+
+ // EOF inside a known repeated value.
+ assert_does_not_parse_at_eof(
+ cat( tag(rep_fieldnum, wire_type), incomplete ));
+
+ // EOF inside an unknown value.
+ assert_does_not_parse_at_eof(
+ cat( tag(UNKNOWN_FIELD, wire_type), incomplete ));
+
+ if (wire_type == UPB_WIRE_TYPE_DELIMITED) {
+ // EOF in the middle of delimited data for known non-repeated value.
+ assert_does_not_parse_at_eof(
+ cat( tag(fieldnum, wire_type), varint(1) ));
+
+ // EOF in the middle of delimited data for known repeated value.
+ assert_does_not_parse_at_eof(
+ cat( tag(rep_fieldnum, wire_type), varint(1) ));
+
+ // EOF in the middle of delimited data for unknown value.
+ assert_does_not_parse_at_eof(
+ cat( tag(UNKNOWN_FIELD, wire_type), varint(1) ));
+
+ if (type == UPB_DESCRIPTOR_TYPE_MESSAGE) {
+ // Submessage ends in the middle of a value.
+ buffer incomplete_submsg =
+ cat ( tag(UPB_DESCRIPTOR_TYPE_INT32, UPB_WIRE_TYPE_VARINT),
+ incompletes[UPB_WIRE_TYPE_VARINT] );
+ assert_does_not_parse(
+ cat( tag(fieldnum, UPB_WIRE_TYPE_DELIMITED),
+ varint(incomplete_submsg.len()),
+ incomplete_submsg ));
+ }
+ } else {
+ // Packed region ends in the middle of a value.
+ assert_does_not_parse(
+ cat( tag(rep_fieldnum, UPB_WIRE_TYPE_DELIMITED),
+ varint(incomplete.len()),
+ incomplete ));
+
+ // EOF in the middle of packed region.
+ assert_does_not_parse_at_eof(
+ cat( tag(rep_fieldnum, UPB_WIRE_TYPE_DELIMITED), varint(1) ));
+ }
+}
+
+// "33" and "66" are just two random values that all numeric types can
+// represent.
+void test_valid_data_for_type(upb_descriptortype_t type,
+ const buffer& enc33, const buffer& enc66) {
+ uint32_t fieldnum = type;
+ uint32_t rep_fieldnum = rep_fn(type);
+ int wire_type = upb_decoder_types[type].native_wire_type;
+
+ // Non-repeated
+ assert_successful_parse(
+ cat( tag(fieldnum, wire_type), enc33,
+ tag(fieldnum, wire_type), enc66 ),
+ LINE("<")
+ LINE("%u:33")
+ LINE("%u:66")
+ LINE(">"), fieldnum, fieldnum);
+
+ // Non-packed repeated.
+ assert_successful_parse(
+ cat( tag(rep_fieldnum, wire_type), enc33,
+ tag(rep_fieldnum, wire_type), enc66 ),
+ LINE("<")
+ LINE("%u:[")
+ LINE(" %u:33")
+ LINE(" %u:66")
+ LINE("]")
+ LINE(">"), rep_fieldnum, rep_fieldnum, rep_fieldnum);
+
+ // Packed repeated.
+ assert_successful_parse(
+ cat( tag(rep_fieldnum, UPB_WIRE_TYPE_DELIMITED),
+ delim(cat( enc33, enc66 )) ),
+ LINE("<")
+ LINE("%u:[")
+ LINE(" %u:33")
+ LINE(" %u:66")
+ LINE("]")
+ LINE(">"), rep_fieldnum, rep_fieldnum, rep_fieldnum);
+}
+
+void test_valid_data_for_signed_type(upb_descriptortype_t type,
+ const buffer& enc33, const buffer& enc66) {
+ uint32_t fieldnum = type;
+ uint32_t rep_fieldnum = rep_fn(type);
+ int wire_type = upb_decoder_types[type].native_wire_type;
+
+ // Non-repeated
+ assert_successful_parse(
+ cat( tag(fieldnum, wire_type), enc33,
+ tag(fieldnum, wire_type), enc66 ),
+ LINE("<")
+ LINE("%u:33")
+ LINE("%u:-66")
+ LINE(">"), fieldnum, fieldnum);
+
+ // Non-packed repeated.
+ assert_successful_parse(
+ cat( tag(rep_fieldnum, wire_type), enc33,
+ tag(rep_fieldnum, wire_type), enc66 ),
+ LINE("<")
+ LINE("%u:[")
+ LINE(" %u:33")
+ LINE(" %u:-66")
+ LINE("]")
+ LINE(">"), rep_fieldnum, rep_fieldnum, rep_fieldnum);
+
+ // Packed repeated.
+ assert_successful_parse(
+ cat( tag(rep_fieldnum, UPB_WIRE_TYPE_DELIMITED),
+ delim(cat( enc33, enc66 )) ),
+ LINE("<")
+ LINE("%u:[")
+ LINE(" %u:33")
+ LINE(" %u:-66")
+ LINE("]")
+ LINE(">"), rep_fieldnum, rep_fieldnum, rep_fieldnum);
+}
+
+// Test that invalid protobufs are properly detected (without crashing) and
+// have an error reported. Field numbers match registered handlers above.
+void test_invalid() {
+ test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_DOUBLE);
+ test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_FLOAT);
+ test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_INT64);
+ test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_UINT64);
+ test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_INT32);
+ test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_FIXED64);
+ test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_FIXED32);
+ test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_BOOL);
+ test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_STRING);
+ test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_BYTES);
+ test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_UINT32);
+ test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_ENUM);
+ test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_SFIXED32);
+ test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_SFIXED64);
+ test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_SINT32);
+ test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_SINT64);
+
+ // EOF inside a tag's varint.
+ assert_does_not_parse_at_eof( buffer("\x80") );
+
+ // EOF inside a known group.
+ // TODO(haberman): add group to decoder test schema.
+ //assert_does_not_parse_at_eof( tag(4, UPB_WIRE_TYPE_START_GROUP) );
+
+ // EOF inside an unknown group.
+ // TODO(haberman): unknown groups not supported yet.
+ //assert_does_not_parse_at_eof( tag(UNKNOWN_FIELD, UPB_WIRE_TYPE_START_GROUP) );
+
+ // End group that we are not currently in.
+ assert_does_not_parse( tag(4, UPB_WIRE_TYPE_END_GROUP) );
+
+ // Field number is 0.
+ assert_does_not_parse(
+ cat( tag(0, UPB_WIRE_TYPE_DELIMITED), varint(0) ));
+
+ // Field number is too large.
+ assert_does_not_parse(
+ cat( tag(UPB_MAX_FIELDNUMBER + 1, UPB_WIRE_TYPE_DELIMITED),
+ varint(0) ));
+
+ // Known group inside a submessage has ENDGROUP tag AFTER submessage end.
+ assert_does_not_parse(
+ cat ( submsg(UPB_DESCRIPTOR_TYPE_MESSAGE,
+ tag(UPB_DESCRIPTOR_TYPE_GROUP, UPB_WIRE_TYPE_START_GROUP)),
+ tag(UPB_DESCRIPTOR_TYPE_GROUP, UPB_WIRE_TYPE_END_GROUP)));
+
+ // Test exceeding the resource limit of stack depth.
+ buffer buf;
+ for (int i = 0; i <= UPB_DECODER_MAX_NESTING; i++) {
+ buf.assign(submsg(UPB_DESCRIPTOR_TYPE_MESSAGE, buf));
+ }
+ assert_does_not_parse(buf);
+}
+
+void test_valid() {
+ // Empty protobuf.
+ assert_successful_parse(buffer(""), "<\n>\n");
+
+ // Empty protobuf where we never call PutString between
+ // StartString/EndString.
+ {
+ upb::Status status;
+ upb::pb::Decoder decoder(global_method, &status);
+ upb::Sink sink(global_handlers, &closures[0]);
+ decoder.ResetOutput(&sink);
+ output.clear();
+ bool ok = upb::BufferSource::PutBuffer("", 0, decoder.input());
+ ASSERT(ok);
+ ASSERT(status.ok());
+ ASSERT(output.eql(buffer("<\n>\n")));
+ }
+
+ test_valid_data_for_signed_type(UPB_DESCRIPTOR_TYPE_DOUBLE,
+ dbl(33),
+ dbl(-66));
+ test_valid_data_for_signed_type(UPB_DESCRIPTOR_TYPE_FLOAT, flt(33), flt(-66));
+ test_valid_data_for_signed_type(UPB_DESCRIPTOR_TYPE_INT64,
+ varint(33),
+ varint(-66));
+ test_valid_data_for_signed_type(UPB_DESCRIPTOR_TYPE_INT32,
+ varint(33),
+ varint(-66));
+ test_valid_data_for_signed_type(UPB_DESCRIPTOR_TYPE_ENUM,
+ varint(33),
+ varint(-66));
+ test_valid_data_for_signed_type(UPB_DESCRIPTOR_TYPE_SFIXED32,
+ uint32(33),
+ uint32(-66));
+ test_valid_data_for_signed_type(UPB_DESCRIPTOR_TYPE_SFIXED64,
+ uint64(33),
+ uint64(-66));
+ test_valid_data_for_signed_type(UPB_DESCRIPTOR_TYPE_SINT32,
+ zz32(33),
+ zz32(-66));
+ test_valid_data_for_signed_type(UPB_DESCRIPTOR_TYPE_SINT64,
+ zz64(33),
+ zz64(-66));
+
+ test_valid_data_for_type(UPB_DESCRIPTOR_TYPE_UINT64, varint(33), varint(66));
+ test_valid_data_for_type(UPB_DESCRIPTOR_TYPE_UINT32, varint(33), varint(66));
+ test_valid_data_for_type(UPB_DESCRIPTOR_TYPE_FIXED64, uint64(33), uint64(66));
+ test_valid_data_for_type(UPB_DESCRIPTOR_TYPE_FIXED32, uint32(33), uint32(66));
+
+ // Unknown fields.
+ int int32_type = UPB_DESCRIPTOR_TYPE_INT32;
+ int msg_type = UPB_DESCRIPTOR_TYPE_MESSAGE;
+ assert_successful_parse(
+ cat( tag(12345, UPB_WIRE_TYPE_VARINT), varint(2345678) ),
+ "<\n>\n");
+ assert_successful_parse(
+ cat( tag(12345, UPB_WIRE_TYPE_32BIT), uint32(2345678) ),
+ "<\n>\n");
+ assert_successful_parse(
+ cat( tag(12345, UPB_WIRE_TYPE_64BIT), uint64(2345678) ),
+ "<\n>\n");
+ assert_successful_parse(
+ submsg(12345, buffer(" ")),
+ "<\n>\n");
+
+ assert_successful_parse(
+ cat(
+ submsg(UPB_DESCRIPTOR_TYPE_MESSAGE,
+ submsg(UPB_DESCRIPTOR_TYPE_MESSAGE,
+ cat( tag(int32_type, UPB_WIRE_TYPE_VARINT), varint(2345678),
+ tag(12345, UPB_WIRE_TYPE_VARINT), varint(2345678) ))),
+ tag(int32_type, UPB_WIRE_TYPE_VARINT), varint(22222)),
+ LINE("<")
+ LINE("%u:{")
+ LINE(" <")
+ LINE(" %u:{")
+ LINE(" <")
+ LINE(" %u:2345678")
+ LINE(" >")
+ LINE(" }")
+ LINE(" >")
+ LINE("}")
+ LINE("%u:22222")
+ LINE(">"), msg_type, msg_type, int32_type, int32_type);
+
+ assert_successful_parse(
+ cat( tag(UPB_DESCRIPTOR_TYPE_INT32, UPB_WIRE_TYPE_VARINT), varint(1),
+ tag(12345, UPB_WIRE_TYPE_VARINT), varint(2345678) ),
+ LINE("<")
+ LINE("%u:1")
+ LINE(">"), UPB_DESCRIPTOR_TYPE_INT32);
+
+ // Test implicit startseq/endseq.
+ uint32_t repfl_fn = rep_fn(UPB_DESCRIPTOR_TYPE_FLOAT);
+ uint32_t repdb_fn = rep_fn(UPB_DESCRIPTOR_TYPE_DOUBLE);
+ assert_successful_parse(
+ cat( tag(repfl_fn, UPB_WIRE_TYPE_32BIT), flt(33),
+ tag(repdb_fn, UPB_WIRE_TYPE_64BIT), dbl(66) ),
+ LINE("<")
+ LINE("%u:[")
+ LINE(" %u:33")
+ LINE("]")
+ LINE("%u:[")
+ LINE(" %u:66")
+ LINE("]")
+ LINE(">"), repfl_fn, repfl_fn, repdb_fn, repdb_fn);
+
+ // Submessage tests.
+ uint32_t msg_fn = UPB_DESCRIPTOR_TYPE_MESSAGE;
+ assert_successful_parse(
+ submsg(msg_fn, submsg(msg_fn, submsg(msg_fn, buffer()))),
+ LINE("<")
+ LINE("%u:{")
+ LINE(" <")
+ LINE(" %u:{")
+ LINE(" <")
+ LINE(" %u:{")
+ LINE(" <")
+ LINE(" >")
+ LINE(" }")
+ LINE(" >")
+ LINE(" }")
+ LINE(" >")
+ LINE("}")
+ LINE(">"), msg_fn, msg_fn, msg_fn);
+
+ uint32_t repm_fn = rep_fn(UPB_DESCRIPTOR_TYPE_MESSAGE);
+ assert_successful_parse(
+ submsg(repm_fn, submsg(repm_fn, buffer())),
+ LINE("<")
+ LINE("%u:[")
+ LINE(" %u:{")
+ LINE(" <")
+ LINE(" %u:[")
+ LINE(" %u:{")
+ LINE(" <")
+ LINE(" >")
+ LINE(" }")
+ LINE(" ]")
+ LINE(" >")
+ LINE(" }")
+ LINE("]")
+ LINE(">"), repm_fn, repm_fn, repm_fn, repm_fn);
+
+ // Staying within the stack limit should work properly.
+ buffer buf;
+ buffer textbuf;
+ int total = UPB_DECODER_MAX_NESTING - 1;
+ for (int i = 0; i < total; i++) {
+ buf.assign(submsg(UPB_DESCRIPTOR_TYPE_MESSAGE, buf));
+ indentbuf(&textbuf, i);
+ textbuf.append("<\n");
+ indentbuf(&textbuf, i);
+ textbuf.appendf("%u:{\n", UPB_DESCRIPTOR_TYPE_MESSAGE);
+ }
+ indentbuf(&textbuf, total);
+ textbuf.append("<\n");
+ indentbuf(&textbuf, total);
+ textbuf.append(">\n");
+ for (int i = 0; i < total; i++) {
+ indentbuf(&textbuf, total - i - 1);
+ textbuf.append("}\n");
+ indentbuf(&textbuf, total - i - 1);
+ textbuf.append(">\n");
+ }
+ assert_successful_parse(buf, "%s", textbuf.buf());
+}
+
+void run_tests() {
+ test_invalid();
+ test_valid();
+}
+
+upb::reffed_ptr<const upb::pb::DecoderMethod> NewMethod(
+ const upb::Handlers* dest_handlers, bool allow_jit) {
+ upb::pb::CodeCache cache;
+ cache.set_allow_jit(allow_jit);
+ return cache.GetDecoderMethodForDestHandlers(dest_handlers);
+}
+
+void test_emptyhandlers(bool allowjit) {
+ // Create an empty handlers to make sure that the decoder can handle empty
+ // messages.
+ upb::reffed_ptr<upb::Handlers> h(
+ upb::Handlers::New(UPB_TEST_DECODER_EMPTYMESSAGE));
+ bool ok = h->Freeze(NULL);
+ ASSERT(ok);
+ NewMethod(h.get(), allowjit);
+}
+
+extern "C" {
+
+int run_tests(int argc, char *argv[]) {
+ if (argc > 1)
+ filter_hash = strtol(argv[1], NULL, 16);
+ for (int i = 0; i < UPB_DECODER_MAX_NESTING; i++) {
+ closures[i] = i;
+ }
+
+ upb::reffed_ptr<const upb::pb::DecoderMethod> method;
+ upb::reffed_ptr<const upb::Handlers> handlers;
+
+ // Construct decoder plan.
+ handlers = NewHandlers();
+ global_handlers = handlers.get();
+
+ // Count tests.
+ method = NewMethod(handlers.get(), false);
+ global_method = method.get();
+ count_only = true;
+ count = &total;
+ total = 0;
+ run_tests();
+ count_only = false;
+ count = &completed;
+
+ // Test without JIT.
+ method = NewMethod(handlers.get(), false);
+ global_method = method.get();
+ ASSERT(!global_method->is_native());
+ completed = 0;
+ run_tests();
+
+ test_emptyhandlers(false);
+
+#ifdef UPB_USE_JIT_X64
+ // Test JIT.
+ method = NewMethod(handlers.get(), true);
+ global_method = method.get();
+ ASSERT(global_method->is_native());
+ completed = 0;
+ run_tests();
+
+ test_emptyhandlers(true);
+#endif
+
+ printf("All tests passed, %d assertions.\n", num_assertions);
+ return 0;
+}
+
+}
diff --git a/tests/pb/test_decoder_schema.proto b/tests/pb/test_decoder_schema.proto
new file mode 100644
index 0000000..50bfca9
--- /dev/null
+++ b/tests/pb/test_decoder_schema.proto
@@ -0,0 +1,64 @@
+//
+// upb - a minimalist implementation of protocol buffers.
+//
+// Copyright (c) 2012 Google Inc. See LICENSE for details.
+// Author: Josh Haberman <jhaberman@gmail.com>
+//
+// Schema used in test_decoder.cc. It contains two fields (one optional
+// and one repeated) for each type.
+
+package upb.test_decoder;
+
+message M {
+ optional M m = 1;
+}
+
+enum E {
+ FOO = 1;
+}
+
+message EmptyMessage {}
+
+message DecoderTest {
+ optional double f_double = 1;
+ optional float f_float = 2;
+ optional int64 f_int64 = 3;
+ optional uint64 f_uint64 = 4;
+ optional int32 f_int32 = 5;
+ optional fixed64 f_fixed64 = 6;
+ optional fixed32 f_fixed32 = 7;
+ optional bool f_bool = 8;
+ optional string f_string = 9;
+ optional bytes f_bytes = 12;
+ optional uint32 f_uint32 = 13;
+ optional sfixed32 f_sfixed32 = 15;
+ optional sfixed64 f_sfixed64 = 16;
+ optional sint32 f_sint32 = 17;
+ optional sint64 f_sint64 = 18;
+
+ optional DecoderTest f_message = 11;
+ optional E f_enum = 14;
+
+
+ repeated double r_double = 536869912;
+ repeated float r_float = 536869913;
+ repeated int64 r_int64 = 536869914;
+ repeated uint64 r_uint64 = 536869915;
+ repeated int32 r_int32 = 536869916;
+ repeated fixed64 r_fixed64 = 536869917;
+ repeated fixed32 r_fixed32 = 536869918;
+ repeated bool r_bool = 536869919;
+ repeated string r_string = 536869920;
+ repeated bytes r_bytes = 536869923;
+ repeated uint32 r_uint32 = 536869924;
+ repeated sfixed32 r_sfixed32 = 536869926;
+ repeated sfixed64 r_sfixed64 = 536869927;
+ repeated sint32 r_sint32 = 536869928;
+ repeated sint64 r_sint64 = 536869929;
+
+ repeated DecoderTest r_message = 536869922;
+ repeated E r_enum = 536869925;
+
+ // To allow arbitrary padding.
+ optional string nop_field = 40;
+}
diff --git a/tests/pb/test_varint.c b/tests/pb/test_varint.c
new file mode 100644
index 0000000..84ff831
--- /dev/null
+++ b/tests/pb/test_varint.c
@@ -0,0 +1,255 @@
+/*
+ * upb - a minimalist implementation of protocol buffers.
+ *
+ * Copyright (c) 2011 Google Inc. See LICENSE for details.
+ */
+
+#include <stdio.h>
+#include "upb/pb/varint.int.h"
+#include "tests/upb_test.h"
+
+// Test that we can round-trip from int->varint->int.
+static void test_varint_for_num(upb_decoderet (*decoder)(const char*),
+ uint64_t num) {
+ char buf[16];
+ memset(buf, 0xff, sizeof(buf));
+ size_t bytes = upb_vencode64(num, buf);
+
+ if (num <= UINT32_MAX) {
+ char buf2[16];
+ memset(buf2, 0, sizeof(buf2));
+ uint64_t encoded = upb_vencode32(num);
+ memcpy(&buf2, &encoded, 8);
+ upb_decoderet r = decoder(buf2);
+ ASSERT(r.val == num);
+ ASSERT(r.p == buf2 + upb_value_size(encoded));
+ ASSERT(upb_zzenc_32(upb_zzdec_32(num)) == num);
+ }
+
+ upb_decoderet r = decoder(buf);
+ ASSERT(r.val == num);
+ ASSERT(r.p == buf + bytes);
+ ASSERT(upb_zzenc_64(upb_zzdec_64(num)) == num);
+}
+
+static void test_varint_decoder(upb_decoderet (*decoder)(const char*)) {
+#define TEST(bytes, expected_val) {\
+ size_t n = sizeof(bytes) - 1; /* for NULL */ \
+ char buf[UPB_PB_VARINT_MAX_LEN]; \
+ memset(buf, 0xff, sizeof(buf)); \
+ memcpy(buf, bytes, n); \
+ upb_decoderet r = decoder(buf); \
+ ASSERT(r.val == expected_val); \
+ ASSERT(r.p == buf + n); \
+ }
+
+ TEST("\x00", 0ULL);
+ TEST("\x01", 1ULL);
+ TEST("\x81\x14", 0xa01ULL);
+ TEST("\x81\x03", 0x181ULL);
+ TEST("\x81\x83\x07", 0x1c181ULL);
+ TEST("\x81\x83\x87\x0f", 0x1e1c181ULL);
+ TEST("\x81\x83\x87\x8f\x1f", 0x1f1e1c181ULL);
+ TEST("\x81\x83\x87\x8f\x9f\x3f", 0x1f9f1e1c181ULL);
+ TEST("\x81\x83\x87\x8f\x9f\xbf\x7f", 0x1fdf9f1e1c181ULL);
+ TEST("\x81\x83\x87\x8f\x9f\xbf\xff\x01", 0x3fdf9f1e1c181ULL);
+ TEST("\x81\x83\x87\x8f\x9f\xbf\xff\x81\x03", 0x303fdf9f1e1c181ULL);
+ TEST("\x81\x83\x87\x8f\x9f\xbf\xff\x81\x83\x07", 0x8303fdf9f1e1c181ULL);
+#undef TEST
+
+ char twelvebyte[16] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x01, 0x01};
+ const char *twelvebyte_buf = twelvebyte;
+ // A varint that terminates before hitting the end of the provided buffer,
+ // but in too many bytes (11 instead of 10).
+ upb_decoderet r = decoder(twelvebyte_buf);
+ ASSERT(r.p == NULL);
+
+
+ for (uint64_t num = 5; num * 1.5 < UINT64_MAX; num *= 1.5) {
+ test_varint_for_num(decoder, num);
+ }
+ test_varint_for_num(decoder, 0);
+}
+
+
+#define TEST_VARINT_DECODER(decoder) \
+ /* Create non-inline versions for convenient inspection of assembly language \
+ * output. */ \
+ upb_decoderet _upb_vdecode_ ## decoder(const char *p) { \
+ return upb_vdecode_ ## decoder(p); \
+ } \
+ void test_ ## decoder() { \
+ printf("Testing varint decoder: " #decoder "..."); \
+ fflush(stdout); \
+ test_varint_decoder(&_upb_vdecode_ ## decoder); \
+ printf("ok.\n"); \
+ } \
+
+TEST_VARINT_DECODER(check2_branch32);
+TEST_VARINT_DECODER(check2_branch64);
+TEST_VARINT_DECODER(check2_wright);
+TEST_VARINT_DECODER(check2_massimino);
+
+int run_tests(int argc, char *argv[]) {
+ UPB_UNUSED(argc);
+ UPB_UNUSED(argv);
+ test_check2_branch32();
+ test_check2_branch64();
+ test_check2_wright();
+ test_check2_massimino();
+ return 0;
+}
+
+#if 0
+static void test_get_v_uint32_t()
+{
+#define TEST(name, bytes, val) {\
+ upb_status status = UPB_STATUS_INIT; \
+ const uint8_t name[] = bytes; \
+ const uint8_t *name ## _buf = name; \
+ uint32_t name ## _val = 0; \
+ name ## _buf = upb_get_v_uint32_t(name, name + sizeof(name), &name ## _val, &status); \
+ ASSERT(upb_ok(&status)); \
+ ASSERT(name ## _val == val); \
+ ASSERT(name ## _buf == name + sizeof(name) - 1); /* - 1 for NULL */ \
+ /* Test NEED_MORE_DATA. */ \
+ if(sizeof(name) > 2) { \
+ name ## _buf = upb_get_v_uint32_t(name, name + sizeof(name) - 2, &name ## _val, &status); \
+ ASSERT(status.code == UPB_STATUS_NEED_MORE_DATA); \
+ } \
+ }
+
+ TEST(zero, "\x00", 0UL);
+ TEST(one, "\x01", 1UL);
+ TEST(twob, "\x81\x03", 0x181UL);
+ TEST(threeb, "\x81\x83\x07", 0x1c181UL);
+ TEST(fourb, "\x81\x83\x87\x0f", 0x1e1c181UL);
+ /* get_v_uint32_t truncates, so all the rest return the same thing. */
+ TEST(fiveb, "\x81\x83\x87\x8f\x1f", 0xf1e1c181UL);
+ TEST(sixb, "\x81\x83\x87\x8f\x9f\x3f", 0xf1e1c181UL);
+ TEST(sevenb, "\x81\x83\x87\x8f\x9f\xbf\x7f", 0xf1e1c181UL);
+ TEST(eightb, "\x81\x83\x87\x8f\x9f\xbf\xff\x01", 0xf1e1c181UL);
+ TEST(nineb, "\x81\x83\x87\x8f\x9f\xbf\xff\x81\x03", 0xf1e1c181UL);
+ TEST(tenb, "\x81\x83\x87\x8f\x9f\xbf\xff\x81\x83\x07", 0xf1e1c181UL);
+#undef TEST
+
+ uint8_t twelvebyte[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01, 0x01};
+ uint32_t twelvebyte_val = 0;
+ upb_status status = UPB_STATUS_INIT;
+ /* A varint that terminates before hitting the end of the provided buffer,
+ * but in too many bytes (11 instead of 10). */
+ upb_get_v_uint32_t(twelvebyte, twelvebyte + 12, &twelvebyte_val, &status);
+ ASSERT(status.code == UPB_ERROR_UNTERMINATED_VARINT);
+
+ /* A varint that terminates simultaneously with the end of the provided
+ * buffer, but in too many bytes (11 instead of 10). */
+ upb_reset(&status);
+ upb_get_v_uint32_t(twelvebyte, twelvebyte + 11, &twelvebyte_val, &status);
+ ASSERT(status.code == UPB_ERROR_UNTERMINATED_VARINT);
+
+ /* A varint whose buffer ends on exactly the byte where the varint must
+ * terminate, but the final byte does not terminate. The absolutely most
+ * correct return code here is UPB_ERROR_UNTERMINATED_VARINT, because we know
+ * by this point that the varint does not properly terminate. But we also
+ * allow a return value of UPB_STATUS_NEED_MORE_DATA here, because it does not
+ * compromise overall correctness -- clients who supply more data later will
+ * then receive a UPB_ERROR_UNTERMINATED_VARINT error; clients who have no
+ * more data to supply will (rightly) conclude that their protobuf is corrupt.
+ */
+ upb_reset(&status);
+ upb_get_v_uint32_t(twelvebyte, twelvebyte + 10, &twelvebyte_val, &status);
+ ASSERT(status.code == UPB_ERROR_UNTERMINATED_VARINT ||
+ status.code == UPB_STATUS_NEED_MORE_DATA);
+
+ upb_reset(&status);
+ upb_get_v_uint32_t(twelvebyte, twelvebyte + 9, &twelvebyte_val, &status);
+ ASSERT(status.code == UPB_STATUS_NEED_MORE_DATA);
+}
+
+static void test_skip_v_uint64_t()
+{
+#define TEST(name, bytes) {\
+ upb_status status = UPB_STATUS_INIT; \
+ const uint8_t name[] = bytes; \
+ const uint8_t *name ## _buf = name; \
+ name ## _buf = upb_skip_v_uint64_t(name ## _buf, name + sizeof(name), &status); \
+ ASSERT(upb_ok(&status)); \
+ ASSERT(name ## _buf == name + sizeof(name) - 1); /* - 1 for NULL */ \
+ /* Test NEED_MORE_DATA. */ \
+ if(sizeof(name) > 2) { \
+ name ## _buf = upb_skip_v_uint64_t(name, name + sizeof(name) - 2, &status); \
+ ASSERT(status.code == UPB_STATUS_NEED_MORE_DATA); \
+ } \
+ }
+
+ TEST(zero, "\x00");
+ TEST(one, "\x01");
+ TEST(twob, "\x81\x03");
+ TEST(threeb, "\x81\x83\x07");
+ TEST(fourb, "\x81\x83\x87\x0f");
+ TEST(fiveb, "\x81\x83\x87\x8f\x1f");
+ TEST(sixb, "\x81\x83\x87\x8f\x9f\x3f");
+ TEST(sevenb, "\x81\x83\x87\x8f\x9f\xbf\x7f");
+ TEST(eightb, "\x81\x83\x87\x8f\x9f\xbf\xff\x01");
+ TEST(nineb, "\x81\x83\x87\x8f\x9f\xbf\xff\x81\x03");
+ TEST(tenb, "\x81\x83\x87\x8f\x9f\xbf\xff\x81\x83\x07");
+#undef TEST
+
+ uint8_t twelvebyte[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01, 0x01};
+ upb_status status = UPB_STATUS_INIT;
+ /* A varint that terminates before hitting the end of the provided buffer,
+ * but in too many bytes (11 instead of 10). */
+ upb_skip_v_uint64_t(twelvebyte, twelvebyte + 12, &status);
+ ASSERT(status.code == UPB_ERROR_UNTERMINATED_VARINT);
+
+ /* A varint that terminates simultaneously with the end of the provided
+ * buffer, but in too many bytes (11 instead of 10). */
+ upb_reset(&status);
+ upb_skip_v_uint64_t(twelvebyte, twelvebyte + 11, &status);
+ ASSERT(status.code == UPB_ERROR_UNTERMINATED_VARINT);
+
+ /* A varint whose buffer ends on exactly the byte where the varint must
+ * terminate, but the final byte does not terminate. The absolutely most
+ * correct return code here is UPB_ERROR_UNTERMINATED_VARINT, because we know
+ * by this point that the varint does not properly terminate. But we also
+ * allow a return value of UPB_STATUS_NEED_MORE_DATA here, because it does not
+ * compromise overall correctness -- clients who supply more data later will
+ * then receive a UPB_ERROR_UNTERMINATED_VARINT error; clients who have no
+ * more data to supply will (rightly) conclude that their protobuf is corrupt.
+ */
+ upb_reset(&status);
+ upb_skip_v_uint64_t(twelvebyte, twelvebyte + 10, &status);
+ ASSERT(status.code == UPB_ERROR_UNTERMINATED_VARINT ||
+ status.code == UPB_STATUS_NEED_MORE_DATA);
+
+ upb_reset(&status);
+ upb_skip_v_uint64_t(twelvebyte, twelvebyte + 9, &status);
+ ASSERT(status.code == UPB_STATUS_NEED_MORE_DATA);
+}
+
+static void test_get_f_uint32_t()
+{
+#define TEST(name, bytes, val) {\
+ upb_status status = UPB_STATUS_INIT; \
+ const uint8_t name[] = bytes; \
+ const uint8_t *name ## _buf = name; \
+ uint32_t name ## _val = 0; \
+ name ## _buf = upb_get_f_uint32_t(name ## _buf, name + sizeof(name), &name ## _val, &status); \
+ ASSERT(upb_ok(&status)); \
+ ASSERT(name ## _val == val); \
+ ASSERT(name ## _buf == name + sizeof(name) - 1); /* - 1 for NULL */ \
+ }
+
+ TEST(zero, "\x00\x00\x00\x00", 0x0UL);
+ TEST(one, "\x01\x00\x00\x00", 0x1UL);
+
+ uint8_t threeb[] = {0x00, 0x00, 0x00};
+ uint32_t threeb_val;
+ upb_status status = UPB_STATUS_INIT;
+ upb_get_f_uint32_t(threeb, threeb + sizeof(threeb), &threeb_val, &status);
+ ASSERT(status.code == UPB_STATUS_NEED_MORE_DATA);
+
+#undef TEST
+}
+#endif
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback