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>
176 lines
9.9 KiB
C
176 lines
9.9 KiB
C
/* test_store.c -- what is on a storage port, spoken to in messages.
|
|
* docs/v4.0.0/MESH.md sections 8.3 and 8.4.
|
|
*
|
|
* No node: the test is the neighbour. The chains are v3's block subsystem
|
|
* (v3/src/block_subsystem.c), two of them, each with its fast RAM and a
|
|
* device of 64 blocks.
|
|
*/
|
|
#include "v4/storage.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "block_subsystem.h"
|
|
|
|
extern void sf_time_init(void);
|
|
extern void log_set_level(int level);
|
|
|
|
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)
|
|
|
|
#define STORE 90
|
|
#define DEVICE_BLOCKS 64u
|
|
#define RAM_BYTES ((size_t)BLK_RAM_BLOCKS * BLK_FORTH_SIZE)
|
|
|
|
static blk_chain_t *make_chain(blk_chain_t *c)
|
|
{
|
|
blk_chain_t *was = blk_chain_select(c);
|
|
int ok = blk_subsys_init((uint8_t *)calloc(1, RAM_BYTES), RAM_BYTES) == BLK_OK
|
|
&& blk_subsys_add_raw_device((uint8_t *)calloc(DEVICE_BLOCKS, BLK_FORTH_SIZE), DEVICE_BLOCKS) == BLK_OK;
|
|
blk_chain_select(was);
|
|
return ok ? c : 0;
|
|
}
|
|
|
|
/* give the device a whole message; 1 if every word was taken */
|
|
static int send(v4_store *s, const v4_message *m)
|
|
{
|
|
unsigned i;
|
|
for (i = 0; i < m->count; i++) if (!v4_store_take(s, m->word[i])) return 0;
|
|
return 1;
|
|
}
|
|
static int ask(v4_store *s, v4_cell from, v4_cell type, v4_cell block)
|
|
{
|
|
v4_message m;
|
|
v4_message_text(&m, STORE, from, type, "\0\0\0\0", 4);
|
|
m.word[V4_MSG_HEADER] = block;
|
|
return send(s, &m);
|
|
}
|
|
static int data(v4_store *s, v4_cell from, char fill)
|
|
{
|
|
static char block[V4_STORE_BYTES];
|
|
v4_message m;
|
|
memset(block, fill, sizeof block);
|
|
v4_message_text(&m, STORE, from, V4_MSG_BLOCK_DATA, block, sizeof block);
|
|
return send(s, &m);
|
|
}
|
|
/* the device's next answer, whole; 0 if it has none */
|
|
static int answer(v4_store *s, v4_message *m)
|
|
{
|
|
v4_cell w;
|
|
m->count = 0;
|
|
if (!v4_store_give(s, &w)) return 0;
|
|
while (!v4_message_word(m, w)) if (!v4_store_give(s, &w)) return 0;
|
|
return 1;
|
|
}
|
|
static int is_done(const v4_message *m, v4_cell to, v4_cell status)
|
|
{
|
|
return v4_message_to(m) == to && v4_message_from(m) == STORE && v4_message_type(m) == V4_MSG_BLOCK_DONE
|
|
&& v4_message_length(m) == 4 && m->word[V4_MSG_HEADER] == status;
|
|
}
|
|
static int is_data(const v4_message *m, v4_cell to, char fill)
|
|
{
|
|
unsigned i;
|
|
if (v4_message_to(m) != to || v4_message_from(m) != STORE || v4_message_type(m) != V4_MSG_BLOCK_DATA
|
|
|| v4_message_length(m) != V4_STORE_BYTES) return 0;
|
|
for (i = 0; i < V4_STORE_BYTES; i++) if (v4_message_char(m, i) != fill) return 0;
|
|
return 1;
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
static v4_store both, common_only, private_only;
|
|
static v4_message m;
|
|
blk_chain_t *common, *own;
|
|
v4_cell top = (v4_cell)(v4_ucell)V4_STORE_TOP;
|
|
|
|
printf("v4 storage tests: V4_CELL_BITS=%d\n", V4_CELL_BITS);
|
|
sf_time_init();
|
|
log_set_level(-1);
|
|
common = make_chain(blk_chain_default());
|
|
own = make_chain(blk_chain_new());
|
|
CHECK(common && own && common != own, "two chains are made");
|
|
if (!common || !own) return 1;
|
|
v4_store_init(&both, &v4_store_v3, common, own, STORE);
|
|
v4_store_init(&common_only, &v4_store_v3, common, 0, STORE);
|
|
v4_store_init(&private_only, &v4_store_v3, 0, own, STORE);
|
|
|
|
/* ---- a block is written and read ---- */
|
|
CHECK(!answer(&both, &m), "with nothing asked there is nothing to read");
|
|
CHECK(ask(&both, 11, V4_MSG_BLOCK_WRITE, 2050) && !answer(&both, &m), "a write is not answered until its block has come");
|
|
CHECK(data(&both, 11, 'A') && answer(&both, &m) && is_done(&m, 11, V4_STORE_OK), "then it is answered done, to who sent it, from storage");
|
|
CHECK(ask(&both, 12, V4_MSG_BLOCK_READ, 2050) && answer(&both, &m) && is_data(&m, 12, 'A'), "another reads what was written");
|
|
CHECK(ask(&common_only, 12, V4_MSG_BLOCK_READ, 2050) && answer(&common_only, &m) && is_data(&m, 12, 'A'), "and so does one on another port of the same chain");
|
|
CHECK(ask(&both, 12, V4_MSG_BLOCK_WRITE, 20) && data(&both, 12, 'R') && answer(&both, &m) && is_done(&m, 12, V4_STORE_OK)
|
|
&& ask(&common_only, 13, V4_MSG_BLOCK_READ, 20) && answer(&common_only, &m) && is_data(&m, 13, 'R'), "a block below 2048 is the common chain's fast RAM");
|
|
|
|
/* ---- private blocks, from the top down ---- */
|
|
CHECK(ask(&both, 13, V4_MSG_BLOCK_WRITE, top) && data(&both, 13, 'P') && answer(&both, &m) && is_done(&m, 13, V4_STORE_OK), "the top number is written");
|
|
CHECK(ask(&both, 13, V4_MSG_BLOCK_READ, top) && answer(&both, &m) && is_data(&m, 13, 'P'), "and read back");
|
|
CHECK(ask(&private_only, 13, V4_MSG_BLOCK_READ, top) && answer(&private_only, &m) && is_data(&m, 13, 'P'), "it is the private chain's");
|
|
{
|
|
blk_chain_t *was = blk_chain_select(own);
|
|
uint8_t *b = blk_get_buffer((uint32_t)V4_STORE_FIRST, 0);
|
|
CHECK(b && b[0] == 'P' && b[1023] == 'P', "private block 0 is that chain's block 2048");
|
|
blk_chain_select(common);
|
|
b = blk_get_buffer((uint32_t)V4_STORE_FIRST, 0);
|
|
CHECK(b && b[0] != 'P', "and is not in the common chain");
|
|
blk_chain_select(was);
|
|
}
|
|
CHECK(ask(&both, 13, V4_MSG_BLOCK_WRITE, top - (v4_cell)(DEVICE_BLOCKS - 1)) && data(&both, 13, 'L') && answer(&both, &m) && is_done(&m, 13, V4_STORE_OK), "the last private block is written");
|
|
CHECK(ask(&both, 13, V4_MSG_BLOCK_READ, top - (v4_cell)DEVICE_BLOCKS) && answer(&both, &m) && is_done(&m, 13, V4_STORE_RANGE), "one below the private blocks is no block of the common chain");
|
|
CHECK(ask(&common_only, 12, V4_MSG_BLOCK_READ, top) && answer(&common_only, &m) && is_done(&m, 12, V4_STORE_RANGE), "with no private chain the top number is no such block");
|
|
CHECK(ask(&private_only, 13, V4_MSG_BLOCK_READ, 2050) && answer(&private_only, &m) && is_done(&m, 13, V4_STORE_NONE), "with no common chain a common number has no storage");
|
|
CHECK(ask(&private_only, 13, V4_MSG_BLOCK_WRITE, 20) && data(&private_only, 13, 'r') && answer(&private_only, &m) && is_done(&m, 13, V4_STORE_OK)
|
|
&& ask(&private_only, 13, V4_MSG_BLOCK_READ, 20) && answer(&private_only, &m) && is_data(&m, 13, 'r'), "and a number below 2048 is the private chain's fast RAM");
|
|
CHECK(ask(&common_only, 12, V4_MSG_BLOCK_READ, 20) && answer(&common_only, &m) && is_data(&m, 12, 'R'), "which is not the common chain's");
|
|
|
|
/* ---- numbers that are no block ---- */
|
|
CHECK(ask(&both, 11, V4_MSG_BLOCK_READ, 0) && answer(&both, &m) && is_done(&m, 11, V4_STORE_RANGE), "0 is no block");
|
|
CHECK(ask(&both, 11, V4_MSG_BLOCK_READ, (v4_cell)(V4_STORE_FIRST + DEVICE_BLOCKS)) && answer(&both, &m) && is_done(&m, 11, V4_STORE_RANGE), "one past the end of the common chain is no block");
|
|
CHECK(ask(&both, 11, V4_MSG_BLOCK_WRITE, (v4_cell)(V4_STORE_FIRST + DEVICE_BLOCKS)) && data(&both, 11, 'X') && answer(&both, &m) && is_done(&m, 11, V4_STORE_RANGE), "nor can it be written");
|
|
#if V4_CELL_BITS > 32
|
|
CHECK(ask(&both, 11, V4_MSG_BLOCK_READ, (v4_cell)0x100000000LL) && answer(&both, &m) && is_done(&m, 11, V4_STORE_RANGE), "a number 32 bits do not hold is no block");
|
|
CHECK(ask(&both, 11, V4_MSG_BLOCK_READ, -1) && answer(&both, &m) && is_done(&m, 11, V4_STORE_RANGE), "nor is a negative one, on cells wider than 32 bits");
|
|
#else
|
|
CHECK(top == -1, "on 32-bit cells the top number is -1");
|
|
#endif
|
|
|
|
/* ---- data nobody announced ---- */
|
|
CHECK(data(&both, 14, 'Z') && answer(&both, &m) && is_done(&m, 14, V4_STORE_REFUSED), "a block of data with no write before it is refused");
|
|
CHECK(ask(&both, 12, V4_MSG_BLOCK_READ, 2050) && answer(&both, &m) && is_data(&m, 12, 'A'), "and nothing was written");
|
|
CHECK(ask(&both, 11, V4_MSG_BLOCK_WRITE, 2051) && data(&both, 11, 'B') && answer(&both, &m) && is_done(&m, 11, V4_STORE_OK)
|
|
&& data(&both, 11, 'C') && answer(&both, &m) && is_done(&m, 11, V4_STORE_REFUSED)
|
|
&& ask(&both, 11, V4_MSG_BLOCK_READ, 2051) && answer(&both, &m) && is_data(&m, 11, 'B'), "a write is for one block of data and no more");
|
|
|
|
/* ---- two senders at once ---- */
|
|
CHECK(ask(&both, 11, V4_MSG_BLOCK_WRITE, 2060) && ask(&both, 12, V4_MSG_BLOCK_WRITE, 2061)
|
|
&& data(&both, 12, 'b') && data(&both, 11, 'a'), "two writes are begun before either's block comes, and the blocks come the other way round");
|
|
CHECK(answer(&both, &m) && is_done(&m, 12, V4_STORE_OK) && answer(&both, &m) && is_done(&m, 11, V4_STORE_OK), "each is answered, in the order the blocks came");
|
|
CHECK(ask(&both, 11, V4_MSG_BLOCK_READ, 2060) && ask(&both, 12, V4_MSG_BLOCK_READ, 2061), "a second request is taken while the answer to the first has not been read");
|
|
CHECK(answer(&both, &m) && is_data(&m, 11, 'a') && answer(&both, &m) && is_data(&m, 12, 'b'), "and each block is its own sender's");
|
|
|
|
/* ---- what is not for storage ---- */
|
|
{
|
|
v4_message other;
|
|
unsigned long before = both.taken;
|
|
v4_message_text(&other, 55, 11, V4_MSG_BLOCK_READ, "\0\0\0\0", 4);
|
|
other.word[V4_MSG_HEADER] = 2050;
|
|
CHECK(send(&both, &other) && both.taken == before + 1 && !answer(&both, &m), "a message for another number is taken and let go");
|
|
v4_message_text(&other, STORE, 11, V4_MSG_TEXT, "1 2 +", 5);
|
|
CHECK(send(&both, &other) && !answer(&both, &m), "and so is text");
|
|
}
|
|
|
|
/* ---- what v3 wrote reads through the port ---- */
|
|
{
|
|
blk_chain_t *was = blk_chain_select(common);
|
|
uint8_t *b = blk_get_buffer(2070, 1);
|
|
CHECK(b != 0, "v3 gives a buffer for block 2070");
|
|
if (b) { memset(b, 'V', BLK_FORTH_SIZE); CHECK(blk_update(2070) == BLK_OK && blk_flush(2070) == BLK_OK, "and v3 writes it"); }
|
|
blk_chain_select(was);
|
|
CHECK(ask(&both, 11, V4_MSG_BLOCK_READ, 2070) && answer(&both, &m) && is_data(&m, 11, 'V'), "what v3's own calls wrote is read through the port");
|
|
CHECK(blk_chain_select(0) == was, "the chain selected is the one that was, after the device has been used");
|
|
}
|
|
|
|
printf(" %d checks, %d failures\n", checks, failures);
|
|
return failures ? 1 : 0;
|
|
}
|