work on network encoding and documenting eventual component interfaces

This commit is contained in:
2026-04-05 01:04:24 +02:00
parent e3959a24da
commit dad8e34cca
6 changed files with 467 additions and 33 deletions

View File

@@ -266,6 +266,23 @@ void nn_memset(void *dest, int x, size_t len) {
for(size_t i = 0; i < len; i++) out[i] = (char)x;
}
void nn_memreverse(void *dest, size_t len) {
size_t mid = len/2;
char *bytes = (char *)dest;
for(size_t i = 0; i < mid; i++) {
size_t j = len - i - 1;
char tmp = bytes[i];
bytes[i] = bytes[j];
bytes[j] = tmp;
}
}
bool nn_isLittleEndian() {
union {char c; size_t x;} test;
test.x = 1;
return test.c == 1;
}
// taken from https://wiki.osdev.org/CRC32
// OSDev wiki is really useful sometimes
// TODO: maybe allow one that uses compiler intrinsics
@@ -911,10 +928,13 @@ static const nn_HashContext nn_methodHasher = {
.handler = (nn_HashHandler *)nn_methodHash,
};
// currently just a wrapper around a context
// but will be way more in the future
typedef struct nn_Universe {
nn_Context ctx;
void *userdata;
// 0 for unbounded
size_t memoryLimit;
// 0 for unbounded
size_t storageLimit;
} nn_Universe;
typedef struct nn_ComponentEntry {
@@ -1030,10 +1050,13 @@ typedef struct nn_Computer {
char *users[NN_MAX_USERS];
} nn_Computer;
nn_Universe *nn_createUniverse(nn_Context *ctx) {
nn_Universe *nn_createUniverse(nn_Context *ctx, void *userdata) {
nn_Universe *u = nn_alloc(ctx, sizeof(nn_Universe));
if(u == NULL) return NULL;
u->ctx = *ctx;
u->userdata = userdata;
u->memoryLimit = 0;
u->storageLimit = 0;
return u;
}
@@ -1042,6 +1065,38 @@ void nn_destroyUniverse(nn_Universe *universe) {
nn_free(&ctx, universe, sizeof(nn_Universe));
}
void *nn_getUniverseData(nn_Universe *universe) {
return universe->userdata;
}
size_t nn_getUniverseMemoryLimit(nn_Universe *universe) {
return universe->memoryLimit;
}
void nn_setUniverseMemoryLimit(nn_Universe *universe, size_t limit) {
universe->memoryLimit = limit;
}
size_t nn_limitMemory(nn_Universe *universe, size_t memory) {
if(universe->memoryLimit == 0) return memory;
if(memory > universe->memoryLimit) memory = universe->memoryLimit;
return memory;
}
size_t nn_getUniverseStorageLimit(nn_Universe *universe) {
return universe->storageLimit;
}
void nn_setUniverseStorageLimit(nn_Universe *universe, size_t limit) {
universe->storageLimit = limit;
}
size_t nn_limitStorage(nn_Universe *universe, size_t storage) {
if(universe->memoryLimit == 0) return storage;
if(storage > universe->memoryLimit) storage = universe->storageLimit;
return storage;
}
double nn_default_energyHandler(void *state, nn_Computer *computer, double amount) {
(void)state;
(void)amount;
@@ -1062,6 +1117,8 @@ size_t nn_ramSizes[8] = {
nn_Computer *nn_createComputer(nn_Universe *universe, void *userdata, const char *address, size_t totalMemory, size_t maxComponents, size_t maxDevices) {
nn_Context *ctx = &universe->ctx;
totalMemory = nn_limitMemory(universe, totalMemory);
nn_Computer *c = nn_alloc(ctx, sizeof(nn_Computer));
if(c == NULL) return NULL;
@@ -2491,8 +2548,8 @@ const nn_Drive nn_defaultSSDs[4] = {
.sectorSize = 512,
.platterCount = 2,
.cacheLineSize = 2,
.readsPerTick = 20,
.writesPerTick = 10,
.readsPerTick = 10,
.writesPerTick = 5,
.rpm = 0,
.onlySpinForwards = false,
.dataEnergyCost = 64.0 / NN_MiB,
@@ -2502,8 +2559,8 @@ const nn_Drive nn_defaultSSDs[4] = {
.sectorSize = 512,
.platterCount = 4,
.cacheLineSize = 4,
.readsPerTick = 30,
.writesPerTick = 15,
.readsPerTick = 15,
.writesPerTick = 7,
.rpm = 0,
.onlySpinForwards = false,
.dataEnergyCost = 128.0 / NN_MiB,
@@ -2513,8 +2570,8 @@ const nn_Drive nn_defaultSSDs[4] = {
.sectorSize = 512,
.platterCount = 8,
.cacheLineSize = 8,
.readsPerTick = 40,
.writesPerTick = 20,
.readsPerTick = 20,
.writesPerTick = 10,
.rpm = 0,
.onlySpinForwards = false,
.dataEnergyCost = 256.0 / NN_MiB,
@@ -2524,8 +2581,8 @@ const nn_Drive nn_defaultSSDs[4] = {
.sectorSize = 512,
.platterCount = 16,
.cacheLineSize = 16,
.readsPerTick = 60,
.writesPerTick = 30,
.readsPerTick = 30,
.writesPerTick = 15,
.rpm = 0,
.onlySpinForwards = false,
.dataEnergyCost = 512.0 / NN_MiB,
@@ -2537,8 +2594,8 @@ const nn_Drive nn_floppySSD = {
.sectorSize = 512,
.platterCount = 1,
.cacheLineSize = 2,
.readsPerTick = 10,
.writesPerTick = 5,
.readsPerTick = 5,
.writesPerTick = 2,
.rpm = 0,
.onlySpinForwards = true,
.dataEnergyCost = 16.0 / NN_MiB,
@@ -2553,6 +2610,7 @@ const nn_ScreenConfig nn_defaultScreens[4] = {
.paletteColors = 0,
.editableColors = 0,
.features = NN_SCRF_NONE,
.energyPerPixel = 0.05,
},
NN_INIT(nn_ScreenConfig) {
.maxWidth = 80,
@@ -2562,6 +2620,7 @@ const nn_ScreenConfig nn_defaultScreens[4] = {
.paletteColors = 16,
.editableColors = 0,
.features = NN_SCRF_MOUSE | NN_SCRF_TOUCHINVERTED,
.energyPerPixel = 0.05,
},
NN_INIT(nn_ScreenConfig) {
.maxWidth = 160,
@@ -2571,6 +2630,7 @@ const nn_ScreenConfig nn_defaultScreens[4] = {
.paletteColors = 256,
.editableColors = 16,
.features = NN_SCRF_MOUSE | NN_SCRF_TOUCHINVERTED | NN_SCRF_PRECISE | NN_SCRF_EDITABLECOLORS,
.energyPerPixel = 0.05,
},
NN_INIT(nn_ScreenConfig) {
.maxWidth = 240,
@@ -2580,6 +2640,7 @@ const nn_ScreenConfig nn_defaultScreens[4] = {
.paletteColors = 256,
.editableColors = 256,
.features = NN_SCRF_NONE | NN_SCRF_EDITABLECOLORS,
.energyPerPixel = 0.05,
},
};
@@ -3280,6 +3341,117 @@ nn_Exit nn_pushLClipboard(nn_Computer *computer, const char *keyboardAddress, co
return nn_pushSignal(computer, 3);
}
nn_Exit nn_pushRedstoneChanged(nn_Computer *computer, const char *redstoneAddress, int side, int oldValue, int newValue, int color);
nn_Exit nn_pushMotion(nn_Computer *computer, double relX, double relY, double relZ, const char *entityName);
typedef enum nn_NetworkValueTag {
NN_NETVAL_NULL = 0x00,
NN_NETVAL_TRUE = 0x01,
NN_NETVAL_FALSE = 0x02,
NN_NETVAL_NUM = 0x03,
NN_NETVAL_STR = 0x04,
NN_NETVAL_RESOURCE = 0x05,
NN_NETVAL_TABLE = 0x06,
} nn_NetworkValueTag;
static size_t nn_sizeOfNetworkValue(nn_Value val);
static size_t nn_sizeOfNetworkContents(nn_Value *vals, size_t len) {
size_t s = 0;
for(size_t i = 0; i < len; i++) s += nn_sizeOfNetworkValue(vals[i]);
return s;
}
static size_t nn_sizeOfNetworkValue(nn_Value val) {
// 1-byte tag, + value-dependant encoding
size_t n = 1;
switch(val.type) {
case NN_VAL_NULL:
case NN_VAL_BOOL:
break;
case NN_VAL_NUM:
n += sizeof(double);
break;
case NN_VAL_STR:
n += sizeof(size_t) + val.string->len;
break;
case NN_VAL_USERDATA:
n += sizeof(size_t);
break;
case NN_VAL_TABLE:
n += sizeof(size_t) + nn_sizeOfNetworkContents(val.table->vals, val.table->len);
break;
}
return n;
}
static size_t nn_encodeNetworkValue(nn_Value val, char *buf) {
size_t n = 0;
switch(val.type) {
case NN_VAL_NULL:
*buf = NN_NETVAL_NULL;
return 1;
case NN_VAL_BOOL:
*buf = val.boolean ? NN_NETVAL_TRUE : NN_NETVAL_FALSE;
return 1;
case NN_VAL_NUM:
*buf = NN_NETVAL_NUM;
nn_memcpy(buf + 1, &val.number, sizeof(double));
return 1 + sizeof(double);
case NN_VAL_STR:
*buf = NN_NETVAL_STR;
nn_memcpy(buf + 1, &val.string->len, sizeof(size_t));
nn_memcpy(buf + 1 + sizeof(size_t), val.string->data, val.string->len);
return 1 + sizeof(size_t) + val.string->len;
case NN_VAL_USERDATA:
*buf = NN_NETVAL_RESOURCE;
nn_memcpy(buf + 1, &val.userdataIdx, sizeof(size_t));
return 1 + sizeof(size_t);
case NN_VAL_TABLE:
*buf = NN_NETVAL_TABLE;
n = 1;
nn_memcpy(buf + n, &val.table->len, sizeof(size_t));
n += sizeof(size_t);
for(size_t i = 0; i < val.table->len; i++) {
n += nn_encodeNetworkValue(val.table->vals[i], buf + n);
}
return n;
}
*buf = NN_NETVAL_NULL;
return 1;
}
nn_Exit nn_encodeNetworkContents(nn_Computer *computer, nn_EncodedNetworkContents *contents, size_t valueCount) {
if(computer->stackSize < valueCount) return NN_EBELOWSTACK;
nn_Value *vals = computer->callstack + computer->stackSize - valueCount;
size_t len = nn_sizeOfNetworkContents(vals, valueCount);
contents->ctx = &computer->universe->ctx;
contents->valueCount = valueCount;
contents->buflen = len;
contents->buf = nn_alloc(contents->ctx, len);
if(contents->buf == NULL) return NN_ENOMEM;
nn_memset(contents->buf, 0, len);
size_t n = 0;
for(size_t i = 0; i < valueCount; i++) {
n += nn_encodeNetworkValue(vals[i], contents->buf + n);
}
return NN_OK;
}
nn_Exit nn_copyNetworkContents(nn_Context *ctx, nn_EncodedNetworkContents *contents, const char *buf, size_t buflen, size_t valueCount);
void nn_dropNetworkContents(nn_EncodedNetworkContents *contents) {
nn_free(contents->ctx, contents->buf, contents->buflen);
}
nn_Exit nn_pushNetworkContents(nn_Computer *computer, const nn_EncodedNetworkContents *contents);
nn_Exit nn_pushModemMessage(nn_Computer *computer, const char *modemAddress, const char *sender, int port, double distance, const nn_EncodedNetworkContents *contents);
typedef enum nn_EENum {
NN_EENUM_GETSIZE,
NN_EENUM_GETDATASIZE,
@@ -3860,6 +4032,22 @@ nn_Component *nn_createFilesystem(nn_Universe *universe, const char *address, co
return c;
}
bool nn_mergeFilesystems(nn_Filesystem *merged, const nn_Filesystem *fs, size_t len) {
if(len == 0) return false;
*merged = fs[0];
for(size_t i = 1; i < len; i++) {
merged->readsPerTick += fs[i].readsPerTick;
merged->writesPerTick += fs[i].writesPerTick;
if(merged->maxReadSize < fs[i].maxReadSize) merged->maxReadSize = fs[i].maxReadSize;
merged->dataEnergyCost += fs[i].dataEnergyCost;
merged->spaceTotal += fs[i].spaceTotal;
}
merged->readsPerTick /= len;
merged->writesPerTick /= len;
merged->dataEnergyCost /= len;
return true;
}
static void nn_drive_seekPenalty(nn_Computer *C, size_t lastSector, size_t newSector, const nn_Drive *drive) {
// Check if SSD
if(drive->rpm == 0) return;
@@ -4064,6 +4252,36 @@ nn_Component *nn_createDrive(nn_Universe *universe, const char *address, const n
return c;
}
bool nn_mergeDrives(nn_Drive *merged, const nn_Drive *drives, size_t len) {
if(len == 0) return false;
*merged = drives[0];
for(size_t i = 1; i < len; i++) {
nn_Drive d = drives[i];
// invalid SSD/HDD combo
if(d.rpm == 0 && merged->rpm != 0) return false;
if(d.rpm != 0 && merged->rpm == 0) return false;
// conflicting sector sizes
if(d.sectorSize != merged->sectorSize) return false;
if(d.rpm != 0) {
if(d.onlySpinForwards && !merged->onlySpinForwards) return false;
if(!d.onlySpinForwards && merged->onlySpinForwards) return false;
}
merged->readsPerTick += d.readsPerTick;
merged->writesPerTick += d.writesPerTick;
merged->dataEnergyCost += d.dataEnergyCost;
merged->rpm += d.rpm;
merged->capacity += d.capacity;
merged->cacheLineSize += d.cacheLineSize;
merged->platterCount += d.platterCount;
}
merged->readsPerTick /= len;
merged->writesPerTick /= len;
merged->dataEnergyCost /= len;
merged->rpm /= len;
return true;
}
typedef enum nn_ScreenNum {
NN_SCRNUM_ISON,
NN_SCRNUM_TURNON,
@@ -4656,14 +4874,16 @@ static nn_Exit nn_gpuHandler(nn_ComponentRequest *req) {
g.copy.h = nn_tointeger(C, 3);
g.copy.tx = nn_tointeger(C, 4);
g.copy.ty = nn_tointeger(C, 5);
// prevent issues
if(g.copy.w < 0) g.copy.w = 0;
if(g.copy.w > g.gpu->maxWidth) g.copy.w = g.gpu->maxWidth;
if(g.copy.h < 0) g.copy.h = 0;
if(g.copy.h > g.gpu->maxHeight) g.copy.h = g.gpu->maxHeight;
e = cls->handler(&g);
if(e) return e;
req->returnCount = 1;
nn_costComponent(C, req->compAddress, cls->gpu.copyPerTick);
// prevent issues
if(g.copy.tx < 1) g.copy.tx = 1;
if(g.copy.ty < 1) g.copy.ty = 1;
nn_removeEnergy(C, cls->gpu.energyPerWrite * g.copy.tx * g.copy.ty);
nn_removeEnergy(C, cls->gpu.energyPerWrite * g.copy.w * g.copy.h);
return nn_pushbool(C, true);
}
// fill
@@ -4681,15 +4901,17 @@ static nn_Exit nn_gpuHandler(nn_ComponentRequest *req) {
g.fill.y = nn_tointeger(C, 1);
g.fill.w = nn_tointeger(C, 2);
g.fill.h = nn_tointeger(C, 3);
// prevent issues
if(g.fill.w < 0) g.fill.w = 0;
if(g.fill.w > g.gpu->maxWidth) g.fill.w = g.gpu->maxWidth;
if(g.fill.h < 0) g.fill.h = 0;
if(g.fill.h > g.gpu->maxHeight) g.fill.h = g.gpu->maxHeight;
g.fill.codepoint = nn_unicode_firstCodepoint(
nn_tostring(C, 4));
e = cls->handler(&g);
if(e) return e;
req->returnCount = 1;
nn_costComponent(C, req->compAddress, cls->gpu.fillPerTick);
// prevent issues
if(g.fill.w < 1) g.fill.w = 1;
if(g.fill.h < 1) g.fill.h = 1;
nn_removeEnergy(C, (g.fill.codepoint == ' ' ? cls->gpu.energyPerClear : cls->gpu.energyPerWrite) * g.fill.w * g.fill.h);
return nn_pushbool(C, true);
}
@@ -4798,6 +5020,15 @@ static nn_Exit nn_gpuHandler(nn_ComponentRequest *req) {
g.bitblt.src = nn_tointeger(C, 5);
g.bitblt.fromCol = nn_tointeger(C, 6);
g.bitblt.fromRow = nn_tointeger(C, 7);
if(g.bitblt.w < 0) g.copy.w = 0;
if(g.bitblt.w > g.gpu->maxWidth) g.bitblt.w = g.gpu->maxWidth;
if(g.bitblt.h < 0) g.copy.h = 0;
if(g.bitblt.h > g.gpu->maxHeight) g.bitblt.h = g.gpu->maxHeight;
if(g.bitblt.dst == 0 || g.bitblt.src == 0) {
// taxed as a copy
nn_costComponent(C, req->compAddress, g.gpu->copyPerTick);
nn_removeEnergy(C, g.gpu->energyPerWrite * g.bitblt.w * g.bitblt.h);
}
e = cls->handler(&g);
if(e) return e;
req->returnCount = 1;