aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/module.c12
-rw-r--r--core/module.h4
-rw-r--r--core/test-module.c49
3 files changed, 59 insertions, 6 deletions
diff --git a/core/module.c b/core/module.c
index 3657843..64ec8f6 100644
--- a/core/module.c
+++ b/core/module.c
@@ -35,11 +35,12 @@ enum { READ = IN_VIEW | IN_HANDLER };
* a fold runs in, a number, bool or i64 state (A_VALUE), or the value's type
* (A_TYPE: 0 a number, 1 a bool, 2 an i64), how I64_BITS computes (A_BITS),
* what a position is (A_POSITION: 0 a number, 1 an i64), what CONCAT joins
- * (A_JOINED: 0 texts, 1 bytes), how NUM_TEXT writes (A_FORMAT) or the value
+ * (A_JOINED: 0 texts, 1 bytes), what TEXT_COMPARE relates (A_ORDER: a relation,
+ * and PURE_COMPARE_BYTES for two bytes), how NUM_TEXT writes (A_FORMAT) or the value
* held by name (A_HELD), or a function (A_FUNCTION: CALL's, and RETURN's own). */
enum { A_NONE, A_STATE, A_ITEM = A_STATE + PURE_STATE_I64 + 1, A_FIELD, A_CONSTANT, A_IN_TEXT, A_IN_LIST,
A_ROW, A_TARGET, A_DEPTH, A_FOLD, A_RELATION, A_KEYED, A_OUTPUT, A_OUTER, A_EACH, A_LOCAL, A_VALUE, A_TYPE,
- A_LOOP, A_BITS, A_POSITION, A_JOINED, A_FORMAT, A_HELD, A_FUNCTION };
+ A_LOOP, A_BITS, A_POSITION, A_JOINED, A_FORMAT, A_HELD, A_FUNCTION, A_ORDER };
/* A fold's block is verified as a column that leaves one bool (count, first)
* or a value and whether to include it (a bool): a number (FOLD_TWO) or an
* i64 (FOLD_I64). A table's filter is verified as a column that leaves a
@@ -119,7 +120,7 @@ static const struct { unsigned char pops, in[3], out; unsigned short where; unsi
[PURE_OP_PICK] = {0, {0}, 0, READ, A_DEPTH, O_ANY},
[PURE_OP_SLIDE] = {0, {0}, 0, READ, A_DEPTH, O_ANY},
[PURE_OP_FOLD] = {0, {0}, T_NUM, READ, A_FOLD, O_ANY},
- [PURE_OP_TEXT_COMPARE] = {2, {T_TEXT, T_TEXT}, T_BOOL, READ, A_RELATION, O_ANY},
+ [PURE_OP_TEXT_COMPARE] = {2, {T_TEXT, T_TEXT}, T_BOOL, READ, A_ORDER, O_ANY},
[PURE_OP_LIST_FIND] = {1, {T_KEY}, T_INDEX, READ, A_KEYED, O_ARG},
[PURE_OP_LIST_HAS] = {1, {T_KEY}, T_BOOL, READ, A_KEYED, O_ANY},
[PURE_OP_NOW] = {0, {0}, T_NUM, IN_HANDLER, A_NONE, O_ANY},
@@ -459,6 +460,7 @@ PURE_COLD static bool names_arg(const PureModule *m, unsigned kind, uint32_t arg
(form != PURE_FORMAT_FIXED || type == 0) && (form == PURE_FORMAT_FIXED || form == PURE_FORMAT_DECIMAL || !digits);
}
case A_RELATION: return arg <= PURE_NOT_EQUAL;
+ case A_ORDER: return (arg & ~PURE_COMPARE_BYTES) <= PURE_NOT_EQUAL; /* a relation, of texts or of bytes */
case A_KEYED: return state_of(m, arg, PURE_STATE_LIST) && m->state_key[arg];
case A_OUTPUT: return arg < m->inputs && m->input[arg].kind == PURE_KIND_OUTPUT;
case A_NONE: case A_TARGET: case A_DEPTH: return true;
@@ -668,7 +670,7 @@ PURE_COLD static const char *verify_code(const PureModule *m, PureBlock block, i
need[0] = state_type(m, arg);
} else if (op->op == PURE_OP_NUM_TEXT && arg & 3) {
need[0] = (arg & 3) == 1 ? T_BOOL : T_I64;
- } else if (op->op == PURE_OP_CONCAT && arg) {
+ } else if ((op->op == PURE_OP_CONCAT && arg) || (op->op == PURE_OP_TEXT_COMPARE && arg & PURE_COMPARE_BYTES)) {
need[0] = need[1] = T_BYTES;
} else if (op->op == PURE_OP_BYTES_SLICE && arg) {
need[1] = need[2] = T_I64; /* the positions i64s */
@@ -4233,7 +4235,7 @@ static inline __attribute__((always_inline)) int step(Frame *frame, const PureOp
int order = shorter ? memcmp(a.data, b.data, shorter) : 0;
if (!order) order = (a.size > b.size) - (a.size < b.size);
arena->used = mark;
- stack[sp - 1].num = related(arg, order);
+ stack[sp - 1].num = related(arg & ~PURE_COMPARE_BYTES, order);
NEXT;
}
case PURE_OP_LIST_FIND: case PURE_OP_LIST_HAS: {
diff --git a/core/module.h b/core/module.h
index 75e27ba..6fad0ec 100644
--- a/core/module.h
+++ b/core/module.h
@@ -209,8 +209,10 @@ enum { PURE_LOOP_INTEGER = 256, PURE_MODULE_NESTING = 8 };
#define PURE_WORK_DEFAULT (UINT64_C(1) << 28)
#define PURE_GROWTH_DEFAULT ((size_t)1 << 28)
/* TEXT_COMPARE's argument: the relation, in the order of the number
- * comparisons (LT LE GT GE EQ NE); texts order by their bytes. */
+ * comparisons (LT LE GT GE EQ NE), and PURE_COMPARE_BYTES with it when its
+ * two are bytes (texts and bytes alike order by their bytes). */
typedef enum { PURE_LESS, PURE_LESS_EQUAL, PURE_GREATER, PURE_GREATER_EQUAL, PURE_EQUAL, PURE_NOT_EQUAL } PureRelation;
+#define PURE_COMPARE_BYTES 0x100u
/* An input is a message (027's 4d: the only thing an app takes), an output
* or a view's arguments. An output is a message the app sends (SEND, in a
diff --git a/core/test-module.c b/core/test-module.c
index f89e198..8795bf8 100644
--- a/core/test-module.c
+++ b/core/test-module.c
@@ -6164,6 +6164,54 @@ static void bits_and_bytes(void) {
printf("{\"bits_and_bytes\":{\"bits\":%zu,\"reads\":%zu,\"refused\":%zu}}\n", LEN(cases), LEN(reads), LEN(refused));
}
+/* ---- Bytes compared (after research 140's DNS and HTTP) --------------------
+ * TEXT_COMPARE with PURE_COMPARE_BYTES relates two bytes by their bytes, as
+ * it relates texts (a shorter before a longer it begins), UTF-8 asked of
+ * neither; without the bit it takes texts alone, with it bytes alone. */
+static void bytes_compared(void) {
+ enum { CMP = PURE_OP_TEXT_COMPARE, BF = PURE_OP_BYTES_FROM, JZ_ = PURE_OP_JZ, BYTE_ = PURE_OP_BYTE, B = PURE_COMPARE_BYTES };
+ const O word = {TK, 0, 0}, none = {TK, 1, 0}; /* "Az\xc3\xa9" and "" */
+ const struct { O a[2], b[2]; uint32_t relation; double holds; } cases[] = {
+ {{word, {BF, 0, 0}}, {word, {BF, 0, 0}}, PURE_EQUAL, 1},
+ {{word, {BF, 0, 0}}, {none, {BF, 0, 0}}, PURE_GREATER, 1},
+ {{none, {BF, 0, 0}}, {word, {BF, 0, 0}}, PURE_LESS, 1},
+ {{none, {BF, 0, 0}}, {none, {BF, 0, 0}}, PURE_LESS_EQUAL, 1},
+ {{IC(0xFF), {BYTE_, 0, 0}}, {word, {BF, 0, 0}}, PURE_GREATER, 1}, /* no UTF-8: 0xff after 'A' */
+ {{IC(0xFF), {BYTE_, 0, 0}}, {IC(0xFF), {BYTE_, 0, 0}}, PURE_NOT_EQUAL, 0},
+ {{word, {BF, 0, 0}}, {IC('A'), {BYTE_, 0, 0}}, PURE_GREATER_EQUAL, 1}, /* longer, the same first byte */
+ {{word, {BF, 0, 0}}, {IC('A'), {BYTE_, 0, 0}}, PURE_EQUAL, 0},
+ };
+ for (size_t k = 0; k < LEN(cases); ++k) {
+ O code[] = {cases[k].a[0], cases[k].a[1], cases[k].b[0], cases[k].b[1], {CMP, cases[k].relation | B, 0}, {JZ_, 8, 0},
+ {C, 0, 1}, {ST, 0, 0}};
+ Byted ran = run_bytes(code, LEN(code));
+ if (ran.fault != PURE_FAULT_NONE || ran.n != cases[k].holds) {
+ fprintf(stderr, "bytes compared %zu: fault %d, %g\n", k, (int)ran.fault, ran.n);
+ abort();
+ }
+ }
+ /* Refused: the bit on texts, no bit on bytes, a text with bytes, a relation past NE. */
+ const O refused[][5] = {
+ {word, none, {CMP, PURE_EQUAL | B, 0}, {JZ_, 5, 0}, {C, 0, 1}},
+ {word, {BF, 0, 0}, none, {BF, 0, 0}, {CMP, PURE_EQUAL, 0}},
+ {word, {BF, 0, 0}, none, {CMP, PURE_EQUAL | B, 0}, {JZ_, 5, 0}},
+ {word, {BF, 0, 0}, none, {BF, 0, 0}, {CMP, (PURE_NOT_EQUAL + 1) | B, 0}},
+ };
+ for (size_t k = 0; k < LEN(refused); ++k) {
+ static const S nums[] = {{"n", 0}};
+ static const T others[] = {{"note", 1, 0}, {"bytes", 3, 0}, {"i", PURE_STATE_I64, 0}, {"said", 1, 1}};
+ static const RF byte_fields[] = {{"v", 3, 0}};
+ static const R records[] = {{"Byte", byte_fields, 1}};
+ static const char *const constants[] = {"Az\xc3\xa9", ""};
+ static const I inputs[] = {KEY("go", "KeyG")};
+ H handler = {1, 0, 5};
+ Spec spec = {nums, 1, inputs, 1, NULL, 0, &handler, 1, refused[k], 5, NULL, 0, others, LEN(others), constants,
+ LEN(constants), NULL, 0, records, 1, NULL, 0};
+ if (load(spec, &module_b) == PURE_OK) { fprintf(stderr, "bytes compared: refused %zu loaded\n", k); abort(); }
+ }
+ printf("{\"bytes_compared\":{\"cases\":%zu,\"refused\":%zu}}\n", LEN(cases), LEN(refused));
+}
+
/* ---- Formats, slices and values held by name (docs/design/027, stage 2a) --
* NUM_TEXT writes a number, a bool or an i64 in a width, with zeros or
* spaces, in a base or with fixed digits, or an integer in minor units
@@ -7342,6 +7390,7 @@ int main(void) {
bools();
integers();
bits_and_bytes();
+ bytes_compared();
formats_and_held();
lists_in_records();
functions_and_characters();