diff options
Diffstat (limited to 'serve')
| -rw-r--r-- | serve/deliver.c | 6 | ||||
| -rw-r--r-- | serve/message-json.c | 34 | ||||
| -rw-r--r-- | serve/message-json.h | 4 | ||||
| -rw-r--r-- | serve/serve.c | 28 | ||||
| -rw-r--r-- | serve/test-message-json.c | 11 |
5 files changed, 62 insertions, 21 deletions
diff --git a/serve/deliver.c b/serve/deliver.c index b00984b..6e402a5 100644 --- a/serve/deliver.c +++ b/serve/deliver.c @@ -149,6 +149,8 @@ static bool encode(const PureWire *w, PureSpan payload, Text *t) { char digits[32]; if (field->type == PURE_FIELD_U32) { put(t, digits, (size_t)snprintf(digits, sizeof(digits), "%u", (unsigned)read_u32(payload.data + field->offset))); + } else if (field->type == PURE_FIELD_BOOL) { + put(t, payload.data[field->offset] ? "true" : "false", payload.data[field->offset] ? 4 : 5); } else if (field->type == PURE_FIELD_NUMBER) { uint64_t bits = read_u32(payload.data + field->offset) | (uint64_t)read_u32(payload.data + field->offset + 4) << 32; double value; @@ -544,8 +546,8 @@ static bool form_answer(const PureWire *w, int status, const Text *body, const P free(bytes); return false; } - if (field->type == PURE_FIELD_U32 || field->type == PURE_FIELD_NUMBER) - memcpy(prefix + field->offset, p->sent + from->offset, field->type == PURE_FIELD_U32 ? 4 : 8); + if (pure_field_fixed(field->type)) + memcpy(prefix + field->offset, p->sent + from->offset, pure_field_size(field->type)); else { write_u32(bytes + 8 * f, (uint32_t)(copy + start - prefix)); write_u32(bytes + 8 * f + 4, (uint32_t)length); diff --git a/serve/message-json.c b/serve/message-json.c index 643cc28..0ace68f 100644 --- a/serve/message-json.c +++ b/serve/message-json.c @@ -83,9 +83,22 @@ static bool json_double(const unsigned char *text, size_t size, double *value) { 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. */ +/* A JSON true or false at text: its length (4 or 5), or 0. */ +static size_t json_bool(const unsigned char *text, size_t size, bool *value) { + if (size >= 4 && !memcmp(text, "true", 4)) return *value = true, 4; + if (size >= 5 && !memcmp(text, "false", 5)) return *value = false, 5; + return 0; +} + +/* A fixed field's value from its text (a query value or a JSON value) into + * the prefix: a u32 in range, a finite number, or true or false for a bool. */ bool pure_json_fixed(const PureField *field, const unsigned char *text, size_t size, unsigned char *prefix) { + if (field->type == PURE_FIELD_BOOL) { /* true or false, as a JSON value says it */ + bool value; + if (!size || json_bool(text, size, &value) != size) return false; + prefix[field->offset] = value; + return true; + } bool integer; if (!size || json_number(text, size, &integer) != size || size > 40) return false; if (field->type == PURE_FIELD_U32) { @@ -304,7 +317,14 @@ static const char *rows_of(PureJsonMessage *b, unsigned char *json, size_t size, } if (given[k]) return "json_field"; given[k] = true; - bool text = m->record_field_text[record->first + k]; + bool text = m->record_field_text[record->first + k], truth; + if (m->record_field_bool[record->first + k]) { /* true or false, nothing else */ + size_t word = json_bool(json + *at, size - *at, &truth); + if (!word) return "json_type"; + numbers[k] = truth; + *at = pure_json_space(json, *at + word, size); + continue; + } if (*at < size && json[*at] == '"') { size_t end = json_string_end(json + *at + 1, size - *at - 1), written; if (!end) return "json"; @@ -406,7 +426,13 @@ static const char *members(PureJsonMessage *b, unsigned char *json, size_t size, if (b->seen[f] || (b->given && b->given[f])) return "json_field"; b->seen[f] = true; const PureField *field = &m->field[b->shape->field_start + f]; - bool variable = field->type == PURE_FIELD_BYTES || field->type == PURE_FIELD_TEXT, integer; + bool variable = field->type == PURE_FIELD_BYTES || field->type == PURE_FIELD_TEXT, integer, truth; + size_t word = field->type == PURE_FIELD_BOOL ? json_bool(json + *at, size - *at, &truth) : 0; + if (word) { /* any other value for a bool is of another type (below) */ + b->payload[field->offset] = truth; + *at = pure_json_space(json, *at + word, size); + continue; + } if (*at < size && json[*at] == '"') { size_t end = json_string_end(json + *at + 1, size - *at - 1), written; if (!end) return "json"; diff --git a/serve/message-json.h b/serve/message-json.h index c5332b4..ffb1651 100644 --- a/serve/message-json.h +++ b/serve/message-json.h @@ -26,7 +26,7 @@ typedef struct { * "limit": rows past what a payload holds, "memory"). */ const char *pure_json_message(PureJsonMessage *, unsigned char *json, size_t size, size_t *at); size_t pure_json_space(const unsigned char *json, size_t at, size_t size); -/* 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. */ +/* A fixed field's value from its text (a query value or a JSON value) into + * the prefix: a u32 in range, a finite number, or true or false for a bool. */ bool pure_json_fixed(const PureField *field, const unsigned char *text, size_t size, unsigned char *prefix); #endif diff --git a/serve/serve.c b/serve/serve.c index f2d455f..43297c1 100644 --- a/serve/serve.c +++ b/serve/serve.c @@ -805,7 +805,7 @@ static const char *resolve(const Host *host, Connection *c, char *query, Route * while (f < (message ? shape->field_count : 0) && strcmp(m->field[shape->field_start + f].name, pair)) ++f; const PureField *field = message && f < shape->field_count ? &m->field[shape->field_start + f] : NULL; long length = percent_decoded(value, strlen(value)); /* as a view's arguments are */ - if (!field || c->given[f] || (field->type != PURE_FIELD_U32 && field->type != PURE_FIELD_NUMBER) || + if (!field || c->given[f] || !pure_field_fixed(field->type) || length < 0 || !pure_json_fixed(field, (const unsigned char *)value, (size_t)length, c->fields)) return "query"; c->given[f] = true; @@ -814,7 +814,7 @@ static const char *resolve(const Host *host, Connection *c, char *query, Route * } for (uint32_t f = 0; message && !c->json && f < shape->field_count; ++f) { uint8_t type = m->field[shape->field_start + f].type; - if ((type == PURE_FIELD_U32 || type == PURE_FIELD_NUMBER) && !c->given[f]) return "missing_field"; + if (pure_field_fixed(type) && !c->given[f]) return "missing_field"; } for (uint32_t f = 0; c->batch > 1 && f < shape->field_count; ++f) if (c->given[f]) return "delivery"; /* a batch's fields are all in its objects */ @@ -871,8 +871,8 @@ static const char *convert(Connection *c) { if (b.seen[f] || c->given[f]) continue; const PureField *field = &c->host->module->field[shape->field_start + f]; if (!delivered || field->type == PURE_FIELD_ROWS) why = "missing_field"; - else if (field->type == PURE_FIELD_U32 || field->type == PURE_FIELD_NUMBER) - memset(b.payload + field->offset, 0, field->type == PURE_FIELD_NUMBER ? 8 : 4); + else if (pure_field_fixed(field->type)) + memset(b.payload + field->offset, 0, pure_field_size(field->type)); else put_u32(b.table + 8 * f, 0), put_u32(b.table + 8 * f + 4, 0); } if (!why && c->batch > 1 && b.rows_size) why = "delivery"; /* an output sends no rows */ @@ -1258,7 +1258,7 @@ static unsigned char *arguments_payload(const PureModule *m, int input, const ch size_t at = shape->prefix; for (uint32_t f = 0; ok && f < shape->field_count; ++f) { const PureField *field = &m->field[shape->field_start + f]; - if (field->type == PURE_FIELD_U32 || field->type == PURE_FIELD_NUMBER) { + if (pure_field_fixed(field->type)) { ok = values[f] && pure_json_fixed(field, (const unsigned char *)values[f], (size_t)lengths[f], payload); continue; } @@ -1303,10 +1303,11 @@ static PureResult show_given(PureApp *app, const Route *route, size_t available, PureResult status = PURE_OK; double value; if (m->view_type[view] == PURE_VIEW_TABLE && !(o.bytes = malloc(o.capacity = 1024))) return PURE_ALLOCATION; - if (m->view_type[view] == PURE_VIEW_NUM) { + if (m->view_type[view] == PURE_VIEW_NUM || m->view_type[view] == PURE_VIEW_BOOL) { *type = "text/plain; charset=utf-8"; - if (pure_app_view(app, view, &value)) put_number(&o, value); - else status = PURE_NOT_FOUND; + if (!pure_app_view(app, view, &value)) status = PURE_NOT_FOUND; + else if (m->view_type[view] == PURE_VIEW_BOOL) put_bytes(&o, value ? "true" : "false", value ? 4 : 5); + else put_number(&o, value); } else if (m->view_type[view] == PURE_VIEW_TEXT) { *type = "text/plain; charset=utf-8"; /* A text state shown as it is goes out as its stored extents. */ @@ -1341,9 +1342,11 @@ static PureResult show_given(PureApp *app, const Route *route, size_t available, put_bytes(&o, ",{" + (row == from), 2 - (row == from)); for (unsigned k = 0; k < count; ++k) { put_bytes(&o, columns[k].key + !k, columns[k].length - !k); - if (m->view_type[columns[k].view] == PURE_VIEW_NUM) { - if (pure_app_cell(app, columns[k].view, row, &value)) put_number(&o, value); - else put_bytes(&o, "null", 4); + uint8_t kind = m->view_type[columns[k].view]; + if (kind == PURE_VIEW_NUM || kind == PURE_VIEW_BOOL) { + if (!pure_app_cell(app, columns[k].view, row, &value)) put_bytes(&o, "null", 4); + else if (kind == PURE_VIEW_BOOL) put_bytes(&o, value ? "true" : "false", value ? 4 : 5); + else put_number(&o, value); continue; } size_t mark = o.size; @@ -1855,7 +1858,8 @@ static bool same_states(const PureModule *a, const PureModule *b) { a->record[i].count != b->record[i].count) return false; for (uint32_t i = 0; i < a->record_fields; ++i) - if (strcmp(a->record_field_names[i], b->record_field_names[i]) || a->record_field_text[i] != b->record_field_text[i]) + if (strcmp(a->record_field_names[i], b->record_field_names[i]) || a->record_field_text[i] != b->record_field_text[i] || + a->record_field_bool[i] != b->record_field_bool[i]) return false; for (uint32_t i = 0; i < a->collections; ++i) if (strcmp(a->collection_names[i], b->collection_names[i])) return false; diff --git a/serve/test-message-json.c b/serve/test-message-json.c index 618a363..b2bad37 100644 --- a/serve/test-message-json.c +++ b/serve/test-message-json.c @@ -1,7 +1,7 @@ /* JSON numbers are read as strtod reads them, bit for bit: in the forms a * client writes (a program's shortest digits, fixed decimals, exponents), at * the edges of the exact path (2^53, 10^22) and past them, and refused when - * not finite or not JSON. */ + * not finite or not JSON. A bool is true or false, nothing else. */ #include "message-json.h" #include <math.h> #include <stdint.h> @@ -43,6 +43,15 @@ int main(void) { double value; CHECK(!read_number(refused[i], &value)); } + static const PureField truth = {.type = PURE_FIELD_BOOL, .offset = 1}; + unsigned char prefix[2] = {7, 7}; + CHECK(pure_json_fixed(&truth, (const unsigned char *)"true", 4, prefix) && prefix[1] == 1 && prefix[0] == 7); + CHECK(pure_json_fixed(&truth, (const unsigned char *)"false", 5, prefix) && prefix[1] == 0); + static const char *const not_bools[] = {"", "1", "0", "True", "\"true\"", "tru", "truex", "false ", "null", "t"}; + for (size_t i = 0; i < sizeof(not_bools) / sizeof(*not_bools); ++i) + CHECK(!pure_json_fixed(&truth, (const unsigned char *)not_bools[i], strlen(not_bools[i]), prefix) && prefix[1] == 0); + double number; + CHECK(!read_number("true", &number) && !read_number("false", &number)); uint64_t seed = 0x6a736f6eu; size_t read = 0; for (unsigned i = 0; i < 400000; ++i) { |
