aboutsummaryrefslogtreecommitdiffstats
path: root/serve/message-json.c
diff options
context:
space:
mode:
Diffstat (limited to 'serve/message-json.c')
-rw-r--r--serve/message-json.c139
1 files changed, 134 insertions, 5 deletions
diff --git a/serve/message-json.c b/serve/message-json.c
index 66c812d..8c288ce 100644
--- a/serve/message-json.c
+++ b/serve/message-json.c
@@ -42,22 +42,33 @@ static size_t json_number(const unsigned char *p, size_t size, bool *integer) {
return i;
}
-/* A fixed field's value from its text (a query value or a JSON number) into
- * the prefix: a u32 in range, or a finite number. */
-bool pure_json_fixed(const PureField *field, const unsigned char *text, size_t size, unsigned char *prefix) {
+/* A JSON number's finite value. */
+static bool json_double(const unsigned char *text, size_t size, double *value) {
bool integer;
if (!size || json_number(text, size, &integer) != size || size > 40) return false;
char digits[48];
memcpy(digits, text, size);
digits[size] = 0;
+ *value = strtod(digits, NULL);
+ return isfinite(*value);
+}
+
+/* A fixed field's value from its text (a query value or a JSON number) into
+ * the prefix: a u32 in range, or a finite number. */
+bool pure_json_fixed(const PureField *field, const unsigned char *text, size_t size, unsigned char *prefix) {
+ bool integer;
+ if (!size || json_number(text, size, &integer) != size || size > 40) return false;
if (field->type == PURE_FIELD_U32) {
+ char digits[48];
uint64_t n;
+ memcpy(digits, text, size);
+ digits[size] = 0;
if (!integer || !decimal(digits, UINT32_MAX, &n)) return false;
write_u32(prefix + field->offset, (uint32_t)n);
return true;
}
- double value = strtod(digits, NULL);
- if (!isfinite(value)) return false;
+ double value;
+ if (!json_double(text, size, &value)) return false;
memcpy(prefix + field->offset, &value, 8);
return true;
}
@@ -203,6 +214,116 @@ static uint32_t field_named(const PureJsonMessage *b, const char *path, size_t p
return b->shape->field_count;
}
+/* Room for more bytes of rows. */
+static bool rows_room(PureJsonMessage *b, size_t more) {
+ if (more <= b->rows_capacity - b->rows_size) return true;
+ size_t capacity = b->rows_capacity ? b->rows_capacity : 4096;
+ while (capacity - b->rows_size < more) {
+ if (capacity > SIZE_MAX / 2) return false;
+ capacity *= 2;
+ }
+ unsigned char *grown = realloc(b->rows, capacity);
+ if (!grown) return false;
+ b->rows = grown;
+ b->rows_capacity = capacity;
+ return true;
+}
+
+/* A rows field's array (at its opening bracket): each an object of its
+ * record's fields by name, a number or a string, a field left out taking its
+ * default; encoded one after another as the list's stored form (a number's
+ * binary64 bits, a text's u32le length and bytes). */
+static const char *rows_of(PureJsonMessage *b, unsigned char *json, size_t size, size_t *at, uint32_t f, unsigned depth) {
+ const PureModule *m = b->module;
+ const PureField *field = &m->field[b->shape->field_start + f];
+ const PureRecord *record = &m->record[m->state_ref[field->rows]];
+ size_t start = b->rows_size;
+ if (*at == size || json[(*at)++] != '[') return "json_type";
+ *at = pure_json_space(json, *at, size);
+ for (bool first = true; *at < size && json[*at] != ']'; first = false) {
+ if (!first) {
+ if (json[(*at)++] != ',') return "json";
+ *at = pure_json_space(json, *at, size);
+ }
+ if (*at == size || json[(*at)++] != '{') return "json_type";
+ double numbers[PURE_RECORD_FIELDS];
+ PureSpan texts[PURE_RECORD_FIELDS];
+ bool given[PURE_RECORD_FIELDS] = {false};
+ *at = pure_json_space(json, *at, size);
+ for (bool member = true; *at < size && json[*at] != '}'; member = false) {
+ if (!member) {
+ if (json[(*at)++] != ',') return "json";
+ *at = pure_json_space(json, *at, size);
+ }
+ size_t key_end = *at < size && json[*at] == '"' ? json_string_end(json + *at + 1, size - *at - 1) : 0;
+ if (!key_end) return "json";
+ const unsigned char *key = json + *at + 1;
+ size_t key_size = key_end - 1;
+ *at = pure_json_space(json, *at + 1 + key_end, size);
+ if (*at == size || json[(*at)++] != ':') return "json";
+ *at = pure_json_space(json, *at, size);
+ unsigned k = 0;
+ while (k < record->count && (strlen(m->record_field_names[record->first + k]) != key_size ||
+ memcmp(m->record_field_names[record->first + k], key, key_size)))
+ ++k;
+ if (k == record->count) {
+ if (!b->lenient) return "json_field";
+ if (!skip_value(json, size, at, depth + 1)) return "json";
+ *at = pure_json_space(json, *at, size);
+ continue;
+ }
+ if (given[k]) return "json_field";
+ given[k] = true;
+ bool text = m->record_field_text[record->first + k];
+ if (*at < size && json[*at] == '"') {
+ size_t end = json_string_end(json + *at + 1, size - *at - 1), written;
+ if (!end) return "json";
+ if (!text) return "json_type";
+ unsigned char *value = json + *at + 1;
+ if (!memchr(value, '\\', end - 1)) written = end - 1;
+ else if (!json_unescape(value, end - 1, value, &written)) return "json";
+ if (!pure_utf8_valid((PureSpan){value, written})) return "json";
+ texts[k] = (PureSpan){value, written};
+ *at = pure_json_space(json, *at + 1 + end, size);
+ continue;
+ }
+ bool integer;
+ size_t length = json_number(json + *at, size - *at, &integer);
+ if (!length) return *at < size && (json[*at] == '[' || json[*at] == '{' || json[*at] == 't' || json[*at] == 'f' ||
+ json[*at] == 'n') ? "json_type" : "json";
+ if (text || !json_double(json + *at, length, &numbers[k])) return "json_type";
+ *at = pure_json_space(json, *at + length, size);
+ }
+ if (*at == size) return "json";
+ ++*at;
+ for (unsigned k = 0; k < record->count; ++k) {
+ uint32_t at_field = record->first + k;
+ if (m->record_field_text[at_field]) {
+ uint32_t constant = (uint32_t)m->record_field_default[at_field];
+ PureSpan text = given[k] ? texts[k]
+ : (PureSpan){m->constant_data + m->constant_offset[constant], m->constant_length[constant]};
+ if (!rows_room(b, 4 + text.size)) return "memory";
+ write_u32(b->rows + b->rows_size, (uint32_t)text.size);
+ if (text.size) memcpy(b->rows + b->rows_size + 4, text.data, text.size);
+ b->rows_size += 4 + text.size;
+ } else {
+ double value = given[k] ? numbers[k] : m->record_field_default[at_field];
+ uint64_t bits;
+ memcpy(&bits, &value, 8);
+ if (!rows_room(b, 8)) return "memory";
+ for (unsigned i = 0; i < 8; ++i) b->rows[b->rows_size + i] = (unsigned char)(bits >> (8 * i));
+ b->rows_size += 8;
+ }
+ }
+ }
+ if (*at == size) return "json";
+ ++*at;
+ if (b->rows_base + b->rows_size > UINT32_MAX >> 2) return "limit";
+ write_u32(b->table + 8 * f, (uint32_t)(b->rows_base + start));
+ write_u32(b->table + 8 * f + 4, (uint32_t)(b->rows_size - start));
+ return NULL;
+}
+
/* An object's members from json[*at] (its opening brace), in the group named
* path: a field's value, or a group's object. Strings stay where they are
* (an escaped one is unescaped over itself, which never makes it longer).
@@ -226,6 +347,14 @@ static const char *members(PureJsonMessage *b, unsigned char *json, size_t size,
*at = pure_json_space(json, *at, size);
bool group;
uint32_t f = field_named(b, path, path_size, key, key_size, &group);
+ if (f < b->shape->field_count && m->field[b->shape->field_start + f].type == PURE_FIELD_ROWS) {
+ if (b->seen[f] || (b->given && b->given[f])) return "json_field";
+ b->seen[f] = true;
+ const char *why = rows_of(b, json, size, at, f, depth);
+ if (why) return why;
+ *at = pure_json_space(json, *at, size);
+ continue;
+ }
if (f == b->shape->field_count && !group && b->lenient) { /* not the app's: skipped */
if (!skip_value(json, size, at, depth)) return "json";
*at = pure_json_space(json, *at, size);