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:
co-authored by
Claude Opus 5.5
parent
50be766e7b
commit
d5cd64605d
+3
-3
@@ -260,7 +260,7 @@ unsigned v4_exec_step_word(v4_node *n, v4_exec_state *es, v4_heat *h)
|
||||
|
||||
/* D-14: a node that faulted with no handler has stopped. A node that
|
||||
* has written to its port is blocked until it has been served. */
|
||||
if (n->stopped || n->asking || n->reading) return 0;
|
||||
if (n->stopped || n->asking || n->reading || n->doing) return 0;
|
||||
if (es->resume && es->resume_faults != n->faults) es->resume = 0; /* it faulted while blocked: P is the handler */
|
||||
if (es->resume) {
|
||||
/* unblocked: the rest of the word it was in */
|
||||
@@ -305,8 +305,8 @@ unsigned v4_exec_step_word(v4_node *n, v4_exec_state *es, v4_heat *h)
|
||||
}
|
||||
executed++;
|
||||
|
||||
if (n->asking) {
|
||||
/* The store was to the port. The node is blocked here; what is
|
||||
if (n->asking || n->doing) {
|
||||
/* The store was to the port, or to Do. The node is blocked here; what is
|
||||
* left of the word waits until it has been served. */
|
||||
if (slot + 1 < V4_SLOT_COUNT) {
|
||||
es->resume = 1;
|
||||
|
||||
+236
-4
@@ -9,7 +9,10 @@ static void clear_place(v4_place *p)
|
||||
unsigned k;
|
||||
p->node = 0;
|
||||
p->asleep = 0;
|
||||
for (k = 0; k < V4_PORTS; k++) { p->wire[k].kind = V4_WIRE_NONE; p->wire[k].node = 0; p->wire[k].port = 0; p->wire[k].device = 0; }
|
||||
for (k = 0; k < V4_PORTS; k++) {
|
||||
p->wire[k].kind = V4_WIRE_NONE; p->wire[k].node = 0; p->wire[k].port = 0; p->wire[k].device = 0;
|
||||
p->wire[k].rx = 0; p->wire[k].tx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void v4_fabric_init(v4_fabric *f, v4_place *places, unsigned capacity)
|
||||
@@ -17,6 +20,10 @@ void v4_fabric_init(v4_fabric *f, v4_place *places, unsigned capacity)
|
||||
unsigned i;
|
||||
f->place = places;
|
||||
f->capacity = capacity;
|
||||
f->queue = 0;
|
||||
f->queue_count = 0;
|
||||
f->queue_free = 0;
|
||||
f->queue_first = 0;
|
||||
f->gone_error = 0;
|
||||
f->interrupt_error = 0;
|
||||
for (i = 0; i < capacity; i++) clear_place(&places[i]);
|
||||
@@ -65,34 +72,102 @@ static int there(const v4_fabric *f, unsigned id, unsigned port)
|
||||
return id < f->capacity && f->place[id].node && port < V4_PORTS;
|
||||
}
|
||||
|
||||
void v4_fabric_queues(v4_fabric *f, v4_wire_queue *pool, unsigned count)
|
||||
{
|
||||
unsigned i;
|
||||
f->queue = pool;
|
||||
f->queue_count = count;
|
||||
f->queue_free = count;
|
||||
f->queue_first = count ? 1 : 0;
|
||||
for (i = 0; i < count; i++) {
|
||||
v4_wire_reset(&pool[i]);
|
||||
pool[i].head = i + 1 < count ? i + 2 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* a queue that is not on a wire; the fabric has been given some, and there is one */
|
||||
static v4_wire_queue *take_queue(v4_fabric *f)
|
||||
{
|
||||
v4_wire_queue *q = &f->queue[f->queue_first - 1];
|
||||
f->queue_first = q->head;
|
||||
f->queue_free--;
|
||||
v4_wire_reset(q);
|
||||
return q;
|
||||
}
|
||||
|
||||
/* a queue comes off a wire: what was waiting on it is let go */
|
||||
static void give_queue(v4_fabric *f, v4_wire_queue *q)
|
||||
{
|
||||
if (!q) return;
|
||||
v4_wire_reset(q);
|
||||
q->head = f->queue_first;
|
||||
f->queue_first = (unsigned)(q - f->queue) + 1;
|
||||
f->queue_free++;
|
||||
}
|
||||
|
||||
/* Can the ports a and b be wired: if the fabric has been given queues, are
|
||||
* there two once what those ports are wired to now has been given back? */
|
||||
static int queues_for(const v4_fabric *f, unsigned a, unsigned pa, unsigned b, unsigned pb)
|
||||
{
|
||||
const v4_wire_queue *seen[4];
|
||||
unsigned i, k, n = 0, back = 0;
|
||||
|
||||
if (!f->queue) return 1;
|
||||
seen[n++] = f->place[a].wire[pa].rx; seen[n++] = f->place[a].wire[pa].tx;
|
||||
if (b < f->capacity) { seen[n++] = f->place[b].wire[pb].rx; seen[n++] = f->place[b].wire[pb].tx; }
|
||||
for (i = 0; i < n; i++) {
|
||||
if (!seen[i]) continue;
|
||||
for (k = 0; k < i && seen[k] != seen[i]; k++) { }
|
||||
if (k == i) back++;
|
||||
}
|
||||
return f->queue_free + back >= 2;
|
||||
}
|
||||
|
||||
/* cut what is on a port, at both ends */
|
||||
static void cut(v4_fabric *f, unsigned a, unsigned pa)
|
||||
{
|
||||
v4_wire *w = &f->place[a].wire[pa];
|
||||
if (w->kind == V4_WIRE_NODE && w->node < f->capacity && w->port < V4_PORTS) {
|
||||
v4_wire *other = &f->place[w->node].wire[w->port];
|
||||
if (other->kind == V4_WIRE_NODE && other->node == a && other->port == pa) { other->kind = V4_WIRE_NONE; other->device = 0; }
|
||||
if (other->kind == V4_WIRE_NODE && other->node == a && other->port == pa) {
|
||||
other->kind = V4_WIRE_NONE; other->device = 0;
|
||||
other->rx = 0; other->tx = 0; /* the same two queues as w's */
|
||||
}
|
||||
}
|
||||
if (f->queue) { give_queue(f, w->rx); give_queue(f, w->tx); }
|
||||
w->kind = V4_WIRE_NONE;
|
||||
w->device = 0;
|
||||
w->rx = 0;
|
||||
w->tx = 0;
|
||||
}
|
||||
|
||||
int v4_fabric_wire(v4_fabric *f, unsigned a, unsigned pa, unsigned b, unsigned pb)
|
||||
{
|
||||
if (!there(f, a, pa) || !there(f, b, pb) || (a == b && pa == pb)) return 0;
|
||||
if (!queues_for(f, a, pa, b, pb)) return 0;
|
||||
cut(f, a, pa);
|
||||
cut(f, b, pb);
|
||||
f->place[a].wire[pa].kind = V4_WIRE_NODE; f->place[a].wire[pa].node = b; f->place[a].wire[pa].port = pb;
|
||||
f->place[b].wire[pb].kind = V4_WIRE_NODE; f->place[b].wire[pb].node = a; f->place[b].wire[pb].port = pa;
|
||||
if (f->queue) {
|
||||
v4_wire_queue *ab = take_queue(f), *ba = take_queue(f);
|
||||
f->place[a].wire[pa].tx = ab; f->place[b].wire[pb].rx = ab;
|
||||
f->place[b].wire[pb].tx = ba; f->place[a].wire[pa].rx = ba;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int v4_fabric_wire_device(v4_fabric *f, unsigned a, unsigned pa, const v4_device *device)
|
||||
{
|
||||
if (!there(f, a, pa) || !device) return 0;
|
||||
if (!queues_for(f, a, pa, f->capacity, 0)) return 0;
|
||||
cut(f, a, pa);
|
||||
f->place[a].wire[pa].kind = V4_WIRE_DEVICE;
|
||||
f->place[a].wire[pa].device = device;
|
||||
if (f->queue) {
|
||||
f->place[a].wire[pa].rx = take_queue(f); /* from the device */
|
||||
f->place[a].wire[pa].tx = take_queue(f); /* to it */
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -103,6 +178,25 @@ int v4_fabric_unwire(v4_fabric *f, unsigned a, unsigned pa)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int v4_fabric_device_put(v4_fabric *f, unsigned node, unsigned port, const v4_cell *header7, const v4_cell *text)
|
||||
{
|
||||
const v4_wire *w;
|
||||
if (!there(f, node, port)) return V4_WIRE_NO_ONE;
|
||||
w = &f->place[node].wire[port];
|
||||
if (w->kind != V4_WIRE_DEVICE || !w->rx) return V4_WIRE_NO_ONE;
|
||||
return v4_wire_put(w->rx, header7, text);
|
||||
}
|
||||
|
||||
int v4_fabric_device_take(v4_fabric *f, unsigned node, unsigned port, v4_cell *header7, v4_cell *text, unsigned text_cap)
|
||||
{
|
||||
const v4_wire *w;
|
||||
if (!there(f, node, port)) return V4_WIRE_NO_ONE;
|
||||
w = &f->place[node].wire[port];
|
||||
if (w->kind != V4_WIRE_DEVICE || !w->tx) return V4_WIRE_NO_ONE;
|
||||
v4_wire_first(w->tx); /* the device takes them in the order they were put */
|
||||
return v4_wire_take(w->tx, header7, text, text_cap);
|
||||
}
|
||||
|
||||
v4_fabric_node *v4_fabric_remove(v4_fabric *f, unsigned id)
|
||||
{
|
||||
v4_fabric_node *storage;
|
||||
@@ -189,21 +283,159 @@ static void look_about(v4_fabric *f, unsigned a)
|
||||
v4_node_port_status(&f->place[a].node->n, writers, readers);
|
||||
}
|
||||
|
||||
/* ---- whole messages: what a node asked for by storing to Do (MESH.md 7d.3) ---- */
|
||||
|
||||
/* 1 if `count` words from `addr` are memory that is not the ports, or the
|
||||
* addresses after them, of this node: they may be copied to and from. */
|
||||
static int span_ok(const v4_node *n, v4_cell addr, unsigned count)
|
||||
{
|
||||
if (count == 0) return 1;
|
||||
if (count > V4_NODE_WORDS || addr < 0 || (v4_ucell)addr > (v4_ucell)(V4_NODE_WORDS - count)) return 0;
|
||||
if (n->port >= 0 && addr <= n->port + (v4_cell)V4_PORT_HAVE && addr + (v4_cell)count > n->port) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* the queue of messages that come to the node's wire, and that go from it; 0: no one is on it */
|
||||
static v4_wire_queue *coming(const v4_fabric *f, unsigned id, unsigned port)
|
||||
{
|
||||
return port < V4_PORTS ? f->place[id].wire[port].rx : 0;
|
||||
}
|
||||
static v4_wire_queue *going(const v4_fabric *f, unsigned id, unsigned port)
|
||||
{
|
||||
return port < V4_PORTS ? f->place[id].wire[port].tx : 0;
|
||||
}
|
||||
|
||||
/* has a message come to any of the node's wires since it last fetched which have one */
|
||||
static int arrived(const v4_fabric *f, unsigned id)
|
||||
{
|
||||
unsigned k;
|
||||
for (k = 0; k < V4_PORTS; k++) {
|
||||
const v4_wire_queue *q = f->place[id].wire[k].rx;
|
||||
if (q && q->arrived) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Node `id` is doing an operation. Returns 1 and puts how it went in
|
||||
* *how, or returns 0: it waits (8 and 9 only). Nothing is copied, put or
|
||||
* taken unless the answer is 0 -- done. */
|
||||
static int operate(v4_fabric *f, unsigned id, v4_cell *how)
|
||||
{
|
||||
v4_node *n = &f->place[id].node->n;
|
||||
v4_wire_queue *rx = coming(f, id, n->wire), *tx = going(f, id, n->wire), *to;
|
||||
v4_cell a = n->wire_a, b = n->wire_b, h[V4_WIRE_HEADER];
|
||||
unsigned cells;
|
||||
|
||||
switch (n->do_op) {
|
||||
case 1: /* look */
|
||||
if (!rx) { *how = V4_WIRE_NO_ONE; break; }
|
||||
if (rx->mark >= rx->used) { *how = V4_WIRE_NO_MESSAGE; break; }
|
||||
if (!span_ok(n, a, V4_WIRE_HEADER)) { *how = V4_WIRE_NOT_A_MESSAGE; break; }
|
||||
*how = v4_wire_look(rx, &n->mem[a]);
|
||||
break;
|
||||
case 2: /* take */
|
||||
if (!rx) { *how = V4_WIRE_NO_ONE; break; }
|
||||
if (rx->mark >= rx->used) { *how = V4_WIRE_NO_MESSAGE; break; }
|
||||
(void)v4_wire_look(rx, h);
|
||||
cells = v4_wire_cells(h[6]) - V4_WIRE_HEADER;
|
||||
if (!span_ok(n, a, V4_WIRE_HEADER) || !span_ok(n, b, cells)) { *how = V4_WIRE_NOT_A_MESSAGE; break; }
|
||||
*how = v4_wire_take(rx, &n->mem[a], cells ? &n->mem[b] : &n->mem[0], cells);
|
||||
break;
|
||||
case 3: /* move */
|
||||
if (!rx) { *how = V4_WIRE_NO_ONE; break; }
|
||||
if (rx->mark >= rx->used) { *how = V4_WIRE_NO_MESSAGE; break; }
|
||||
to = a >= 0 && a < (v4_cell)V4_PORTS ? going(f, id, (unsigned)a) : 0;
|
||||
*how = to ? v4_wire_move(rx, to) : V4_WIRE_NO_ONE;
|
||||
break;
|
||||
case 4: /* drop */
|
||||
*how = rx ? v4_wire_drop(rx) : V4_WIRE_NO_ONE;
|
||||
break;
|
||||
case 5: /* put */
|
||||
if (!tx) { *how = V4_WIRE_NO_ONE; break; }
|
||||
if (!span_ok(n, a, V4_WIRE_HEADER)) { *how = V4_WIRE_NOT_A_MESSAGE; break; }
|
||||
cells = v4_wire_cells(n->mem[a + 6]);
|
||||
if (cells == 0 || !span_ok(n, b, cells - V4_WIRE_HEADER)) { *how = V4_WIRE_NOT_A_MESSAGE; break; }
|
||||
*how = v4_wire_put(tx, &n->mem[a], cells > V4_WIRE_HEADER ? &n->mem[b] : &n->mem[0]);
|
||||
break;
|
||||
case 6: /* first */
|
||||
if (rx) v4_wire_first(rx);
|
||||
*how = rx ? V4_WIRE_DONE : V4_WIRE_NO_ONE;
|
||||
break;
|
||||
case 7: /* next */
|
||||
*how = rx ? v4_wire_next(rx) : V4_WIRE_NO_ONE;
|
||||
break;
|
||||
case 8: /* sleep */
|
||||
if (!arrived(f, id)) return 0;
|
||||
*how = V4_WIRE_DONE;
|
||||
break;
|
||||
case 9: /* sleep for room */
|
||||
if (!tx) { *how = V4_WIRE_NO_ONE; break; }
|
||||
if (a < 0) { *how = V4_WIRE_NOT_A_MESSAGE; break; }
|
||||
if (a <= (v4_cell)V4_WIRE_CELLS && v4_wire_room(tx, (unsigned)a) == V4_WIRE_DONE) { *how = V4_WIRE_DONE; break; }
|
||||
if (!arrived(f, id)) return 0;
|
||||
*how = V4_WIRE_NO_ROOM;
|
||||
break;
|
||||
case 10: /* room */
|
||||
if (!tx) { *how = V4_WIRE_NO_ONE; break; }
|
||||
if (a < 0) { *how = V4_WIRE_NOT_A_MESSAGE; break; }
|
||||
*how = a <= (v4_cell)V4_WIRE_CELLS ? v4_wire_room(tx, (unsigned)a) : V4_WIRE_NO_ROOM;
|
||||
break;
|
||||
default: /* not an operation */
|
||||
*how = V4_WIRE_NOT_A_MESSAGE;
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsigned v4_fabric_step(v4_fabric *f)
|
||||
{
|
||||
unsigned i, done = 0;
|
||||
unsigned i, k, done = 0;
|
||||
|
||||
/* which of its wires have a message waiting, for each node to fetch */
|
||||
for (i = 0; i < f->capacity; i++)
|
||||
if (awake(f, i)) {
|
||||
unsigned have = 0;
|
||||
for (k = 0; k < V4_PORTS; k++) {
|
||||
const v4_wire_queue *q = f->place[i].wire[k].rx;
|
||||
if (q && q->used > 0) have |= 1u << k;
|
||||
}
|
||||
v4_node_have(&f->place[i].node->n, have);
|
||||
}
|
||||
|
||||
/* a node that is blocked fetches nothing, and is looked about for when
|
||||
* it is next to execute */
|
||||
for (i = 0; i < f->capacity; i++)
|
||||
if (awake(f, i)) {
|
||||
const v4_node *n = &f->place[i].node->n;
|
||||
if (!n->stopped && !n->asking && !(n->reading && !n->given)) look_about(f, i);
|
||||
if (!n->stopped && !n->asking && !n->doing && !(n->reading && !n->given)) look_about(f, i);
|
||||
}
|
||||
|
||||
for (i = 0; i < f->capacity; i++)
|
||||
if (awake(f, i)) done += v4_exec_step_word(&f->place[i].node->n, &f->place[i].node->es, &f->place[i].node->h);
|
||||
|
||||
/* a node that fetched which wires have a message has seen what had come */
|
||||
for (i = 0; i < f->capacity; i++)
|
||||
if (awake(f, i) && f->place[i].node->n.have_fetched) {
|
||||
for (k = 0; k < V4_PORTS; k++)
|
||||
if (f->place[i].wire[k].rx) f->place[i].wire[k].rx->arrived = 0;
|
||||
f->place[i].node->n.have_fetched = 0;
|
||||
}
|
||||
|
||||
/* the operations on the wires, each whole; the two that wait are looked at last, so that
|
||||
* what every other node has put or taken this step has been, whatever their numbers */
|
||||
for (i = 0; i < f->capacity; i++)
|
||||
if (awake(f, i) && f->place[i].node->n.doing && f->place[i].node->n.do_op != 8 && f->place[i].node->n.do_op != 9) {
|
||||
v4_cell how;
|
||||
(void)operate(f, i, &how);
|
||||
v4_node_done(&f->place[i].node->n, how);
|
||||
done++;
|
||||
}
|
||||
for (i = 0; i < f->capacity; i++)
|
||||
if (awake(f, i) && f->place[i].node->n.doing) {
|
||||
v4_cell how;
|
||||
if (operate(f, i, &how)) { v4_node_done(&f->place[i].node->n, how); done++; }
|
||||
}
|
||||
|
||||
for (i = 0; i < f->capacity; i++)
|
||||
if (awake(f, i) && f->place[i].node->n.asking) done += hand_over_write(f, i);
|
||||
for (i = 0; i < f->capacity; i++)
|
||||
|
||||
+36
-2
@@ -87,13 +87,17 @@ void v4_node_store(v4_node *n, v4_cell addr, v4_cell value)
|
||||
* neighbour has taken it. */
|
||||
{
|
||||
int port = v4_node_port_index(n, addr);
|
||||
if (port == (int)V4_PORT_WIRE) { n->wire = (value >= 0 && value < (v4_cell)V4_PORTS) ? (unsigned)value : V4_PORTS; return; }
|
||||
if (port == (int)V4_PORT_A) { n->wire_a = value; return; }
|
||||
if (port == (int)V4_PORT_B) { n->wire_b = value; return; }
|
||||
if (port == (int)V4_PORT_DO) { n->do_op = value; n->doing = 1; return; }
|
||||
if (port >= 0 && port < (int)V4_PORTS) {
|
||||
n->request = value;
|
||||
n->ask_port = (unsigned)port;
|
||||
n->asking = 1;
|
||||
return;
|
||||
}
|
||||
if (port >= 0) { v4_node_fault(n, V4_FAULT_ADDRESS, addr); return; } /* "any" and "which port" are not written to */
|
||||
if (port >= 0) { v4_node_fault(n, V4_FAULT_ADDRESS, addr); return; } /* "any", "which port", How and Have are not written to */
|
||||
}
|
||||
/* The stack registers: a store empties the stack. -1 when not attached. */
|
||||
if (addr == n->dstack_reg) { v4_dstack_clear(&n->ds); return; }
|
||||
@@ -122,6 +126,12 @@ v4_cell v4_node_fetch(v4_node *n, v4_cell addr)
|
||||
if (port == (int)V4_PORTS + 1) return (v4_cell)n->last_from;
|
||||
if (port == (int)V4_PORTS + 2) { n->since_look = 0; return (v4_cell)n->writers; }
|
||||
if (port == (int)V4_PORTS + 3) { n->since_look = 0; return (v4_cell)n->readers; }
|
||||
if (port == (int)V4_PORT_WIRE) return (v4_cell)n->wire;
|
||||
if (port == (int)V4_PORT_A) return n->wire_a;
|
||||
if (port == (int)V4_PORT_B) return n->wire_b;
|
||||
if (port == (int)V4_PORT_DO) return n->do_op;
|
||||
if (port == (int)V4_PORT_HOW) return n->how;
|
||||
if (port == (int)V4_PORT_HAVE) { n->have_fetched = 1; return (v4_cell)n->have; }
|
||||
if (port >= 0) {
|
||||
/* the word its neighbour wrote; v4_node_read_ready has said it is there */
|
||||
if (!n->given) return 0;
|
||||
@@ -180,6 +190,14 @@ void v4_node_port_attach(v4_node *n, v4_cell base)
|
||||
n->writers = 0;
|
||||
n->readers = 0;
|
||||
n->since_look = 0;
|
||||
n->wire = 0;
|
||||
n->wire_a = 0;
|
||||
n->wire_b = 0;
|
||||
n->doing = 0;
|
||||
n->do_op = 0;
|
||||
n->how = 0;
|
||||
n->have = 0;
|
||||
n->have_fetched = 0;
|
||||
}
|
||||
|
||||
void v4_node_port_status(v4_node *n, unsigned writers, unsigned readers)
|
||||
@@ -190,7 +208,7 @@ void v4_node_port_status(v4_node *n, unsigned writers, unsigned readers)
|
||||
|
||||
int v4_node_port_index(const v4_node *n, v4_cell addr)
|
||||
{
|
||||
if (n->port < 0 || addr < n->port || addr > n->port + (v4_cell)V4_PORTS + 3) return -1;
|
||||
if (n->port < 0 || addr < n->port || addr > n->port + (v4_cell)V4_PORT_HAVE) return -1;
|
||||
return (int)(addr - n->port);
|
||||
}
|
||||
|
||||
@@ -225,6 +243,22 @@ void v4_node_port_served(v4_node *n)
|
||||
n->since_look++;
|
||||
}
|
||||
|
||||
int v4_node_doing(const v4_node *n)
|
||||
{
|
||||
return n->doing;
|
||||
}
|
||||
|
||||
void v4_node_done(v4_node *n, v4_cell how)
|
||||
{
|
||||
n->how = how;
|
||||
n->doing = 0;
|
||||
}
|
||||
|
||||
void v4_node_have(v4_node *n, unsigned mask)
|
||||
{
|
||||
n->have = mask;
|
||||
}
|
||||
|
||||
unsigned v4_node_words_since_look(const v4_node *n)
|
||||
{
|
||||
return n->since_look;
|
||||
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/* wire.c -- a queue of whole messages. See wire.h.
|
||||
*
|
||||
* Nothing here uses the C library: the bare-metal kernel links this file.
|
||||
*/
|
||||
#include "v4/wire.h"
|
||||
#include "v4/message.h"
|
||||
|
||||
void v4_wire_reset(v4_wire_queue *q)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < V4_WIRE_CELLS; i++) q->cell[i] = 0;
|
||||
q->head = 0;
|
||||
q->used = 0;
|
||||
q->mark = 0;
|
||||
q->arrived = 0;
|
||||
}
|
||||
|
||||
unsigned v4_wire_cells(v4_cell length)
|
||||
{
|
||||
if (length < 0 || length > (v4_cell)V4_MSG_MAX_CHARS) return 0;
|
||||
return V4_WIRE_HEADER + ((unsigned)length + 3u) / 4u;
|
||||
}
|
||||
|
||||
/* the cell `off` cells from the front */
|
||||
static unsigned slot(const v4_wire_queue *q, unsigned off)
|
||||
{
|
||||
unsigned s = q->head + off;
|
||||
return s >= V4_WIRE_CELLS ? s - V4_WIRE_CELLS : s;
|
||||
}
|
||||
|
||||
/* the cells the message `off` cells from the front takes */
|
||||
static unsigned size_at(const v4_wire_queue *q, unsigned off)
|
||||
{
|
||||
return v4_wire_cells(q->cell[slot(q, off + 6u)]);
|
||||
}
|
||||
|
||||
int v4_wire_room(const v4_wire_queue *q, unsigned cells)
|
||||
{
|
||||
return cells <= V4_WIRE_CELLS - q->used ? V4_WIRE_DONE : V4_WIRE_NO_ROOM;
|
||||
}
|
||||
|
||||
/* `n` cells, already checked to fit, onto the back */
|
||||
static unsigned back(v4_wire_queue *q, unsigned n)
|
||||
{
|
||||
unsigned at = q->used;
|
||||
q->used += n;
|
||||
q->arrived = 1;
|
||||
return at;
|
||||
}
|
||||
|
||||
int v4_wire_put(v4_wire_queue *q, const v4_cell *header7, const v4_cell *text)
|
||||
{
|
||||
unsigned n = v4_wire_cells(header7[6]), at, i;
|
||||
if (n == 0) return V4_WIRE_NOT_A_MESSAGE;
|
||||
if (v4_wire_room(q, n) != V4_WIRE_DONE) return V4_WIRE_NO_ROOM;
|
||||
at = back(q, n);
|
||||
for (i = 0; i < V4_WIRE_HEADER; i++) q->cell[slot(q, at + i)] = header7[i];
|
||||
for (; i < n; i++) q->cell[slot(q, at + i)] = text[i - V4_WIRE_HEADER];
|
||||
return V4_WIRE_DONE;
|
||||
}
|
||||
|
||||
void v4_wire_first(v4_wire_queue *q)
|
||||
{
|
||||
q->mark = 0;
|
||||
}
|
||||
|
||||
int v4_wire_next(v4_wire_queue *q)
|
||||
{
|
||||
if (q->mark >= q->used) return V4_WIRE_NO_MESSAGE;
|
||||
q->mark += size_at(q, q->mark);
|
||||
return q->mark < q->used ? V4_WIRE_DONE : V4_WIRE_NO_MESSAGE;
|
||||
}
|
||||
|
||||
int v4_wire_look(const v4_wire_queue *q, v4_cell *header7)
|
||||
{
|
||||
unsigned i;
|
||||
if (q->mark >= q->used) return V4_WIRE_NO_MESSAGE;
|
||||
for (i = 0; i < V4_WIRE_HEADER; i++) header7[i] = q->cell[slot(q, q->mark + i)];
|
||||
return V4_WIRE_DONE;
|
||||
}
|
||||
|
||||
/* the marked message is off the queue; the mark is at the one that followed */
|
||||
static void remove_marked(v4_wire_queue *q)
|
||||
{
|
||||
unsigned n = size_at(q, q->mark), i;
|
||||
if (q->mark == 0) {
|
||||
q->head = slot(q, n);
|
||||
} else {
|
||||
for (i = q->mark; i + n < q->used; i++) q->cell[slot(q, i)] = q->cell[slot(q, i + n)];
|
||||
}
|
||||
q->used -= n;
|
||||
}
|
||||
|
||||
int v4_wire_take(v4_wire_queue *q, v4_cell *header7, v4_cell *text, unsigned text_cap)
|
||||
{
|
||||
unsigned n, i;
|
||||
if (q->mark >= q->used) return V4_WIRE_NO_MESSAGE;
|
||||
n = size_at(q, q->mark);
|
||||
if (n - V4_WIRE_HEADER > text_cap) return V4_WIRE_NOT_A_MESSAGE;
|
||||
for (i = 0; i < V4_WIRE_HEADER; i++) header7[i] = q->cell[slot(q, q->mark + i)];
|
||||
for (; i < n; i++) text[i - V4_WIRE_HEADER] = q->cell[slot(q, q->mark + i)];
|
||||
remove_marked(q);
|
||||
return V4_WIRE_DONE;
|
||||
}
|
||||
|
||||
int v4_wire_drop(v4_wire_queue *q)
|
||||
{
|
||||
if (q->mark >= q->used) return V4_WIRE_NO_MESSAGE;
|
||||
remove_marked(q);
|
||||
return V4_WIRE_DONE;
|
||||
}
|
||||
|
||||
int v4_wire_move(v4_wire_queue *from, v4_wire_queue *to)
|
||||
{
|
||||
unsigned n, at, i;
|
||||
if (from->mark >= from->used) return V4_WIRE_NO_MESSAGE;
|
||||
n = size_at(from, from->mark);
|
||||
if (from == to || v4_wire_room(to, n) != V4_WIRE_DONE) return V4_WIRE_NO_ROOM;
|
||||
at = back(to, n);
|
||||
for (i = 0; i < n; i++) to->cell[slot(to, at + i)] = from->cell[slot(from, from->mark + i)];
|
||||
to->cell[slot(to, at + 3u)] = (v4_cell)(from->cell[slot(from, from->mark + 3u)] - 1);
|
||||
remove_marked(from);
|
||||
return V4_WIRE_DONE;
|
||||
}
|
||||
Reference in New Issue
Block a user