Queues of whole messages on the wires between nodes (v4/wire.h), the six addresses and ten operations a node asks of the fabric (MESH.md 7d.3), and the fabric's pool of queues. Engine only: the nucleus does not use any of it yet. The ports in the host map move to BUF0_W - 140 so that the new addresses are not among the nucleus variables, with a compile-time check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
295 lines
16 KiB
C
295 lines
16 KiB
C
/* test_wire.c -- a queue of whole messages, one way along a wire, with no
|
|
* node and no fabric: docs/v4.0.0/MESH.md section 7d.2.
|
|
*
|
|
* The first attempt at messages that wait (7c.6) moved a message a word at a
|
|
* time and left both ends out of step when a fault came in the middle. So
|
|
* what is tested here, over and over, is that a queue which answers anything
|
|
* but "done" is exactly as it was.
|
|
*/
|
|
#include "v4/wire.h"
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
static int failures = 0, checks = 0;
|
|
#define CHECK(c,...) do{checks++; if(!(c)){failures++; printf("FAIL %s:%d: ",__FILE__,__LINE__); printf(__VA_ARGS__); printf("\n");}}while(0)
|
|
|
|
static v4_wire_queue q, other, before, before_other;
|
|
|
|
/* A message of `len` characters: seven words, then its text. */
|
|
static void make(v4_cell *h, v4_cell *t, v4_cell len, v4_cell seed)
|
|
{
|
|
unsigned i, n = len > 0 ? (unsigned)(len + 3) / 4u : 0;
|
|
for (i = 0; i < 7; i++) h[i] = seed * 100 + (v4_cell)i;
|
|
h[6] = len;
|
|
for (i = 0; i < n; i++) t[i] = seed * 1000 + 17 * (v4_cell)i + 5;
|
|
}
|
|
|
|
/* 1 if the message taken back is the one made from `len` and `seed` */
|
|
static int same(const v4_cell *h, const v4_cell *t, v4_cell len, v4_cell seed)
|
|
{
|
|
v4_cell wh[7], wt[V4_WIRE_CELLS];
|
|
unsigned i, n = (unsigned)(len + 3) / 4u;
|
|
make(wh, wt, len, seed);
|
|
for (i = 0; i < 7; i++) if (h[i] != wh[i]) return 0;
|
|
for (i = 0; i < n; i++) if (t[i] != wt[i]) return 0;
|
|
return 1;
|
|
}
|
|
|
|
static void put_ok(v4_wire_queue *w, v4_cell len, v4_cell seed, int *ok)
|
|
{
|
|
v4_cell h[7], t[V4_WIRE_CELLS];
|
|
make(h, t, len, seed);
|
|
if (v4_wire_put(w, h, t) != V4_WIRE_DONE) *ok = 0;
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
static const v4_cell lens[] = { 0, 1, 4, 5, 1024 };
|
|
v4_cell h[7], t[V4_WIRE_CELLS], h2[7], t2[V4_WIRE_CELLS];
|
|
unsigned i, k, round, count, wrapped, cap_cells;
|
|
int ok;
|
|
|
|
printf("v4 wire tests: V4_CELL_BITS=%d, %d cells to a queue\n", V4_CELL_BITS, V4_WIRE_CELLS);
|
|
|
|
/* ---- an empty queue ---- */
|
|
v4_wire_reset(&q);
|
|
CHECK(v4_wire_room(&q, 1024) == V4_WIRE_DONE, "an empty queue has room for 1,024");
|
|
CHECK(v4_wire_room(&q, 1025) == V4_WIRE_NO_ROOM, "and not for 1,025");
|
|
memcpy(&before, &q, sizeof q);
|
|
CHECK(v4_wire_look(&q, h) == V4_WIRE_NO_MESSAGE, "look at nothing answers 3");
|
|
CHECK(v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_NO_MESSAGE, "take answers 3");
|
|
CHECK(v4_wire_drop(&q) == V4_WIRE_NO_MESSAGE, "drop answers 3");
|
|
CHECK(v4_wire_next(&q) == V4_WIRE_NO_MESSAGE, "next answers 3");
|
|
CHECK(memcmp(&before, &q, sizeof q) == 0, "and none of them changed it");
|
|
CHECK(q.mark == q.used && q.used == 0 && q.arrived == 0, "its mark is at no message, and nothing has arrived");
|
|
|
|
/* ---- the cells a message takes ---- */
|
|
CHECK(v4_wire_cells(0) == 7 && v4_wire_cells(1) == 8 && v4_wire_cells(4) == 8 && v4_wire_cells(5) == 9 && v4_wire_cells(1024) == 263,
|
|
"a message takes 7 cells and one for each four characters: 7 8 8 9 and 263");
|
|
CHECK(v4_wire_cells(-1) == 0 && v4_wire_cells(1025) == 0, "a length of -1 or 1,025 is not a message");
|
|
|
|
/* ---- lengths of 0, 1, 4, 5 and 1,024, each put and taken back ---- */
|
|
for (k = 0; k < sizeof lens / sizeof lens[0]; k++) {
|
|
v4_wire_reset(&q);
|
|
make(h, t, lens[k], (v4_cell)k + 1);
|
|
CHECK(v4_wire_put(&q, h, t) == V4_WIRE_DONE, "a message of %ld characters is put", (long)lens[k]);
|
|
CHECK(q.used == v4_wire_cells(lens[k]), "it takes %u cells: %u", v4_wire_cells(lens[k]), q.used);
|
|
memset(h2, 0, sizeof h2); memset(t2, 0, sizeof t2);
|
|
CHECK(v4_wire_look(&q, h2) == V4_WIRE_DONE && memcmp(h, h2, sizeof h2) == 0, "look gives its seven words");
|
|
CHECK(q.used == v4_wire_cells(lens[k]), "and takes nothing");
|
|
CHECK(v4_wire_take(&q, h2, t2, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h2, t2, lens[k], (v4_cell)k + 1),
|
|
"taken, its seven words and every word of its text are what was put");
|
|
CHECK(q.used == 0 && q.mark == 0, "and the queue is empty again, its mark at no message");
|
|
}
|
|
|
|
/* ---- round the end of the array: fill and empty it three times, with lengths that do not divide 1,024 ---- */
|
|
v4_wire_reset(&q);
|
|
wrapped = 0;
|
|
for (round = 0; round < 3; round++) {
|
|
static const v4_cell mix[] = { 100, 37, 333, 1, 250, 58, 0, 411 };
|
|
v4_cell seed[200], len[200];
|
|
count = 0;
|
|
for (;;) {
|
|
v4_cell l = mix[(count + round * 3) % 8];
|
|
make(h, t, l, (v4_cell)(round * 50 + count + 1));
|
|
if (q.head + v4_wire_cells(l) > V4_WIRE_CELLS && q.used + v4_wire_cells(l) <= V4_WIRE_CELLS) wrapped++;
|
|
if (v4_wire_put(&q, h, t) != V4_WIRE_DONE) break;
|
|
seed[count] = (v4_cell)(round * 50 + count + 1);
|
|
len[count++] = l;
|
|
}
|
|
CHECK(count > 5 && count < 200 && q.used <= V4_WIRE_CELLS, "round %u: %u messages fill it, %u cells", round, count, q.used);
|
|
ok = 1;
|
|
for (i = 0; i < count; i++) {
|
|
if (v4_wire_take(&q, h2, t2, V4_WIRE_CELLS) != V4_WIRE_DONE || !same(h2, t2, len[i], seed[i])) ok = 0;
|
|
}
|
|
CHECK(ok && q.used == 0, "round %u: they come out whole and in the order they went in", round);
|
|
}
|
|
CHECK(wrapped > 0, "and some of them went across the end of the array: %u", wrapped);
|
|
|
|
/* ---- a put that does not fit changes nothing ---- */
|
|
v4_wire_reset(&q);
|
|
ok = 1;
|
|
put_ok(&q, 1024, 1, &ok); /* 263 */
|
|
put_ok(&q, 1024, 2, &ok); /* 526 */
|
|
put_ok(&q, 1024, 3, &ok); /* 789 */
|
|
CHECK(ok && q.used == 789, "three long messages take 789 cells");
|
|
CHECK(v4_wire_room(&q, 235) == V4_WIRE_DONE && v4_wire_room(&q, 236) == V4_WIRE_NO_ROOM, "which leaves room for 235 and not 236");
|
|
make(h, t, 4 * (235 - 7) + 1, 9); /* 236 cells */
|
|
memcpy(&before, &q, sizeof q);
|
|
CHECK(v4_wire_put(&q, h, t) == V4_WIRE_NO_ROOM, "a message of 236 cells is refused, 1");
|
|
CHECK(memcmp(&before, &q, sizeof q) == 0, "and the queue is exactly as it was: head, used, mark and every cell");
|
|
make(h, t, 4 * (235 - 7), 9); /* 235 cells: the last that fits */
|
|
CHECK(v4_wire_put(&q, h, t) == V4_WIRE_DONE && q.used == V4_WIRE_CELLS, "one of 235 cells fits, and fills it");
|
|
CHECK(v4_wire_room(&q, 0) == V4_WIRE_DONE && v4_wire_room(&q, 1) == V4_WIRE_NO_ROOM, "a full queue has room for none");
|
|
/* with 200 cells free: a short message goes, a long one is refused */
|
|
v4_wire_reset(&q);
|
|
ok = 1;
|
|
put_ok(&q, 1024, 1, &ok); put_ok(&q, 1024, 2, &ok); put_ok(&q, 1024, 3, &ok);
|
|
put_ok(&q, 4 * (35 - 7), 4, &ok);
|
|
CHECK(ok && V4_WIRE_CELLS - q.used == 200, "a queue with 200 cells free: %u", V4_WIRE_CELLS - q.used);
|
|
make(h, t, 4 * (201 - 7), 5);
|
|
memcpy(&before, &q, sizeof q);
|
|
CHECK(v4_wire_put(&q, h, t) == V4_WIRE_NO_ROOM && memcmp(&before, &q, sizeof q) == 0, "refuses a message of 201 cells and is as it was");
|
|
make(h, t, 4 * (200 - 7), 5);
|
|
CHECK(v4_wire_put(&q, h, t) == V4_WIRE_DONE, "and takes one of 200");
|
|
v4_wire_reset(&q);
|
|
put_ok(&q, 1024, 1, &ok); put_ok(&q, 1024, 2, &ok); put_ok(&q, 1024, 3, &ok); put_ok(&q, 4 * (35 - 7), 4, &ok);
|
|
make(h, t, 40, 6);
|
|
CHECK(v4_wire_put(&q, h, t) == V4_WIRE_DONE && q.used == 824 + v4_wire_cells(40), "and a short one, of 40 characters");
|
|
|
|
/* ---- not a message ---- */
|
|
v4_wire_reset(&q);
|
|
ok = 1;
|
|
put_ok(&q, 100, 1, &ok); put_ok(&q, 7, 2, &ok);
|
|
memcpy(&before, &q, sizeof q);
|
|
make(h, t, 10, 3);
|
|
h[6] = -1;
|
|
CHECK(v4_wire_put(&q, h, t) == V4_WIRE_NOT_A_MESSAGE, "a length of -1 answers 4");
|
|
CHECK(memcmp(&before, &q, sizeof q) == 0, "and changes nothing");
|
|
h[6] = 1025;
|
|
CHECK(v4_wire_put(&q, h, t) == V4_WIRE_NOT_A_MESSAGE, "a length of 1,025 answers 4");
|
|
CHECK(memcmp(&before, &q, sizeof q) == 0, "and changes nothing");
|
|
v4_wire_reset(&q);
|
|
ok = 1;
|
|
put_ok(&q, 1024, 1, &ok); put_ok(&q, 1024, 2, &ok); put_ok(&q, 1024, 3, &ok); put_ok(&q, 900, 4, &ok);
|
|
memcpy(&before, &q, sizeof q);
|
|
h[6] = 1025;
|
|
CHECK(v4_wire_put(&q, h, t) == V4_WIRE_NOT_A_MESSAGE && memcmp(&before, &q, sizeof q) == 0,
|
|
"a length that is not a message is 4 and not 1 even when the queue is nearly full");
|
|
|
|
/* ---- first and next walk the messages; take and drop of the middle one ---- */
|
|
v4_wire_reset(&q);
|
|
ok = 1;
|
|
put_ok(&q, 20, 1, &ok); put_ok(&q, 33, 2, &ok); put_ok(&q, 9, 3, &ok);
|
|
v4_wire_first(&q);
|
|
CHECK(ok && q.mark == 0 && v4_wire_look(&q, h) == V4_WIRE_DONE && h[0] == 100, "first puts the mark at the front message");
|
|
CHECK(v4_wire_next(&q) == V4_WIRE_DONE && v4_wire_look(&q, h) == V4_WIRE_DONE && h[0] == 200, "next, at the second");
|
|
CHECK(v4_wire_next(&q) == V4_WIRE_DONE && v4_wire_look(&q, h) == V4_WIRE_DONE && h[0] == 300, "next, at the third");
|
|
memcpy(&before, &q, sizeof q);
|
|
CHECK(v4_wire_next(&q) == V4_WIRE_NO_MESSAGE, "and then 3");
|
|
CHECK(q.mark == q.used && v4_wire_look(&q, h) == V4_WIRE_NO_MESSAGE, "the mark is then at no message");
|
|
CHECK(v4_wire_next(&q) == V4_WIRE_NO_MESSAGE && q.mark == q.used, "and stays there");
|
|
|
|
v4_wire_first(&q);
|
|
(void)v4_wire_next(&q); /* the middle one */
|
|
CHECK(v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 33, 2), "take of the middle one gives it");
|
|
CHECK(q.used == v4_wire_cells(20) + v4_wire_cells(9) && v4_wire_look(&q, h) == V4_WIRE_DONE && h[0] == 300, "the mark is at the third");
|
|
v4_wire_first(&q);
|
|
CHECK(v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 20, 1), "the first is still first");
|
|
CHECK(v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 9, 3), "and the third follows it, whole");
|
|
CHECK(q.used == 0, "and nothing is left");
|
|
|
|
v4_wire_reset(&q);
|
|
ok = 1;
|
|
put_ok(&q, 20, 1, &ok); put_ok(&q, 33, 2, &ok); put_ok(&q, 9, 3, &ok);
|
|
v4_wire_first(&q);
|
|
(void)v4_wire_next(&q);
|
|
CHECK(v4_wire_drop(&q) == V4_WIRE_DONE && v4_wire_look(&q, h) == V4_WIRE_DONE && h[0] == 300, "drop of the middle one: the mark is at the third");
|
|
v4_wire_first(&q);
|
|
CHECK(v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 20, 1) && v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 9, 3) && q.used == 0,
|
|
"the first and third are left, in order");
|
|
/* the last one, dropped, leaves the mark at no message */
|
|
ok = 1;
|
|
put_ok(&q, 20, 1, &ok); put_ok(&q, 33, 2, &ok);
|
|
v4_wire_first(&q);
|
|
(void)v4_wire_next(&q);
|
|
CHECK(v4_wire_drop(&q) == V4_WIRE_DONE && q.mark == q.used && q.used == v4_wire_cells(20), "drop of the last: the mark is at no message");
|
|
|
|
/* middle take across the end of the array */
|
|
v4_wire_reset(&q);
|
|
ok = 1;
|
|
put_ok(&q, 1024, 1, &ok); put_ok(&q, 1024, 2, &ok); put_ok(&q, 1024, 3, &ok);
|
|
v4_wire_first(&q);
|
|
(void)v4_wire_take(&q, h, t, V4_WIRE_CELLS); /* head is at 263 */
|
|
put_ok(&q, 1024, 4, &ok); put_ok(&q, 77, 5, &ok); /* the last of them goes round */
|
|
CHECK(ok && q.head + q.used > V4_WIRE_CELLS, "messages are across the end of the array");
|
|
v4_wire_first(&q);
|
|
(void)v4_wire_next(&q); (void)v4_wire_next(&q); /* the 1,024 that came last but one */
|
|
CHECK(v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 1024, 4), "the middle one is taken, and the one after it moves down across the end");
|
|
CHECK(v4_wire_look(&q, h) == V4_WIRE_DONE && h[0] == 500, "the mark is at the one that followed it");
|
|
v4_wire_first(&q);
|
|
ok = 1;
|
|
ok &= v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 1024, 2);
|
|
ok &= v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 1024, 3);
|
|
ok &= v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 77, 5);
|
|
CHECK(ok && q.used == 0, "and the others come out whole, in order");
|
|
|
|
/* ---- move ---- */
|
|
v4_wire_reset(&q); v4_wire_reset(&other);
|
|
ok = 1;
|
|
put_ok(&q, 20, 1, &ok); put_ok(&q, 33, 2, &ok); put_ok(&other, 5, 7, &ok);
|
|
other.arrived = 0; q.arrived = 0;
|
|
v4_wire_first(&q);
|
|
(void)v4_wire_next(&q);
|
|
CHECK(ok && v4_wire_move(&q, &other) == V4_WIRE_DONE, "the marked message moves to another queue");
|
|
CHECK(other.arrived != 0, "which has had a message arrive");
|
|
CHECK(v4_wire_look(&q, h) == V4_WIRE_NO_MESSAGE && q.used == v4_wire_cells(20), "it is off the first, whose mark is at no message");
|
|
v4_wire_first(&other);
|
|
CHECK(v4_wire_take(&other, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 5, 7), "the other's own message is first");
|
|
CHECK(v4_wire_take(&other, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && h[3] == 203 - 1 && h[0] == 200 && h[6] == 33 && h[5] == 205,
|
|
"and the moved one is at the back, its fourth word one fewer and the rest as they were");
|
|
{
|
|
v4_cell wh[7], wt[V4_WIRE_CELLS];
|
|
unsigned n;
|
|
make(wh, wt, 33, 2);
|
|
for (n = 0; n < 9 && t[n] == wt[n]; n++) { }
|
|
CHECK(n == 9, "and every word of its text");
|
|
}
|
|
/* no room in the other: 1, and neither changes */
|
|
v4_wire_reset(&q); v4_wire_reset(&other);
|
|
ok = 1;
|
|
put_ok(&q, 100, 1, &ok);
|
|
put_ok(&other, 1024, 2, &ok); put_ok(&other, 1024, 3, &ok); put_ok(&other, 1024, 4, &ok);
|
|
put_ok(&other, 4 * (226 - 7), 5, &ok);
|
|
v4_wire_first(&q);
|
|
memcpy(&before, &q, sizeof q); memcpy(&before_other, &other, sizeof other);
|
|
CHECK(ok && V4_WIRE_CELLS - other.used < v4_wire_cells(100), "the other has %u cells free, a message of 100 characters takes %u", V4_WIRE_CELLS - other.used, v4_wire_cells(100));
|
|
CHECK(v4_wire_move(&q, &other) == V4_WIRE_NO_ROOM, "move to a queue with no room answers 1");
|
|
CHECK(memcmp(&before, &q, sizeof q) == 0 && memcmp(&before_other, &other, sizeof other) == 0, "and changes neither");
|
|
v4_wire_reset(&other);
|
|
v4_wire_reset(&q);
|
|
CHECK(v4_wire_move(&q, &other) == V4_WIRE_NO_MESSAGE && other.used == 0, "move with nothing marked answers 3");
|
|
|
|
/* ---- take with too little room for the text ---- */
|
|
v4_wire_reset(&q);
|
|
ok = 1;
|
|
put_ok(&q, 100, 1, &ok); /* 25 cells of text */
|
|
memcpy(&before, &q, sizeof q);
|
|
memset(h2, 0, sizeof h2); memset(t2, 0, sizeof t2);
|
|
cap_cells = 24;
|
|
CHECK(v4_wire_take(&q, h2, t2, cap_cells) == V4_WIRE_NOT_A_MESSAGE, "take with room for 24 cells of a text of 25 answers 4");
|
|
CHECK(memcmp(&before, &q, sizeof q) == 0, "the queue is as it was");
|
|
ok = 1;
|
|
for (i = 0; i < 7; i++) if (h2[i] != 0) ok = 0;
|
|
for (i = 0; i < 25; i++) if (t2[i] != 0) ok = 0;
|
|
CHECK(ok, "and nothing was copied out");
|
|
CHECK(v4_wire_take(&q, h2, t2, 25) == V4_WIRE_DONE && same(h2, t2, 100, 1), "with room for 25 it is taken");
|
|
v4_wire_reset(&q);
|
|
ok = 1;
|
|
put_ok(&q, 0, 1, &ok);
|
|
CHECK(ok && v4_wire_take(&q, h2, 0, 0) == V4_WIRE_DONE, "a message with no text needs no room for any");
|
|
|
|
/* ---- arrived ---- */
|
|
v4_wire_reset(&q); v4_wire_reset(&other);
|
|
CHECK(q.arrived == 0, "a queue that has had nothing has had nothing arrive");
|
|
ok = 1;
|
|
put_ok(&q, 4, 1, &ok);
|
|
CHECK(ok && q.arrived != 0, "a put sets arrived");
|
|
q.arrived = 0;
|
|
make(h, t, 4, 2);
|
|
h[6] = 2000;
|
|
(void)v4_wire_put(&q, h, t);
|
|
CHECK(q.arrived == 0, "a put that is refused does not");
|
|
put_ok(&other, 4, 3, &ok);
|
|
other.arrived = 0;
|
|
v4_wire_first(&other);
|
|
CHECK(v4_wire_move(&other, &q) == V4_WIRE_DONE && q.arrived != 0, "nor does a move into it, when it is done, fail to");
|
|
q.arrived = 0;
|
|
v4_wire_first(&q);
|
|
(void)v4_wire_take(&q, h, t, V4_WIRE_CELLS);
|
|
CHECK(q.arrived == 0, "a take does not set it");
|
|
|
|
printf(" %d checks, %d failures\n", checks, failures);
|
|
return failures != 0;
|
|
}
|