aboutsummaryrefslogtreecommitdiffstats
path: root/serve
diff options
context:
space:
mode:
Diffstat (limited to 'serve')
-rw-r--r--serve/deliver.c6
-rw-r--r--serve/message-json.c34
-rw-r--r--serve/message-json.h4
-rw-r--r--serve/serve.c28
-rw-r--r--serve/test-message-json.c11
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) {