feat(v4.0.0): the wire in the engine -- whole messages, put and taken in one step

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>
This commit is contained in:
rajames
2026-10-08 13:50:15 -04:00
co-authored by Claude Opus 5.5
parent 50be766e7b
commit d5cd64605d
14 changed files with 1267 additions and 22 deletions
+6 -3
View File
@@ -73,7 +73,7 @@
#define OWED (BUF0_W - 112) /* those: for each, the node it is owed to and the node the refused message was for */
#define ACL_HOOK (BUF0_W - 6) /* the xt of the access control recheck word, or 0 */
#define LINE_STATUS (BUF0_W - 9) /* how the last line ended: 0 QUIT, 1 completed, 2 an error */
#define PORT (BUF0_W - 32) /* the node's ports (node.h): V4_PORTS of them, then "any port" and "which port". Port 0 is where its requests go. */
#define PORT (BUF0_W - 140) /* the node's ports (node.h): V4_PORTS of them, then "any port", "which port" and the rest as far as V4_PORT_HAVE. Port 0 is where its requests go. */
#define CONSOLE (BUF0_W - 16) /* the node what this one prints is sent to; 0: whoever sent the text being served */
#define ROUTE_COUNT (BUF0_W - 17) /* how many entries the table of ways holds */
#define ROUTE_DEFAULT (BUF0_W - 18) /* the port address for a node not in the table, or 0: there is none */
@@ -105,8 +105,11 @@
#define LOG_LEVEL (BUF0_W - 5) /* the level at or below which a message is printed */
#define BOOT_CELLS (BVARS + 14) /* (BOOT): DP and LATEST as the loader left them, 2 cells */
#define BUF0_W (BVARS - (v4_cell)(V4_BLOCK_SLOTS * V4_BLOCK_CELLS)) /* the block window: the kernel's slots, 256 cells each (v4/blocks.h) */
/* the ports' ten addresses and the two that follow them end below BUF0_W - 20 */
typedef char host_map_ports_fit[(V4_PORTS <= 8u) ? 1 : -1];
/* The ports and the addresses after them, through V4_PORT_HAVE (node.h, MESH.md 7d.3), are not memory and must be where
* no variable is: after the output buffer, which ends at BUF0_W - 144, and before A_FOUND, the first variable above it.
* With 8 ports that is 18 addresses, BUF0_W - 140 to BUF0_W - 123. (They were once among the variables, and what was
* stored to the variables was stored to ports.) */
typedef char host_map_ports_fit[(PORT >= OUT_W + OUT_CELLS && PORT + (v4_cell)V4_PORT_HAVE < A_FOUND) ? 1 : -1];
/* Nothing printed and no message waiting: a node's variables for them, as at switch-on. */
#define HOST_MESSAGES_EMPTY(n) do { (n)->mem[OUT_PTR] = OUT_W; (n)->mem[MQ_HEAD] = MQ_W; (n)->mem[MQ_TAIL] = MQ_W; (n)->mem[MQ_COUNT] = 0; } while (0)
#define QVARS (BUF0_W - 4) /* (Q): quit.v4, system.v4 and blocks.v4, 4 cells */
+397 -1
View File
@@ -15,7 +15,13 @@ 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)
/* The memory map is open (D-4); the test chooses where the ports are. */
#define PB ((v4_cell)(V4_NODE_WORDS - 16u)) /* port 0 */
#define PB ((v4_cell)(V4_NODE_WORDS - 24u)) /* port 0: the ports, and the addresses after them (node.h), end inside memory */
#define P_WIRE (PB + (v4_cell)V4_PORT_WIRE)
#define P_A (PB + (v4_cell)V4_PORT_A)
#define P_B (PB + (v4_cell)V4_PORT_B)
#define P_DO (PB + (v4_cell)V4_PORT_DO)
#define P_HOW (PB + (v4_cell)V4_PORT_HOW)
#define P_HAVE (PB + (v4_cell)V4_PORT_HAVE)
#define ANY (PB + (v4_cell)V4_PORT_ANY)
#define ORG ((v4_cell)16) /* where a test's programme starts */
#define OUT ((v4_cell)900) /* where it leaves what it found */
@@ -105,6 +111,60 @@ static unsigned capsule(v4_cell *out, const v4_cell *words, unsigned count, v4_c
return at;
}
/* ---- messages on a wire: what the tests below drive a node with ---- */
/* A message in a node's memory: seven words at `hdr` and its text at `text`. */
static void put_msg(v4_node *n, v4_cell hdr, v4_cell text, v4_cell len, v4_cell seed)
{
unsigned i;
for (i = 0; i < 7; i++) n->mem[hdr + (v4_cell)i] = seed * 100 + (v4_cell)i;
n->mem[hdr + 6] = len;
for (i = 0; len > 0 && len <= 1024 && i < ((unsigned)len + 3u) / 4u; i++) n->mem[text + (v4_cell)i] = seed * 1000 + (v4_cell)i;
}
/* 1 if the message in the node's memory is the one put_msg makes, but for the word `except` of the header */
static int msg_is_but(const v4_node *n, v4_cell hdr, v4_cell text, v4_cell len, v4_cell seed, unsigned except)
{
unsigned i;
for (i = 0; i < 7; i++) {
v4_cell want = i == 6 ? len : seed * 100 + (v4_cell)i;
if (i != except && n->mem[hdr + (v4_cell)i] != want) return 0;
}
for (i = 0; i < ((unsigned)len + 3u) / 4u; i++) if (n->mem[text + (v4_cell)i] != seed * 1000 + (v4_cell)i) return 0;
return 1;
}
static int msg_is(const v4_node *n, v4_cell hdr, v4_cell text, v4_cell len, v4_cell seed)
{
return msg_is_but(n, hdr, text, len, seed, 7);
}
/* A queue filled until `leave` cells are free. */
static void fill(v4_wire_queue *q, unsigned leave)
{
v4_cell h[7] = { 1, 2, 3, 4, 5, 6, 1024 };
static v4_cell t[V4_WIRE_CELLS];
while (V4_WIRE_CELLS - q->used >= 263u + leave) (void)v4_wire_put(q, h, t);
if (V4_WIRE_CELLS - q->used > leave) {
h[6] = 4 * (v4_cell)(V4_WIRE_CELLS - q->used - leave - 7u);
(void)v4_wire_put(q, h, t);
}
}
/* Ask node `id` for an operation from outside, as its own programme would by
* storing to its wire, A, B and Do; let one step of the engine pass; and give
* how it went, or -99 if the node is still blocked at it. */
static v4_cell ask(unsigned id, v4_cell wire, v4_cell a, v4_cell b, v4_cell op)
{
v4_node *n = &ND(id)->n;
v4_node_store(n, P_WIRE, wire);
v4_node_store(n, P_A, a);
v4_node_store(n, P_B, b);
v4_node_store(n, P_DO, op);
(void)v4_fabric_step(&f);
return n->doing ? -99 : v4_node_fetch(n, P_HOW);
}
int main(void)
{
unsigned a, b, c, i, steps;
@@ -423,6 +483,342 @@ int main(void)
CHECK(ND(a)->n.asking && ND(a)->n.request == 2 && ND(a)->n.faults == 0, "blocked on its second word with the device still pending, the writer waits");
}
/* ---- messages on the wire (MESH.md 7d): the engine only ---- */
{
static v4_wire_queue qpool[8];
v4_cell h[7], t[V4_WIRE_CELLS], snap[V4_NODE_WORDS];
v4_wire_queue copy;
v4_device nothing;
unsigned w, k;
memset(&nothing, 0, sizeof nothing);
/* a node puts a message on its wire 0 and a neighbour takes it from its wire 3 */
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, qpool, 8);
a = loaded(0);
LIT(P_WIRE); O(BANG_B); LIT(0); O(STORE_B);
LIT(P_A); O(BANG_B); LIT(100); O(STORE_B);
LIT(P_B); O(BANG_B); LIT(110); O(STORE_B);
LIT(P_DO); O(BANG_B); LIT(5); O(STORE_B);
LIT(P_HOW); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A);
wait_for_ever();
b = loaded(1);
LIT(P_WIRE); O(BANG_B); LIT(3); O(STORE_B);
LIT(P_DO); O(BANG_B); LIT(8); O(STORE_B);
LIT(P_HAVE); O(BANG_B); O(FETCH_B); LIT(OUT + 1); O(BANG_A); O(STORE_A);
LIT(P_A); O(BANG_B); LIT(200); O(STORE_B);
LIT(P_B); O(BANG_B); LIT(220); O(STORE_B);
LIT(P_DO); O(BANG_B); LIT(2); O(STORE_B);
LIT(P_HOW); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A);
wait_for_ever();
CHECK(v4_asm_ok(&as), "a node that puts and a node that sleeps and takes assemble");
CHECK(v4_fabric_wire(&f, a, 0, b, 3), "wire 0 of the first is wired to wire 3 of the second");
ND(a)->n.mem[OUT] = ND(b)->n.mem[OUT] = -7;
put_msg(&ND(a)->n, 100, 110, 13, 5);
for (steps = 0; steps < 300 && ND(a)->n.do_op != 5; steps++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.do_op == 5 && !ND(a)->n.doing && ND(a)->n.how == 0 && f.place[a].wire[0].tx->used == 7 + 4,
"the put was done in the engine step that asked for it: the message is on the wire and the node is not blocked");
steps = settle(2000);
CHECK(steps < 2000 && ND(b)->n.do_op == 2 && ND(a)->n.mem[OUT] == 0 && ND(b)->n.mem[OUT] == 0, "how it went is 0 on both: %ld and %ld", (long)ND(a)->n.mem[OUT], (long)ND(b)->n.mem[OUT]);
CHECK(msg_is(&ND(b)->n, 200, 220, 13, 5), "the seven words and the text taken are what was put");
CHECK(ND(b)->n.mem[OUT + 1] == (1 << 3), "and while it waited it saw the message on its wire 3: %ld", (long)ND(b)->n.mem[OUT + 1]);
CHECK(f.place[a].wire[0].tx->used == 0 && f.place[b].wire[3].rx->used == 0, "the wire is empty again");
/* a put with nothing on the port is 2, to a wire with no room is 1; neither changes the wire */
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, qpool, 8);
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
CHECK(v4_fabric_wire(&f, a, 0, b, 3), "a wire for the refusals");
put_msg(&ND(a)->n, 100, 110, 20, 3);
CHECK(ask(a, 5, 100, 110, 5) == 2, "a put to a port with nothing on it answers 2");
CHECK(ask(a, (v4_cell)V4_PORTS, 100, 110, 5) == 2 && ask(a, -1, 100, 110, 5) == 2, "and so does one to a port that is not there");
fill(f.place[a].wire[0].tx, 0);
memcpy(&copy, f.place[a].wire[0].tx, sizeof copy);
CHECK(ask(a, 0, 100, 110, 5) == 1, "a put to a wire with no room answers 1");
CHECK(memcmp(&copy, f.place[a].wire[0].tx, sizeof copy) == 0, "and the wire is exactly as it was");
v4_wire_reset(f.place[a].wire[0].tx);
fill(f.place[a].wire[0].tx, 200);
put_msg(&ND(a)->n, 100, 110, 4 * (201 - 7), 3);
memcpy(&copy, f.place[a].wire[0].tx, sizeof copy);
CHECK(ask(a, 0, 100, 110, 5) == 1 && memcmp(&copy, f.place[a].wire[0].tx, sizeof copy) == 0, "a message of 201 cells to a wire with 200 free: 1, and nothing put");
put_msg(&ND(a)->n, 100, 110, 4 * (200 - 7), 3);
CHECK(ask(a, 0, 100, 110, 5) == 0 && f.place[a].wire[0].tx->used == V4_WIRE_CELLS, "one of 200 cells goes");
put_msg(&ND(a)->n, 100, 110, 5000, 3);
v4_wire_reset(f.place[a].wire[0].tx);
CHECK(ask(a, 0, 100, 110, 5) == 4 && f.place[a].wire[0].tx->used == 0, "a message whose length is not 0 to 1,024 answers 4");
put_msg(&ND(a)->n, 100, 110, -1, 3);
CHECK(ask(a, 0, 100, 110, 5) == 4 && f.place[a].wire[0].tx->used == 0, "so does a length of -1");
CHECK(ask(a, 0, 100, 110, 99) == 4 && ask(a, 0, 100, 110, 0) == 4 && ask(a, 0, 100, 110, 11) == 4, "and an operation that is not one of the ten");
/* addresses off the end of memory, or inside the ports: 4, and nothing copied */
{
v4_cell bad[7];
v4_wire_queue before_rx, before_tx;
unsigned op, i;
const unsigned ops[3] = { 1, 2, 5 };
bad[0] = (v4_cell)V4_NODE_WORDS - 3; /* the seven words run off the end */
bad[1] = (v4_cell)V4_NODE_WORDS;
bad[2] = -1;
bad[3] = PB + 2; /* inside the ports */
bad[4] = PB - 3; /* runs into them */
bad[5] = PB + (v4_cell)V4_PORT_HAVE; /* the last of them */
bad[6] = PB + (v4_cell)V4_PORT_HAVE - 6; /* the seven end on it */
for (op = 0; op < 3; op++)
for (k = 0; k < 7; k++) {
unsigned faults = ND(a)->n.faults;
v4_wire_reset(f.place[a].wire[0].tx);
v4_wire_reset(f.place[a].wire[0].rx);
put_msg(&ND(a)->n, 100, 110, 30, 6); /* a message waiting for look and take to find */
for (i = 0; i < 7; i++) t[i] = ND(a)->n.mem[100 + i];
for (i = 0; i < 8; i++) t[8 + i] = ND(a)->n.mem[110 + i];
CHECK(v4_wire_put(f.place[a].wire[0].rx, t, t + 8) == V4_WIRE_DONE, "(a message waits)");
memcpy(snap, ND(a)->n.mem, sizeof snap);
memcpy(&before_rx, f.place[a].wire[0].rx, sizeof before_rx);
memcpy(&before_tx, f.place[a].wire[0].tx, sizeof before_tx);
CHECK(ask(a, 0, bad[k], 110, ops[op]) == 4, "op %u with its seven words at %ld: 4", ops[op], (long)bad[k]);
CHECK(memcmp(snap, ND(a)->n.mem, sizeof snap) == 0 && ND(a)->n.faults == faults, "op %u at %ld: nothing was copied, and the node did not fault", ops[op], (long)bad[k]);
CHECK(memcmp(&before_rx, f.place[a].wire[0].rx, sizeof before_rx) == 0 && memcmp(&before_tx, f.place[a].wire[0].tx, sizeof before_tx) == 0,
"op %u at %ld: neither wire has changed", ops[op], (long)bad[k]);
}
/* a good header and a text address off the end or inside the ports: 4, and the header is not copied either */
bad[0] = (v4_cell)V4_NODE_WORDS - 10;
bad[1] = PB + 3;
bad[2] = -5;
for (k = 0; k < 3; k++) {
v4_wire_reset(f.place[a].wire[0].tx);
v4_wire_reset(f.place[a].wire[0].rx);
put_msg(&ND(a)->n, 100, 110, 30, 6);
for (i = 0; i < 7; i++) t[i] = ND(a)->n.mem[100 + i];
for (i = 0; i < 8; i++) t[8 + i] = ND(a)->n.mem[110 + i];
(void)v4_wire_put(f.place[a].wire[0].rx, t, t + 8);
memcpy(snap, ND(a)->n.mem, sizeof snap);
memcpy(&before_rx, f.place[a].wire[0].rx, sizeof before_rx);
CHECK(ask(a, 0, 300, bad[k], 2) == 4 && memcmp(snap, ND(a)->n.mem, sizeof snap) == 0 && memcmp(&before_rx, f.place[a].wire[0].rx, sizeof before_rx) == 0,
"take with its text at %ld: 4, nothing copied (not the seven words either), the message still there", (long)bad[k]);
CHECK(ask(a, 0, 100, bad[k], 5) == 4 && f.place[a].wire[0].tx->used == 0, "put with its text at %ld: 4, nothing put", (long)bad[k]);
}
/* a message with no text needs no address for it */
put_msg(&ND(a)->n, 100, 110, 0, 6);
v4_wire_reset(f.place[a].wire[0].tx);
CHECK(ask(a, 0, 100, -1, 5) == 0 && f.place[a].wire[0].tx->used == 7, "a message of no text is put with any address for its text");
v4_wire_reset(f.place[a].wire[0].tx);
}
/* the wire, look, take, first, next, drop and the mark */
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, qpool, 8);
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
CHECK(v4_fabric_wire(&f, a, 0, b, 3), "a wire for looking");
CHECK(ask(a, 0, 200, 220, 1) == 3 && ask(a, 0, 200, 220, 2) == 3 && ask(a, 0, 200, 220, 4) == 3 && ask(a, 0, 200, 220, 3) == 3 && ask(a, 5, 200, 220, 1) == 2,
"look, take, drop and move on a wire with no message answer 3, on a port with nothing 2");
put_msg(&ND(b)->n, 100, 110, 9, 1); CHECK(ask(b, 3, 100, 110, 5) == 0, "three messages are put");
put_msg(&ND(b)->n, 100, 110, 21, 2); CHECK(ask(b, 3, 100, 110, 5) == 0, "the second");
put_msg(&ND(b)->n, 100, 110, 4, 3); CHECK(ask(b, 3, 100, 110, 5) == 0, "and the third");
CHECK(ask(a, 0, 200, 220, 6) == 0 && ask(a, 0, 200, 220, 1) == 0 && ND(a)->n.mem[200] == 100 && ND(a)->n.mem[206] == 9, "first, and look: the first message's seven words");
CHECK(f.place[a].wire[0].rx->used == 10 + 13 + 8, "look took nothing");
CHECK(ask(a, 0, 200, 220, 7) == 0 && ask(a, 0, 200, 220, 1) == 0 && ND(a)->n.mem[200] == 200, "next: the second");
CHECK(ask(a, 0, 200, 220, 2) == 0 && msg_is(&ND(a)->n, 200, 220, 21, 2), "take of the second");
CHECK(ask(a, 0, 200, 220, 1) == 0 && ND(a)->n.mem[200] == 300, "the mark is at the third");
CHECK(ask(a, 0, 200, 220, 6) == 0 && ask(a, 0, 200, 220, 4) == 0 && ask(a, 0, 200, 220, 1) == 0 && ND(a)->n.mem[200] == 300, "first and drop: the first is gone, the third is left");
CHECK(ask(a, 0, 200, 220, 7) == 3 && ask(a, 0, 200, 220, 1) == 3, "next at the end answers 3 and the mark is at no message");
CHECK(ask(a, 0, 200, 220, 6) == 0 && ask(a, 0, 200, 220, 2) == 0 && msg_is(&ND(a)->n, 200, 220, 4, 3) && f.place[a].wire[0].rx->used == 0, "and the third is taken");
/* "which wires have a message": a bit to a wire; fetching it clears arrived */
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, qpool, 8);
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
c = (unsigned)v4_fabric_add(&f, &pool[2], PB);
CHECK(v4_fabric_wire(&f, a, 2, b, 0) && v4_fabric_wire(&f, a, 6, c, 1), "a node with two neighbours");
(void)v4_fabric_step(&f);
CHECK(v4_node_fetch(&ND(a)->n, P_HAVE) == 0, "with nothing on any wire, no bit");
put_msg(&ND(b)->n, 100, 110, 3, 1); CHECK(ask(b, 0, 100, 110, 5) == 0, "one neighbour puts");
put_msg(&ND(c)->n, 100, 110, 3, 1); CHECK(ask(c, 1, 100, 110, 5) == 0, "the other puts");
CHECK(f.place[a].wire[2].rx->arrived && f.place[a].wire[6].rx->arrived, "a message has arrived on each");
(void)v4_fabric_step(&f);
CHECK(v4_node_fetch(&ND(a)->n, P_HAVE) == ((1 << 2) | (1 << 6)), "the wires with a message have each their bit");
(void)v4_fabric_step(&f);
CHECK(!f.place[a].wire[2].rx->arrived && !f.place[a].wire[6].rx->arrived, "and fetching it clears what has arrived");
CHECK(v4_node_fetch(&ND(a)->n, P_HAVE) == ((1 << 2) | (1 << 6)), "though the messages are still there");
/* sleep: blocked, and the engine's clock does not move; a put to one of its wires wakes it */
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, qpool, 8);
a = loaded(0);
LIT(P_DO); O(BANG_B); LIT(8); O(STORE_B);
LIT(1); LIT(OUT); O(BANG_A); O(STORE_A);
wait_for_ever();
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 4, b, 2), "a node that sleeps and then leaves its mark");
(void)settle(100);
CHECK(ND(a)->n.doing && ND(a)->n.do_op == 8 && ND(a)->n.mem[OUT] == 0, "it is blocked at its sleep");
{
v4_uheat_t clock = ND(a)->es.anticlock;
unsigned moved = 0;
for (i = 0; i < 500; i++) moved += v4_fabric_step(&f);
CHECK(moved == 0 && ND(a)->es.anticlock == clock && ND(a)->n.doing && ND(a)->n.mem[OUT] == 0, "for 500 steps nothing happens and its clock does not move");
put_msg(&ND(b)->n, 100, 110, 3, 1);
CHECK(ask(b, 2, 100, 110, 5) == 0, "a put to one of its wires");
(void)settle(100);
CHECK(!ND(a)->n.doing && ND(a)->n.how == 0 && ND(a)->n.mem[OUT] == 1, "wakes it, and it goes on");
}
/* a node that looks, leaves the message there, fetches "which wires" and sleeps again stays asleep until a second comes */
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, qpool, 8);
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
CHECK(v4_fabric_wire(&f, a, 1, b, 4), "two nodes");
put_msg(&ND(b)->n, 100, 110, 3, 1);
CHECK(ask(b, 4, 100, 110, 5) == 0, "a message comes");
CHECK(ask(a, 0, 200, 220, 8) == 0, "a sleep with a message come is not a sleep");
CHECK(ask(a, 1, 200, 220, 6) == 0 && ask(a, 1, 200, 220, 1) == 0 && ND(a)->n.mem[200] == 100, "it looks, and leaves it there");
(void)v4_node_fetch(&ND(a)->n, P_HAVE);
v4_node_store(&ND(a)->n, P_DO, 8);
for (i = 0; i < 500; i++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.doing && f.place[a].wire[1].rx->used == 8, "it sleeps again, with the message still on the wire, and stays asleep for 500 steps");
put_msg(&ND(b)->n, 100, 110, 3, 2);
CHECK(ask(b, 4, 100, 110, 5) == 0, "a second message comes");
(void)v4_fabric_step(&f);
CHECK(!ND(a)->n.doing && ND(a)->n.how == 0, "and wakes it");
/* sleep for room */
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, qpool, 8);
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
CHECK(v4_fabric_wire(&f, a, 0, b, 3), "a writer and a reader");
fill(f.place[a].wire[0].tx, 100);
CHECK(ask(a, 0, 50, 0, 10) == 0 && ask(a, 0, 100, 0, 10) == 0 && ask(a, 0, 101, 0, 10) == 1 && ask(a, 0, 5000, 0, 10) == 1,
"room, with 100 cells free: for 50 and for 100, not for 101");
CHECK(ask(a, 5, 10, 0, 10) == 2 && ask(a, 0, -1, 0, 10) == 4, "room on a port with nothing is 2, for -1 cells 4");
CHECK(f.place[a].wire[0].tx->used == V4_WIRE_CELLS - 100 && !ND(a)->n.doing, "and it changes nothing");
(void)v4_node_fetch(&ND(a)->n, P_HAVE);
(void)v4_fabric_step(&f);
CHECK(ask(a, 0, 200, 0, 9) == -99 && ND(a)->n.doing, "sleep for 200 cells with 100 free: blocked");
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.doing, "and still blocked");
v4_wire_first(f.place[b].wire[3].rx);
CHECK(ask(b, 3, 400, 420, 4) == 0, "the reader drops one message (which takes a few cells)");
CHECK(ND(a)->n.doing || f.place[a].wire[0].tx->used + 200 <= V4_WIRE_CELLS, "woken when the reader has taken enough, not before");
while (ND(a)->n.doing && f.place[b].wire[3].rx->used > 0) {
(void)ask(b, 3, 400, 420, 4);
}
CHECK(!ND(a)->n.doing && ND(a)->n.how == 0 && f.place[a].wire[0].tx->used + 200 <= V4_WIRE_CELLS, "woken, with how it went 0");
/* woken by a message coming instead */
v4_wire_reset(f.place[a].wire[0].tx);
fill(f.place[a].wire[0].tx, 100);
(void)v4_node_fetch(&ND(a)->n, P_HAVE);
(void)v4_fabric_step(&f);
CHECK(ask(a, 0, 200, 0, 9) == -99, "asleep for room again");
put_msg(&ND(b)->n, 100, 110, 3, 1);
CHECK(ask(b, 3, 100, 110, 5) == 0, "a message comes on the other wire");
(void)v4_fabric_step(&f);
CHECK(!ND(a)->n.doing && ND(a)->n.how == 1 && f.place[a].wire[0].tx->used + 200 > V4_WIRE_CELLS, "it wakes, and how it went says 1: no room, but a message");
/* move from one of a node's wires to another */
v4_fabric_init(&f, more_places, 8);
v4_fabric_queues(&f, qpool, 8);
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
c = (unsigned)v4_fabric_add(&f, &pool[2], PB);
CHECK(v4_fabric_wire(&f, a, 0, b, 3) && v4_fabric_wire(&f, a, 1, c, 2), "a node between two");
put_msg(&ND(b)->n, 100, 110, 21, 4);
ND(b)->n.mem[103] = 9;
CHECK(ask(b, 3, 100, 110, 5) == 0 && ask(a, 0, 0, 0, 6) == 0, "a message comes on wire 0");
CHECK(ask(a, 0, 1, 0, 3) == 0, "and is moved to wire 1");
CHECK(f.place[a].wire[0].rx->used == 0 && f.place[c].wire[2].rx->used == 7 + 6, "it is off the one and on the other");
CHECK(ask(c, 2, 200, 220, 6) == 0 && ask(c, 2, 200, 220, 2) == 0 && ND(c)->n.mem[203] == 8 && msg_is_but(&ND(c)->n, 200, 220, 21, 4, 3),
"and arrives whole, its fourth word one fewer");
/* the other full: 1, and nothing moved */
put_msg(&ND(b)->n, 100, 110, 21, 4);
CHECK(ask(b, 3, 100, 110, 5) == 0 && ask(a, 0, 0, 0, 6) == 0, "another comes");
fill(f.place[a].wire[1].tx, 0);
memcpy(&copy, f.place[a].wire[0].rx, sizeof copy);
{
v4_wire_queue copy2;
memcpy(&copy2, f.place[a].wire[1].tx, sizeof copy2);
CHECK(ask(a, 0, 1, 0, 3) == 1, "moved to a wire with no room: 1");
CHECK(memcmp(&copy, f.place[a].wire[0].rx, sizeof copy) == 0 && memcmp(&copy2, f.place[a].wire[1].tx, sizeof copy2) == 0, "and neither wire has changed");
}
CHECK(ask(a, 0, 5, 0, 3) == 2 && ask(a, 0, -1, 0, 3) == 2 && ask(a, 5, 1, 0, 3) == 2, "move to a port with nothing, or from one: 2");
/* a node removed with messages waiting both ways on two wires */
v4_fabric_init(&f, more_places, 8);
v4_fabric_queues(&f, qpool, 4);
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
c = (unsigned)v4_fabric_add(&f, &pool[2], PB);
CHECK(v4_fabric_wire(&f, a, 0, b, 3) && v4_fabric_wire(&f, c, 1, b, 5), "a node in the middle with two neighbours");
put_msg(&ND(a)->n, 100, 110, 21, 1);
CHECK(ask(a, 0, 100, 110, 5) == 0 && ask(b, 3, 100, 110, 5) == 0, "a message each way on the first wire");
CHECK(ask(c, 1, 100, 110, 5) == 0 && ask(b, 5, 100, 110, 5) == 0, "and on the second");
(void)ask(a, 0, 0, 0, 7);
(void)ask(b, 3, 0, 0, 7);
{
v4_wire_queue *q0 = f.place[a].wire[0].tx, *q1 = f.place[a].wire[0].rx, *q2 = f.place[c].wire[1].tx, *q3 = f.place[c].wire[1].rx;
fill(q0, 0);
fill(q2, 0);
(void)v4_node_fetch(&ND(a)->n, P_HAVE);
(void)v4_node_fetch(&ND(c)->n, P_HAVE);
(void)v4_fabric_step(&f);
CHECK(ask(a, 0, 300, 0, 9) == -99 && ask(c, 1, 300, 0, 9) == -99, "both neighbours are asleep, waiting for room on the wire to it");
CHECK(v4_fabric_remove(&f, b) == &pool[1], "the node in the middle is removed");
(void)v4_fabric_step(&f);
CHECK(!ND(a)->n.doing && ND(a)->n.how == 2 && !ND(c)->n.doing && ND(c)->n.how == 2, "each neighbour asleep for room is woken with 2");
CHECK(q0->used == 0 && q0->mark == q0->used && q1->used == 0 && q1->mark == q1->used && q2->used == 0 && q2->mark == q2->used && q3->used == 0 && q3->mark == q3->used,
"and what waited on the four queues is let go, their marks at no message");
CHECK(f.place[a].wire[0].kind == V4_WIRE_NONE && f.place[a].wire[0].rx == 0 && f.place[a].wire[0].tx == 0, "the neighbour's wire is cut, with no queue");
CHECK(ask(a, 0, 100, 110, 5) == 2 && ask(a, 0, 100, 110, 1) == 2, "a put or a look on it is 2");
CHECK(v4_fabric_wire(&f, a, 4, c, 4) && v4_fabric_wire(&f, a, 5, c, 5) && v4_fabric_wire(&f, a, 6, c, 6) == 0,
"the queues went back to the pool: all four, so two wires and not a third");
}
/* a device's wire */
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, qpool, 8);
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
CHECK(v4_fabric_wire_device(&f, a, 4, &nothing) && v4_fabric_wire(&f, a, 1, b, 1), "a device on wire 4 of a node, and another node on wire 1");
put_msg(&ND(a)->n, 100, 110, 17, 8);
for (w = 0; w < 7; w++) h[w] = ND(a)->n.mem[100 + w];
for (w = 0; w < 5; w++) t[w] = ND(a)->n.mem[110 + w];
CHECK(v4_fabric_device_put(&f, a, 4, h, t) == 0, "the device puts a message");
CHECK(ask(a, 4, 200, 220, 6) == 0 && ask(a, 4, 200, 220, 2) == 0 && msg_is(&ND(a)->n, 200, 220, 17, 8), "and the node takes it, whole");
put_msg(&ND(a)->n, 100, 110, 3, 9);
CHECK(ask(a, 4, 100, 110, 5) == 0, "the node puts one");
memset(h, 0, sizeof h); memset(t, 0, sizeof t);
CHECK(v4_fabric_device_take(&f, a, 4, h, t, 0) == 4, "the device, with no room for its text, is told 4");
CHECK(v4_fabric_device_take(&f, a, 4, h, t, V4_WIRE_CELLS) == 0 && h[6] == 3 && h[0] == 900 && h[2] == 902, "and takes it with room");
CHECK(v4_fabric_device_take(&f, a, 4, h, t, V4_WIRE_CELLS) == 3, "no more: 3");
CHECK(v4_fabric_device_put(&f, a, 1, h, t) == 2 && v4_fabric_device_take(&f, a, 1, h, t, 1) == 2, "a wire to a node is not a device's: 2");
CHECK(v4_fabric_device_put(&f, a, 6, h, t) == 2 && v4_fabric_device_take(&f, a, 6, h, t, 1) == 2 && v4_fabric_device_put(&f, 7, 4, h, t) == 2, "nor is a port with nothing, or a node that is not there");
h[6] = 1025;
CHECK(v4_fabric_device_put(&f, a, 4, h, t) == 4, "a device cannot put what is not a message");
h[6] = 1024;
fill(f.place[a].wire[4].rx, 0);
CHECK(v4_fabric_device_put(&f, a, 4, h, t) == 1, "nor onto a full wire: 1");
/* a wire when the pool has queues for two: a third fails and changes nothing */
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, qpool, 4);
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
c = (unsigned)v4_fabric_add(&f, &pool[2], PB);
CHECK(v4_fabric_wire(&f, a, 0, b, 0) && v4_fabric_wire_device(&f, c, 0, &nothing), "two wires take the four queues");
CHECK(v4_fabric_wire(&f, a, 1, c, 1) == 0 && v4_fabric_wire_device(&f, b, 1, &nothing) == 0, "a third wire, of either kind, fails");
CHECK(f.place[a].wire[1].kind == V4_WIRE_NONE && f.place[c].wire[1].kind == V4_WIRE_NONE && f.place[b].wire[1].kind == V4_WIRE_NONE,
"and it is not there");
CHECK(f.place[a].wire[0].kind == V4_WIRE_NODE && f.place[a].wire[0].node == b && f.place[c].wire[0].kind == V4_WIRE_DEVICE,
"and what was wired still is");
CHECK(v4_fabric_wire(&f, a, 0, c, 2) && f.place[b].wire[0].kind == V4_WIRE_NONE && f.place[c].wire[2].node == a,
"a port that is wired again gives back its queues first, and gets them");
CHECK(v4_fabric_unwire(&f, a, 0) && v4_fabric_wire(&f, a, 1, b, 1), "and one that is cut gives them back");
CHECK(ask(a, 0, 100, 110, 5) == 2, "a put on the cut one: 2");
}
printf(" %d checks, %d failures\n", checks, failures);
return failures != 0;
}
+8 -3
View File
@@ -1136,19 +1136,24 @@ int main(void)
/* A flush of the output buffer that ends in a fault leaves the buffer full: what is printed next -- the
* error's own message -- must not go past its end (MESH.md 7c.6). */
{
/* The cells just after the buffer that are still plain memory: the ports follow them (host_map.h), and a
* store to a port is not a store to memory -- it blocks the node, which is seen below, as is the pointer
* itself: it is past the end only if something was put there. */
const unsigned plain = (unsigned)(PORT - (OUT_W + OUT_CELLS));
unsigned over = 0, worst = 0, at;
for (k = most > 6 ? most - 6 : 0; k <= most + 3; k++) {
char line[4 * V4_DATA_DEPTH + 16];
size_t n_at = 0;
unsigned m;
boot_bare();
for (m = 0; m < 28; m++) n.mem[OUT_W + 256 + m] = 0;
for (m = 0; m < plain; m++) n.mem[OUT_W + OUT_CELLS + m] = 0;
for (m = 0; m < k; m++) n_at += (size_t)snprintf(line + n_at, sizeof line - n_at, "1 ");
snprintf(line + n_at, sizeof line - n_at, "\n");
(void)say(line);
(void)say("WORDS\n");
for (at = 0; at < 28; at++) if (n.mem[OUT_W + 256 + at] != 0) { over++; worst = k; break; }
if (n.mem[OUT_PTR] > OUT_W + 256) { over++; worst = k; }
for (at = 0; at < plain; at++) if (n.mem[OUT_W + OUT_CELLS + at] != 0) { over++; worst = k; break; }
if (n.mem[OUT_PTR] > OUT_W + OUT_CELLS) { over++; worst = k; }
if (n.asking || n.doing) { over++; worst = k; } /* it stored to a port */
CHECK(strcmp(say("1 2 + .\n"), "3 ok\nok> ") == 0, "WORDS with %u values on the stack: the node goes on", k);
}
CHECK(over == 0, "nothing was ever put past the end of the output buffer (it was, with %u values on the stack)", worst);
+294
View File
@@ -0,0 +1,294 @@
/* 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;
}