Added JSON decoder to conformance tests, and fixed tons of bugs.
We are now down to only 10 conformance failures across all of upb.
diff --git a/upb/json_encode.c b/upb/json_encode.c
index 0069e4a..caa0c8a 100644
--- a/upb/json_encode.c
+++ b/upb/json_encode.c
@@ -29,13 +29,23 @@
static void jsonenc_scalar(jsonenc *e, upb_msgval val, const upb_fielddef *f);
static void jsonenc_msgfield(jsonenc *e, const upb_msg *msg,
const upb_msgdef *m);
+static void jsonenc_msgfields(jsonenc *e, const upb_msg *msg,
+ const upb_msgdef *m);
static void jsonenc_value(jsonenc *e, const upb_msg *msg, const upb_msgdef *m);
-static void jsonenc_err(jsonenc *e, const char *msg) {
+UPB_NORETURN static void jsonenc_err(jsonenc *e, const char *msg) {
upb_status_seterrmsg(e->status, msg);
longjmp(e->err, 1);
}
+static upb_arena *jsonenc_arena(jsonenc *e) {
+ /* Create lazily, since it's only needed for Any */
+ if (!e->arena) {
+ e->arena = upb_arena_new();
+ }
+ return e->arena;
+}
+
static void jsonenc_putbytes(jsonenc *e, const void *data, size_t len) {
size_t have = e->end - e->ptr;
if (UPB_LIKELY(have >= len)) {
@@ -70,19 +80,19 @@
}
static void jsonenc_nanos(jsonenc *e, int32_t nanos) {
- const char zeros[3] = "000";
+ int digits = 9;
if (nanos == 0) return;
if (nanos < 0 || nanos >= 1000000000) {
jsonenc_err(e, "error formatting timestamp as JSON: invalid nanos");
}
- jsonenc_printf(e, "%09" PRId32, nanos);
-
- /* Remove trailing zeros, 3 at a time. */
- while ((e->ptr - e->buf) >= 3 && memcmp(e->ptr, zeros, 3) == 0) {
- e->ptr -= 3;
+ while (nanos % 1000 == 0) {
+ nanos /= 1000;
+ digits -= 3;
}
+
+ jsonenc_printf(e, ".%0.*" PRId32, digits, nanos);
}
static void jsonenc_timestamp(jsonenc *e, const upb_msg *msg,
@@ -107,7 +117,7 @@
* Fliegel, H. F., and Van Flandern, T. C., "A Machine Algorithm for
* Processing Calendar Dates," Communications of the Association of
* Computing Machines, vol. 11 (1968), p. 657. */
- L = (seconds / 86400) + 2440588;
+ L = (seconds / 86400) + 68569 + 2440588;
N = 4 * L / 146097;
L = L - (146097 * N + 3) / 4;
I = 4000 * (L + 1) / 1461001;
@@ -138,6 +148,10 @@
jsonenc_err(e, "bad duration");
}
+ if (nanos < 0) {
+ nanos = -nanos;
+ }
+
jsonenc_printf(e, "\"%" PRId64, seconds);
jsonenc_nanos(e, nanos);
jsonenc_putstr(e, "s\"");
@@ -158,8 +172,8 @@
/* This is the regular base64, not the "web-safe" version. */
static const char base64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
- const char *ptr = str.data;
- const char *end = ptr + str.size;
+ const unsigned char *ptr = (unsigned char*)str.data;
+ const unsigned char *end = ptr + str.size;
char buf[4];
jsonenc_putstr(e, "\"");
@@ -182,6 +196,7 @@
jsonenc_putbytes(e, buf, 4);
break;
case 1:
+ fprintf(stderr, "Base64 encode: %d\n", (int)ptr[0]);
buf[0] = base64[ptr[0] >> 2];
buf[1] = base64[((ptr[0] & 0x3) << 4)];
buf[2] = '=';
@@ -212,10 +227,10 @@
jsonenc_putstr(e, "\\\"");
break;
case '\f':
- jsonenc_putstr(e, "\f'");
+ jsonenc_putstr(e, "\\f");
break;
case '\b':
- jsonenc_putstr(e, "\b'");
+ jsonenc_putstr(e, "\\b");
break;
case '\\':
jsonenc_putstr(e, "\\\\");
@@ -255,7 +270,7 @@
static void jsonenc_wrapper(jsonenc *e, const upb_msg *msg,
const upb_msgdef *m) {
const upb_fielddef *val_f = upb_msgdef_itof(m, 1);
- upb_msgval val = upb_msg_get(m, val_f);
+ upb_msgval val = upb_msg_get(msg, val_f);
jsonenc_scalar(e, val, val_f);
}
@@ -263,13 +278,14 @@
/* Find last '/', if any. */
const char *end = type_url.data + type_url.size;
const char *ptr = end;
+ const upb_msgdef *ret;
- if (!e->ext_pool || type_url.size == 0) return NULL;
+ if (!e->ext_pool || type_url.size == 0) goto badurl;
while (true) {
if (--ptr == type_url.data) {
/* Type URL must contain at least one '/', with host before. */
- return NULL;
+ goto badurl;
}
if (*ptr == '/') {
ptr++;
@@ -277,19 +293,29 @@
}
}
- return upb_symtab_lookupmsg2(e->ext_pool, ptr, end - ptr);
+ ret = upb_symtab_lookupmsg2(e->ext_pool, ptr, end - ptr);
+
+ if (!ret) {
+ jsonenc_err(e, "Couldn't find Any type");
+ }
+
+ return ret;
+
+badurl:
+ jsonenc_err(e, "Bad type URL");
}
static void jsonenc_any(jsonenc *e, const upb_msg *msg, const upb_msgdef *m) {
const upb_fielddef *type_url_f = upb_msgdef_itof(m, 1);
- const upb_fielddef *value_f = upb_msgdef_itof(m, 1);
+ const upb_fielddef *value_f = upb_msgdef_itof(m, 2);
upb_strview type_url = upb_msg_get(msg, type_url_f).str_val;
upb_strview value = upb_msg_get(msg, value_f).str_val;
const upb_msgdef *any_m = jsonenc_getanymsg(e, type_url);
const upb_msglayout *any_layout = upb_msgdef_layout(any_m);
- upb_msg *any = upb_msg_new(any_m, e->arena);
+ upb_arena *arena = jsonenc_arena(e);
+ upb_msg *any = upb_msg_new(any_m, arena);
- if (!upb_decode(value.data, value.size, any, any_layout, e->arena)) {
+ if (!upb_decode(value.data, value.size, any, any_layout, arena)) {
jsonenc_err(e, "Error decoding message in Any");
}
@@ -297,9 +323,9 @@
jsonenc_string(e, type_url);
jsonenc_putstr(e, ", ");
- if (upb_msgdef_wellknowntype(m) == UPB_WELLKNOWN_UNSPECIFIED) {
+ if (upb_msgdef_wellknowntype(any_m) == UPB_WELLKNOWN_UNSPECIFIED) {
/* Regular messages: {"@type": "...", "foo": 1, "bar": 2} */
- jsonenc_msg(e, any, any_m);
+ jsonenc_msgfields(e, any, any_m);
} else {
/* Well-known type: {"@type": "...", "value": <well-known encoding>} */
jsonenc_putstr(e, "value: ");
@@ -468,7 +494,7 @@
jsonenc_listvalue(e, msg, m);
break;
case UPB_WELLKNOWN_STRUCT:
- jsonenc_listvalue(e, msg, m);
+ jsonenc_struct(e, msg, m);
break;
}
}
@@ -532,6 +558,7 @@
break;
case UPB_TYPE_STRING:
jsonenc_stringbody(e, val.str_val);
+ break;
default:
UPB_UNREACHABLE();
}
@@ -595,13 +622,12 @@
}
}
-static void jsonenc_msg(jsonenc *e, const upb_msg *msg, const upb_msgdef *m) {
+static void jsonenc_msgfields(jsonenc *e, const upb_msg *msg,
+ const upb_msgdef *m) {
upb_msgval val;
const upb_fielddef *f;
bool first = true;
- jsonenc_putstr(e, "{");
-
if (e->options & UPB_JSONENC_EMITDEFAULTS) {
/* Iterate over all fields. */
upb_msg_field_iter i;
@@ -617,7 +643,11 @@
jsonenc_fieldval(e, f, val, &first);
}
}
+}
+static void jsonenc_msg(jsonenc *e, const upb_msg *msg, const upb_msgdef *m) {
+ jsonenc_putstr(e, "{");
+ jsonenc_msgfields(e, msg, m);
jsonenc_putstr(e, "}");
}
@@ -644,9 +674,11 @@
e.options = options;
e.ext_pool = ext_pool;
e.status = status;
+ e.arena = NULL;
if (setjmp(e.err)) return -1;
jsonenc_msg(&e, msg, m);
+ if (e.arena) upb_arena_free(e.arena);
return jsonenc_nullz(&e, size);
}