Files
LithosAnanake/v4/system/storage.c
T
rajamesandClaude Opus 5.5 4a505a154d feat(v4.0.0): storage speaks messages -- a device over v3's block chains, common and private
What is on a storage port (v4/system/storage.c): it takes Block read,
Block write and Block data and answers Block data or Block done.  It
holds a view of a common chain, a private chain, or both; private block k
is number 2^32 - 1 - k.  Behind it is v3's block subsystem, reached
through v4/system/store_v3.c, the one v4 file that includes v3's headers.

v3's block code links here with its two device back ends, its log and
its clock, and nothing else of v3.

v4/tests/test_store.c: 36 checks at 64 bits, 35 at 32, and under the
sanitizers.  docs/v4.0.0/MESH.md 8.3, 8.4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 20:41:07 -04:00

162 lines
5.6 KiB
C

/* storage.c -- what is on a storage port. See storage.h.
* docs/v4.0.0/MESH.md section 8.
*
* Nothing here uses the C library: the bare-metal kernel links this file.
*/
#include "v4/storage.h"
void v4_store_init(v4_store *s, const v4_store_ops *ops, void *common, void *private_chain, v4_cell number)
{
unsigned i;
s->ops = ops;
s->common = common;
s->private_chain = private_chain;
s->number = number;
s->in.count = 0;
for (i = 0; i < V4_STORE_PENDING; i++) s->writing[i].used = 0;
s->head = 0;
s->count = 0;
s->out.count = 0;
s->out_at = 0;
s->out_ready = 0;
s->taken = 0;
}
/* The chain a number means and the block in it; or 0, with why in *status. */
static void *chain_for(const v4_store *s, uint64_t n, uint32_t *block, int *status)
{
*status = V4_STORE_RANGE;
if (n == 0 || n > V4_STORE_TOP) return 0;
if (s->private_chain) {
uint64_t k = V4_STORE_TOP - n, have = s->ops->blocks(s->private_chain);
if (have > V4_STORE_FIRST && k < have - V4_STORE_FIRST) { *block = (uint32_t)(V4_STORE_FIRST + k); return s->private_chain; }
}
*block = (uint32_t)n;
if (s->common) return s->common;
if (s->private_chain && n < V4_STORE_FIRST) return s->private_chain;
*status = V4_STORE_NONE;
return 0;
}
static void queue(v4_store *s, v4_cell to, int read, uint64_t block, int status)
{
unsigned at = (s->head + s->count) % V4_STORE_PENDING;
if (s->count == V4_STORE_PENDING) return; /* take does not let it come to this */
s->answer[at].to = to;
s->answer[at].read = read;
s->answer[at].block = block;
s->answer[at].status = status;
s->count++;
}
/* A block of data has come from `from`: it is written where that sender's
* BLOCK_WRITE said, if there was one. */
static int write_block(v4_store *s, v4_cell from)
{
unsigned i, k;
uint32_t block;
int status;
void *chain;
for (i = 0; i < V4_STORE_PENDING && !(s->writing[i].used && s->writing[i].from == from); i++) { }
if (i == V4_STORE_PENDING) return V4_STORE_REFUSED;
s->writing[i].used = 0;
if (v4_message_length(&s->in) != V4_STORE_BYTES) return V4_STORE_REFUSED;
chain = chain_for(s, s->writing[i].block, &block, &status);
if (!chain) return status;
for (k = 0; k < V4_STORE_BYTES; k++) s->bytes[k] = (unsigned char)v4_message_char(&s->in, k);
return s->ops->write(chain, block, s->bytes);
}
/* The message in s->in is whole. */
static void served(v4_store *s)
{
v4_cell from = v4_message_from(&s->in), type = v4_message_type(&s->in);
uint64_t block = (uint64_t)(v4_ucell)s->in.word[V4_MSG_HEADER];
unsigned i, free_at = V4_STORE_PENDING;
s->taken++;
if (v4_message_to(&s->in) != s->number) return;
if (type == V4_MSG_BLOCK_DATA) { queue(s, from, 0, 0, write_block(s, from)); return; }
if (type != V4_MSG_BLOCK_READ && type != V4_MSG_BLOCK_WRITE) return;
if (v4_message_length(&s->in) != 4) { queue(s, from, 0, 0, V4_STORE_REFUSED); return; }
if (type == V4_MSG_BLOCK_READ) { queue(s, from, 1, block, 0); return; }
/* a write: its number is kept until its block comes. A sender's second
* write before then takes the place of its first. */
for (i = 0; i < V4_STORE_PENDING; i++) {
if (s->writing[i].used && s->writing[i].from == from) { free_at = i; break; }
if (!s->writing[i].used && free_at == V4_STORE_PENDING) free_at = i;
}
if (free_at == V4_STORE_PENDING) { queue(s, from, 0, 0, V4_STORE_REFUSED); return; }
s->writing[free_at].used = 1;
s->writing[free_at].from = from;
s->writing[free_at].block = block;
}
int v4_store_take(void *self, v4_cell value)
{
v4_store *s = (v4_store *)self;
if (s->in.count == 0 && s->count == V4_STORE_PENDING) return 0;
if (v4_message_word(&s->in, value)) {
served(s);
s->in.count = 0;
}
return 1;
}
/* The oldest answer waiting becomes the message in s->out. */
static void answer(v4_store *s)
{
v4_cell to = s->answer[s->head].to;
int status = s->answer[s->head].status;
unsigned i;
if (s->answer[s->head].read) {
uint32_t block;
void *chain = chain_for(s, s->answer[s->head].block, &block, &status);
if (chain) status = s->ops->read(chain, block, s->bytes);
} else if (status == V4_STORE_OK) {
status = -1; /* a write that worked: said below */
}
s->head = (s->head + 1) % V4_STORE_PENDING;
s->count--;
s->out.word[0] = to;
s->out.word[1] = s->number;
s->out.word[3] = 0;
s->out.word[4] = 0;
s->out.word[5] = 0;
if (status == V4_STORE_OK) { /* a read that worked: the block */
s->out.word[2] = V4_MSG_BLOCK_DATA;
s->out.word[6] = (v4_cell)V4_STORE_BYTES;
for (i = 0; i < V4_STORE_BYTES / 4u; i++)
s->out.word[V4_MSG_HEADER + i] = (v4_cell)((v4_ucell)s->bytes[4u * i] | (v4_ucell)s->bytes[4u * i + 1u] << 8
| (v4_ucell)s->bytes[4u * i + 2u] << 16 | (v4_ucell)s->bytes[4u * i + 3u] << 24);
s->out.count = V4_MSG_HEADER + V4_STORE_BYTES / 4u;
} else {
s->out.word[2] = V4_MSG_BLOCK_DONE;
s->out.word[6] = 4;
s->out.word[V4_MSG_HEADER] = status < 0 ? V4_STORE_OK : status;
s->out.count = V4_MSG_HEADER + 1u;
}
s->out_at = 0;
s->out_ready = 1;
}
int v4_store_give(void *self, v4_cell *value)
{
v4_store *s = (v4_store *)self;
if (!s->out_ready) {
if (s->count == 0) return 0;
answer(s);
}
*value = s->out.word[s->out_at++];
if (s->out_at == s->out.count) s->out_ready = 0;
return 1;
}