erase the old version

This commit is contained in:
2026-03-15 18:22:01 +01:00
parent dcafbd3e21
commit 59f7dd1492
48 changed files with 1700 additions and 13708 deletions

78
TODO.md
View File

@@ -1,51 +1,65 @@
# Code quality stuff
# For MVP functionality
- Literally rewrite the whole thing.
- Use the same namespacing style everywhere, that being
`nn_<class>_<method>` for functions related to "classes",
`nn_<function>` for static functions,
`nn_<type>_t` for types.
- Get rid of `nn_address`
- Make a lot more stuff const
- Rework to API to be much more future-proof to reduce potential breaking changes.
- `drive` component
- volatile filesystem
- volatile drive
- device info
# Parity with Vanilla OC (only the stuff that makes sense for an emulator)
# To re-evaluate
- `hologram` component
- More stack manipulation functions to allow libraries to have better APIs.
- Having a copy of the context stored directly in requests instead of having to use getComputerContext, as it simplifies the APIs and allows them
to be made more portable.
- Exposing more context wrappers than just the memory allocation functions.
- Exposing more internal functions that may be useful to the user to prevent pointless rewriting and duplicate machine code.
# Vanilla components needed
Not everything OC has (as a few of them are really MC-centered) but most of it.
- `data` component (note: maybe add hamming code support?)
- `modem` component
- `tunnel` component
- `relay` component (note: OCDoc still refers to it as `access_point`)
- `computer` component
- `data` component (with error correction codes and maybe synthesizing audio)
- `geolyzer` component
- `net_splitter` component
- `redstone` component
- `internet` component
- `motion_sensor` component
- `printer3d` component (note: maybe add signal for when printer completes?)
- `sign` component
- `microcontroller` component
- `hologram` component
- `disk_drive` component
- `note_block` component
# Bugfixes
- Do a huge audit at some point
- `nn_unicode_charWidth` appears to be bugged, look into that.
- validate against moving `a/b` into `a` or moving `a` into `a/b` in the filesystem component.
# The extra components
# Custom components needed
- `oled` component (OLED screen, a store of draw commands and resolution from NN's perspective)
- `ipu` component, an Image Processing Unit. Can bind with `oled`s, and issues said draw commands
- `vt`, a virtual terminal with ANSI-like escapes. (and a function to get its resolution)
- `vt`, a combination of a GPU, a screen and a keyboard with limited functionality. Offers direct operations on the video memory
- (maybe) `qpu` component, a Quantum Processing Unit for quantum computing
- `radio_controller` and `radio_tower` components, for radio telecommunications
- (maybe) `clock` component for arbitrary precision time-keeping
- `led` component for LED matrixes and/or LED lights
- `speaker` component, allows playing audio by asking for binary samples and pushing a signal when it needs more
- `microphone` component, allows reading audio from nearby sources
- `tape_drive` component, compatible with Computronics, except maybe with proper seek times and support for multiple tapes
- `tape_drive` component, compatible with Computronics
- `cd_reader` and `cd_writer` components, to work with CDs
- `serial` component, for serial communications with other devices (USB?)
- `iron_noteblock` component
- `colorful_lamp` component
# Internal changes
# To make it good
- use more arenas!!!!!!!
- make sure OOMs are recoverable
- rework some interfaces to use pre-allocated or stack-allocated memory more
- use dynamic arrays for signals (and maybe components), but still keep the maximums to prevent memory hogging
- setup an extensive testing system to find bugs easier
- optimize the codebase by using globals instead of universe userdata
- use compiler hints to let the optimizer make the code even faster
- (maybe) rework a bunch of internal structures to use tagged unions over vtables (such as components). This may make certain APIs unnecessary or slightly
different. This can improve performance at the cost of making the codebase more complex
- make more stuff const if it can be. Gotta help out the optimizer
- write a bunch of unit tests to ensure the public API works correctly
- ensure OOMs are recoverable
- do a hude audit for bugs at some point
# To make it fast
NOTE: we're mostly bottlenecked by the architecture (typically a Lua VM) and the intentional bottlenecking from call costs.
- make signals use a circular buffer instead of a simple array
- use a hashmap for components (and device info), this may require reworking how iterating over them is handled

View File

@@ -1,65 +0,0 @@
# For MVP functionality
- `drive` component
- volatile filesystem
- volatile drive
- device info
# To re-evaluate
- More stack manipulation functions to allow libraries to have more APIs.
- Having a copy of the context stored directly in requests instead of having to use getComputerContext, as it simplifies the APIs and allows them
to be made more portable.
- Exposing more context wrappers than just the memory allocation functions.
- Exposing more internal functions that may be useful to the user to prevent pointless rewriting and duplicate machine code.
# Vanilla components needed
Not everything OC has (as a few of them are really MC-centered) but most of it.
- `data` component (note: maybe add hamming code support?)
- `modem` component
- `tunnel` component
- `relay` component (note: OCDoc still refers to it as `access_point`)
- `computer` component
- `geolyzer` component
- `net_splitter` component
- `redstone` component
- `motion_sensor` component
- `printer3d` component (note: maybe add signal for when printer completes?)
- `sign` component
- `microcontroller` component
- `hologram` component
- `disk_drive` component
- `note_block` component
# Custom components needed
- `oled` component (OLED screen, a store of draw commands and resolution from NN's perspective)
- `ipu` component, an Image Processing Unit. Can bind with `oled`s, and issues said draw commands
- `vt`, a combination of a GPU, a screen and a keyboard with limited functionality. Offers direct operations on the video memory
- (maybe) `qpu` component, a Quantum Processing Unit for quantum computing
- `radio_controller` and `radio_tower` components, for radio telecommunications
- (maybe) `clock` component for arbitrary precision time-keeping
- `led` component for LED matrixes and/or LED lights
- `speaker` component, allows playing audio by asking for binary samples and pushing a signal when it needs more
- `microphone` component, allows reading audio from nearby sources
- `tape_drive` component, compatible with Computronics
- `cd_reader` and `cd_writer` components, to work with CDs
- `serial` component, for serial communications with other devices (USB?)
- `iron_noteblock` component
- `colorful_lamp` component
# To make it good
- make more stuff const if it can be. Gotta help out the optimizer
- write a bunch of unit tests to ensure the public API works correctly
- ensure OOMs are recoverable
- do a hude audit for bugs at some point
# To make it fast
NOTE: we're mostly bottlenecked by the architecture (typically a Lua VM) and the intentional bottlenecking from call costs.
- make signals use a circular buffer instead of a simple array
- use a hashmap for components (and device info), this may require reworking how iterating over them is handled

File diff suppressed because it is too large Load Diff

View File

@@ -1,150 +0,0 @@
#include "neonucleus.h"
#include "component.h"
nn_componentTable *nn_newComponentTable(nn_Alloc *alloc, const char *typeName, void *userdata, nn_componentConstructor *constructor, nn_componentDestructor *destructor) {
nn_componentTable *table = nn_alloc(alloc, sizeof(nn_componentTable));
if(table == NULL) {
return NULL;
}
table->name = nn_strdup(alloc, typeName);
if(table->name == NULL) {
nn_dealloc(alloc, table, sizeof(nn_componentTable));
return NULL;
}
table->userdata = userdata;
table->constructor = constructor;
table->destructor = destructor;
table->methodCount = 0;
table->alloc = *alloc;
return table;
}
void nn_destroyComponentTable(nn_componentTable *table) {
nn_Alloc alloc = table->alloc;
nn_deallocStr(&alloc, table->name);
for(nn_size_t i = 0; i < table->methodCount; i++) {
nn_method_t method = table->methods[i];
nn_deallocStr(&alloc, method.name);
nn_deallocStr(&alloc, method.doc);
}
nn_dealloc(&alloc, table, sizeof(nn_componentTable));
}
nn_method_t *nn_defineMethod(nn_componentTable *table, const char *methodName, nn_componentMethod *methodFunc, const char *methodDoc) {
if(table->methodCount == NN_MAX_METHODS) return NULL;
nn_method_t method;
method.method = methodFunc;
method.name = nn_strdup(&table->alloc, methodName);
if(method.name == NULL) return NULL;
method.direct = true;
method.doc = nn_strdup(&table->alloc, methodDoc);
if(method.doc == NULL) {
nn_deallocStr(&table->alloc, method.name);
return NULL;
}
method.userdata = NULL;
method.condition = NULL;
table->methods[table->methodCount] = method;
nn_method_t *ptr = table->methods + table->methodCount;
table->methodCount++;
return ptr;
}
void nn_method_setDirect(nn_method_t *method, nn_bool_t direct) {
method->direct = direct;
}
void nn_method_setUserdata(nn_method_t *method, void *userdata) {
method->userdata = userdata;
}
void nn_method_setCondition(nn_method_t *method, nn_componentMethodCondition_t *condition) {
method->condition = condition;
}
const char *nn_getTableMethod(nn_componentTable *table, nn_size_t idx, nn_bool_t *outDirect) {
if(idx >= table->methodCount) return NULL;
nn_method_t method = table->methods[idx];
if(outDirect != NULL) *outDirect = method.direct;
return method.name;
}
const char *nn_methodDoc(nn_componentTable *table, const char *methodName) {
for(nn_size_t i = 0; i < table->methodCount; i++) {
if(nn_strcmp(table->methods[i].name, methodName) == 0) {
return table->methods[i].doc;
}
}
return NULL;
}
static nn_bool_t nni_checkMethodEnabled(nn_method_t method, void *statePtr) {
if(method.condition == NULL) return true;
return method.condition(statePtr, method.userdata);
}
nn_bool_t nn_isMethodEnabled(nn_component *component, const char *methodName) {
nn_componentTable *table = component->table;
for(nn_size_t i = 0; i < table->methodCount; i++) {
if(nn_strcmp(table->methods[i].name, methodName) == 0) {
nn_method_t method = table->methods[i];
return nni_checkMethodEnabled(method, component->statePtr);
}
}
return false;
}
nn_computer *nn_getComputerOfComponent(nn_component *component) {
return component->computer;
}
nn_address nn_getComponentAddress(nn_component *component) {
return component->address;
}
int nn_getComponentSlot(nn_component *component) {
return component->slot;
}
nn_componentTable *nn_getComponentTable(nn_component *component) {
return component->table;
}
const char *nn_getComponentType(nn_componentTable *table) {
return table->name;
}
void *nn_getComponentUserdata(nn_component *component) {
return component->statePtr;
}
nn_bool_t nn_invokeComponentMethod(nn_component *component, const char *name) {
nn_componentTable *table = component->table;
for(nn_size_t i = 0; i < table->methodCount; i++) {
nn_method_t method = table->methods[i];
if(nn_strcmp(method.name, name) == 0) {
nn_callCost(component->computer, NN_CALL_COST);
if(!method.direct) {
nn_triggerIndirect(component->computer);
}
if(!nni_checkMethodEnabled(method, component->statePtr)) {
return false; // pretend it's gone
}
method.method(component->statePtr, method.userdata, component, component->computer);
return true;
}
}
// no such method
return false;
}
void nn_simulateBufferedIndirect(nn_component *component, double amount, double amountPerTick) {
double maximum = 100.0;
double x = amount * maximum / amountPerTick;
component->indirectBufferProgress += x;
if(component->indirectBufferProgress >= maximum) {
component->indirectBufferProgress = 0;
nn_triggerIndirect(component->computer);
}
}

View File

@@ -1,35 +0,0 @@
#ifndef NEONUCLEUS_COMPONENT_H
#define NEONUCLEUS_COMPONENT_H
#include "neonucleus.h"
#include "computer.h"
typedef struct nn_method_t {
char *name;
nn_componentMethod *method;
void *userdata;
char *doc;
nn_bool_t direct;
nn_componentMethodCondition_t *condition;
} nn_method_t;
typedef struct nn_componentTable {
char *name;
nn_Alloc alloc;
void *userdata;
nn_componentConstructor *constructor;
nn_componentDestructor *destructor;
nn_method_t methods[NN_MAX_METHODS];
nn_size_t methodCount;
} nn_componentTable;
typedef struct nn_component {
nn_address address;
int slot;
float indirectBufferProgress;
nn_componentTable *table;
void *statePtr;
nn_computer *computer;
} nn_component;
#endif

View File

@@ -1,112 +0,0 @@
#include "../neonucleus.h"
typedef struct nn_diskDrive {
nn_Context ctx;
nn_guard *lock;
nn_refc refc;
nn_diskDriveTable table;
} nn_diskDrive;
nn_diskDrive *nn_newDiskDrive(nn_Context *context, nn_diskDriveTable table) {
nn_diskDrive *drive = nn_alloc(&context->allocator, sizeof(nn_diskDrive));
if(drive == NULL) return NULL;
drive->lock = nn_newGuard(context);
if(drive->lock == NULL) {
nn_dealloc(&context->allocator, drive, sizeof(nn_diskDrive));
}
drive->refc = 1;
drive->table = table;
drive->ctx = *context;
return drive;
}
nn_guard *nn_getDiskDriveLock(nn_diskDrive *diskDrive) {
return diskDrive->lock;
}
void nn_retainDiskDrive(nn_diskDrive *diskDrive) {
nn_incRef(&diskDrive->refc);
}
nn_bool_t nn_destroyDiskDrive(nn_diskDrive *diskDrive) {
if(!nn_decRef(&diskDrive->refc)) return false;
if(diskDrive->table.deinit != NULL) {
diskDrive->table.deinit(diskDrive->table.userdata);
}
nn_Context ctx = diskDrive->ctx;
nn_Alloc a = ctx.allocator;
nn_deleteGuard(&ctx, diskDrive->lock);
nn_dealloc(&a, diskDrive, sizeof(nn_diskDrive));
return true;
}
void nn_diskDrive_destroy(void *_, nn_component *component, nn_diskDrive *diskDrive) {
nn_destroyDiskDrive(diskDrive);
}
void nn_diskDrive_eject(nn_diskDrive *diskDrive, void *_, nn_component *component, nn_computer *computer) {
double velocity = nn_toNumberOr(nn_getArgument(computer, 0), 0);
nn_errorbuf_t err = "";
nn_lock(&diskDrive->ctx, diskDrive->lock);
if(diskDrive->table.isEmpty(diskDrive->table.userdata)) {
nn_unlock(&diskDrive->ctx, diskDrive->lock);
nn_return_boolean(computer, false);
return;
}
diskDrive->table.eject(diskDrive->table.userdata, velocity, err);
nn_unlock(&diskDrive->ctx, diskDrive->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_boolean(computer, true);
}
void nn_diskDrive_isEmpty(nn_diskDrive *diskDrive, void *_, nn_component *component, nn_computer *computer) {
nn_lock(&diskDrive->ctx, diskDrive->lock);
nn_bool_t empty = diskDrive->table.isEmpty(diskDrive->table.userdata);
nn_unlock(&diskDrive->ctx, diskDrive->lock);
nn_return_boolean(computer, empty);
}
void nn_diskDrive_media(nn_diskDrive *diskDrive, void *_, nn_component *component, nn_computer *computer) {
nn_errorbuf_t err = "";
nn_Alloc *a = &diskDrive->ctx.allocator;
nn_lock(&diskDrive->ctx, diskDrive->lock);
if(diskDrive->table.isEmpty(diskDrive->table.userdata)) {
nn_unlock(&diskDrive->ctx, diskDrive->lock);
nn_setCError(computer, "drive is empty");
return;
}
nn_address s = diskDrive->table.media(diskDrive->table.userdata, a, err);
nn_unlock(&diskDrive->ctx, diskDrive->lock);
if(!nn_error_isEmpty(err)) {
nn_deallocStr(a, s);
nn_setError(computer, err);
return;
}
nn_return_string(computer, s, nn_strlen(s));
nn_deallocStr(a, s);
}
void nn_loadDiskDriveTable(nn_universe *universe) {
nn_componentTable *diskDriveTable = nn_newComponentTable(nn_getAllocator(universe), "disk_drive", NULL, NULL, (void *)nn_diskDrive_destroy);
nn_storeUserdata(universe, "NN:DISK_DRIVE", diskDriveTable);
nn_defineMethod(diskDriveTable, "eject", (nn_componentMethod *)nn_diskDrive_eject, "eject([velocity: number]): boolean - Ejects the floopy, if present. Returns whether it was present.");
nn_defineMethod(diskDriveTable, "isEmpty", (nn_componentMethod *)nn_diskDrive_isEmpty, "isEmpty(): boolean - Returns whether the drive is empty.");
nn_defineMethod(diskDriveTable, "media", (nn_componentMethod *)nn_diskDrive_media, "media(): string - Returns the address of the inner floppy disk.");
}
nn_component *nn_addDiskDrive(nn_computer *computer, nn_address address, int slot, nn_diskDrive *diskDrive) {
nn_componentTable *diskDriveTable = nn_queryUserdata(nn_getUniverse(computer), "NN:DISK_DRIVE");
return nn_newComponent(computer, address, slot, diskDriveTable, diskDrive);
}

View File

@@ -1,257 +0,0 @@
#include "../neonucleus.h"
typedef struct nn_drive {
nn_refc refc;
nn_guard *lock;
nn_Context ctx;
nn_size_t currentSector;
nn_driveTable table;
nn_driveControl ctrl;
} nn_drive;
nn_drive *nn_newDrive(nn_Context *context, nn_driveTable table, nn_driveControl control) {
nn_drive *d = nn_alloc(&context->allocator, sizeof(nn_drive));
if(d == NULL) return NULL;
d->lock = nn_newGuard(context);
if(d->lock == NULL) {
nn_dealloc(&context->allocator, d, sizeof(nn_drive));
return NULL;
}
d->refc = 1;
d->table = table;
d->ctrl = control;
d->currentSector = 0;
d->ctx = *context;
return d;
}
nn_guard *nn_getDriveLock(nn_drive *drive) {
return drive->lock;
}
void nn_retainDrive(nn_drive *drive) {
nn_incRef(&drive->refc);
}
nn_bool_t nn_destroyDrive(nn_drive *drive) {
if(!nn_decRef(&drive->refc)) return false;
if(drive->table.deinit != NULL) {
drive->table.deinit(drive->table.userdata);
}
nn_Context ctx = drive->ctx;
nn_deleteGuard(&ctx, drive->lock);
nn_dealloc(&ctx.allocator, drive, sizeof(nn_drive));
return true;
}
void nn_drive_destroy(void *_, nn_component *component, nn_drive *drive) {
nn_destroyDrive(drive);
}
void nni_drive_readCost(nn_component *component, nn_drive *drive) {
nn_driveControl ctrl = drive->ctrl;
nn_computer *computer = nn_getComputerOfComponent(component);
nn_simulateBufferedIndirect(component, 1, ctrl.readSectorsPerTick);
nn_addHeat(computer, ctrl.readHeatPerSector);
nn_removeEnergy(computer, ctrl.readEnergyPerSector);
}
void nni_drive_writeCost(nn_component *component, nn_drive *drive) {
nn_driveControl ctrl = drive->ctrl;
nn_computer *computer = nn_getComputerOfComponent(component);
nn_simulateBufferedIndirect(component, 1, ctrl.writeSectorsPerTick);
nn_addHeat(computer, ctrl.writeHeatPerSector);
nn_removeEnergy(computer, ctrl.writeEnergyPerSector);
}
void nni_drive_seekTo(nn_component *component, nn_drive *drive, nn_size_t sector) {
sector = sector - 1; // switch to 0 to N-1 sector addressing,
// which is much nicer to do math with
// and Lua made a big oopsie.
nn_size_t old = drive->currentSector;
nn_driveControl ctrl = drive->ctrl;
if(ctrl.seekSectorsPerTick == 0) return; // seek latency disabled
nn_computer *computer = nn_getComputerOfComponent(component);
// Compute important constants
nn_size_t sectorSize = drive->table.sectorSize;
nn_size_t platterCount = drive->table.platterCount;
nn_size_t capacity = drive->table.capacity;
nn_size_t sectorsPerPlatter = (capacity / sectorSize) / platterCount;
sector %= sectorsPerPlatter;
if(old == sector) return;
drive->currentSector = sector;
nn_size_t moved = 0;
if(sector > old) {
moved = sector - old; // moved forwards
} else if(sector < old) {
// moved back, depends on some options
if(ctrl.reversable) {
// horribly fucking unrealistic, as HDDs
// spin at ~7200 RPM, and if it decides
// to spontaneously instantly reverse direction,
// the force would crack the disk
// However, real life is a myth.
moved = old - sector;
} else {
// full turn
moved = sectorsPerPlatter - old;
}
}
nn_simulateBufferedIndirect(component, moved, ctrl.seekSectorsPerTick);
nn_addHeat(computer, ctrl.motorHeatPerSector * moved);
nn_removeEnergy(computer, ctrl.motorEnergyPerSector * moved);
}
void nn_drive_getLabel(nn_drive *drive, void *_, nn_component *component, nn_computer *computer) {
char buf[NN_LABEL_SIZE];
nn_size_t l = NN_LABEL_SIZE;
nn_lock(&drive->ctx, drive->lock);
drive->table.getLabel(drive->table.userdata, buf, &l);
nn_unlock(&drive->ctx, drive->lock);
if(l == 0) {
nn_return(computer, nn_values_nil());
} else {
nn_return_string(computer, buf, l);
}
}
void nn_drive_setLabel(nn_drive *drive, void *_, nn_component *component, nn_computer *computer) {
nn_size_t l = 0;
nn_value label = nn_getArgument(computer, 0);
const char *buf = nn_toString(label, &l);
if(buf == NULL) {
nn_setCError(computer, "bad label (string expected)");
return;
}
nn_lock(&drive->ctx, drive->lock);
l = drive->table.setLabel(drive->table.userdata, buf, l);
nn_unlock(&drive->ctx, drive->lock);
nn_return_string(computer, buf, l);
}
void nn_drive_getSectorSize(nn_drive *drive, void *_, nn_component *component, nn_computer *computer) {
nn_size_t sector_size = drive->table.sectorSize;
nn_return(computer, nn_values_integer(sector_size));
}
void nn_drive_getPlatterCount(nn_drive *drive, void *_, nn_component *component, nn_computer *computer) {
nn_size_t platter_count = drive->table.platterCount;
nn_return(computer, nn_values_integer(platter_count));
}
void nn_drive_getCapacity(nn_drive *drive, void *_, nn_component *component, nn_computer *computer) {
nn_size_t capacity = drive->table.capacity;
nn_return(computer, nn_values_integer(capacity));
}
void nn_drive_readSector(nn_drive *drive, void *_, nn_component *component, nn_computer *computer) {
nn_value sectorValue = nn_getArgument(computer, 0);
int sector = nn_toInt(sectorValue);
nn_size_t sector_size = drive->table.sectorSize;
// we leave the +1 intentionally to compare the end of the real sector
if (sector < 1 || (sector * sector_size > drive->table.capacity)) {
nn_setCError(computer, "bad argument #1 (sector out of range)");
return;
}
char buf[sector_size];
nn_lock(&drive->ctx, drive->lock);
drive->table.readSector(drive->table.userdata, sector, buf);
nn_unlock(&drive->ctx, drive->lock);
nn_return_string(computer, buf, sector_size);
}
void nn_drive_writeSector(nn_drive *drive, void *_, nn_component *component, nn_computer *computer) {
nn_value sectorValue = nn_getArgument(computer, 0);
int sector = nn_toInt(sectorValue);
nn_size_t sector_size = drive->table.sectorSize;
nn_value bufValue = nn_getArgument(computer, 1);
nn_size_t buf_size = 0;
const char *buf = nn_toString(bufValue, &buf_size);
if (buf_size != sector_size) {
nn_setCError(computer, "bad argument #2 (expected buffer of length `sectorSize`)");
return;
}
// we leave the +1 intentionally to compare the end of the real sector
if (sector < 1 || (sector * sector_size > drive->table.capacity)) {
nn_setCError(computer, "bad argument #1 (sector out of range)");
return;
}
nn_lock(&drive->ctx, drive->lock);
drive->table.writeSector(drive->table.userdata, sector, buf);
nn_unlock(&drive->ctx, drive->lock);
nn_return_boolean(computer, true);
}
void nn_drive_readByte(nn_drive *drive, void *_, nn_component *component, nn_computer *computer) {
nn_value offsetValue = nn_getArgument(computer, 0);
nn_size_t disk_offset = nn_toInt(offsetValue) - 1;
nn_size_t sector_size = drive->table.sectorSize;
int sector = (disk_offset / sector_size) + 1;
nn_size_t sector_offset = disk_offset % sector_size;
if (disk_offset >= drive->table.capacity) {
nn_setCError(computer, "bad argument #1 (index out of range)");
return;
}
char buf[sector_size];
nn_lock(&drive->ctx, drive->lock);
drive->table.readSector(drive->table.userdata, sector, buf);
nn_unlock(&drive->ctx, drive->lock);
nn_return(computer, nn_values_integer(buf[sector_offset]));
}
void nn_drive_writeByte(nn_drive *drive, void *_, nn_component *component, nn_computer *computer) {
nn_value offsetValue = nn_getArgument(computer, 0);
nn_value writeValue = nn_getArgument(computer, 1);
nn_size_t disk_offset = nn_toInt(offsetValue) - 1;
nn_integer_t write = nn_toInt(writeValue);
nn_size_t sector_size = drive->table.sectorSize;
int sector = (disk_offset / sector_size) + 1;
nn_size_t sector_offset = disk_offset % sector_size;
if (write < -128 || write > 255) {
nn_setCError(computer, "bad argument #2 (byte out of range)");
return;
}
if (disk_offset >= drive->table.capacity) {
nn_setCError(computer, "bad argument #1 (index out of range)");
return;
}
nn_lock(&drive->ctx, drive->lock);
char buf[sector_size];
drive->table.readSector(drive->table.userdata, sector, buf);
buf[sector_offset] = write;
drive->table.writeSector(drive->table.userdata, sector, buf);
nn_unlock(&drive->ctx, drive->lock);
nn_return_boolean(computer, true);
}
void nn_loadDriveTable(nn_universe *universe) {
nn_componentTable *driveTable = nn_newComponentTable(nn_getAllocator(universe), "drive", NULL, NULL, (nn_componentDestructor *)nn_drive_destroy);
nn_storeUserdata(universe, "NN:DRIVE", driveTable);
nn_defineMethod(driveTable, "getLabel", (nn_componentMethod *)nn_drive_getLabel, "getLabel():string - Get the current label of the drive.");
nn_defineMethod(driveTable, "setLabel", (nn_componentMethod *)nn_drive_setLabel, "setLabel(value:string):string - Sets the label of the drive. Returns the new value, which may be truncated.");
nn_defineMethod(driveTable, "getSectorSize", (nn_componentMethod *)nn_drive_getSectorSize, "getSectorSize():number - Returns the size of a single sector on the drive, in bytes.");
nn_defineMethod(driveTable, "getPlatterCount", (nn_componentMethod *)nn_drive_getPlatterCount, "getPlatterCount():number - Returns the number of platters in the drive.");
nn_defineMethod(driveTable, "getCapacity", (nn_componentMethod *)nn_drive_getCapacity, "getCapacity():number - Returns the total capacity of the drive, in bytes.");
nn_defineMethod(driveTable, "readSector", (nn_componentMethod *)nn_drive_readSector, "readSector(sector:number):string - Read the current contents of the specified sector.");
nn_defineMethod(driveTable, "writeSector", (nn_componentMethod *)nn_drive_writeSector, "writeSector(sector:number, value:string) - Write the specified contents to the specified sector.");
nn_defineMethod(driveTable, "readByte", (nn_componentMethod *)nn_drive_readByte, "readByte(offset:number):number - Read a single byte at the specified offset.");
nn_defineMethod(driveTable, "writeByte", (nn_componentMethod *)nn_drive_writeByte, "writeByte(offset:number, value:number) - Write a single byte to the specified offset.");
}
nn_component *nn_addDrive(nn_computer *computer, nn_address address, int slot, nn_drive *drive) {
nn_componentTable *driveTable = nn_queryUserdata(nn_getUniverse(computer), "NN:DRIVE");
return nn_newComponent(computer, address, slot, driveTable, drive);
}

View File

@@ -1,390 +0,0 @@
#include "../neonucleus.h"
typedef struct nn_eeprom {
nn_Context ctx;
nn_refc refc;
nn_guard *lock;
nn_eepromTable table;
nn_eepromControl control;
} nn_eeprom;
nn_eeprom *nn_newEEPROM(nn_Context *context, nn_eepromTable table, nn_eepromControl control) {
nn_eeprom *e = nn_alloc(&context->allocator, sizeof(nn_eeprom));
if(e == NULL) return NULL;
e->lock = nn_newGuard(context);
if(e->lock == NULL) {
nn_dealloc(&context->allocator, e, sizeof(nn_eeprom));
return NULL;
}
e->ctx = *context;
e->refc = 1;
e->control = control;
e->table = table;
return e;
}
nn_guard *nn_getEEPROMLock(nn_eeprom *eeprom) {
return eeprom->lock;
}
void nn_retainEEPROM(nn_eeprom *eeprom) {
nn_incRef(&eeprom->refc);
}
nn_bool_t nn_destroyEEPROM(nn_eeprom *eeprom) {
if(!nn_decRef(&eeprom->refc)) return false;
// no need to lock, we are the only one with a reference
if(eeprom->table.deinit != NULL) {
eeprom->table.deinit(eeprom->table.userdata);
}
nn_Context ctx = eeprom->ctx;
nn_deleteGuard(&ctx, eeprom->lock);
nn_dealloc(&ctx.allocator, eeprom, sizeof(nn_eeprom));
return true;
}
static void nn_eeprom_readCost(nn_component *component, nn_size_t bytesRead) {
nn_eepromControl control = ((nn_eeprom *)nn_getComponentUserdata(component))->control;
nn_computer *computer = nn_getComputerOfComponent(component);
nn_removeEnergy(computer, control.readEnergyCostPerByte * bytesRead);
nn_addHeat(computer, control.readHeatPerByte * bytesRead);
nn_simulateBufferedIndirect(component, bytesRead, control.bytesReadPerTick);
}
static void nn_eeprom_writeCost(nn_component *component, nn_size_t bytesWritten) {
nn_eepromControl control = ((nn_eeprom *)nn_getComponentUserdata(component))->control;
nn_computer *computer = nn_getComputerOfComponent(component);
nn_removeEnergy(computer, control.writeEnergyCostPerByte * bytesWritten);
nn_addHeat(computer, control.writeHeatPerByte * bytesWritten);
nn_simulateBufferedIndirect(component, bytesWritten, control.bytesWrittenPerTick);
}
void nn_eeprom_destroy(void *_, nn_component *component, nn_eeprom *eeprom) {
nn_destroyEEPROM(eeprom);
}
void nn_eeprom_getSize(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) {
nn_return(computer, nn_values_integer(eeprom->table.size));
}
void nn_eeprom_getDataSize(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) {
nn_return(computer, nn_values_integer(eeprom->table.dataSize));
}
void nn_eeprom_getLabel(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) {
char buf[NN_LABEL_SIZE];
nn_size_t l = NN_LABEL_SIZE;
nn_errorbuf_t err = "";
nn_lock(&eeprom->ctx, eeprom->lock);
eeprom->table.getLabel(eeprom->table.userdata, buf, &l, err);
nn_unlock(&eeprom->ctx, eeprom->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
if(l == 0) {
nn_return(computer, nn_values_nil());
} else {
nn_return_string(computer, buf, l);
}
// Latency, energy costs and stuff
nn_eeprom_readCost(component, l);
}
void nn_eeprom_setLabel(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) {
nn_size_t l = 0;
nn_value label = nn_getArgument(computer, 0);
const char *buf = nn_toString(label, &l);
if(buf == NULL) {
nn_setCError(computer, "bad label (string expected)");
return;
}
nn_errorbuf_t err = "";
nn_lock(&eeprom->ctx, eeprom->lock);
l = eeprom->table.setLabel(eeprom->table.userdata, buf, l, err);
nn_unlock(&eeprom->ctx, eeprom->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_string(computer, buf, l);
// Latency, energy costs and stuff
nn_eeprom_writeCost(component, l);
}
void nn_eeprom_get(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) {
nn_size_t cap = eeprom->table.size;
nn_Alloc *alloc = nn_getAllocator(nn_getUniverse(computer));
char *buf = nn_alloc(alloc, cap);
if(buf == NULL) {
nn_setCError(computer, "out of memory");
return;
}
nn_errorbuf_t err = "";
nn_lock(&eeprom->ctx, eeprom->lock);
nn_size_t len = eeprom->table.get(eeprom->table.userdata, buf, err);
nn_unlock(&eeprom->ctx, eeprom->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_string(computer, buf, len);
nn_dealloc(alloc, buf, cap);
nn_eeprom_readCost(component, len);
}
void nn_eeprom_set(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) {
nn_value data = nn_getArgument(computer, 0);
nn_size_t len;
const char *buf = nn_toString(data, &len);
if(len > eeprom->table.size) {
nn_setCError(computer, "out of space");
return;
}
if(buf == NULL) {
if(data.tag == NN_VALUE_NIL) {
buf = "";
len = 0;
} else {
nn_setCError(computer, "bad data (string expected)");
return;
}
}
nn_errorbuf_t err = "";
nn_lock(&eeprom->ctx, eeprom->lock);
if(eeprom->table.isReadonly(eeprom->table.userdata, err)) {
nn_unlock(&eeprom->ctx, eeprom->lock);
nn_setCError(computer, "readonly");
return;
}
if(!nn_error_isEmpty(err)) {
nn_unlock(&eeprom->ctx, eeprom->lock);
nn_setError(computer, err);
return;
}
eeprom->table.set(eeprom->table.userdata, buf, len, err);
nn_unlock(&eeprom->ctx, eeprom->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_eeprom_writeCost(component, len);
}
void nn_eeprom_getData(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) {
nn_size_t cap = eeprom->table.dataSize;
nn_Alloc *alloc = nn_getAllocator(nn_getUniverse(computer));
char *buf = nn_alloc(alloc, cap);
if(buf == NULL) {
nn_setCError(computer, "out of memory");
return;
}
nn_errorbuf_t err = "";
nn_lock(&eeprom->ctx, eeprom->lock);
nn_size_t len = eeprom->table.getData(eeprom->table.userdata, buf, err);
nn_unlock(&eeprom->ctx, eeprom->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_string(computer, buf, len);
nn_dealloc(alloc, buf, cap);
nn_eeprom_readCost(component, len);
}
void nn_eeprom_setData(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) {
nn_value data = nn_getArgument(computer, 0);
nn_size_t len = 0;
const char *buf = nn_toString(data, &len);
if(buf == NULL) {
if(data.tag == NN_VALUE_NIL) {
buf = "";
len = 0;
} else {
nn_setCError(computer, "bad data (string expected)");
return;
}
}
if(len > eeprom->table.dataSize) {
nn_setCError(computer, "out of space");
return;
}
nn_errorbuf_t err = "";
nn_lock(&eeprom->ctx, eeprom->lock);
if(eeprom->table.isReadonly(eeprom->table.userdata, err)) {
nn_unlock(&eeprom->ctx, eeprom->lock);
nn_setCError(computer, "readonly");
return;
}
if(!nn_error_isEmpty(err)) {
nn_unlock(&eeprom->ctx, eeprom->lock);
nn_setError(computer, err);
return;
}
eeprom->table.setData(eeprom->table.userdata, buf, len, err);
nn_unlock(&eeprom->ctx, eeprom->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_eeprom_writeCost(component, len);
}
void nn_eeprom_getArchitecture(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) {
nn_Alloc *alloc = nn_getAllocator(nn_getUniverse(computer));
nn_errorbuf_t err = "";
nn_lock(&eeprom->ctx, eeprom->lock);
char *s = eeprom->table.getArchitecture(alloc, eeprom->table.userdata, err);
nn_unlock(&eeprom->ctx, eeprom->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
if(s == NULL) {
nn_return_nil(computer);
return;
}
nn_size_t l = nn_strlen(s);
nn_return_string(computer, s, nn_strlen(s));
nn_deallocStr(alloc, s);
nn_eeprom_readCost(component, l);
}
void nn_eeprom_setArchitecture(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) {
nn_value data = nn_getArgument(computer, 0);
const char *buf = nn_toCString(data);
if(buf == NULL) {
nn_setCError(computer, "bad data (string expected)");
return;
}
nn_errorbuf_t err = "";
nn_lock(&eeprom->ctx, eeprom->lock);
if(eeprom->table.isReadonly(eeprom->table.userdata, err)) {
nn_unlock(&eeprom->ctx, eeprom->lock);
nn_setCError(computer, "readonly");
return;
}
if(!nn_error_isEmpty(err)) {
nn_unlock(&eeprom->ctx, eeprom->lock);
nn_setError(computer, err);
return;
}
eeprom->table.setArchitecture(eeprom->table.userdata, buf, err);
nn_unlock(&eeprom->ctx, eeprom->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_eeprom_writeCost(component, nn_strlen(buf));
}
void nn_eeprom_isReadOnly(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) {
nn_errorbuf_t err = "";
nn_lock(&eeprom->ctx, eeprom->lock);
nn_return(computer, nn_values_boolean(eeprom->table.isReadonly(eeprom->table.userdata, err)));
nn_unlock(&eeprom->ctx, eeprom->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
}
void nn_eeprom_makeReadonly(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) {
nn_errorbuf_t err = "";
nn_lock(&eeprom->ctx, eeprom->lock);
nn_bool_t done =eeprom->table.makeReadonly(eeprom->table.userdata, err);
nn_unlock(&eeprom->ctx, eeprom->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_boolean(computer, done);
}
void nn_eeprom_getChecksum(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) {
nn_size_t dataCap = eeprom->table.dataSize;
nn_size_t codeCap = eeprom->table.size;
nn_Alloc *alloc = nn_getAllocator(nn_getUniverse(computer));
char *buf = nn_alloc(alloc, dataCap + codeCap);
if(buf == NULL) {
nn_setCError(computer, "out of memory");
return;
}
nn_errorbuf_t err = "";
nn_lock(&eeprom->ctx, eeprom->lock);
nn_size_t dataLen = eeprom->table.getData(eeprom->table.userdata, buf, err);
if(!nn_error_isEmpty(err)) {
nn_unlock(&eeprom->ctx, eeprom->lock);
nn_dealloc(alloc, buf, dataCap + codeCap);
nn_setError(computer, err);
return;
}
int codeLen = eeprom->table.get(eeprom->table.userdata, buf + dataLen, err);
if(!nn_error_isEmpty(err)) {
nn_unlock(&eeprom->ctx, eeprom->lock);
nn_dealloc(alloc, buf, dataCap + codeCap);
nn_setError(computer, err);
return;
}
nn_unlock(&eeprom->ctx, eeprom->lock);
char hash[4];
nn_data_crc32(buf, dataLen + codeLen, hash);
nn_dealloc(alloc, buf, dataCap + codeCap);
char encoded[8];
const char *hex = "0123456789abcdef";
for(int i = 0; i < 4; i++) {
unsigned char b = hash[i];
encoded[i*2] = hex[b >> 4];
encoded[i*2+1] = hex[b & 0xF];
}
nn_return_string(computer, encoded, sizeof(encoded));
nn_eeprom_readCost(component, dataLen + codeLen);
}
void nn_loadEepromTable(nn_universe *universe) {
nn_componentTable *eepromTable = nn_newComponentTable(nn_getAllocator(universe), "eeprom", NULL, NULL, (void *)nn_eeprom_destroy);
nn_storeUserdata(universe, "NN:EEPROM", eepromTable);
nn_defineMethod(eepromTable, "getSize", (void *)nn_eeprom_getSize, "getSize(): integer - Returns the maximum code capacity of the EEPROM.");
nn_defineMethod(eepromTable, "getDataSize", (void *)nn_eeprom_getDataSize, "getDataSize(): integer - Returns the maximum data capacity of the EEPROM.");
nn_defineMethod(eepromTable, "getLabel", (void *)nn_eeprom_getLabel, "getLabel(): string - Returns the current label.");
nn_defineMethod(eepromTable, "setLabel", (void *)nn_eeprom_setLabel, "setLabel(label: string): string - Sets the new label. Returns the actual label set to, which may be truncated.");
nn_defineMethod(eepromTable, "get", (void *)nn_eeprom_get, "get(): string - Reads the current code contents.");
nn_defineMethod(eepromTable, "set", (void *)nn_eeprom_set, "set(data: string) - Sets the current code contents.");
nn_defineMethod(eepromTable, "getData", (void *)nn_eeprom_getData, "getData(): string - Reads the current data contents.");
nn_defineMethod(eepromTable, "setData", (void *)nn_eeprom_setData, "setData(data: string) - Sets the current data contents.");
nn_defineMethod(eepromTable, "getArchitecture", (void *)nn_eeprom_getArchitecture, "getArchitecture(): string - Gets the intended architecture.");
nn_defineMethod(eepromTable, "setArchitecture", (void *)nn_eeprom_setArchitecture, "setArchitecture(data: string) - Sets the intended architecture.");
nn_defineMethod(eepromTable, "isReadOnly", (void *)nn_eeprom_isReadOnly, "isReadOnly(): boolean - Returns whether this EEPROM is read-only.");
nn_defineMethod(eepromTable, "makeReadOnly", (void *)nn_eeprom_makeReadonly, "makeReadOnly() - Makes the current EEPROM read-only. Normally, this cannot be undone.");
nn_defineMethod(eepromTable, "makeReadonly", (void *)nn_eeprom_makeReadonly, "makeReadonly() - Legacy alias to makeReadOnly()");
nn_defineMethod(eepromTable, "getChecksum", (void *)nn_eeprom_getChecksum, "getChecksum(): string - Returns a checksum of the data on the EEPROM.");
}
nn_component *nn_addEEPROM(nn_computer *computer, nn_address address, int slot, nn_eeprom *eeprom) {
nn_componentTable *eepromTable = nn_queryUserdata(nn_getUniverse(computer), "NN:EEPROM");
return nn_newComponent(computer, address, slot, eepromTable, eeprom);
}

View File

@@ -1,194 +0,0 @@
#include "../neonucleus.h"
typedef struct nn_externalComputer_t {
nn_Context ctx;
nn_refc refc;
nn_guard *lock;
nn_externalComputerTable_t table;
} nn_externalComputer_t;
nn_externalComputer_t *nn_newExternalComputer(nn_Context *ctx, nn_externalComputerTable_t table) {
nn_externalComputer_t *external = nn_alloc(&ctx->allocator, sizeof(nn_externalComputer_t));
if(external == NULL) return NULL;
external->lock = nn_newGuard(ctx);
if(external->lock == NULL) {
nn_dealloc(&ctx->allocator, external, sizeof(nn_externalComputer_t));
return NULL;
}
external->refc = 1;
external->table = table;
return external;
}
nn_guard *nn_externalComputer_getLock(nn_externalComputer_t *external) {
return external->lock;
}
void nn_externalComputer_retain(nn_externalComputer_t *external) {
nn_incRef(&external->refc);
}
nn_bool_t nn_externalComputer_destroy(nn_externalComputer_t *external) {
if(!nn_decRef(&external->refc)) return false;
nn_Context ctx = external->ctx;
nn_deleteGuard(&ctx, external->lock);
nn_dealloc(&ctx.allocator, external, sizeof(nn_externalComputer_t));
return true;
}
void nni_externalComputer_componentDestroy(void *_, nn_component *component, nn_externalComputer_t *external) {
nn_externalComputer_destroy(external);
}
static void nni_externalComputer_start(nn_externalComputer_t *external, void *_, nn_component *component, nn_computer *computer) {
nn_errorbuf_t err = "";
nn_lock(&external->ctx, external->lock);
nn_bool_t worked = external->table.start(external->table.userdata, computer, err);
nn_unlock(&external->ctx, external->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_boolean(computer, worked);
}
static void nni_externalComputer_stop(nn_externalComputer_t *external, void *_, nn_component *component, nn_computer *computer) {
nn_errorbuf_t err = "";
nn_lock(&external->ctx, external->lock);
nn_bool_t worked = external->table.stop(external->table.userdata, computer, err);
nn_unlock(&external->ctx, external->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_boolean(computer, worked);
}
static void nni_externalComputer_isRunning(nn_externalComputer_t *external, void *_, nn_component *component, nn_computer *computer) {
nn_errorbuf_t err = "";
nn_lock(&external->ctx, external->lock);
nn_bool_t truthy = external->table.isRunning(external->table.userdata, computer, err);
nn_unlock(&external->ctx, external->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_boolean(computer, truthy);
}
static void nni_externalComputer_isRobot(nn_externalComputer_t *external, void *_, nn_component *component, nn_computer *computer) {
nn_errorbuf_t err = "";
nn_lock(&external->ctx, external->lock);
nn_bool_t truthy = external->table.isRobot(external->table.userdata, computer, err);
nn_unlock(&external->ctx, external->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_boolean(computer, truthy);
}
static void nni_externalComputer_getArchitecture(nn_externalComputer_t *external, void *_, nn_component *component, nn_computer *computer) {
nn_errorbuf_t err = "";
nn_lock(&external->ctx, external->lock);
nn_architecture *arch = external->table.getArchitecture(external->table.userdata, computer, err);
nn_unlock(&external->ctx, external->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_cstring(computer, arch->archName);
}
static void nni_externalComputer_getDeviceInfo(nn_externalComputer_t *external, void *_, nn_component *component, nn_computer *computer) {
nn_errorbuf_t err = "";
nn_deviceInfoList_t *info = nn_newDeviceInfoList(&external->ctx, 16);
if(info == NULL) {
nn_setCError(computer, "out of memory");
return;
}
nn_lock(&external->ctx, external->lock);
external->table.getDeviceInfo(external->table.userdata, info, computer, err);
nn_unlock(&external->ctx, external->lock);
if(!nn_error_isEmpty(err)) {
nn_deleteDeviceInfoList(info);
nn_setError(computer, err);
return;
}
nn_size_t deviceCount = nn_getDeviceCount(info);
nn_value devicesSerialized = nn_return_table(computer, deviceCount);
for(nn_size_t i = 0; i < deviceCount; i++) {
nn_deviceInfo_t *device = nn_getDeviceInfoAt(info, i);
nn_size_t deviceInfoSize = nn_getDeviceKeyCount(device);
nn_value deviceTable = nn_values_table(&external->ctx.allocator, deviceInfoSize);
for(nn_size_t j = 0; j < deviceInfoSize; j++) {
const char *value = NULL;
const char *key = nn_iterateDeviceInfoKeys(device, j, &value);
nn_values_setPair(
deviceTable,
j,
nn_values_string(&external->ctx.allocator, key, nn_strlen(key)),
nn_values_string(&external->ctx.allocator, value, nn_strlen(value))
);
}
const char *addr = nn_getDeviceInfoAddress(device);
nn_values_setPair(
devicesSerialized,
i,
nn_values_string(&external->ctx.allocator, addr, nn_strlen(addr)),
deviceTable
);
}
nn_deleteDeviceInfoList(info);
}
void nn_loadExternalComputerTable(nn_universe *universe) {
nn_componentTable *computerTable = nn_newComponentTable(nn_getAllocator(universe), "modem", NULL, NULL, (nn_componentDestructor *)nni_externalComputer_componentDestroy);
nn_storeUserdata(universe, "NN:COMPUTER", computerTable);
nn_method_t *method;
method = nn_defineMethod(computerTable, "start", (nn_componentMethod *)nni_externalComputer_start, "start(): boolean - Starts the computer. Returns whether it was successful");
nn_method_setDirect(method, false);
method = nn_defineMethod(computerTable, "stop", (nn_componentMethod *)nni_externalComputer_stop, "stop(): boolean - Stops the computer. Returns whether it was successful");
nn_method_setDirect(method, false);
method = nn_defineMethod(computerTable, "isRunning", (nn_componentMethod *)nni_externalComputer_isRunning, "isRunning(): boolean - Returns whether the computer was running");
nn_method_setDirect(method, false);
method = nn_defineMethod(computerTable, "isRobot", (nn_componentMethod *)nni_externalComputer_isRobot, "isRobot(): boolean - Returns whether the computer was running");
nn_method_setDirect(method, false);
method = nn_defineMethod(computerTable, "getArchitecture", (nn_componentMethod *)nni_externalComputer_getArchitecture, "getArchitecture(): string - Returns the name of the architecture of the computer");
nn_method_setDirect(method, false);
method = nn_defineMethod(computerTable, "getDeviceInfo", (nn_componentMethod *)nni_externalComputer_getDeviceInfo, "getDeviceList(): {[string]: {[string]: string}} - Returns information about the devices connected to the computer");
nn_method_setDirect(method, false);
}
nn_component *nn_externalComputer_addTo(nn_computer *computer, nn_address address, int slot, nn_externalComputer_t *external) {
nn_componentTable *computerTable = nn_queryUserdata(nn_getUniverse(computer), "NN:COMPUTER");
return nn_newComponent(computer, address, slot, computerTable, external);
}

View File

@@ -1,687 +0,0 @@
#include "../neonucleus.h"
typedef struct nn_filesystem {
nn_refc refc;
nn_guard *lock;
nn_Context ctx;
nn_filesystemTable table;
nn_filesystemControl control;
nn_size_t spaceUsedCache;
// last due to cache concerns (this struck is massive)
void *files[NN_MAX_OPEN_FILES];
} nn_filesystem;
void nn_fs_destroy(nn_componentMethod *_, nn_component *component, nn_filesystem *fs) {
nn_destroyFilesystem(fs);
}
nn_filesystem *nn_newFilesystem(nn_Context *context, nn_filesystemTable table, nn_filesystemControl control) {
nn_filesystem *fs = nn_alloc(&context->allocator, sizeof(nn_filesystem));
if(fs == NULL) return NULL;
fs->refc = 1;
fs->ctx = *context;
fs->table = table;
fs->control = control;
fs->spaceUsedCache = 0;
fs->lock = nn_newGuard(context);
if(fs->lock == NULL) {
nn_dealloc(&context->allocator, fs, sizeof(nn_filesystem));
return NULL;
}
for(nn_size_t i = 0; i < NN_MAX_OPEN_FILES; i++) {
fs->files[i] = NULL;
}
return fs;
}
nn_guard *nn_getFilesystemLock(nn_filesystem *fs) {
return fs->lock;
}
void nn_retainFilesystem(nn_filesystem *fs) {
nn_incRef(&fs->refc);
}
nn_bool_t nn_destroyFilesystem(nn_filesystem *fs) {
if(!nn_decRef(&fs->refc)) return false;
nn_errorbuf_t err = ""; // ignored
// close all files
for(nn_size_t i = 0; i < NN_MAX_OPEN_FILES; i++) {
void *f = fs->files[i];
if(f != NULL) fs->table.close(fs->table.userdata, f, err);
}
if(fs->table.deinit != NULL) {
fs->table.deinit(fs->table.userdata);
}
nn_Context ctx = fs->ctx;
nn_deleteGuard(&ctx, fs->lock);
nn_dealloc(&ctx.allocator, fs, sizeof(nn_filesystem));
return true;
}
nn_size_t nn_fs_getSpaceUsed(nn_filesystem *fs) {
nn_lock(&fs->ctx, fs->lock);
if(fs->spaceUsedCache != 0) {
nn_unlock(&fs->ctx, fs->lock);
return fs->spaceUsedCache;
}
nn_size_t spaceUsed = fs->table.spaceUsed(fs->table.userdata);
fs->spaceUsedCache = spaceUsed;
nn_unlock(&fs->ctx, fs->lock);
return spaceUsed;
}
void nn_fs_invalidateSpaceUsed(nn_filesystem *fs) {
nn_lock(&fs->ctx, fs->lock);
fs->spaceUsedCache = 0;
nn_unlock(&fs->ctx, fs->lock);
}
nn_size_t nn_fs_getSpaceRemaining(nn_filesystem *fs) {
nn_lock(&fs->ctx, fs->lock);
nn_size_t used = nn_fs_getSpaceUsed(fs);
nn_size_t total = fs->table.spaceTotal;
nn_unlock(&fs->ctx, fs->lock);
return total - used;
}
void *nn_fs_unwrapFD(nn_filesystem *fs, nn_size_t fd) {
if(fd >= NN_MAX_OPEN_FILES) {
return NULL;
}
nn_lock(&fs->ctx, fs->lock);
void *file = fs->files[fd];
nn_unlock(&fs->ctx, fs->lock);
if(file == NULL) {
return NULL;
}
return file;
}
void nn_fs_readCost(nn_filesystem *fs, nn_size_t bytes, nn_component *component) {
nn_filesystemControl control = fs->control;
nn_computer *computer = nn_getComputerOfComponent(component);
nn_simulateBufferedIndirect(component, bytes, control.readBytesPerTick);
nn_removeEnergy(computer, control.readEnergyPerByte * bytes);
nn_addHeat(computer, control.readHeatPerByte * bytes);
}
void nn_fs_writeCost(nn_filesystem *fs, nn_size_t bytes, nn_component *component) {
nn_filesystemControl control = fs->control;
nn_computer *computer = nn_getComputerOfComponent(component);
nn_simulateBufferedIndirect(component, bytes, control.writeBytesPerTick);
nn_removeEnergy(computer, control.writeEnergyPerByte * bytes);
nn_addHeat(computer, control.writeHeatPerByte * bytes);
}
void nn_fs_removeCost(nn_filesystem *fs, nn_size_t count, nn_component *component) {
nn_filesystemControl control = fs->control;
nn_computer *computer = nn_getComputerOfComponent(component);
nn_simulateBufferedIndirect(component, count, control.removeFilesPerTick);
nn_removeEnergy(computer, control.removeEnergy * count);
nn_addHeat(computer, control.removeHeat * count);
}
void nn_fs_createCost(nn_filesystem *fs, nn_size_t count, nn_component *component) {
nn_filesystemControl control = fs->control;
nn_computer *computer = nn_getComputerOfComponent(component);
nn_simulateBufferedIndirect(component, count, control.createFilesPerTick);
nn_removeEnergy(computer, control.createEnergy * count);
nn_addHeat(computer, control.createHeat * count);
}
void nn_fs_getLabel(nn_filesystem *fs, void *_, nn_component *component, nn_computer *computer) {
char buf[NN_LABEL_SIZE];
nn_size_t l = NN_LABEL_SIZE;
nn_errorbuf_t err = "";
nn_lock(&fs->ctx, fs->lock);
fs->table.getLabel(fs->table.userdata, buf, &l, err);
nn_unlock(&fs->ctx, fs->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
if(l == 0) {
nn_return(computer, nn_values_nil());
} else {
nn_return_string(computer, buf, l);
}
nn_fs_readCost(fs, l, component);
}
void nn_fs_setLabel(nn_filesystem *fs, void *_, nn_component *component, nn_computer *computer) {
nn_size_t l = 0;
nn_value label = nn_getArgument(computer, 0);
const char *buf = nn_toString(label, &l);
if(buf == NULL) {
nn_setCError(computer, "bad label (string expected)");
return;
}
nn_errorbuf_t err = "";
nn_lock(&fs->ctx, fs->lock);
l = fs->table.setLabel(fs->table.userdata, buf, l, err);
nn_unlock(&fs->ctx, fs->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_string(computer, buf, l);
nn_fs_writeCost(fs, l, component);
}
void nn_fs_spaceUsed(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) {
nn_size_t space = nn_fs_getSpaceUsed(fs);
nn_return(computer, nn_values_integer(space));
}
void nn_fs_spaceTotal(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) {
nn_return(computer, nn_values_integer(fs->table.spaceTotal));
}
void nn_fs_isReadOnly(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) {
nn_errorbuf_t err = "";
nn_lock(&fs->ctx, fs->lock);
nn_return_boolean(computer, fs->table.isReadOnly(fs->table.userdata, err));
nn_unlock(&fs->ctx, fs->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
}
void nn_fs_size(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) {
nn_value pathValue = nn_getArgument(computer, 0);
const char *path = nn_toCString(pathValue);
if(path == NULL) {
nn_setCError(computer, "bad path (string expected)");
return;
}
char canonical[NN_MAX_PATH];
if(nn_path_canonical(path, canonical)) {
nn_setCError(computer, "bad path (illegal path)");
return;
}
nn_errorbuf_t err = "";
nn_lock(&fs->ctx, fs->lock);
nn_size_t byteSize = fs->table.size(fs->table.userdata, canonical, err);
nn_unlock(&fs->ctx, fs->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return(computer, nn_values_integer(byteSize));
}
void nn_fs_remove(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) {
nn_value pathValue = nn_getArgument(computer, 0);
const char *path = nn_toCString(pathValue);
if(path == NULL) {
nn_setCError(computer, "bad path (string expected)");
return;
}
char canonical[NN_MAX_PATH];
if(nn_path_canonical(path, canonical)) {
nn_setCError(computer, "bad path (illegal path)");
return;
}
nn_errorbuf_t err = "";
nn_lock(&fs->ctx, fs->lock);
nn_size_t removed = fs->table.remove(fs->table.userdata, canonical, err);
nn_unlock(&fs->ctx, fs->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_boolean(computer, removed > 0);
nn_fs_removeCost(fs, removed, component);
}
void nn_fs_lastModified(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) {
nn_value pathValue = nn_getArgument(computer, 0);
const char *path = nn_toCString(pathValue);
if(path == NULL) {
nn_setCError(computer, "bad path (string expected)");
return;
}
char canonical[NN_MAX_PATH];
if(nn_path_canonical(path, canonical)) {
nn_setCError(computer, "bad path (illegal path)");
return;
}
nn_errorbuf_t err = "";
nn_lock(&fs->ctx, fs->lock);
nn_timestamp_t t = fs->table.lastModified(fs->table.userdata, canonical, err);
nn_unlock(&fs->ctx, fs->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
// OpenOS does BULLSHIT with this thing, dividing it by 1000 and expecting it to be
// fucking usable as a date, meaning it needs to be an int.
// Because of that, we ensure it is divisible by 1000
t -= t % 1000;
nn_return(computer, nn_values_integer(t));
}
void nn_fs_rename(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) {
nn_value fromValue = nn_getArgument(computer, 0);
const char *from = nn_toCString(fromValue);
if(from == NULL) {
nn_setCError(computer, "bad path #1 (string expected)");
return;
}
char canonicalFrom[NN_MAX_PATH];
if(nn_path_canonical(from, canonicalFrom)) {
nn_setCError(computer, "bad path #1 (illegal path)");
return;
}
nn_value toValue = nn_getArgument(computer, 0);
const char *to = nn_toCString(toValue);
if(to == NULL) {
nn_setCError(computer, "bad path #2 (string expected)");
return;
}
char canonicalTo[NN_MAX_PATH];
if(nn_path_canonical(to, canonicalTo)) {
nn_setCError(computer, "bad path #2 (illegal path)");
return;
}
// TODO: validate against cases such as a/b -> a or a -> a/b
nn_errorbuf_t err = "";
nn_lock(&fs->ctx, fs->lock);
if(!fs->table.exists(fs->table.userdata, canonicalFrom, err)) {
nn_unlock(&fs->ctx, fs->lock);
nn_setCError(computer, "No such file or directory");
return;
}
if(fs->table.exists(fs->table.userdata, canonicalTo, err)) {
nn_unlock(&fs->ctx, fs->lock);
nn_setCError(computer, "Destination exists");
return;
}
nn_size_t movedCount = fs->table.rename(fs->table.userdata, canonicalFrom, canonicalTo, err);
nn_unlock(&fs->ctx, fs->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return(computer, nn_values_boolean(movedCount > 0));
nn_fs_removeCost(fs, movedCount, component);
nn_fs_createCost(fs, movedCount, component);
}
void nn_fs_exists(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) {
nn_value pathValue = nn_getArgument(computer, 0);
const char *path = nn_toCString(pathValue);
if(path == NULL) {
nn_setCError(computer, "bad path (string expected)");
return;
}
char canonical[NN_MAX_PATH];
if(nn_path_canonical(path, canonical)) {
nn_setCError(computer, "bad path (illegal path)");
return;
}
nn_errorbuf_t err = "";
nn_lock(&fs->ctx, fs->lock);
nn_return_boolean(computer, fs->table.exists(fs->table.userdata, canonical, err));
nn_unlock(&fs->ctx, fs->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
}
void nn_fs_isDirectory(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) {
nn_value pathValue = nn_getArgument(computer, 0);
const char *path = nn_toCString(pathValue);
if(path == NULL) {
nn_setCError(computer, "bad path (string expected)");
return;
}
char canonical[NN_MAX_PATH];
if(nn_path_canonical(path, canonical)) {
nn_setCError(computer, "bad path (illegal path)");
return;
}
nn_errorbuf_t err = "";
nn_lock(&fs->ctx, fs->lock);
nn_return_boolean(computer, fs->table.isDirectory(fs->table.userdata, canonical, err));
nn_unlock(&fs->ctx, fs->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
}
}
void nn_fs_makeDirectory(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) {
nn_value pathValue = nn_getArgument(computer, 0);
const char *path = nn_toCString(pathValue);
if(path == NULL) {
nn_setCError(computer, "bad path (string expected)");
return;
}
char canonical[NN_MAX_PATH];
if(nn_path_canonical(path, canonical)) {
nn_setCError(computer, "bad path (illegal path)");
return;
}
nn_errorbuf_t err = "";
nn_lock(&fs->ctx, fs->lock);
nn_return_boolean(computer, fs->table.makeDirectory(fs->table.userdata, canonical, err));
nn_unlock(&fs->ctx, fs->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
}
nn_fs_createCost(fs, 1, component);
}
void nn_fs_list(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) {
nn_value pathValue = nn_getArgument(computer, 0);
const char *path = nn_toCString(pathValue);
if(path == NULL) {
nn_setCError(computer, "bad path (string expected)");
return;
}
char canonical[NN_MAX_PATH];
if(nn_path_canonical(path, canonical)) {
nn_setCError(computer, "bad path (illegal path)");
return;
}
nn_Alloc *alloc = nn_getAllocator(nn_getUniverse(computer));
nn_errorbuf_t err = "";
nn_size_t fileCount = 0;
nn_lock(&fs->ctx, fs->lock);
char **files = fs->table.list(alloc, fs->table.userdata, canonical, &fileCount, err);
nn_unlock(&fs->ctx, fs->lock);
if(!nn_error_isEmpty(err)) {
if(files != NULL) {
for(nn_size_t i = 0; i < fileCount; i++) {
nn_deallocStr(alloc, files[i]);
}
nn_dealloc(alloc, files, sizeof(char *) * fileCount);
}
nn_setError(computer, err);
}
if(files != NULL) {
// operation succeeded
nn_value arr = nn_values_array(alloc, fileCount);
for(nn_size_t i = 0; i < fileCount; i++) {
nn_values_set(arr, i, nn_values_string(alloc, files[i], nn_strlen(files[i])));
nn_deallocStr(alloc, files[i]);
}
nn_dealloc(alloc, files, sizeof(char *) * fileCount);
nn_return(computer, arr);
}
}
void nn_fs_open(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) {
nn_value pathValue = nn_getArgument(computer, 0);
const char *path = nn_toCString(pathValue);
if(path == NULL) {
nn_setCError(computer, "bad path (string expected)");
return;
}
char canonical[NN_MAX_PATH];
if(nn_path_canonical(path, canonical)) {
nn_setCError(computer, "bad path (illegal path)");
return;
}
nn_value modeValue = nn_getArgument(computer, 1);
const char *mode = nn_toCString(modeValue);
if(mode == NULL) {
mode = "r";
}
nn_errorbuf_t err = "";
nn_lock(&fs->ctx, fs->lock);
// technically wrongfully
if(!fs->table.exists(fs->table.userdata, canonical, err)) {
nn_fs_createCost(fs, 1, component);
}
if(!nn_error_isEmpty(err)) {
nn_unlock(&fs->ctx, fs->lock);
nn_setError(computer, err);
return;
}
nn_size_t fd = 0;
while(fs->files[fd] != NULL) {
fd++;
if(fd == NN_MAX_OPEN_FILES) {
nn_unlock(&fs->ctx, fs->lock);
nn_setCError(computer, "too many open files");
return;
}
}
void *file = fs->table.open(fs->table.userdata, canonical, mode, err);
if(!nn_error_isEmpty(err)) {
if(file != NULL) {
fs->table.close(fs->table.userdata, file, err);
}
nn_unlock(&fs->ctx, fs->lock);
nn_setError(computer, err);
return;
}
if(file == NULL) {
nn_unlock(&fs->ctx, fs->lock);
nn_setCError(computer, "no such file or directory");
return;
}
nn_unlock(&fs->ctx, fs->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
fs->files[fd] = file;
nn_return(computer, nn_values_integer(fd));
}
void nn_fs_close(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) {
nn_value fdValue = nn_getArgument(computer, 0);
nn_size_t fd = nn_toInt(fdValue);
void *file = nn_fs_unwrapFD(fs, fd);
if(file == NULL) {
nn_setCError(computer, "bad file descriptor");
return;
}
nn_errorbuf_t err = "";
nn_lock(&fs->ctx, fs->lock);
nn_bool_t closed = fs->table.close(fs->table.userdata, file, err);
if(closed) {
fs->files[fd] = NULL;
}
nn_unlock(&fs->ctx, fs->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return(computer, nn_values_boolean(closed));
}
void nn_fs_write(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) {
nn_value fdValue = nn_getArgument(computer, 0);
nn_size_t fd = nn_toInt(fdValue);
nn_value bufferValue = nn_getArgument(computer, 1);
nn_size_t len = 0;
const char *buf = nn_toString(bufferValue, &len);
if(buf == NULL) {
nn_setCError(computer, "bad buffer (string expected)");
return;
}
nn_errorbuf_t err = "";
nn_lock(&fs->ctx, fs->lock);
nn_size_t spaceRemaining = nn_fs_getSpaceRemaining(fs);
// overwriting would still work but OC does the same thing so...
if(spaceRemaining < len) {
nn_unlock(&fs->ctx, fs->lock);
nn_setCError(computer, "out of space");
return;
}
void *file = nn_fs_unwrapFD(fs, fd);
if(file == NULL) {
nn_unlock(&fs->ctx, fs->lock);
nn_setCError(computer, "bad file descriptor");
return;
}
nn_bool_t written = fs->table.write(fs->table.userdata, file, buf, len, err);
nn_return(computer, nn_values_boolean(written));
if(written) nn_fs_invalidateSpaceUsed(fs);
nn_unlock(&fs->ctx, fs->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_fs_writeCost(fs, len, component);
}
void nn_fs_read(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) {
nn_value fdValue = nn_getArgument(computer, 0);
int fd = nn_toInt(fdValue);
nn_value lenValue = nn_getArgument(computer, 1);
double len = nn_toNumber(lenValue);
// TODO: be smarter
nn_size_t capacity = fs->table.spaceTotal;
if(len > capacity) len = capacity;
nn_size_t byteLen = len;
nn_errorbuf_t err = "";
nn_lock(&fs->ctx, fs->lock);
void *file = nn_fs_unwrapFD(fs, fd);
if(file == NULL) {
nn_unlock(&fs->ctx, fs->lock);
nn_setCError(computer, "bad file descriptor");
return;
}
nn_Alloc *alloc = nn_getAllocator(nn_getUniverse(computer));
char *buf = nn_alloc(alloc, byteLen);
if(buf == NULL) {
nn_unlock(&fs->ctx, fs->lock);
nn_setCError(computer, "out of memory");
return;
}
nn_size_t readLen = fs->table.read(fs->table.userdata, file, buf, byteLen, err);
nn_unlock(&fs->ctx, fs->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
if(readLen > 0) {
// Nothing read means EoF.
nn_return_string(computer, buf, readLen);
}
nn_dealloc(alloc, buf, byteLen);
nn_fs_readCost(fs, len, component);
}
nn_bool_t nn_fs_validWhence(const char *s) {
return
nn_strcmp(s, "set") == 0 ||
nn_strcmp(s, "cur") == 0 ||
nn_strcmp(s, "end") == 0;
}
void nn_fs_seek(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) {
nn_size_t fd = nn_toInt(nn_getArgument(computer, 0));
const char *whence = nn_toCString(nn_getArgument(computer, 1));
int off = nn_toInt(nn_getArgument(computer, 2));
if(whence == NULL) {
nn_setCError(computer, "bad whence (string expected)");
return;
}
if(!nn_fs_validWhence(whence)) {
nn_setCError(computer, "bad whence");
return;
}
nn_errorbuf_t err = "";
nn_lock(&fs->ctx, fs->lock);
void *file = nn_fs_unwrapFD(fs, fd);
if(file == NULL) {
nn_unlock(&fs->ctx, fs->lock);
nn_setCError(computer, "bad file descriptor");
return;
}
nn_size_t pos = fs->table.seek(fs->table.userdata, file, whence, off, err);
nn_unlock(&fs->ctx, fs->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_integer(computer, pos);
}
void nn_loadFilesystemTable(nn_universe *universe) {
nn_componentTable *fsTable = nn_newComponentTable(nn_getAllocator(universe), "filesystem", NULL, NULL, (nn_componentDestructor *)nn_fs_destroy);
nn_storeUserdata(universe, "NN:FILESYSTEM", fsTable);
nn_defineMethod(fsTable, "getLabel", (nn_componentMethod *)nn_fs_getLabel, "getLabel(): string - Returns the label of the filesystem.");
nn_defineMethod(fsTable, "setLabel", (nn_componentMethod *)nn_fs_setLabel, "setLabel(label: string): string - Sets a new label for the filesystem and returns the new label of the filesystem, which may have been truncated.");
nn_defineMethod(fsTable, "spaceUsed", (nn_componentMethod *)nn_fs_spaceUsed, "spaceUsed(): integer - Returns the amounts of bytes used.");
nn_defineMethod(fsTable, "spaceTotal", (nn_componentMethod *)nn_fs_spaceTotal, "spaceTotal(): integer - Returns the capacity of the filesystem.");
nn_defineMethod(fsTable, "isReadOnly", (nn_componentMethod *)nn_fs_isReadOnly, "isReadOnly(): boolean - Returns whether the filesystem is in read-only mode.");
nn_defineMethod(fsTable, "size", (nn_componentMethod *)nn_fs_size, "size(path: string): integer - Gets the size, in bytes, of a file.");
nn_defineMethod(fsTable, "remove", (nn_componentMethod *)nn_fs_remove, "remove(path: string): boolean - Removes a file. Returns whether the operation succeeded.");
nn_defineMethod(fsTable, "lastModified", (nn_componentMethod *)nn_fs_lastModified, "remove(path: string): boolean - Removes a file. Returns whether the operation succeeded.");
nn_defineMethod(fsTable, "rename", (nn_componentMethod *)nn_fs_rename, "rename(from: string, to: string): boolean - Moves files from one path to another.");
nn_defineMethod(fsTable, "exists", (nn_componentMethod *)nn_fs_exists, "exists(path: string): boolean - Checks whether a file exists.");
nn_defineMethod(fsTable, "isDirectory", (nn_componentMethod *)nn_fs_isDirectory, "isDirectory(path: string): boolean - Returns whether a file is actually a directory.");
nn_defineMethod(fsTable, "makeDirectory", (nn_componentMethod *)nn_fs_makeDirectory, "makeDirectory(path: string): boolean - Creates a new directory at the given path. Returns whether it succeeded.");
nn_defineMethod(fsTable, "list", (nn_componentMethod *)nn_fs_list, "list(path: string): string[] - Returns a list of file paths. Directories will have a / after them");
nn_defineMethod(fsTable, "open", (nn_componentMethod *)nn_fs_open, "open(path: string[, mode: string = \"r\"]): integer - Opens a file, may create it.");
nn_defineMethod(fsTable, "close", (nn_componentMethod *)nn_fs_close, "close(fd: integer): boolean - Closes a file.");
nn_defineMethod(fsTable, "write", (nn_componentMethod *)nn_fs_write, "write(fd: integer, data: string): boolean - Writes data to a file.");
nn_defineMethod(fsTable, "read", (nn_componentMethod *)nn_fs_read, "read(fd: integer, len: number): string - Reads bytes from a file. Infinity is a valid length, in which case it reads as much as possible.");
nn_defineMethod(fsTable, "seek", (nn_componentMethod *)nn_fs_seek, "seek(fd: integer, whence: string, offset: integer): integer - Seeks a file. Returns the new position. Valid whences are set, cur and end.");
}
nn_component *nn_addFileSystem(nn_computer *computer, nn_address address, int slot, nn_filesystem *filesystem) {
nn_componentTable *fsTable = nn_queryUserdata(nn_getUniverse(computer), "NN:FILESYSTEM");
return nn_newComponent(computer, address, slot, fsTable, filesystem);
}

View File

@@ -1,960 +0,0 @@
#include "../neonucleus.h"
#include "screen.h"
typedef struct nni_buffer {
int width;
int height;
nn_scrchr_t *data;
} nni_buffer;
typedef struct nni_gpu {
nn_Alloc alloc;
nn_screen *currentScreen;
nn_address screenAddress;
nn_gpuControl ctrl;
int currentFg;
int currentBg;
nn_bool_t isFgPalette;
nn_bool_t isBgPalette;
// TODO: think about buffers and stuff
int usedVRAM;
int activeBuffer;
int *vramIDBuf; // pre-allocated memory
nni_buffer **buffers; // array of pointers
} nni_gpu;
// utils
nn_scrchr_t nni_gpu_makePixel(nni_gpu *gpu, const char *s) {
return (nn_scrchr_t) {
.codepoint = nn_unicode_codepointAt(s, 0),
.fg = gpu->currentFg,
.bg = gpu->currentBg,
.isFgPalette = gpu->isFgPalette,
.isBgPalette = gpu->isBgPalette,
};
}
nn_bool_t nni_samePixel(nn_scrchr_t a, nn_scrchr_t b) {
return
a.codepoint == b.codepoint &&
a.fg == b.fg &&
a.bg == b.bg &&
a.isFgPalette == b.isFgPalette &&
a.isBgPalette == b.isBgPalette
;
}
nn_bool_t nni_inBounds(nni_gpu *gpu, int x, int y) {
if(gpu->currentScreen == NULL) return false;
return
x >= 0 &&
y >= 0 &&
x < gpu->currentScreen->width &&
y < gpu->currentScreen->height &&
true;
}
nn_size_t nni_vramNeededForSize(int w, int h) {
return w * h;
}
nn_size_t nni_vramNeededForScreen(nn_screen *screen) {
if(screen == NULL) return 0;
int w, h;
nn_maxResolution(screen, &w, &h);
return nni_vramNeededForSize(w, h);
}
// VRAM
nni_buffer *nni_vram_newBuffer(nn_Alloc *alloc, int width, int height) {
int area = width * height;
nni_buffer *buf = nn_alloc(alloc, sizeof(nni_buffer));
if(buf == NULL) {
return NULL;
}
buf->width = width;
buf->height = height;
buf->data = nn_alloc(alloc, sizeof(nn_scrchr_t) * area);
for(int i = 0; i < area; i++) {
buf->data[i] = (nn_scrchr_t) {
.codepoint = ' ',
.fg = 0xFFFFFF,
.bg = 0x000000,
.isFgPalette = false,
.isBgPalette = false,
};
}
if(buf->data == NULL) {
nn_dealloc(alloc, buf, sizeof(nni_buffer));
}
return buf;
}
void nni_vram_deinit(nn_Alloc *alloc, nni_buffer *buffer) {
int area = buffer->width * buffer->height;
nn_dealloc(alloc, buffer->data, sizeof(nn_scrchr_t) * area);
nn_dealloc(alloc, buffer, sizeof(nni_buffer));
}
nn_bool_t nni_vram_inBounds(nni_buffer *buffer, int x, int y) {
return
x >= 0 &&
y >= 0 &&
x < buffer->width &&
y < buffer->height
;
}
nn_scrchr_t nni_vram_getPixel(nni_buffer *buffer, int x, int y) {
if(!nni_vram_inBounds(buffer, x, y)) {
return (nn_scrchr_t) {
.codepoint = 0,
.fg = 0xFFFFFF,
.bg = 0x000000,
.isFgPalette = false,
.isBgPalette = false,
};
}
return buffer->data[x + y * buffer->width];
}
void nni_vram_setPixel(nni_buffer *buffer, int x, int y, nn_scrchr_t pixel) {
if(!nni_vram_inBounds(buffer, x, y)) return;
buffer->data[x + y * buffer->width] = pixel;
}
void nni_vram_set(nni_gpu *gpu, int x, int y, const char *s, nn_bool_t vertical) {
nni_buffer *buffer = gpu->buffers[gpu->activeBuffer - 1];
nn_size_t cur = 0;
while(s[cur]) {
unsigned int cp = nn_unicode_nextCodepointPermissive(s, &cur);
char encoded[NN_MAXIMUM_UNICODE_BUFFER];
nn_unicode_codepointToChar(encoded, cp, NULL);
nni_vram_setPixel(buffer, x, y, nni_gpu_makePixel(gpu, encoded));
// peak software
if(vertical) {
y++;
} else {
x++;
}
}
}
void nni_vram_fill(nni_gpu *gpu, int x, int y, int w, int h, const char *s) {
nni_buffer *buffer = gpu->buffers[gpu->activeBuffer - 1];
// DoS mitigation
if(x < 0) x = 0;
if(y < 0) y = 0;
if(w > buffer->width) w = buffer->width - x;
if(h > buffer->height) h = buffer->height - y;
nn_scrchr_t p = nni_gpu_makePixel(gpu, s);
for(int py = 0; py < h; py++) {
for(int px = 0; px < w; px++) {
nni_vram_setPixel(buffer, px, py, p);
}
}
}
void nni_vram_copy(nni_gpu *gpu, int x, int y, int w, int h, int tx, int ty, nn_errorbuf_t err) {
nni_buffer *buffer = gpu->buffers[gpu->activeBuffer - 1];
// DoS mitigation
if(x < 0) x = 0;
if(y < 0) y = 0;
if(w > buffer->width) w = buffer->width - x;
if(h > buffer->height) h = buffer->height - y;
nn_size_t tmpBufSize = sizeof(nn_scrchr_t) * w * h;
nn_scrchr_t *tmpBuf = nn_alloc(&gpu->alloc, tmpBufSize);
if(tmpBuf == NULL) {
nn_error_write(err, "out of memory");
return;
}
// copy
for(int iy = 0; iy < h; iy++) {
for(int ix = 0; ix < w; ix++) {
tmpBuf[ix + iy * w] = nni_vram_getPixel(buffer, x, y);
}
}
for(int iy = 0; iy < h; iy++) {
for(int ix = 0; ix < w; ix++) {
nn_scrchr_t p = tmpBuf[ix + iy * w];
nni_vram_setPixel(buffer, x + ix + tx, y + iy + ty, p);
}
}
nn_dealloc(&gpu->alloc, tmpBuf, tmpBufSize);
}
// GPU stuff
nni_gpu *nni_newGPU(nn_Alloc *alloc, nn_gpuControl *ctrl) {
nni_gpu *gpu = nn_alloc(alloc, sizeof(nni_gpu));
if(gpu == NULL) return NULL;
gpu->alloc = *alloc;
gpu->currentScreen = NULL;
gpu->screenAddress = NULL;
gpu->ctrl = *ctrl;
gpu->currentFg = 0xFFFFFF;
gpu->currentBg = 0x000000;
gpu->isFgPalette = false;
gpu->isBgPalette = false;
gpu->vramIDBuf = nn_alloc(alloc, sizeof(int) * ctrl->maximumBufferCount);
if(gpu->vramIDBuf == NULL) {
nn_dealloc(alloc, gpu, sizeof(nni_gpu));
return NULL;
}
// gpu->vramIDBuf can be left uninitialized! Its only tmp storage!
gpu->buffers = nn_alloc(alloc, sizeof(nn_screen *) * ctrl->maximumBufferCount);
if(gpu->buffers == NULL) {
nn_dealloc(alloc, gpu->vramIDBuf, sizeof(int) * ctrl->maximumBufferCount);
nn_dealloc(alloc, gpu, sizeof(nni_gpu));
return NULL;
}
for(int i = 0; i < ctrl->maximumBufferCount; i++) {
gpu->buffers[i] = NULL;
}
gpu->activeBuffer = 0;
gpu->usedVRAM = 0;
return gpu;
}
void nni_gpuDeinit(nni_gpu *gpu) {
if(gpu->currentScreen != NULL) {
nn_destroyScreen(gpu->currentScreen);
}
nn_Alloc a = gpu->alloc;
if(gpu->screenAddress != NULL) {
nn_deallocStr(&a, gpu->screenAddress);
}
int maximumBufferCount = gpu->ctrl.maximumBufferCount;
for(int i = 0; i < maximumBufferCount; i++) {
}
nn_dealloc(&a, gpu->vramIDBuf, sizeof(int) * maximumBufferCount);
nn_dealloc(&a, gpu->buffers, sizeof(nn_screen) * maximumBufferCount);
nn_dealloc(&a, gpu, sizeof(nni_gpu));
}
nn_bool_t nni_gpu_validActiveScreen(nni_gpu *gpu, int activeBuffer) {
if(activeBuffer < 0 || activeBuffer > gpu->ctrl.maximumBufferCount) return false;
return true;
}
void nni_gpu_bind(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
nn_value argVal = nn_getArgument(computer, 0);
nn_value resetVal = nn_getArgument(computer, 1);
const char *addr = nn_toCString(argVal);
if(addr == NULL) {
nn_setCError(computer, "bad argument #1 (address expected)");
return;
}
nn_bool_t reset = false;
if(resetVal.tag == NN_VALUE_BOOL) reset = nn_toBoolean(resetVal);
nn_component *c = nn_findComponent(computer, (nn_address)addr);
if(c == NULL) {
nn_setCError(computer, "no such screen");
return;
}
nn_componentTable *supportedTable = nn_getScreenTable(nn_getUniverse(computer));
if(supportedTable != nn_getComponentTable(c)) {
nn_setCError(computer, "incompatible screen");
return;
}
nn_screen *oldScreen = gpu->currentScreen;
nn_size_t oldScreenVRAM = nni_vramNeededForScreen(oldScreen);
nn_screen *screen = nn_getComponentUserdata(c);
nn_size_t screenVRAM = nni_vramNeededForScreen(screen);
if(gpu->usedVRAM - oldScreenVRAM + screenVRAM > gpu->ctrl.totalVRAM) {
nn_setCError(computer, "out of vram");
return;
}
gpu->usedVRAM -= oldScreenVRAM;
gpu->usedVRAM += screenVRAM;
nn_retainScreen(screen);
if(oldScreen != NULL) nn_destroyScreen(oldScreen);
gpu->currentScreen = screen;
if(reset) {
for(nn_size_t i = 0; i < screen->width; i++) {
for(nn_size_t j = 0; j < screen->height; j++) {
nn_setPixel(screen, i, j, nni_gpu_makePixel(gpu, " "));
}
}
nn_size_t area = screen->width * screen->height;
nn_addHeat(computer, gpu->ctrl.heatPerPixelReset * area);
nn_simulateBufferedIndirect(component, 1, gpu->ctrl.screenFillPerTick);
nn_removeEnergy(computer, gpu->ctrl.energyPerPixelReset * area);
}
gpu->currentScreen = screen;
if(gpu->screenAddress != NULL) {
nn_deallocStr(&gpu->alloc, gpu->screenAddress);
}
// TODO: fix OOM here
gpu->screenAddress = nn_strdup(&gpu->alloc, addr);
nn_return(computer, nn_values_boolean(true));
}
void nni_gpu_set(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
int x = nn_toInt(nn_getArgument(computer, 0)) - 1;
int y = nn_toInt(nn_getArgument(computer, 1)) - 1;
const char *s = nn_toCString(nn_getArgument(computer, 2));
nn_bool_t isVertical = nn_toBoolean(nn_getArgument(computer, 3));
if(s == NULL) {
nn_setCError(computer, "bad argument #3 (string expected in set)");
return;
}
if(gpu->activeBuffer != 0) {
nni_vram_set(gpu, x, y, s, isVertical);
return;
}
if(gpu->currentScreen == NULL) return;
nn_size_t current = 0;
while(s[current] != 0) {
unsigned int codepoint = nn_unicode_nextCodepointPermissive(s, &current);
char buf[NN_MAXIMUM_UNICODE_BUFFER];
nn_unicode_codepointToChar(buf, codepoint, NULL);
nn_setPixel(gpu->currentScreen, x, y, nni_gpu_makePixel(gpu, buf));
if(isVertical) {
y++;
} else {
x++;
}
}
nn_simulateBufferedIndirect(component, 1, gpu->ctrl.screenSetsPerTick);
}
void nni_gpu_get(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
if(gpu->screenAddress == NULL) return;
int x = nn_toInt(nn_getArgument(computer, 0)) - 1;
int y = nn_toInt(nn_getArgument(computer, 1)) - 1;
nn_scrchr_t pxl = nn_getPixel(gpu->currentScreen, x, y);
nn_size_t l;
char chr[NN_MAXIMUM_UNICODE_BUFFER];
nn_unicode_codepointToChar(chr, pxl.codepoint, &l);
// TODO: gosh darn palettes
nn_return_string(computer, chr, l);
nn_return_integer(computer, pxl.fg);
nn_return_integer(computer, pxl.bg);
}
void nni_gpu_getScreen(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
if(gpu->screenAddress == NULL) return;
nn_return_string(computer, gpu->screenAddress, nn_strlen(gpu->screenAddress));
}
void nni_gpu_maxResolution(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
if(gpu->currentScreen == NULL) return;
int w, h;
nn_maxResolution(gpu->currentScreen, &w, &h);
nn_return(computer, nn_values_integer(w));
nn_return(computer, nn_values_integer(h));
}
void nni_gpu_getResolution(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
if(gpu->currentScreen == NULL) return;
int w, h;
nn_getResolution(gpu->currentScreen, &w, &h);
nn_return(computer, nn_values_integer(w));
nn_return(computer, nn_values_integer(h));
}
void nni_gpu_setResolution(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
if(gpu->currentScreen == NULL) return;
int mw, mh;
nn_maxResolution(gpu->currentScreen, &mw, &mh);
int lw, lh;
nn_getResolution(gpu->currentScreen, &lw, &lh);
int w = nn_toInt(nn_getArgument(computer, 0));
int h = nn_toInt(nn_getArgument(computer, 1));
nn_bool_t changed = w != lw || h != lh;
if(w <= 0) w = 1;
if(h <= 0) h = 1;
if(w > mw) w = mw;
if(h > mh) h = mh;
nn_setResolution(gpu->currentScreen, w, h);
nn_setViewport(gpu->currentScreen, w, h);
nn_return(computer, nn_values_boolean(changed));
nn_value signalShit[] = {
nn_values_cstring("screen_resized"),
nn_values_cstring(gpu->screenAddress),
nn_values_integer(w),
nn_values_integer(h),
};
nn_pushSignal(computer, signalShit, 4);
}
void nni_gpu_setBackground(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
if(gpu->currentScreen == NULL) return;
int color = nn_toInt(nn_getArgument(computer, 0));
nn_bool_t isPalette = nn_toBoolean(nn_getArgument(computer, 1));
if(isPalette && (color < 0 || color >= gpu->currentScreen->paletteColors)) {
nn_setCError(computer, "invalid palette index");
return;
}
int old = gpu->currentBg;
int idx = -1;
if(gpu->isBgPalette) {
idx = old;
old = gpu->currentScreen->palette[old];
}
gpu->currentBg = color;
gpu->isBgPalette = isPalette;
nn_return(computer, nn_values_integer(old));
if(idx != -1) {
nn_return(computer, nn_values_integer(idx));
}
}
void nni_gpu_getBackground(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
nn_return(computer, nn_values_integer(gpu->currentBg));
}
void nni_gpu_setForeground(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
if(gpu->currentScreen == NULL) return;
int color = nn_toInt(nn_getArgument(computer, 0));
nn_bool_t isPalette = nn_toBoolean(nn_getArgument(computer, 1));
if(isPalette && (color < 0 || color >= gpu->currentScreen->paletteColors)) {
nn_setCError(computer, "invalid palette index");
return;
}
int old = gpu->currentFg;
int idx = -1;
if(gpu->isFgPalette) {
idx = old;
old = gpu->currentScreen->palette[old];
}
gpu->currentFg = color;
gpu->isFgPalette = isPalette;
nn_return(computer, nn_values_integer(old));
if(idx != -1) {
nn_return(computer, nn_values_integer(idx));
}
}
void nni_gpu_getForeground(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
nn_return(computer, nn_values_integer(gpu->currentFg));
}
void nni_gpu_fill(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
int x = nn_toInt(nn_getArgument(computer, 0)) - 1;
int y = nn_toInt(nn_getArgument(computer, 1)) - 1;
int w = nn_toInt(nn_getArgument(computer, 2));
int h = nn_toInt(nn_getArgument(computer, 3));
const char *s = nn_toCString(nn_getArgument(computer, 4));
if(s == NULL) {
nn_setCError(computer, "bad argument #5 (character expected)");
return;
}
if(gpu->activeBuffer != 0) {
nni_vram_fill(gpu, x, y, w, h, s);
return;
}
if(gpu->currentScreen == NULL) return;
nn_size_t startIdx = 0;
int codepoint = nn_unicode_nextCodepointPermissive(s, &startIdx);
// prevent DoS
if(x < 0) x = 0;
if(y < 0) y = 0;
if(w > gpu->currentScreen->width - x) w = gpu->currentScreen->width - x;
if(h > gpu->currentScreen->height - y) h = gpu->currentScreen->height - y;
int changes = 0, clears = 0;
nn_scrchr_t new = nni_gpu_makePixel(gpu, s);
for(int cx = x; cx < x + w; cx++) {
for(int cy = y; cy < y + h; cy++) {
nn_scrchr_t old = nn_getPixel(gpu->currentScreen, cx, cy);
if(!nni_samePixel(old, new)) {
nn_setPixel(gpu->currentScreen, cx, cy, new);
if(codepoint == ' ')
clears++;
else changes++;
}
}
}
nn_addHeat(computer, gpu->ctrl.heatPerPixelChange * changes);
nn_removeEnergy(computer, gpu->ctrl.energyPerPixelChange * changes);
nn_addHeat(computer, gpu->ctrl.heatPerPixelReset * clears);
nn_removeEnergy(computer, gpu->ctrl.energyPerPixelReset * clears);
nn_simulateBufferedIndirect(component, 1, gpu->ctrl.screenFillPerTick);
nn_return(computer, nn_values_boolean(true));
}
void nni_gpu_copy(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
int x = nn_toInt(nn_getArgument(computer, 0)) - 1;
int y = nn_toInt(nn_getArgument(computer, 1)) - 1;
int w = nn_toInt(nn_getArgument(computer, 2));
int h = nn_toInt(nn_getArgument(computer, 3));
int tx = nn_toInt(nn_getArgument(computer, 4));
int ty = nn_toInt(nn_getArgument(computer, 5));
if(gpu->activeBuffer != 0) {
nn_errorbuf_t err = "";
nni_vram_copy(gpu, x, y, w, h, tx, ty, err);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
}
return;
}
if(gpu->currentScreen == NULL) return;
// prevent DoS
if(x < 0) x = 0;
if(y < 0) y = 0;
if(w > gpu->currentScreen->width) w = gpu->currentScreen->width;
if(h > gpu->currentScreen->height) y = gpu->currentScreen->height;
int changes = 0, clears = 0;
nn_scrchr_t *tmpBuffer = nn_alloc(&gpu->alloc, sizeof(nn_scrchr_t) * w * h);
if(tmpBuffer == NULL) {
nn_setCError(computer, "out of memory");
return;
}
for(int cx = x; cx < x + w; cx++) {
for(int cy = y; cy < y + h; cy++) {
int ox = cx - x;
int oy = cy - y;
nn_scrchr_t src = nn_getPixel(gpu->currentScreen, cx, cy);
nn_scrchr_t old = nn_getPixel(gpu->currentScreen, cx + tx, cy + ty);
tmpBuffer[ox + oy * w] = src;
if(!nni_samePixel(old, src)) {
if(src.codepoint == ' ')
clears++;
else changes++;
}
}
}
for(int ox = 0; ox < w; ox++) {
for(int oy = 0; oy < h; oy++) {
nn_scrchr_t p = tmpBuffer[ox + oy * w];
nn_setPixel(gpu->currentScreen, ox + x + tx, oy + y + ty, p);
}
}
nn_dealloc(&gpu->alloc, tmpBuffer, sizeof(nn_scrchr_t) * w * h);
nn_addHeat(computer, gpu->ctrl.heatPerPixelChange * changes);
nn_removeEnergy(computer, gpu->ctrl.energyPerPixelChange * changes);
nn_addHeat(computer, gpu->ctrl.heatPerPixelReset * clears);
nn_removeEnergy(computer, gpu->ctrl.energyPerPixelReset * clears);
nn_simulateBufferedIndirect(component, 1, gpu->ctrl.screenCopyPerTick);
nn_return(computer, nn_values_boolean(true));
}
void nni_gpu_getViewport(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
int w, h;
nn_getViewport(gpu->currentScreen, &w, &h);
nn_return(computer, nn_values_integer(w));
nn_return(computer, nn_values_integer(h));
}
void nni_gpu_getDepth(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
if(gpu->currentScreen == NULL) return;
nn_return(computer, nn_values_integer(gpu->currentScreen->depth));
}
void nni_gpu_setDepth(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
if(gpu->currentScreen == NULL) return;
int depth = nn_toInt(nn_getArgument(computer, 0));
int maxDepth = nn_maxDepth(gpu->currentScreen);
if(nn_depthName(depth) == NULL) {
nn_setCError(computer, "invalid depth");
return;
}
if(depth > maxDepth) {
nn_setCError(computer, "depth out of range");
return;
}
int old = nn_getDepth(gpu->currentScreen);
nn_setDepth(gpu->currentScreen, depth);
nn_return_cstring(computer, nn_depthName(old));
}
void nni_gpu_maxDepth(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
if(gpu->currentScreen == NULL) return;
nn_return(computer, nn_values_integer(gpu->currentScreen->maxDepth));
}
void nni_gpu_totalMemory(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
nn_return_integer(computer, gpu->ctrl.totalVRAM);
}
void nni_gpu_usedMemory(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
nn_return_integer(computer, gpu->usedVRAM);
}
void nni_gpu_freeMemory(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
nn_return_integer(computer, gpu->ctrl.totalVRAM - gpu->usedVRAM);
}
void nni_gpu_getActiveBuffer(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
nn_return_integer(computer, gpu->activeBuffer);
}
void nni_gpu_setActiveBuffer(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
int buf = nn_toInt(nn_getArgument(computer, 0));
if(!nni_gpu_validActiveScreen(gpu, buf)) {
nn_setCError(computer, "invalid buffer");
return;
}
gpu->activeBuffer = buf;
nn_return_integer(computer, buf);
}
void nni_gpu_buffers(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
int bufCount = 0;
for(int i = 0; i < gpu->ctrl.maximumBufferCount; i++) {
if(gpu->buffers[i] != NULL) {
gpu->vramIDBuf[bufCount] = i + 1;
bufCount++;
}
}
nn_value arr = nn_return_array(computer, bufCount);
for(int i = 0; i < bufCount; i++) {
nn_values_set(arr, i, nn_values_integer(gpu->vramIDBuf[i]));
}
}
void nni_gpu_allocateBuffer(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
int width = gpu->ctrl.defaultBufferWidth;
int height = gpu->ctrl.defaultBufferHeight;
nn_value widthVal = nn_getArgument(computer, 0);
nn_value heightVal = nn_getArgument(computer, 1);
if(widthVal.tag != NN_VALUE_NIL) {
width = nn_toInt(widthVal);
}
if(heightVal.tag != NN_VALUE_NIL) {
height = nn_toInt(heightVal);
}
if(width < 0 || height < 0) {
nn_setCError(computer, "invalid size");
return;
}
nn_size_t vramNeeded = nni_vramNeededForSize(width, height);
if(gpu->usedVRAM + vramNeeded > gpu->ctrl.totalVRAM) {
nn_setCError(computer, "out of vram");
return;
}
nn_size_t idx = 0;
for(nn_size_t i = 0; i < gpu->ctrl.maximumBufferCount; i++) {
if(gpu->buffers[i] == NULL) {
idx = i + 1;
break;
}
}
if(idx == 0) {
nn_setCError(computer, "too many buffers");
return;
}
nni_buffer *buf = nni_vram_newBuffer(&gpu->alloc, width, height);
if(buf == NULL) {
nn_setCError(computer, "out of memory");
return;
}
gpu->buffers[idx - 1] = buf;
gpu->usedVRAM += vramNeeded;
nn_return_integer(computer, idx);
}
void nni_gpu_freeBuffer(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
int bufidx = gpu->activeBuffer;
nn_value bufVal = nn_getArgument(computer, 0);
if(bufVal.tag != NN_VALUE_NIL) {
bufidx = nn_toInt(bufVal);
}
if(!nni_gpu_validActiveScreen(gpu, bufidx) || bufidx == 0) {
nn_setCError(computer, "invalid buffer");
return;
}
nni_buffer *buf = gpu->buffers[bufidx - 1];
if(buf == NULL) {
nn_setCError(computer, "invalid buffer");
return;
}
int vramUsed = buf->width * buf->height;
nni_vram_deinit(&gpu->alloc, buf);
gpu->buffers[bufidx - 1] = NULL;
if(bufidx == gpu->activeBuffer) gpu->activeBuffer = 0;
gpu->usedVRAM -= vramUsed;
}
void nni_gpu_freeAllBuffers(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
gpu->activeBuffer = 0;
for(nn_size_t i = 0; i < gpu->ctrl.maximumBufferCount; i++) {
if(gpu->buffers[i] != NULL) {
int vramUsed = gpu->buffers[i]->width * gpu->buffers[i]->height;
nni_vram_deinit(&gpu->alloc, gpu->buffers[i]);
gpu->buffers[i] = NULL;
gpu->usedVRAM -= vramUsed;
}
}
}
void nni_gpu_getBufferSize(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
int bufidx = gpu->activeBuffer;
nn_value bufVal = nn_getArgument(computer, 0);
if(bufVal.tag != NN_VALUE_NIL) {
bufidx = nn_toInt(bufVal);
}
if(!nni_gpu_validActiveScreen(gpu, bufidx)) {
nn_setCError(computer, "invalid buffer");
return;
}
if(bufidx == 0) {
if(gpu->currentScreen == NULL) {
nn_setCError(computer, "invalid buffer");
return;
}
int w, h;
nn_getResolution(gpu->currentScreen, &w, &h);
nn_return_integer(computer, w);
nn_return_integer(computer, h);
return;
}
nni_buffer *buf = gpu->buffers[bufidx - 1];
if(buf == NULL) {
nn_setCError(computer, "invalid buffer");
return;
}
nn_return_integer(computer, buf->width);
nn_return_integer(computer, buf->height);
}
void nni_gpu_bitblt(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) {
// I will kill OC creators for this
int dst = nn_toIntOr(nn_getArgument(computer, 0), 0);
int x = nn_toIntOr(nn_getArgument(computer, 1), 1);
int y = nn_toIntOr(nn_getArgument(computer, 2), 1);
int width = nn_toIntOr(nn_getArgument(computer, 3), 0);
int height = nn_toIntOr(nn_getArgument(computer, 4), 0);
int src = nn_toIntOr(nn_getArgument(computer, 5), gpu->activeBuffer);
int fromCol = nn_toIntOr(nn_getArgument(computer, 6), 1);
int fromRow = nn_toIntOr(nn_getArgument(computer, 7), 1);
if(x < 1) x = 1;
if(y < 1) y = 1;
if(fromCol < 1) fromCol = 1;
if(fromRow < 1) fromRow = 1;
if(!nni_gpu_validActiveScreen(gpu, dst)) {
nn_setCError(computer, "invalid destination buffer");
return;
}
if(!nni_gpu_validActiveScreen(gpu, src)) {
nn_setCError(computer, "invalid source buffer");
return;
}
// such great parsing
if(dst == 0) {
if(gpu->currentScreen == NULL) return;
int w, h;
nn_getResolution(gpu->currentScreen, &w, &h);
width = width == 0 ? w : width;
height = height == 0 ? h : height;
} else {
nni_buffer *buffer = gpu->buffers[dst - 1];
if(buffer == NULL) return;
width = width == 0 ? buffer->width : width;
height = height == 0 ? buffer->height : height;
}
if(dst == src) {
nn_setCError(computer, "invalid operation, use copy() instead");
return;
}
// from buffer to screen
if(dst == 0 && src != 0) {
nn_screen *screen = gpu->currentScreen;
nni_buffer *buf = gpu->buffers[src - 1];
if(buf == NULL) {
nn_setCError(computer, "invalid source buffer");
return;
}
int w, h;
nn_getResolution(gpu->currentScreen, &w, &h);
if(width > w) width = w;
if(height > h) height = h;
for(int j = 0; j < height; j++) {
for(int i = 0; i < width; i++) {
nn_scrchr_t src = nni_vram_getPixel(buf, i + fromCol - 1, j + fromRow - 1);
nn_setPixel(screen, i + x - 1, j + y - 1, src);
}
}
return;
}
// from screen to buffer
if(dst != 0 && src == 0) {
nn_screen *screen = gpu->currentScreen;
nni_buffer *buf = gpu->buffers[dst - 1];
if(buf == NULL) {
nn_setCError(computer, "invalid destination buffer");
return;
}
if(width > buf->width) width = buf->width;
if(height > buf->height) height = buf->height;
for(int j = 0; j < height; j++) {
for(int i = 0; i < width; i++) {
nn_scrchr_t src = nn_getPixel(screen, i + fromCol - 1, j + fromRow - 1);
nni_vram_setPixel(buf, i + x - 1, j + y - 1, src);
}
}
return;
}
// from buffer to buffer
if(dst != 0 && src != 0) {
nni_buffer *srcBuf = gpu->buffers[src - 1];
if(srcBuf == NULL) {
nn_setCError(computer, "invalid destination buffer");
return;
}
nni_buffer *destBuf = gpu->buffers[dst - 1];
if(destBuf == NULL) {
nn_setCError(computer, "invalid destination buffer");
return;
}
if(width > destBuf->width) width = destBuf->width;
if(height > destBuf->height) height = destBuf->height;
for(int j = 0; j < height; j++) {
for(int i = 0; i < width; i++) {
nn_scrchr_t src = nni_vram_getPixel(srcBuf, i + fromCol - 1, j + fromRow - 1);
nni_vram_setPixel(destBuf, i + x - 1, j + y - 1, src);
}
}
return;
}
}
void nn_loadGraphicsCardTable(nn_universe *universe) {
nn_componentTable *gpuTable = nn_newComponentTable(nn_getAllocator(universe), "gpu", NULL, NULL, (nn_componentDestructor *)nni_gpuDeinit);
nn_storeUserdata(universe, "NN:GPU", gpuTable);
nn_method_t *method = NULL;
method = nn_defineMethod(gpuTable, "bind", (nn_componentMethod *)nni_gpu_bind, "bind(addr: string[, reset: boolean = false]): boolean - Bind a GPU to a screen. Very expensive. If reset is true, it will clear the screen.");
nn_method_setDirect(method, false);
nn_defineMethod(gpuTable, "getScreen", (nn_componentMethod *)nni_gpu_getScreen, "getScreen(): string");
nn_defineMethod(gpuTable, "set", (nn_componentMethod *)nni_gpu_set, "set(x: integer, y: integer, text: string[, vertical: boolean = false]) - Modifies the screen at a specific x or y. If vertical is false, it will display it horizontally. If it is true, it will display it vertically.");
nn_defineMethod(gpuTable, "get", (nn_componentMethod *)nni_gpu_get, "get(x: integer, y: integer): string, integer, integer, integer?, integer? - Returns the character, foreground color, background color, foreground palette index (if applicable), background palette index (if applicable) of a pixel");
nn_defineMethod(gpuTable, "maxResolution", (nn_componentMethod *)nni_gpu_maxResolution, "maxResolution(): integer, integer - Gets the maximum resolution supported by the bound screen.");
nn_defineMethod(gpuTable, "getResolution", (nn_componentMethod *)nni_gpu_getResolution, "getResolution(): integer, integer - Gets the current resolution of the bound screen.");
nn_defineMethod(gpuTable, "setResolution", (nn_componentMethod *)nni_gpu_setResolution, "maxResolution(): integer, integer - Changes the resolution of the bound screen.");
nn_defineMethod(gpuTable, "setBackground", (nn_componentMethod *)nni_gpu_setBackground, "setBackground(color: integer, isPalette: boolean): integer, integer? - Sets the current background color. Returns the old one and palette index if applicable.");
nn_defineMethod(gpuTable, "setForeground", (nn_componentMethod *)nni_gpu_setForeground, "setForeground(color: integer, isPalette: boolean): integer, integer? - Sets the current foreground color. Returns the old one and palette index if applicable.");
nn_defineMethod(gpuTable, "getBackground", (nn_componentMethod *)nni_gpu_getBackground, "setBackground(color: integer, isPalette: boolean): integer, integer? - Sets the current background color. Returns the old one and palette index if applicable.");
nn_defineMethod(gpuTable, "getForeground", (nn_componentMethod *)nni_gpu_getForeground, "setForeground(color: integer, isPalette: boolean): integer, integer? - Sets the current foreground color. Returns the old one and palette index if applicable.");
nn_defineMethod(gpuTable, "getDepth", (nn_componentMethod *)nni_gpu_getDepth, "getDepth(): number - The currently set color depth of the screen, in bits. Can be 1, 4 or 8.");
nn_defineMethod(gpuTable, "setDepth", (nn_componentMethod *)nni_gpu_setDepth, "setDepth(depth: integer): string - Changes the screen depth. Valid values can be 1, 4, 8, 16 or 24, however check maxDepth for the maximum supported value of the screen. Using a depth higher than what is supported by the screen will error. Returns the name of the new depth.");
nn_defineMethod(gpuTable, "maxDepth", (nn_componentMethod *)nni_gpu_maxDepth, "maxDepth(): number - The maximum supported depth of the screen.");
nn_defineMethod(gpuTable, "fill", (nn_componentMethod *)nni_gpu_fill, "fill(x: integer, y: integer, w: integer, h: integer, s: string)");
nn_defineMethod(gpuTable, "copy", (nn_componentMethod *)nni_gpu_copy, "copy(x: integer, y: integer, w: integer, h: integer, tx: integer, ty: integer) - Copies stuff");
nn_defineMethod(gpuTable, "getViewport", (nn_componentMethod *)nni_gpu_getViewport, "getViewport(): integer, integer - Gets the current viewport resolution");
// VRAM buffers
nn_defineMethod(gpuTable, "totalMemory", (nn_componentMethod *)nni_gpu_totalMemory, "totalMemory(): integer - Returns the VRAM capacity of the card");
nn_defineMethod(gpuTable, "usedMemory", (nn_componentMethod *)nni_gpu_usedMemory, "usedMemory(): integer - Returns the amount of used VRAM");
nn_defineMethod(gpuTable, "freeMemory", (nn_componentMethod *)nni_gpu_freeMemory, "freeMemory(): integer - Returns the amount of unused VRAM");
nn_defineMethod(gpuTable, "buffers", (nn_componentMethod *)nni_gpu_buffers, "buffers(): integer[] - Returns the VRAM buffers allocated (not including the screen)");
nn_defineMethod(gpuTable, "setActiveBuffer", (nn_componentMethod *)nni_gpu_setActiveBuffer, "setActiveBuffer(buffer: integer): integer - Changes the current buffer");
nn_defineMethod(gpuTable, "getActiveBuffer", (nn_componentMethod *)nni_gpu_getActiveBuffer, "getActiveBuffer(): integer - Returns the current buffer");
nn_defineMethod(gpuTable, "allocateBuffer", (nn_componentMethod *)nni_gpu_allocateBuffer, "allocateBuffer([width: integer, height: integer]): integer - Allocates a new VRAM buffer. Default size depends on GPU.");
nn_defineMethod(gpuTable, "freeBuffer", (nn_componentMethod *)nni_gpu_freeBuffer, "freeBuffer([buffer: integer]): boolean - Frees a buffer. By default, the current buffer. If the current buffer is freed, it will switch back to the screen.");
nn_defineMethod(gpuTable, "freeAllBuffers", (nn_componentMethod *)nni_gpu_freeAllBuffers, "freeAllBuffers() - Frees every VRAM buffer (if any). Also switches back to the screen.");
nn_defineMethod(gpuTable, "getBufferSize", (nn_componentMethod *)nni_gpu_getBufferSize, "getBufferSize(buffer: integer): integer, integer - Returns the size of the specified buffer.");
nn_defineMethod(gpuTable, "bitblt", (nn_componentMethod *)nni_gpu_bitblt, "bitblt([dst: integer, x: integer, y: integer, w: integer, h: integer, src: integer, fromCol: integer, fromRow: integer]) - Copy regions between buffers");
}
nn_component *nn_addGPU(nn_computer *computer, nn_address address, int slot, nn_gpuControl *control) {
nn_componentTable *gpuTable = nn_queryUserdata(nn_getUniverse(computer), "NN:GPU");
nni_gpu *gpu = nni_newGPU(nn_getAllocator(nn_getUniverse(computer)), control);
if(gpu == NULL) {
return NULL;
}
return nn_newComponent(computer, address, slot, gpuTable, gpu);
}

View File

@@ -1,13 +0,0 @@
#include "hologram.h"
// safety:
// For valid indexes to be valid,
// stuff must be from 0 to limit - 1
nn_size_t nn_positionToIndex(nn_hologram *h, unsigned x, unsigned y, unsigned z) {
return x + y * h->width_x + z * h->width_x * h->height;
}
void nn_hologram_clear(nn_hologram *h) {
}

View File

@@ -1,38 +0,0 @@
#ifndef NN_HOLOGRAM_H
#define NN_HOLOGRAM_H
#include "../neonucleus.h"
typedef struct nn_hologram {
nn_Context ctx;
nn_guard *lock;
nn_refc refc;
int pallette_len;
int* pallette_array;
int width_x;
int width_z;
int height;
float minScale;
float maxScale;
float scale;
int depth;
float min_translationX;
float max_translationX;
float translationX;
float min_translationY;
float max_translationY;
float translationY;
float min_translationZ;
float max_translationZ;
float translationZ;
int* grid; // I don't know what to call this
} nn_hologram;
#endif

View File

@@ -1,11 +0,0 @@
#include "../neonucleus.h"
void nn_loadKeyboardTable(nn_universe *universe) {
nn_componentTable *keyboardTable = nn_newComponentTable(nn_getAllocator(universe), "keyboard", NULL, NULL, NULL);
nn_storeUserdata(universe, "NN:KEYBOARD", keyboardTable);
}
nn_component *nn_mountKeyboard(nn_computer *computer, nn_address address, int slot) {
nn_componentTable *keyboardTable = nn_queryUserdata(nn_getUniverse(computer), "NN:KEYBOARD");
return nn_newComponent(computer, address, slot, keyboardTable, NULL);
}

View File

@@ -1,144 +0,0 @@
#include "../neonucleus.h"
typedef struct nn_modemLoop {
nn_Context ctx;
nn_debugLoopbackNetworkOpts opts;
nn_size_t *openPorts;
nn_size_t strength;
char wakeup[NN_MAX_WAKEUPMSG];
nn_size_t wakeupLen;
} nn_modemLoop;
void nn_loopModem_deinit(nn_modemLoop *loop) {
nn_Context ctx = loop->ctx;
nn_deallocStr(&ctx.allocator, loop->opts.address);
nn_dealloc(&ctx.allocator, loop->openPorts, loop->opts.maxOpenPorts * sizeof(nn_size_t));
nn_dealloc(&ctx.allocator, loop, sizeof(nn_modemLoop));
}
nn_bool_t nn_loopModem_isOpen(nn_modemLoop *loop, nn_size_t port, nn_errorbuf_t err) {
for(nn_size_t i = 0; i < loop->opts.maxOpenPorts; i++) {
if(loop->openPorts[i] == port) {
return true;
}
}
return false;
}
nn_bool_t nn_loopModem_open(nn_modemLoop *loop, nn_size_t port, nn_errorbuf_t err) {
int slot = -1;
for(nn_size_t i = 0; i < loop->opts.maxOpenPorts; i++) {
if(loop->openPorts[i] == NN_PORT_CLOSEALL) {
slot = i;
break;
}
}
if(slot == -1) {
nn_error_write(err, "too many open ports");
return false;
}
loop->openPorts[slot] = port;
return true;
}
nn_bool_t nn_loopModem_close(nn_modemLoop *loop, nn_size_t port, nn_errorbuf_t err) {
if(port == NN_PORT_CLOSEALL) {
for(nn_size_t i = 0; i < loop->opts.maxOpenPorts; i++) loop->openPorts[i] = NN_PORT_CLOSEALL;
return true;
}
for(nn_size_t i = 0; i < loop->opts.maxOpenPorts; i++) {
if(loop->openPorts[i] == port) {
loop->openPorts[i] = NN_PORT_CLOSEALL;
return true;
}
}
nn_error_write(err, "port already closed");
return false;
}
nn_size_t nn_loopModem_getPorts(nn_modemLoop *loop, nn_size_t *ports, nn_errorbuf_t err) {
nn_size_t len = 0;
for(nn_size_t i = 0; i < loop->opts.maxOpenPorts; i++) {
if(loop->openPorts[i] != NN_PORT_CLOSEALL) {
ports[len] = loop->openPorts[i];
len++;
}
}
return len;
}
nn_bool_t nn_loopModem_send(nn_modemLoop *loop, nn_address address, nn_size_t port, nn_value *values, nn_size_t valuec, nn_errorbuf_t err) {
if(address == NULL) {
// broadcasting, set it to our address
address = loop->opts.address;
}
// error is discarded as packet loss
nn_pushNetworkMessage(loop->opts.computer, loop->opts.address, address, port, loop->strength, values, valuec);
return true;
}
double nn_loopModem_getStrength(nn_modemLoop *loop, nn_errorbuf_t err) {
return loop->strength;
}
double nn_loopModem_setStrength(nn_modemLoop *loop, double n, nn_errorbuf_t err) {
loop->strength = n;
return n;
}
nn_size_t nn_loopModem_getWakeMessage(nn_modemLoop *loop, char *msg, nn_errorbuf_t err) {
nn_memcpy(msg, loop->wakeup, loop->wakeupLen);
return loop->wakeupLen;
}
nn_size_t nn_loopModem_setWakeMessage(nn_modemLoop *loop, const char *msg, nn_size_t msglen, nn_bool_t fuzzy, nn_errorbuf_t err) {
loop->wakeupLen = msglen;
nn_memcpy(loop->wakeup, msg, loop->wakeupLen);
return loop->wakeupLen;
}
nn_modem *nn_debugLoopbackModem(nn_Context *context, nn_debugLoopbackNetworkOpts opts, nn_networkControl control) {
opts.address = nn_strdup(&context->allocator, opts.address);
nn_modemLoop *m = nn_alloc(&context->allocator, sizeof(nn_modemLoop));
m->ctx = *context;
m->opts = opts;
m->strength = opts.maxStrength;
m->wakeupLen = 0;
m->openPorts = nn_alloc(&context->allocator, opts.maxOpenPorts * sizeof(nn_size_t));
for(nn_size_t i = 0; i < opts.maxOpenPorts; i++) {
m->openPorts[i] = NN_PORT_CLOSEALL; // used as a NULL port
}
nn_modemTable table = {
.userdata = m,
.deinit = (void *)nn_loopModem_deinit,
.wireless = opts.isWireless,
.maxValues = opts.maxValues,
.maxOpenPorts = opts.maxOpenPorts,
.maxPacketSize = opts.maxPacketSize,
.isOpen = (void *)nn_loopModem_isOpen,
.open = (void *)nn_loopModem_open,
.close = (void *)nn_loopModem_close,
.getPorts = (void *)nn_loopModem_getPorts,
.send = (void *)nn_loopModem_send,
.maxStrength = opts.maxStrength,
.getStrength = (void *)nn_loopModem_getStrength,
.setStrength = (void *)nn_loopModem_setStrength,
.setWakeMessage = (void *)nn_loopModem_setWakeMessage,
.getWakeMessage = (void *)nn_loopModem_getWakeMessage,
};
return nn_newModem(context, table, control);
}

View File

@@ -1,59 +0,0 @@
#include "../neonucleus.h"
typedef struct nn_loopTunnel_t {
nn_Context ctx;
nn_debugLoopbackNetworkOpts opts;
char wakeup[NN_MAX_WAKEUPMSG];
nn_size_t wakeupLen;
} nn_loopTunnel_t;
static void nni_debugTunnel_deinit(nn_loopTunnel_t *t) {
nn_Alloc a = t->ctx.allocator;
nn_deallocStr(&a, t->opts.address);
nn_dealloc(&a, t, sizeof(nn_loopTunnel_t));
}
static nn_size_t nni_debugTunnel_getChannel(nn_loopTunnel_t *t, char *buf, nn_errorbuf_t err) {
nn_strcpy(buf, "loopback");
return 8;
}
static nn_size_t nni_debugTunnel_getWakeMessage(nn_loopTunnel_t *t, char *buf, nn_errorbuf_t err) {
nn_memcpy(buf, t->wakeup, t->wakeupLen);
return t->wakeupLen;
}
static nn_size_t nni_debugTunnel_setWakeMessage(nn_loopTunnel_t *t, const char *buf, nn_size_t buflen, nn_bool_t fuzzy, nn_errorbuf_t err) {
if(buflen > NN_MAX_CHANNEL_SIZE) buflen = NN_MAX_CHANNEL_SIZE;
nn_memcpy(t->wakeup, buf, buflen);
t->wakeupLen = buflen;
return buflen;
}
static void nni_debugTunnel_send(nn_loopTunnel_t *t, nn_value *values, nn_size_t valueCount, nn_errorbuf_t err) {
nn_pushNetworkMessage(t->opts.computer, t->opts.address, "loopback", NN_TUNNEL_PORT, 0, values, valueCount);
}
nn_tunnel *nn_debugLoopbackTunnel(nn_Context *context, nn_debugLoopbackNetworkOpts opts, nn_networkControl control) {
nn_Alloc *alloc = &context->allocator;
nn_loopTunnel_t *t = nn_alloc(alloc, sizeof(nn_loopTunnel_t));
t->ctx = *context;
t->opts = opts;
t->opts.address = nn_strdup(alloc, t->opts.address);
t->wakeupLen = 0;
nn_tunnelTable table = {
.userdata = t,
.deinit = (void *)nni_debugTunnel_deinit,
.maxValues = opts.maxValues,
.maxPacketSize = opts.maxPacketSize,
.getChannel = (void *)nni_debugTunnel_getChannel,
.getWakeMessage = (void *)nni_debugTunnel_getWakeMessage,
.setWakeMessage = (void *)nni_debugTunnel_setWakeMessage,
.send = (void *)nni_debugTunnel_send,
};
return nn_newTunnel(context, table, control);
}

View File

@@ -1,351 +0,0 @@
#include "../neonucleus.h"
typedef struct nn_modem {
nn_Context ctx;
nn_guard *lock;
nn_refc refc;
nn_modemTable table;
nn_networkControl ctrl;
} nn_modem;
nn_modem *nn_newModem(nn_Context *context, nn_modemTable table, nn_networkControl control) {
nn_modem *m = nn_alloc(&context->allocator, sizeof(nn_modem));
if(m == NULL) return m;
m->ctx = *context;
m->lock = nn_newGuard(context);
m->refc = 1;
m->table = table;
m->ctrl = control;
return m;
}
nn_guard *nn_getModemLock(nn_modem *modem) {
return modem->lock;
}
void nn_retainModem(nn_modem *modem) {
nn_incRef(&modem->refc);
}
nn_bool_t nn_destroyModem(nn_modem *modem) {
if(!nn_decRef(&modem->refc)) return false;
if(modem->table.deinit != NULL) {
modem->table.deinit(modem->table.userdata);
}
nn_Context ctx = modem->ctx;
nn_deleteGuard(&ctx, modem->lock);
nn_dealloc(&ctx.allocator, modem, sizeof(nn_modem));
return true;
}
void nn_modem_destroy(void *_, nn_component *component, nn_modem *modem) {
nn_destroyModem(modem);
}
static void nni_modem_isWireless(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) {
nn_return_boolean(computer, modem->table.wireless);
}
static void nni_modem_maxPacketSize(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) {
nn_return_integer(computer, modem->table.maxPacketSize);
}
static void nni_modem_maxOpenPorts(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) {
nn_return_integer(computer, modem->table.maxOpenPorts);
}
static void nni_modem_maxValues(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) {
nn_return_integer(computer, modem->table.maxValues);
}
static nn_bool_t nni_modem_wirelessOnly(nn_modem *modem, void *_) {
return modem->table.wireless;
}
static void nni_modem_maxStrength(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) {
nn_return_number(computer, modem->table.maxStrength);
}
static void nni_modem_getStrength(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) {
nn_errorbuf_t err = "";
nn_lock(&modem->ctx, modem->lock);
double n = modem->table.getStrength(modem->table.userdata, err);
nn_unlock(&modem->ctx, modem->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_number(computer, n);
}
static void nni_modem_setStrength(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) {
double n = nn_toNumber(nn_getArgument(computer, 0));
if(n < 0) n = 0;
if(n > modem->table.maxStrength) n = modem->table.maxStrength;
nn_errorbuf_t err = "";
nn_lock(&modem->ctx, modem->lock);
n = modem->table.setStrength(modem->table.userdata, n, err);
nn_unlock(&modem->ctx, modem->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_number(computer, n);
}
static nn_bool_t nni_modem_validSendPort(nn_integer_t port) {
// 9 quintillion ports just died
if(port < 0) return false;
// the only valid range
if(port > NN_PORT_MAX) return false;
if(port < 1) return false;
// whichever it is reserved as (clean code moment)
if(port == NN_TUNNEL_PORT) return false;
return true;
}
static void nni_modem_isOpen(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) {
nn_integer_t port = nn_toInt(nn_getArgument(computer, 0));
if(!nni_modem_validSendPort(port)) {
nn_setCError(computer, "invalid port");
return;
}
nn_errorbuf_t err = "";
nn_lock(&modem->ctx, modem->lock);
nn_bool_t res = modem->table.isOpen(modem->table.userdata, port, err);
nn_unlock(&modem->ctx, modem->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_boolean(computer, res);
}
static void nni_modem_open(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) {
nn_integer_t port = nn_toInt(nn_getArgument(computer, 0));
if(!nni_modem_validSendPort(port)) {
nn_setCError(computer, "invalid port");
return;
}
nn_errorbuf_t err = "";
nn_lock(&modem->ctx, modem->lock);
nn_bool_t res = modem->table.open(modem->table.userdata, port, err);
nn_unlock(&modem->ctx, modem->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_boolean(computer, res);
}
static void nni_modem_close(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) {
nn_value portVal = nn_getArgument(computer, 0);
nn_integer_t port = portVal.tag == NN_VALUE_NIL ? NN_PORT_CLOSEALL : nn_toInt(portVal);
if(!nni_modem_validSendPort(port) && port != NN_PORT_CLOSEALL) {
nn_setCError(computer, "invalid port");
return;
}
nn_errorbuf_t err = "";
nn_lock(&modem->ctx, modem->lock);
nn_bool_t res = modem->table.close(modem->table.userdata, port, err);
nn_unlock(&modem->ctx, modem->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_boolean(computer, res);
}
static void nni_modem_getPorts(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) {
nn_size_t ports[modem->table.maxOpenPorts];
nn_errorbuf_t err = "";
nn_lock(&modem->ctx, modem->lock);
nn_size_t portCount = modem->table.getPorts(modem->table.userdata, ports, err);
nn_unlock(&modem->ctx, modem->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_value arr = nn_return_array(computer, portCount);
for(nn_size_t i = 0; i < portCount; i++) {
nn_values_set(arr, i, nn_values_integer(ports[i]));
}
}
static void nni_modem_send(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) {
// we pinky promise it won't do a fucky wucky
nn_address addr = (nn_address)nn_toCString(nn_getArgument(computer, 0));
if(addr == NULL) {
nn_setCError(computer, "invalid address");
return;
}
nn_value portVal = nn_getArgument(computer, 1);
nn_integer_t port = portVal.tag == NN_VALUE_NIL ? NN_PORT_CLOSEALL : nn_toInt(portVal);
if(!nni_modem_validSendPort(port) && port != NN_PORT_CLOSEALL) {
nn_setCError(computer, "invalid port");
return;
}
nn_value vals[modem->table.maxValues];
nn_size_t valLen = nn_getArgumentCount(computer) - 2;
if(valLen > modem->table.maxValues) {
nn_setCError(computer, "too many values");
return;
}
for(nn_size_t i = 0; i < valLen; i++) {
vals[i] = nn_getArgument(computer, i + 2);
}
nn_size_t bytesSent = nn_measurePacketSize(vals, valLen);
if(bytesSent > modem->table.maxPacketSize) {
nn_setCError(computer, "packet too big");
return;
}
nn_simulateBufferedIndirect(component, bytesSent, modem->ctrl.packetBytesPerTick);
double d = (double)bytesSent / modem->table.maxPacketSize;
nn_addHeat(computer, d * modem->ctrl.heatPerFullPacket);
nn_removeEnergy(computer, d * modem->ctrl.energyPerFullPacket);
nn_errorbuf_t err = "";
nn_lock(&modem->ctx, modem->lock);
nn_bool_t res = modem->table.send(modem->table.userdata, addr, port, vals, valLen, err);
nn_unlock(&modem->ctx, modem->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_boolean(computer, res);
}
static void nni_modem_broadcast(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) {
nn_value portVal = nn_getArgument(computer, 0);
nn_integer_t port = portVal.tag == NN_VALUE_NIL ? NN_PORT_CLOSEALL : nn_toInt(portVal);
if(!nni_modem_validSendPort(port) && port != NN_PORT_CLOSEALL) {
nn_setCError(computer, "invalid port");
return;
}
nn_value vals[modem->table.maxValues];
nn_size_t valLen = nn_getArgumentCount(computer) - 1;
if(valLen > modem->table.maxValues) {
nn_setCError(computer, "too many values");
return;
}
for(nn_size_t i = 0; i < valLen; i++) {
vals[i] = nn_getArgument(computer, i + 1);
}
nn_size_t bytesSent = nn_measurePacketSize(vals, valLen);
if(bytesSent > modem->table.maxPacketSize) {
nn_setCError(computer, "packet too big");
return;
}
nn_simulateBufferedIndirect(component, bytesSent, modem->ctrl.packetBytesPerTick);
double d = (double)bytesSent / modem->table.maxPacketSize;
nn_addHeat(computer, d * modem->ctrl.heatPerFullPacket);
nn_removeEnergy(computer, d * modem->ctrl.energyPerFullPacket);
nn_errorbuf_t err = "";
nn_lock(&modem->ctx, modem->lock);
nn_bool_t res = modem->table.send(modem->table.userdata, NULL, port, vals, valLen, err);
nn_unlock(&modem->ctx, modem->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_boolean(computer, res);
}
static void nni_modem_getWake(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) {
char msg[NN_MAX_WAKEUPMSG];
nn_errorbuf_t err = "";
nn_lock(&modem->ctx, modem->lock);
nn_size_t len = modem->table.getWakeMessage(modem->table.userdata, msg, err);
nn_unlock(&modem->ctx, modem->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_string(computer, msg, len);
}
static void nni_modem_setWake(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) {
nn_size_t buflen;
const char *buf = nn_toString(nn_getArgument(computer, 0), &buflen);
if(buf == NULL) {
nn_setCError(computer, "invalid wake message");
return;
}
if(buflen > NN_MAX_WAKEUPMSG) {
buflen = NN_MAX_WAKEUPMSG;
}
nn_bool_t fuzzy = nn_toBoolean(nn_getArgument(computer, 1)); // nil is false
nn_errorbuf_t err = "";
nn_lock(&modem->ctx, modem->lock);
buflen = modem->table.setWakeMessage(modem->table.userdata, buf, buflen, fuzzy, err);
nn_unlock(&modem->ctx, modem->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_string(computer, buf, buflen);
}
void nn_loadModemTable(nn_universe *universe) {
nn_componentTable *modemTable = nn_newComponentTable(nn_getAllocator(universe), "modem", NULL, NULL, (nn_componentDestructor *)nn_modem_destroy);
nn_storeUserdata(universe, "NN:MODEM", modemTable);
nn_method_t *method;
nn_defineMethod(modemTable, "isWireless", (nn_componentMethod *)nni_modem_isWireless, "isWireless(): boolean - Returns whether this modem has wireless capabilities");
nn_defineMethod(modemTable, "maxPacketSize", (nn_componentMethod *)nni_modem_maxPacketSize, "maxPacketSize(): integer - Returns the maximum size of a packet");
nn_defineMethod(modemTable, "maxOpenPorts", (nn_componentMethod *)nni_modem_maxOpenPorts, "maxOpenPorts(): integer - Returns the maximum number of ports that can be open at the same time");
nn_defineMethod(modemTable, "maxValues", (nn_componentMethod *)nni_modem_maxValues, "maxValues(): integer - Returns the maximum number of values which can be sent in a packet");
nn_defineMethod(modemTable, "isOpen", (nn_componentMethod *)nni_modem_isOpen, "isOpen(port: integer): boolean - Returns whether a port is open (allows receiving)");
nn_defineMethod(modemTable, "open", (nn_componentMethod *)nni_modem_open, "open(port: integer): boolean - Opens a port, returns whether it was successful");
nn_defineMethod(modemTable, "close", (nn_componentMethod *)nni_modem_close, "close([port: integer]): boolean - Closes a port, or nil for all ports");
nn_defineMethod(modemTable, "getPorts", (nn_componentMethod *)nni_modem_getPorts, "getPorts(): integer[] - Returns the open ports");
nn_defineMethod(modemTable, "send", (nn_componentMethod *)nni_modem_send, "send(address: string, port: integer, ...): boolean - Sends a message to the specified address at the given port. It returns whether the message was sent, not received");
nn_defineMethod(modemTable, "broadcast", (nn_componentMethod *)nni_modem_broadcast, "broadcast(port: integer, ...): boolean - Broadcasts a message at the given port. It returns whether the message was sent, not received");
nn_defineMethod(modemTable, "setWakeMessage", (nn_componentMethod *)nni_modem_setWake, "setWakeMessage(msg: string[, fuzzy: boolean]): string - Sets the wake-up message. This will be compared with the first value of modem messages to turn on computers. Set it to nothing to disable this functionality.");
nn_defineMethod(modemTable, "getWakeMessage", (nn_componentMethod *)nni_modem_getWake, "getWakeMessage(): string - Returns the current wake-up message");
// wireless stuff
method = nn_defineMethod(modemTable, "maxStrength", (nn_componentMethod *)nni_modem_maxStrength, "maxStrength(): number - Returns the maximum strength of the device");
nn_method_setCondition(method, (nn_componentMethodCondition_t *)nni_modem_wirelessOnly);
method = nn_defineMethod(modemTable, "getStrength", (nn_componentMethod *)nni_modem_getStrength, "getStrength(): number - Returns the current strength of the device");
nn_method_setCondition(method, (nn_componentMethodCondition_t *)nni_modem_wirelessOnly);
method = nn_defineMethod(modemTable, "setStrength", (nn_componentMethod *)nni_modem_setStrength, "setStrength(value: number): number - Returns the current strength of the device");
nn_method_setCondition(method, (nn_componentMethodCondition_t *)nni_modem_wirelessOnly);
}
nn_component *nn_addModem(nn_computer *computer, nn_address address, int slot, nn_modem *modem) {
nn_componentTable *modemTable = nn_queryUserdata(nn_getUniverse(computer), "NN:MODEM");
return nn_newComponent(computer, address, slot, modemTable, modem);
}

View File

@@ -1,512 +0,0 @@
#include "screen.h"
nn_screen *nn_newScreen(nn_Context *context, int maxWidth, int maxHeight, int maxDepth, int editableColors, int paletteColors) {
nn_Alloc *alloc = &context->allocator;
// TODO: handle OOMs
nn_screen *screen = nn_alloc(alloc, sizeof(nn_screen));
screen->ctx = *context;
screen->buffer = nn_alloc(alloc, sizeof(nn_scrchr_t) * maxWidth * maxHeight);
screen->lock = nn_newGuard(context);
screen->refc = 1;
screen->width = maxWidth;
screen->height = maxHeight;
screen->viewportWidth = maxWidth;
screen->viewportHeight = maxHeight;
screen->maxWidth = maxWidth;
screen->maxHeight = maxHeight;
screen->maxDepth = maxDepth;
screen->depth = maxDepth;
screen->editableColors = editableColors;
screen->paletteColors = paletteColors;
screen->palette = nn_alloc(alloc, sizeof(int) * screen->paletteColors);
nn_memset(screen->palette, 0, sizeof(int) * screen->paletteColors);
screen->aspectRatioWidth = 1;
screen->aspectRatioHeight = 1;
screen->isOn = true;
screen->isTouchModeInverted = true;
screen->isPrecise = true;
screen->isDirty = true;
screen->keyboardCount = 0;
return screen;
}
void nn_retainScreen(nn_screen *screen) {
nn_incRef(&screen->refc);
}
void nn_destroyScreen(nn_screen *screen) {
if(!nn_decRef(&screen->refc)) return;
nn_Alloc a = screen->ctx.allocator;
nn_deleteGuard(&screen->ctx, screen->lock);
nn_dealloc(&a, screen->buffer, sizeof(nn_scrchr_t) * screen->maxWidth * screen->maxHeight);
nn_dealloc(&a, screen->palette, sizeof(int) * screen->paletteColors);
nn_dealloc(&a, screen, sizeof(nn_screen));
}
void nn_lockScreen(nn_screen *screen) {
nn_lock(&screen->ctx, screen->lock);
}
void nn_unlockScreen(nn_screen *screen) {
nn_unlock(&screen->ctx, screen->lock);
}
void nn_getResolution(nn_screen *screen, int *width, int *height) {
*width = screen->width;
*height = screen->height;
}
void nn_maxResolution(nn_screen *screen, int *width, int *height) {
*width = screen->maxWidth;
*height = screen->maxHeight;
}
void nn_setResolution(nn_screen *screen, int width, int height) {
screen->width = width;
screen->height = height;
}
nn_bool_t nn_unsafeReallocateScreenBuffer(nn_screen *screen, int maxWidth, int maxHeight) {
nn_Alloc *alloc = &screen->ctx.allocator;
nn_scrchr_t *newBuffer = nn_alloc(alloc, sizeof(nn_scrchr_t) * maxWidth * maxHeight);
if(newBuffer == NULL) {
return false;
}
for(nn_size_t y = 0; y < maxHeight; y++) {
for(nn_size_t x = 0; x < maxWidth; x++) {
nn_size_t destIdx = x + y * maxWidth;
newBuffer[destIdx] = nn_getPixel(screen, x, y);
}
}
nn_dealloc(alloc, screen->buffer, sizeof(nn_scrchr_t) * screen->maxWidth * screen->maxHeight);
screen->buffer = newBuffer;
screen->maxWidth = maxWidth;
screen->maxHeight = maxHeight;
return true;
}
void nn_getViewport(nn_screen *screen, int *width, int *height) {
*width = screen->viewportWidth;
*height = screen->viewportHeight;
}
void nn_setViewport(nn_screen *screen, int width, int height) {
screen->viewportWidth = width;
screen->viewportHeight = height;
}
void nn_getAspectRatio(nn_screen *screen, int *width, int *height) {
*width = screen->aspectRatioWidth;
*height = screen->aspectRatioHeight;
}
void nn_setAspectRatio(nn_screen *screen, int width, int height) {
screen->aspectRatioWidth = width;
screen->aspectRatioHeight = height;
}
void nn_addKeyboard(nn_screen *screen, nn_address address) {
if(screen->keyboardCount == NN_MAX_SCREEN_KEYBOARDS) return;
char *kb = nn_strdup(&screen->ctx.allocator, address);
if(kb == NULL) return;
screen->keyboards[screen->keyboardCount++] = kb;
}
void nn_removeKeyboard(nn_screen *screen, nn_address address) {
nn_size_t j = 0;
for(nn_size_t i = 0; i < screen->keyboardCount; i++) {
if(nn_strcmp(screen->keyboards[i], address) == 0) {
nn_deallocStr(&screen->ctx.allocator, screen->keyboards[i]);
} else {
screen->keyboards[j] = screen->keyboards[i];
j++;
}
}
screen->keyboardCount = j;
}
nn_address nn_getKeyboard(nn_screen *screen, nn_size_t idx) {
if(idx >= screen->keyboardCount) return NULL;
return screen->keyboards[idx];
}
nn_size_t nn_getKeyboardCount(nn_screen *screen) {
return screen->keyboardCount;
}
void nn_setEditableColors(nn_screen *screen, int count) {
screen->editableColors = count;
}
int nn_getEditableColors(nn_screen *screen) {
return screen->editableColors;
}
void nn_setPaletteColor(nn_screen *screen, int idx, int color) {
if(idx >= screen->paletteColors) return;
screen->palette[idx] = color;
}
int nn_getPaletteColor(nn_screen *screen, int idx) {
if(idx >= screen->paletteColors) return 0;
return screen->palette[idx];
}
int nn_getPaletteCount(nn_screen *screen) {
return screen->paletteColors;
}
int nn_maxDepth(nn_screen *screen) {
return screen->maxDepth;
}
int nn_getDepth(nn_screen *screen) {
return screen->depth;
}
void nn_setDepth(nn_screen *screen, int depth) {
if(depth > screen->maxDepth) depth = screen->maxDepth;
screen->depth = depth;
}
void nn_setPixel(nn_screen *screen, int x, int y, nn_scrchr_t pixel) {
if(x < 0) return;
if(y < 0) return;
if(x >= screen->width) return;
if(y >= screen->height) return;
screen->buffer[x + y * screen->maxWidth] = pixel;
screen->isDirty = true; // stuff changed
}
nn_scrchr_t nn_getPixel(nn_screen *screen, int x, int y) {
nn_scrchr_t blank = {
.codepoint = ' ',
.fg = 0xFFFFFF,
.bg = 0x000000,
.isFgPalette = false,
.isBgPalette = false,
};
if(x < 0) return blank;
if(y < 0) return blank;
if(x >= screen->width) return blank;
if(y >= screen->height) return blank;
return screen->buffer[x + y * screen->maxWidth];
}
nn_bool_t nn_isDirty(nn_screen *screen) {
return screen->isDirty;
}
void nn_setDirty(nn_screen *screen, nn_bool_t dirty) {
screen->isDirty = dirty;
}
nn_bool_t nn_isPrecise(nn_screen *screen) {
return screen->isPrecise;
}
void nn_setPrecise(nn_screen *screen, nn_bool_t precise) {
screen->isPrecise = precise;
}
nn_bool_t nn_isTouchModeInverted(nn_screen *screen) {
return screen->isTouchModeInverted;
}
void nn_setTouchModeInverted(nn_screen *screen, nn_bool_t touchModeInverted) {
screen->isTouchModeInverted = touchModeInverted;
}
nn_bool_t nn_isOn(nn_screen *buffer) {
return buffer->isOn;
}
void nn_setOn(nn_screen *buffer, nn_bool_t on) {
buffer->isOn = on;
}
void nn_screenComp_destroy(void *_, nn_component *component, nn_screen *screen) {
nn_destroyScreen(screen);
}
void nn_screenComp_getKeyboards(nn_screen *screen, void *_, nn_component *component, nn_computer *computer) {
nn_lockScreen(screen);
nn_value arr = nn_values_array(&screen->ctx.allocator, nn_getKeyboardCount(screen));
nn_size_t len = arr.array->len;
for(nn_size_t i = 0; i < len; i++) {
nn_size_t addrlen = nn_strlen(nn_getKeyboard(screen, i));
nn_value addr = nn_values_string(&screen->ctx.allocator, nn_getKeyboard(screen, i), addrlen);
nn_values_set(arr, i, addr);
}
nn_unlockScreen(screen);
nn_return(computer, arr);
}
void nn_screenComp_getAspectRatio(nn_screen *screen, void *_, nn_component *component, nn_computer *computer) {
nn_lockScreen(screen);
int w, h;
nn_getAspectRatio(screen, &w, &h);
nn_unlockScreen(screen);
nn_return_integer(computer, w);
nn_return_integer(computer, h);
}
void nn_screenComp_isOn(nn_screen *screen, void *_, nn_component *component, nn_computer *computer) {
nn_lockScreen(screen);
nn_bool_t isOn = nn_isOn(screen);
nn_unlockScreen(screen);
nn_return_boolean(computer, isOn);
}
void nn_screenComp_turnOn(nn_screen *screen, void *_, nn_component *component, nn_computer *computer) {
nn_lockScreen(screen);
nn_bool_t isOff = !nn_isOn(screen);
nn_setOn(screen, true);
nn_unlockScreen(screen);
nn_return_boolean(computer, isOff);
nn_return_boolean(computer, true);
}
void nn_screenComp_turnOff(nn_screen *screen, void *_, nn_component *component, nn_computer *computer) {
nn_lockScreen(screen);
nn_bool_t isOn = nn_isOn(screen);
nn_setOn(screen, false);
nn_unlockScreen(screen);
nn_return_boolean(computer, isOn);
nn_return_boolean(computer, false);
}
void nn_screenComp_setPrecise(nn_screen *screen, void *_, nn_component *component, nn_computer *computer) {
nn_bool_t isPrecise = nn_toBooleanOr(nn_getArgument(computer, 0), true);
nn_lockScreen(screen);
nn_setPrecise(screen, isPrecise);
nn_unlockScreen(screen);
nn_return_boolean(computer, isPrecise);
}
void nn_screenComp_isPrecise(nn_screen *screen, void *_, nn_component *component, nn_computer *computer) {
nn_lockScreen(screen);
nn_bool_t isPrecise = nn_isPrecise(screen);
nn_unlockScreen(screen);
nn_return_boolean(computer, isPrecise);
}
void nn_screenComp_setTouchModeInverted(nn_screen *screen, void *_, nn_component *component, nn_computer *computer) {
nn_bool_t isTouchModeInverted = nn_toBooleanOr(nn_getArgument(computer, 0), true);
nn_lockScreen(screen);
nn_setTouchModeInverted(screen, isTouchModeInverted);
nn_unlockScreen(screen);
nn_return_boolean(computer, isTouchModeInverted);
}
void nn_screenComp_isTouchModeInverted(nn_screen *screen, void *_, nn_component *component, nn_computer *computer) {
nn_lockScreen(screen);
nn_bool_t isTouchModeInverted = nn_isTouchModeInverted(screen);
nn_unlockScreen(screen);
nn_return_boolean(computer, isTouchModeInverted);
}
void nn_loadScreenTable(nn_universe *universe) {
nn_componentTable *screenTable = nn_newComponentTable(nn_getAllocator(universe), "screen", NULL, NULL, (nn_componentDestructor *)nn_screenComp_destroy);
nn_storeUserdata(universe, "NN:SCREEN", screenTable);
nn_defineMethod(screenTable, "getKeyboards", (nn_componentMethod *)nn_screenComp_getKeyboards, "getKeyboards(): string[] - Returns the keyboards registered to this screen.");
nn_defineMethod(screenTable, "getAspectRatio", (nn_componentMethod *)nn_screenComp_getAspectRatio, "getAspectRatio(): integer, integer - Returns the dimensions, in blocks, of the screen.");
nn_defineMethod(screenTable, "isOn", (nn_componentMethod *)nn_screenComp_isOn, "isOn(): boolean - Returns whether the screen is on.");
nn_defineMethod(screenTable, "turnOn", (nn_componentMethod *)nn_screenComp_turnOn, "turnOn(): boolean, boolean - Turns the screen on. Returns whether the screen was off and the new power state.");
nn_defineMethod(screenTable, "turnOff", (nn_componentMethod *)nn_screenComp_turnOff, "turnOff(): boolean, boolean - Turns the screen off. Returns whether the screen was on and the new power state.");
nn_defineMethod(screenTable, "setPrecise", (nn_componentMethod *)nn_screenComp_setPrecise, "setPrecise(precise: boolean) - Sets whether precise (sub-pixel) mouse events are enabled");
nn_defineMethod(screenTable, "isPrecise", (nn_componentMethod *)nn_screenComp_isPrecise, "isPrecise(): boolean - Checks whether precise mouse events are enabled");
nn_defineMethod(screenTable, "setTouchModeInverted", (nn_componentMethod *)nn_screenComp_setTouchModeInverted, "setTouchModeInverted(mode: boolean) - Sets whether inverted touch mode is enabled");
nn_defineMethod(screenTable, "isTouchModeInverted", (nn_componentMethod *)nn_screenComp_isTouchModeInverted, "isTouchModeInverted(): boolean - Checks whether inverted touch mode is enabled");
}
nn_componentTable *nn_getScreenTable(nn_universe *universe) {
return nn_queryUserdata(universe, "NN:SCREEN");
}
nn_component *nn_addScreen(nn_computer *computer, nn_address address, int slot, nn_screen *screen) {
nn_componentTable *screenTable = nn_queryUserdata(nn_getUniverse(computer), "NN:SCREEN");
return nn_newComponent(computer, address, slot, screenTable, screen);
}
static const int nni_mcBlack = 0x000000;
static const int nni_mcWhite = 0xFFFFFF;
void nn_getStd4BitPalette(int color[16]) {
color[0] = nni_mcWhite; // white
color[1] = 0xF9801D; // orange
color[2] = 0xC74EBD; // magenta
color[3] = 0x3AB3DA; // lightblue
color[4] = 0xFED83D; // yellow
color[5] = 0x80C71F; // lime
color[6] = 0xF38BAA; // pink
color[7] = 0x474F52; // gray
color[8] = 0x9D9D97; // silver
color[9] = 0x169C9C; // cyan
color[10] = 0x8932B8; // purple
color[11] = 0x3C44AA; // blue
color[12] = 0x835432; // brown
color[13] = 0x5E7C16; // green
color[14] = 0xB02E26; // red
color[15] = nni_mcBlack; // black
}
void nn_getLegacy4BitPalette(int color[16]) {
// taken from https://github.com/MightyPirates/OpenComputers/blob/master-MC1.12/src/main/scala/li/cil/oc/util/PackedColor.scala
color[0] = 0xFFFFFF;
color[1] = 0xFFCC33;
color[2] = 0xCC66CC;
color[3] = 0x6699FF;
color[4] = 0xFFFF33;
color[5] = 0x33CC33;
color[6] = 0xFF6699;
color[7] = 0x333333;
color[8] = 0xCCCCCC;
color[9] = 0x336699;
color[10] = 0x9933CC;
color[11] = 0x333399;
color[12] = 0x663300;
color[13] = 0x336600;
color[14] = 0xFF3333;
color[15] = 0x000000;
}
void nn_getStd8BitPalette(int color[256]) {
// source: https://ocdoc.cil.li/component:gpu
int reds[6] = {0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF};
int greens[8] = {0x00, 0x24, 0x49, 0x6D, 0x92, 0xB6, 0xDB, 0xFF};
int blues[5] = {0x00, 0x40, 0x80, 0xC0, 0xFF};
for(int r = 0; r < 6; r++) {
for(int g = 0; g < 8; g++) {
for(int b = 0; b < 5; b++) {
int i = r * 8 * 5 + g * 5 + b;
color[i] = (reds[r] << 16) | (greens[g] << 8) | (blues[b]);
}
}
}
// TODO: turn into an algorithm
color[240] = 0x0F0F0F;
color[241] = 0x1E1E1E;
color[242] = 0x2D2D2D;
color[243] = 0x3C3C3C;
color[244] = 0x4B4B4B;
color[245] = 0x5A5A5A;
color[246] = 0x696969;
color[247] = 0x787878;
color[248] = 0x878787;
color[249] = 0x969696;
color[250] = 0xA5A5A5;
color[251] = 0xB4B4B4;
color[252] = 0xC3C3C3;
color[253] = 0xD2D2D2;
color[254] = 0xE1E1E1;
color[255] = 0xF0F0F0;
}
static int nni_4bit_colors[16];
static nn_bool_t nni_4bit_did = false;
static int nni_8bit_colors[256];
static nn_bool_t nni_8bit_did = false;
static int nni_4bitl_colors[16];
static nn_bool_t nni_4bitl_did = false;
const char *nn_depthName(int depth) {
if(depth == 1) return "OneBit";
if(depth == 2) return "TwoBit";
if(depth == 3) return "ThreeBit";
if(depth == 4) return "FourBit";
if(depth == 8) return "EightBit";
if(depth == 16) return "SixteenBit";
if(depth == 24) return "TwentyFourBit";
return NULL;
}
int nn_mapDepth(int color, int depth, nn_bool_t legacy) {
if(depth == 1) {
if(color == 0) return nni_mcBlack;
return nni_mcWhite;
}
if(depth == 2) {
int palette[4] = {
0x000000,
0x444444,
0x999999,
0xFFFFFF,
};
return nn_mapColor(color, palette, 4);
}
if(depth == 3) {
int palette[8] = {
0x000000,
0xFF0000,
0x00FF00,
0xFFFF00,
0x0000FF,
0xFF00FF,
0x00FFFF,
0xFFFFFF,
};
return nn_mapColor(color, palette, 8);
}
if(depth == 4) {
if(legacy) {
if(!nni_4bitl_did) {
nni_4bitl_did = true;
nn_getLegacy4BitPalette(nni_4bitl_colors);
}
return nn_mapColor(color, nni_4bitl_colors, 16);
} else {
if(!nni_4bit_did) {
nni_4bit_did = true;
nn_getStd4BitPalette(nni_4bit_colors);
}
return nn_mapColor(color, nni_4bit_colors, 16);
}
}
if(depth == 8) {
if(!nni_8bit_did) {
nni_8bit_did = true;
nn_getStd8BitPalette(nni_8bit_colors);
}
return nn_mapColor(color, nni_8bit_colors, 256);
}
return color;
}

View File

@@ -1,32 +0,0 @@
#ifndef NN_SCREEN_H
#define NN_SCREEN_H
#include "../neonucleus.h"
typedef struct nn_screen {
nn_Context ctx;
nn_scrchr_t *buffer;
nn_guard *lock;
nn_refc refc;
int width;
int height;
int viewportWidth;
int viewportHeight;
int maxWidth;
int maxHeight;
int maxDepth;
int depth;
int editableColors;
int paletteColors;
int *palette;
int aspectRatioWidth;
int aspectRatioHeight;
nn_bool_t isOn;
nn_bool_t isTouchModeInverted;
nn_bool_t isPrecise;
nn_bool_t isDirty;
nn_address keyboards[NN_MAX_SCREEN_KEYBOARDS];
nn_size_t keyboardCount;
} nn_screen;
#endif

View File

@@ -1,158 +0,0 @@
#include "../neonucleus.h"
typedef struct nn_tunnel {
nn_Context ctx;
nn_refc refc;
nn_guard *lock;
nn_tunnelTable table;
nn_networkControl ctrl;
} nn_tunnel;
nn_tunnel *nn_newTunnel(nn_Context *context, nn_tunnelTable table, nn_networkControl control) {
nn_Alloc *a = &context->allocator;
nn_tunnel *t = nn_alloc(a, sizeof(nn_tunnel));
if(t == NULL) return NULL;
t->lock = nn_newGuard(context);
if(t->lock == NULL) {
nn_dealloc(a, t, sizeof(nn_tunnel));
return NULL;
}
t->ctx = *context;
t->refc = 1;
t->table = table;
t->ctrl = control;
return t;
}
nn_guard *nn_getTunnelLock(nn_tunnel *tunnel) {
return tunnel->lock;
}
void nn_retainTunnel(nn_tunnel *tunnel) {
nn_incRef(&tunnel->refc);
}
nn_bool_t nn_destroyTunnel(nn_tunnel *tunnel) {
if(!nn_decRef(&tunnel->refc)) return false;
return true;
}
void nn_tunnel_destroy(void *_, nn_component *component, nn_tunnel *tunnel) {
nn_destroyTunnel(tunnel);
}
static void nni_tunnel_maxPacketSize(nn_tunnel *tunnel, void *_, nn_component *component, nn_computer *computer) {
nn_return_integer(computer, tunnel->table.maxPacketSize);
}
static void nni_tunnel_maxValues(nn_tunnel *tunnel, void *_, nn_component *component, nn_computer *computer) {
nn_return_integer(computer, tunnel->table.maxValues);
}
static void nni_tunnel_getChannel(nn_tunnel *tunnel, void *_, nn_component *component, nn_computer *computer) {
nn_errorbuf_t err = "";
char buf[NN_MAX_CHANNEL_SIZE];
nn_lock(&tunnel->ctx, tunnel->lock);
nn_size_t len = tunnel->table.getChannel(tunnel->table.userdata, buf, err);
nn_unlock(&tunnel->ctx, tunnel->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_string(computer, buf, len);
}
static void nni_tunnel_getWakeMessage(nn_tunnel *tunnel, void *_, nn_component *component, nn_computer *computer) {
nn_errorbuf_t err = "";
char buf[NN_MAX_CHANNEL_SIZE];
nn_lock(&tunnel->ctx, tunnel->lock);
nn_size_t len = tunnel->table.getWakeMessage(tunnel->table.userdata, buf, err);
nn_unlock(&tunnel->ctx, tunnel->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_string(computer, buf, len);
}
static void nni_tunnel_setWakeMessage(nn_tunnel *tunnel, void *_, nn_component *component, nn_computer *computer) {
nn_size_t buflen;
const char *buf = nn_toString(nn_getArgument(computer, 0), &buflen);
if(buf == NULL) {
nn_setCError(computer, "invalid wake message");
return;
}
if(buflen > NN_MAX_WAKEUPMSG) {
buflen = NN_MAX_WAKEUPMSG;
}
nn_bool_t fuzzy = nn_toBoolean(nn_getArgument(computer, 1)); // nil is false
nn_errorbuf_t err = "";
nn_lock(&tunnel->ctx, tunnel->lock);
buflen = tunnel->table.setWakeMessage(tunnel->table.userdata, buf, buflen, fuzzy, err);
nn_unlock(&tunnel->ctx, tunnel->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_string(computer, buf, buflen);
}
static void nni_tunnel_send(nn_tunnel *tunnel, void *_, nn_component *component, nn_computer *computer) {
nn_value vals[tunnel->table.maxValues];
nn_size_t valLen = nn_getArgumentCount(computer);
if(valLen > tunnel->table.maxValues) {
nn_setCError(computer, "too many values");
return;
}
for(nn_size_t i = 0; i < valLen; i++) {
vals[i] = nn_getArgument(computer, i);
}
nn_size_t bytesSent = nn_measurePacketSize(vals, valLen);
if(bytesSent > tunnel->table.maxPacketSize) {
nn_setCError(computer, "packet too big");
return;
}
nn_simulateBufferedIndirect(component, bytesSent, tunnel->ctrl.packetBytesPerTick);
double d = (double)bytesSent / tunnel->table.maxPacketSize;
nn_addHeat(computer, d * tunnel->ctrl.heatPerFullPacket);
nn_removeEnergy(computer, d * tunnel->ctrl.energyPerFullPacket);
nn_errorbuf_t err = "";
nn_lock(&tunnel->ctx, tunnel->lock);
tunnel->table.send(tunnel->table.userdata, vals, valLen, err);
nn_unlock(&tunnel->ctx, tunnel->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_boolean(computer, true);
}
void nn_loadTunnelTable(nn_universe *universe) {
nn_componentTable *tunnelTable = nn_newComponentTable(nn_getAllocator(universe), "tunnel", NULL, NULL, (nn_componentDestructor *)nn_tunnel_destroy);
nn_storeUserdata(universe, "NN:TUNNEL", tunnelTable);
nn_defineMethod(tunnelTable, "maxPacketSize", (nn_componentMethod *)nni_tunnel_maxPacketSize, "maxPacketSize(): integer - Returns the maximum size of a packet");
nn_defineMethod(tunnelTable, "maxValues", (nn_componentMethod *)nni_tunnel_maxValues, "maxValues(): integer - the maximum number of values which can be sent in a packet");
nn_defineMethod(tunnelTable, "getChannel", (nn_componentMethod *)nni_tunnel_getChannel, "getChannel(): string - Gets the ID of the communications channel of the tunnel. Under normal conditions, there are only 2 cards in the same universe which share this.");
nn_defineMethod(tunnelTable, "getWakeMessage", (nn_componentMethod *)nni_tunnel_getWakeMessage, "getWakeMessage(): string - Returns the current wake message");
nn_defineMethod(tunnelTable, "setWakeMessage", (nn_componentMethod *)nni_tunnel_setWakeMessage, "setWakeMessage(msg: string[, fuzzy: boolean]): string - Sets the new wake message");
nn_defineMethod(tunnelTable, "send", (nn_componentMethod *)nni_tunnel_send, "send(...): boolean - Sends a tunnel message. Returns whether it was sent.");
}
nn_component *nn_addTunnel(nn_computer *computer, nn_address address, int slot, nn_tunnel *tunnel) {
nn_componentTable *tunnelTable = nn_queryUserdata(nn_getUniverse(computer), "NN:TUNNEL");
return nn_newComponent(computer, address, slot, tunnelTable, tunnel);
}

View File

@@ -1,74 +0,0 @@
#include "../neonucleus.h"
// TODO: make it allocate lazily
typedef struct nn_vdrive {
nn_Context ctx;
char *buffer;
nn_size_t sectorSize;
nn_size_t capacity;
char label[NN_LABEL_SIZE];
nn_size_t labelLen;
} nn_vdrive;
static void nni_vdrive_deinit(nn_vdrive *vdrive) {
nn_Alloc a = vdrive->ctx.allocator;
nn_dealloc(&a, vdrive->buffer, vdrive->capacity);
nn_dealloc(&a, vdrive, sizeof(nn_vdrive));
}
static void nni_vdrive_getLabel(nn_vdrive *vdrive, char *buf, nn_size_t *buflen) {
nn_memcpy(buf, vdrive->label, vdrive->labelLen);
*buflen = vdrive->labelLen;
}
static nn_size_t nni_vdrive_setLabel(nn_vdrive *vdrive, const char *buf, nn_size_t buflen) {
if(buflen > NN_LABEL_SIZE) buflen = NN_LABEL_SIZE;
nn_memcpy(vdrive->label, buf, buflen);
vdrive->labelLen = buflen;
return buflen;
}
static void nni_vdrive_readSector(nn_vdrive *vdrive, int sector, char *buf) {
nn_memcpy(buf, vdrive->buffer + (sector - 1) * vdrive->sectorSize, vdrive->sectorSize);
}
static void nni_vdrive_writeSector(nn_vdrive *vdrive, int sector, const char *buf) {
nn_memcpy(vdrive->buffer + (sector - 1) * vdrive->sectorSize, buf, vdrive->sectorSize);
}
nn_drive *nn_volatileDrive(nn_Context *context, nn_vdriveOptions opts, nn_driveControl control) {
nn_Alloc *alloc = &context->allocator;
char *buffer = nn_alloc(alloc, opts.capacity);
if(buffer == NULL) return NULL;
nn_vdrive *drive = nn_alloc(alloc, sizeof(nn_vdrive));
if(drive == NULL) {
nn_dealloc(alloc, buffer, opts.capacity);
return NULL;
}
drive->ctx = *context;
drive->buffer = buffer;
drive->sectorSize = opts.sectorSize;
drive->capacity = opts.capacity;
nn_memcpy(drive->label, opts.label, opts.labelLen);
drive->labelLen = opts.labelLen;
if(opts.data == NULL) {
nn_memset(buffer, 0, opts.capacity);
} else {
nn_memcpy(buffer, opts.data, opts.capacity);
}
nn_driveTable table = {
.userdata = drive,
.deinit = (void *)nni_vdrive_deinit,
.getLabel = (void *)nni_vdrive_getLabel,
.setLabel = (void *)nni_vdrive_setLabel,
.readSector = (void *)nni_vdrive_readSector,
.writeSector = (void *)nni_vdrive_writeSector,
.sectorSize = opts.sectorSize,
.platterCount = opts.platterCount,
.capacity = opts.capacity,
};
return nn_newDrive(context, table, control);
}

View File

@@ -1,98 +0,0 @@
#include "../neonucleus.h"
typedef struct nn_veeprom {
nn_Context ctx;
char *code;
nn_size_t codeLen;
nn_size_t codeSize;
char *data;
nn_size_t dataLen;
nn_size_t dataSize;
char label[NN_LABEL_SIZE];
nn_size_t labelLen;
nn_bool_t isReadOnly;
} nn_veeprom;
static void nni_veeprom_deinit(nn_veeprom *veeprom) {
nn_Alloc a = veeprom->ctx.allocator;
nn_dealloc(&a, veeprom->code, veeprom->codeSize);
nn_dealloc(&a, veeprom->data, veeprom->dataSize);
nn_dealloc(&a, veeprom, sizeof(nn_veeprom));
}
static void nni_veeprom_getLabel(nn_veeprom *veeprom, char *buf, nn_size_t *buflen, nn_errorbuf_t err) {
nn_memcpy(buf, veeprom->label, veeprom->labelLen);
*buflen = veeprom->labelLen;
}
static nn_size_t nni_veeprom_setLabel(nn_veeprom *veeprom, const char *buf, nn_size_t buflen, nn_errorbuf_t err) {
if(buflen > NN_LABEL_SIZE) buflen = NN_LABEL_SIZE;
nn_memcpy(veeprom->label, buf, buflen);
veeprom->labelLen = buflen;
return buflen;
}
static nn_size_t nni_veeprom_get(nn_veeprom *veeprom, char *buf, nn_errorbuf_t err) {
nn_memcpy(buf, veeprom->code, veeprom->codeLen);
return veeprom->codeLen;
}
static void nni_veeprom_set(nn_veeprom *veeprom, const char *buf, nn_size_t len, nn_errorbuf_t err) {
nn_memcpy(veeprom->code, buf, len);
veeprom->codeLen = len;
}
static nn_size_t nni_veeprom_getData(nn_veeprom *veeprom, char *buf, nn_errorbuf_t err) {
nn_memcpy(buf, veeprom->data, veeprom->dataLen);
return veeprom->dataLen;
}
static void nni_veeprom_setData(nn_veeprom *veeprom, const char *buf, nn_size_t len, nn_errorbuf_t err) {
nn_memcpy(veeprom->data, buf, len);
veeprom->dataLen = len;
}
static nn_bool_t nni_veeprom_isReadonly(nn_veeprom *eeprom, nn_errorbuf_t err) {
return eeprom->isReadOnly;
}
static void nni_veeprom_makeReadonly(nn_veeprom *eeprom, nn_errorbuf_t err) {
eeprom->isReadOnly = true;
}
nn_eeprom *nn_volatileEEPROM(nn_Context *context, nn_veepromOptions opts, nn_eepromControl control) {
nn_Alloc *a = &context->allocator;
// TODO: handle OOM
nn_veeprom *veeprom = nn_alloc(a, sizeof(nn_veeprom));
veeprom->ctx = *context;
veeprom->codeSize = opts.size;
veeprom->code = nn_alloc(a, veeprom->codeSize);
veeprom->codeLen = opts.len;
veeprom->dataSize = opts.dataSize;
veeprom->data = nn_alloc(a, veeprom->dataSize);
veeprom->dataLen = opts.dataLen;
veeprom->isReadOnly = opts.isReadOnly;
veeprom->labelLen = opts.labelLen;
nn_memcpy(veeprom->label, opts.label, veeprom->labelLen);
nn_memcpy(veeprom->code, opts.code, veeprom->codeLen);
nn_memcpy(veeprom->data, opts.data, veeprom->dataLen);
nn_eepromTable table = {
.userdata = veeprom,
.deinit = (void *)nni_veeprom_deinit,
.size = opts.size,
.dataSize = opts.dataSize,
.getLabel = (void *)nni_veeprom_getLabel,
.setLabel = (void *)nni_veeprom_setLabel,
.get = (void *)nni_veeprom_get,
.set = (void *)nni_veeprom_set,
.getData = (void *)nni_veeprom_getData,
.setData = (void *)nni_veeprom_setData,
.isReadonly = (void *)nni_veeprom_isReadonly,
.makeReadonly = (void *)nni_veeprom_makeReadonly,
};
return nn_newEEPROM(context, table, control);
}

View File

@@ -1,639 +0,0 @@
#include "../neonucleus.h"
// Data structures
typedef struct nn_vfnode {
struct nn_vfilesystem *fs;
char name[NN_MAX_PATH];
struct nn_vfnode *parent;
nn_bool_t isDirectory;
union {
// if directory
struct nn_vfnode **entries;
// if file
char *data;
};
nn_size_t len;
nn_size_t cap;
nn_timestamp_t lastModified;
// this is used to block deleting
nn_refc handleCount;
} nn_vfnode;
typedef enum nn_vfmode {
NN_VFMODE_READ,
NN_VFMODE_WRITE,
NN_VFMODE_APPEND,
} nn_vfmode;
typedef struct nn_vfhandle {
nn_vfnode *node;
nn_integer_t position;
nn_vfmode mode;
} nn_vfhandle;
typedef struct nn_vfilesystem {
nn_Context ctx;
nn_vfilesystemOptions opts;
double birthday;
nn_vfnode *root;
} nn_vfilesystem;
// virtual node helpers
nn_timestamp_t nn_vf_now(nn_vfilesystem *fs) {
nn_Clock c = fs->ctx.clock;
double time = c.proc(c.userdata);
double elapsed = time - fs->birthday;
nn_timestamp_t elapsedMS = elapsed * 1000;
return fs->opts.creationTime + elapsedMS;
}
nn_vfnode *nn_vf_allocFile(nn_vfilesystem *fs, const char *name) {
nn_Alloc *alloc = &fs->ctx.allocator;
nn_vfnode *node = nn_alloc(alloc, sizeof(nn_vfnode));
if(node == NULL) return NULL;
*node = (nn_vfnode) {
.fs = fs,
.lastModified = nn_vf_now(fs),
.parent = NULL,
.isDirectory = false,
.data = NULL,
.len = 0,
.cap = 0,
.handleCount = 0,
};
// we pray
nn_strcpy(node->name, name);
return node;
}
nn_vfnode *nn_vf_allocDirectory(nn_vfilesystem *fs, const char *name) {
nn_Alloc *alloc = &fs->ctx.allocator;
nn_vfnode *node = nn_alloc(alloc, sizeof(nn_vfnode));
if(node == NULL) return NULL;
nn_vfnode **buffer = nn_alloc(alloc, sizeof(nn_vfnode *) * fs->opts.maxDirEntries);
if(buffer == NULL) {
nn_dealloc(alloc, node, sizeof(nn_vfnode));
return NULL;
}
*node = (nn_vfnode) {
.fs = fs,
.lastModified = nn_vf_now(fs),
.parent = NULL,
.isDirectory = true,
.entries = buffer,
.len = 0,
.cap = fs->opts.maxDirEntries,
.handleCount = 0,
};
// we pray
nn_strcpy(node->name, name);
return node;
}
void nn_vf_freeNode(nn_vfnode *node) {
nn_Alloc *alloc = &node->fs->ctx.allocator;
if(node->isDirectory) {
for(nn_size_t i = 0; i < node->len; i++) {
nn_vf_freeNode(node->entries[i]);
}
nn_dealloc(alloc, node->entries, sizeof(nn_vfnode *) * node->cap);
} else {
nn_dealloc(alloc, node->data, node->cap);
}
nn_dealloc(alloc, node, sizeof(nn_vfnode));
}
nn_size_t nn_vf_spaceUsedByNode(nn_vfnode *node) {
if(node->isDirectory) {
nn_size_t sum = 0;
for(nn_size_t i = 0; i < node->len; i++) {
sum += nn_vf_spaceUsedByNode(node->entries[i]);
}
return sum;
} else {
return node->len;
}
}
nn_vfnode *nn_vf_find(nn_vfnode *parent, const char *name) {
if(!parent->isDirectory) return NULL;
for(nn_size_t i = 0; i < parent->len; i++) {
nn_vfnode *entry = parent->entries[i];
if(nn_strcmp(entry->name, name) == 0) {
return entry;
}
}
return NULL;
}
nn_bool_t nn_vf_ensureFileCapacity(nn_vfnode *file, nn_size_t capacity) {
if(file->isDirectory) return false;
nn_Alloc *alloc = &file->fs->ctx.allocator;
if(file->cap >= capacity) return true; // already at that point
char *newData = nn_resize(alloc, file->data, file->cap, capacity);
if(newData == NULL) {
return false; // OOM
}
file->data = newData;
file->cap = capacity;
return true;
}
// this is used to compute exponential backoff
// TODO: add an option to select either a slower growth rate or
// linear backoff to reduce memory usage at the cost of speed
nn_size_t nn_vf_getIdealCapacity(nn_vfnode *file, nn_size_t spaceNeeded) {
nn_size_t cap = file->cap;
if(cap == 0) cap = 1;
while(cap < spaceNeeded) cap *= 2; // this would mean a file with 1,048,577 bytes takes up 2,097,152 bytes, potentially wasting 1,048,575 bytes
return cap;
}
void nn_vf_clampHandlePosition(nn_vfhandle *handle) {
nn_size_t len = handle->node->len;
if(handle->mode == NN_VFMODE_APPEND) {
handle->position = len;
return;
}
if(handle->position < 0) handle->position = 0;
if(handle->position > len) handle->position = len;
}
nn_vfnode *nn_vf_resolvePathFromNode(nn_vfnode *node, const char *path) {
if(path[0] == 0) {
return node;
}
char firstDirectory[NN_MAX_PATH];
char subpath[NN_MAX_PATH];
if(nn_path_firstName(path, firstDirectory, subpath)) {
return nn_vf_find(node, firstDirectory);
}
nn_vfnode *dir = nn_vf_find(node, firstDirectory);
if(dir == NULL) return NULL;
if(!dir->isDirectory) return NULL;
return nn_vf_resolvePathFromNode(dir, subpath);
}
nn_vfnode *nn_vf_resolvePath(nn_vfilesystem *fs, const char *path) {
return nn_vf_resolvePathFromNode(fs->root, path);
}
nn_size_t nn_vf_countTree(nn_vfnode *node) {
if(!node->isDirectory) return 1;
nn_size_t n = 1;
for(nn_size_t i = 0; i < node->len; i++) {
n += nn_vf_countTree(node->entries[i]);
}
return n;
}
nn_bool_t nn_vf_treeHasHandles(nn_vfnode *node) {
if(!node->isDirectory) return node->handleCount > 0;
for(nn_size_t i = 0; i < node->len; i++) {
if(nn_vf_treeHasHandles(node->entries[i])) return true;
}
return false;
}
void nn_vf_appendNode(nn_vfnode *parent, nn_vfnode *node) {
if(!parent->isDirectory) return;
if(parent->len == parent->cap) return;
parent->entries[parent->len] = node;
parent->len++;
node->parent = parent; // just to be sure
}
void nn_vf_removeNode(nn_vfnode *parent, nn_vfnode *node) {
if(!parent->isDirectory) return;
nn_size_t j = 0;
for(nn_size_t i = 0; i < parent->len; i++) {
if(parent->entries[i] != node) {
parent->entries[j] = parent->entries[i];
j++;
}
}
parent->len = j;
}
// methods
void nn_vfs_deinit(nn_vfilesystem *fs) {
nn_Context ctx = fs->ctx;
nn_vf_freeNode(fs->root);
nn_dealloc(&ctx.allocator, fs, sizeof(nn_vfilesystem));
}
void nn_vfs_getLabel(nn_vfilesystem *fs, char *buf, nn_size_t *buflen, nn_errorbuf_t err) {
*buflen = fs->opts.labelLen;
nn_memcpy(buf, fs->opts.label, fs->opts.labelLen);
}
void nn_vfs_setLabel(nn_vfilesystem *fs, const char *buf, nn_size_t buflen, nn_errorbuf_t err) {
nn_memcpy(fs->opts.label, buf, buflen);
fs->opts.labelLen = buflen;
}
nn_size_t nn_vfs_spaceUsed(nn_vfilesystem *fs) {
return nn_vf_spaceUsedByNode(fs->root);
}
nn_bool_t nn_vfs_isReadOnly(nn_vfilesystem *fs, nn_errorbuf_t err) {
return fs->opts.isReadOnly;
}
nn_size_t nn_vfs_size(nn_vfilesystem *fs, const char *path, nn_errorbuf_t err) {
nn_vfnode *node = nn_vf_resolvePath(fs, path);
if(node == NULL) {
nn_error_write(err, "No such file");
return 0;
}
if(node->isDirectory) return 0;
return node->len;
}
nn_size_t nn_vfs_remove(nn_vfilesystem *fs, const char *path, nn_errorbuf_t err) {
nn_vfnode *node = nn_vf_resolvePath(fs, path);
if(node == NULL) {
nn_error_write(err, "No such file");
return 0;
}
nn_vfnode *parent = node->parent;
if(parent == NULL) {
// root, can't delete
nn_error_write(err, "Unable to delete root");
return 0;
}
if(nn_vf_treeHasHandles(node)) {
nn_error_write(err, "Files are pinned by handles");
return 0;
}
nn_size_t removed = nn_vf_countTree(node);
// it is super easy to delete a tree
nn_vf_removeNode(parent, node);
parent->lastModified = nn_vf_now(fs);
nn_vf_freeNode(node);
return removed;
}
nn_timestamp_t nn_vfs_lastModified(nn_vfilesystem *fs, const char *path, nn_errorbuf_t err) {
nn_vfnode *node = nn_vf_resolvePath(fs, path);
if(node == NULL) {
nn_error_write(err, "No such file");
return 0;
}
return node->lastModified;
}
nn_size_t nn_vfs_rename(nn_vfilesystem *fs, const char *from, const char *to, nn_errorbuf_t err) {
// TODO: implement rename
nn_error_write(err, "Unsupported operation");
nn_vfnode *srcNode = nn_vf_resolvePath(fs, from);
if(srcNode == NULL) {
nn_error_write(err, "No such file");
return 0;
}
nn_vfnode *srcParent = srcNode->parent;
if(srcParent == NULL) {
// root, can't move
nn_error_write(err, "Unable to move root");
return 0;
}
if(nn_vf_treeHasHandles(srcNode)) {
nn_error_write(err, "Files are pinned by handles");
return 0;
}
char name[NN_MAX_PATH];
char parentPath[NN_MAX_PATH];
nn_bool_t rootOut = nn_path_lastName(to, name, parentPath);
nn_vfnode *destParent = rootOut ? fs->root : nn_vf_resolvePath(fs, parentPath);
if(destParent == NULL) {
nn_error_write(err, "No such directory");
return 0;
}
if(!destParent->isDirectory) {
nn_error_write(err, "Is a file");
return 0;
}
if(nn_vf_find(destParent, name) != NULL) {
nn_error_write(err, "Already exists");
return 0;
}
if(destParent->len == destParent->cap) {
nn_error_write(err, "Too many entries");
return 0;
}
nn_size_t moved = nn_vf_countTree(srcNode);
// super efficient moving
nn_vf_removeNode(srcParent, srcNode);
nn_vf_appendNode(destParent, srcNode);
return moved;
}
nn_bool_t nn_vfs_exists(nn_vfilesystem *fs, const char *path, nn_errorbuf_t err) {
nn_vfnode *node = nn_vf_resolvePath(fs, path);
return node != NULL;
}
nn_bool_t nn_vfs_isDirectory(nn_vfilesystem *fs, const char *path, nn_errorbuf_t err) {
nn_vfnode *node = nn_vf_resolvePath(fs, path);
if(node == NULL) {
nn_error_write(err, "No such file");
return 0;
}
return node->isDirectory;
}
static nn_bool_t nn_vfs_recursiveMkdir(nn_vfilesystem *fs, const char *path, nn_errorbuf_t err) {
// this code is horribly unoptimized, with a time complexity of O(F^U),
// where F is 8.1 * 10^53 (monsterous) and U is 7 * 10^27 (very human)
// TODO: burn it with fire and make something good
nn_vfnode *node = nn_vf_resolvePath(fs, path);
if(node != NULL) {
if(node->isDirectory) {
return true;
}
nn_error_write(err, "Is a file");
return false;
}
char name[NN_MAX_PATH];
char parentPath[NN_MAX_PATH];
nn_bool_t isRootDir = nn_path_lastName(path, name, parentPath);
if(!isRootDir) {
if(!nn_vfs_recursiveMkdir(fs, parentPath, err)) return false;
}
nn_vfnode *parent = isRootDir ? fs->root : nn_vf_resolvePath(fs, parentPath);
if(parent == NULL) {
nn_error_write(err, "Bad state"); // just a sanity check
return false;
}
if(parent->len == parent->cap) {
nn_error_write(err, "Too many entries");
return false;
}
nn_vfnode *dir = nn_vf_allocDirectory(fs, name);
if(dir == NULL) {
nn_error_write(err, "Out of memory");
return false;
}
dir->parent = parent;
parent->entries[parent->len] = dir;
parent->len++;
return true;
}
nn_bool_t nn_vfs_makeDirectory(nn_vfilesystem *fs, const char *path, nn_errorbuf_t err) {
return nn_vfs_recursiveMkdir(fs, path, err);
}
char **nn_vfs_list(nn_Alloc *alloc, nn_vfilesystem *fs, const char *path, nn_size_t *len, nn_errorbuf_t err) {
nn_vfnode *node = nn_vf_resolvePath(fs, path);
if(node == NULL) {
nn_error_write(err, "No such file");
return NULL;
}
if(!node->isDirectory) {
nn_error_write(err, "Not a directory");
return NULL;
}
nn_size_t count = node->len;
*len = count;
char **buf = nn_alloc(alloc, sizeof(char *) * count);
if(buf == NULL) {
nn_error_write(err, "Out of memory");
return NULL;
}
for(nn_size_t i = 0; i < count; i++) {
nn_vfnode *entry = node->entries[i];
char *s = NULL;
if(entry->isDirectory) {
nn_size_t l = nn_strlen(entry->name);
s = nn_alloc(alloc, l + 2);
if(s != NULL) {
nn_memcpy(s, entry->name, l);
s[l] = '/';
s[l+1] = 0;
}
} else {
s = nn_strdup(alloc, entry->name);
}
if(s == NULL) {
for(nn_size_t j = 0; j < i; j++) {
nn_deallocStr(alloc, buf[j]);
}
nn_error_write(err, "Out of memory");
return NULL;
}
buf[i] = s;
}
return buf;
}
nn_vfhandle *nn_vfs_open(nn_vfilesystem *fs, const char *path, const char *mode, nn_errorbuf_t err) {
// TODO: complete
char m = mode[0];
nn_vfmode fmode = NN_VFMODE_READ;
if(m == 'w') fmode = NN_VFMODE_WRITE;
if(m == 'a') fmode = NN_VFMODE_APPEND;
nn_vfnode *node = nn_vf_resolvePath(fs, path);
if(node == NULL && fmode != NN_VFMODE_READ && !fs->opts.isReadOnly) {
char parentPath[NN_MAX_PATH];
char name[NN_MAX_PATH];
nn_bool_t isRootFile = nn_path_lastName(path, name, parentPath);
nn_vfnode *parent = isRootFile ? fs->root : nn_vf_resolvePath(fs, parentPath);
if(parent == NULL) {
nn_error_write(err, "Missing parent directory");
return NULL;
}
if(!parent->isDirectory) {
nn_error_write(err, "Parent is not a directory");
return NULL;
}
if(parent->len == parent->cap) {
nn_error_write(err, "Too many entries");
return NULL;
}
node = nn_vf_allocFile(fs, name);
if(node == NULL) {
nn_error_write(err, "Out of memory");
return NULL;
}
node->parent = parent;
parent->entries[parent->len] = node;
parent->len++;
}
if(node == NULL) {
nn_error_write(err, "No such file");
return NULL;
}
if(fs->opts.isReadOnly && fmode != NN_VFMODE_READ) {
nn_error_write(err, "readonly");
return NULL;
}
if(node->isDirectory) {
nn_error_write(err, "Is a directory");
return NULL;
}
if(fmode == NN_VFMODE_WRITE) {
node->len = 0;
node->lastModified = nn_vf_now(fs);
}
nn_vfhandle *handle = nn_alloc(&fs->ctx.allocator, sizeof(nn_vfhandle));
if(handle == NULL) {
nn_error_write(err, "Out of memory");
return NULL;
}
handle->mode = fmode;
handle->node = node;
handle->position = 0;
node->handleCount++;
return handle;
}
nn_bool_t nn_vfs_close(nn_vfilesystem *fs, nn_vfhandle *handle, nn_errorbuf_t err) {
handle->node->handleCount--;
nn_dealloc(&fs->ctx.allocator, handle, sizeof(nn_vfhandle));
return true;
}
nn_bool_t nn_vfs_write(nn_vfilesystem *fs, nn_vfhandle *handle, const char *buf, nn_size_t len, nn_errorbuf_t err) {
nn_vf_clampHandlePosition(handle);
if(!nn_vf_ensureFileCapacity(handle->node, handle->position + len)) {
nn_error_write(err, "Out of memory");
return false;
}
nn_memcpy(handle->node->data + handle->position, buf, len);
handle->position += len;
if(handle->node->len < handle->position) handle->node->len = handle->position;
return true;
}
nn_size_t nn_vfs_read(nn_vfilesystem *fs, nn_vfhandle *handle, char *buf, nn_size_t required, nn_errorbuf_t err) {
nn_size_t remaining = handle->node->len - handle->position;
if(required > remaining) required = remaining;
if(required == 0) return 0;
nn_memcpy(buf, handle->node->data + handle->position, required);
handle->position += required;
return required;
}
nn_size_t nn_vfs_seek(nn_vfilesystem *fs, nn_vfhandle *handle, const char *whence, int off, nn_errorbuf_t err) {
if(handle->mode == NN_VFMODE_APPEND) {
nn_error_write(err, "Bad file descriptor");
return handle->node->len;
}
nn_integer_t ptr = handle->position;
if(nn_strcmp(whence, "set") == 0) {
ptr = off;
}
if(nn_strcmp(whence, "cur") == 0) {
ptr += off;
}
if(nn_strcmp(whence, "end") == 0) {
ptr = handle->node->len - off;
}
handle->position = ptr;
nn_vf_clampHandlePosition(handle);
return handle->position;
}
typedef struct nn_vfilesystemImage {
nn_vfilesystemImageNode *nodes;
nn_size_t ptr;
} nn_vfilesystemImage;
static nn_vfilesystemImageNode nni_vfsimg_nextNode(nn_vfilesystemImage *stream) {
nn_vfilesystemImageNode node = stream->nodes[stream->ptr];
stream->ptr++;
return node;
}
static nn_vfnode *nni_vfsimg_parseNode(nn_vfilesystem *fs, nn_vfilesystemImage *stream, nn_vfnode *parent) {
// TODO: make this handle OOMs
nn_vfilesystemImageNode node = nni_vfsimg_nextNode(stream);
if(node.data == NULL) {
// directory!!!!!
nn_vfnode *dir = nn_vf_allocDirectory(fs, node.name);
dir->len = node.len;
dir->parent = parent;
for(int i = 0; i < node.len; i++) {
nn_vfnode *entry = nni_vfsimg_parseNode(fs, stream, dir);
dir->entries[i] = entry;
}
return dir;
}
// file!!!!!
nn_vfnode *file = nn_vf_allocFile(fs, node.name);
nn_vf_ensureFileCapacity(file, node.len);
file->len = node.len;
file->parent = parent;
nn_memcpy(file->data, node.data, node.len);
return file;
}
// constructor
nn_filesystem *nn_volatileFilesystem(nn_Context *context, nn_vfilesystemOptions opts, nn_filesystemControl control) {
// TODO: handle OOM
nn_vfilesystem *fs = nn_alloc(&context->allocator, sizeof(nn_vfilesystem));
fs->ctx = *context;
nn_Clock c = fs->ctx.clock;
double time = c.proc(c.userdata);
fs->birthday = time;
fs->opts = opts;
fs->root = nn_vf_allocDirectory(fs, "/");
if(opts.image != NULL) {
nn_vfilesystemImage stream = {
.nodes = opts.image,
.ptr = 0,
};
// we got supplied an image, shit
fs->root->len = opts.rootEntriesInImage;
for(int i = 0; i < opts.rootEntriesInImage; i++) {
nn_vfnode *entry = nni_vfsimg_parseNode(fs, &stream, fs->root);
fs->root->entries[i] = entry;
}
}
nn_filesystemTable table = {
.userdata = fs,
.deinit = (void *)nn_vfs_deinit,
.getLabel = (void *)nn_vfs_getLabel,
.setLabel = (void *)nn_vfs_setLabel,
.spaceUsed = (void *)nn_vfs_spaceUsed,
.spaceTotal = opts.capacity,
.isReadOnly = (void *)nn_vfs_isReadOnly,
.size = (void *)nn_vfs_size,
.remove = (void *)nn_vfs_remove,
.lastModified = (void *)nn_vfs_lastModified,
.rename = (void *)nn_vfs_rename,
.exists = (void *)nn_vfs_exists,
.isDirectory = (void *)nn_vfs_isDirectory,
.makeDirectory = (void *)nn_vfs_makeDirectory,
.list = (void *)nn_vfs_list,
.open = (void *)nn_vfs_open,
.close = (void *)nn_vfs_close,
.write = (void *)nn_vfs_write,
.read = (void *)nn_vfs_read,
.seek = (void *)nn_vfs_seek,
};
return nn_newFilesystem(context, table, control);
}

View File

@@ -1,674 +0,0 @@
#include "computer.h"
#include "component.h"
#include "universe.h"
#include "neonucleus.h"
#include "resource.h"
nn_computer *nn_newComputer(nn_universe *universe, nn_address address, nn_architecture *arch, void *userdata, nn_size_t memoryLimit, nn_size_t componentLimit) {
nn_Alloc *alloc = &universe->ctx.allocator;
nn_computer *c = nn_alloc(alloc, sizeof(nn_computer));
c->components = nn_alloc(alloc, sizeof(nn_component) * componentLimit);
if(c->components == NULL) {
nn_dealloc(alloc, c, sizeof(nn_computer));
return NULL;
}
c->address = nn_strdup(alloc, address);
if(c->address == NULL) {
nn_dealloc(alloc, c->components, sizeof(nn_component) * componentLimit);
nn_dealloc(alloc, c, sizeof(nn_computer));
return NULL;
}
c->lock = nn_newGuard(&universe->ctx);
if(c->lock == NULL) {
nn_deallocStr(alloc, c->address);
nn_dealloc(alloc, c->components, sizeof(nn_component) * componentLimit);
nn_dealloc(alloc, c, sizeof(nn_computer));
return NULL;
}
// TODO: handle OOM
c->deviceInfo = nn_newDeviceInfoList(&universe->ctx, 16);
c->timeOffset = nn_getTime(universe);
c->supportedArchCount = 0;
c->argc = 0;
c->retc = 0;
c->err = NULL;
c->allocatedError = false;
c->state = NN_STATE_SETUP;
c->componentLen = 0;
c->componentCap = componentLimit;
c->userCount = 0;
c->maxEnergy = 5000;
c->signalCount = 0;
c->universe = universe;
c->arch = arch;
c->nextArch = arch;
c->userdata = userdata;
c->memoryTotal = memoryLimit;
c->tmpAddress = NULL;
c->temperature = 30;
c->roomTemperature = 30;
c->temperatureCoefficient = 1;
c->callCost = 0;
c->callBudget = 256;
// Setup Architecture
c->archState = c->arch->setup(c, c->arch->userdata);
if(c->archState == NULL) {
nn_deleteGuard(&universe->ctx, c->lock);
nn_deallocStr(alloc, c->address);
nn_dealloc(alloc, c->components, sizeof(nn_component) * componentLimit);
nn_dealloc(alloc, c, sizeof(nn_computer));
return NULL;
}
c->rid = NN_NULL_RESOURCE;
for(nn_size_t i = 0; i < NN_MAX_CONCURRENT_RESOURCES; i++) {
c->resources[i].id = NN_NULL_RESOURCE;
}
c->hasBeep = false;
return c;
}
nn_universe *nn_getUniverse(nn_computer *computer) {
return computer->universe;
}
void nn_setTmpAddress(nn_computer *computer, nn_address tmp) {
nn_deallocStr(&computer->universe->ctx.allocator, computer->tmpAddress);
computer->tmpAddress = nn_strdup(&computer->universe->ctx.allocator, tmp);
}
nn_address nn_getComputerAddress(nn_computer *computer) {
return computer->address;
}
nn_address nn_getTmpAddress(nn_computer *computer) {
return computer->tmpAddress;
}
int nn_tickComputer(nn_computer *computer) {
computer->callCost = 0;
computer->state = NN_STATE_RUNNING;
nn_clearError(computer);
computer->arch->tick(computer, computer->archState, computer->arch->userdata);
return nn_getState(computer);
}
double nn_getUptime(nn_computer *computer) {
return nn_getTime(computer->universe) - computer->timeOffset;
}
nn_size_t nn_getComputerMemoryUsed(nn_computer *computer) {
return computer->arch->getMemoryUsage(computer, computer->archState, computer->arch->userdata);
}
nn_size_t nn_getComputerMemoryTotal(nn_computer *computer) {
return computer->memoryTotal;
}
void *nn_getComputerUserData(nn_computer *computer) {
return computer->userdata;
}
void nn_addSupportedArchitecture(nn_computer *computer, nn_architecture *arch) {
if(computer->supportedArchCount == NN_MAX_ARCHITECTURES) return;
computer->supportedArch[computer->supportedArchCount] = arch;
computer->supportedArchCount++;
}
nn_architecture *nn_getSupportedArchitecture(nn_computer *computer, nn_size_t idx) {
if(idx >= computer->supportedArchCount) return NULL;
return computer->supportedArch[idx];
}
nn_architecture *nn_getArchitecture(nn_computer *computer) {
return computer->arch;
}
nn_architecture *nn_getNextArchitecture(nn_computer *computer) {
return computer->nextArch;
}
void nn_setNextArchitecture(nn_computer *computer, nn_architecture *arch) {
computer->nextArch = arch;
}
void nn_deleteComputer(nn_computer *computer) {
nn_clearError(computer);
nn_resetCall(computer);
while(computer->signalCount > 0) {
nn_popSignal(computer);
}
nn_Alloc *a = &computer->universe->ctx.allocator;
for(nn_size_t i = 0; i < computer->userCount; i++) {
nn_deallocStr(a, computer->users[i]);
}
for(nn_size_t i = 0; i < NN_MAX_CONCURRENT_RESOURCES; i++) {
if(computer->resources[i].id != NN_NULL_RESOURCE) {
nn_resource_release(computer, computer->resources[i].id);
}
}
computer->arch->teardown(computer, computer->archState, computer->arch->userdata);
nn_deleteGuard(&computer->universe->ctx, computer->lock);
nn_deallocStr(a, computer->address);
nn_deallocStr(a, computer->tmpAddress);
nn_dealloc(a, computer->components, sizeof(nn_component) * computer->componentCap);
nn_dealloc(a, computer, sizeof(nn_computer));
}
const char *nn_pushSignal(nn_computer *computer, nn_value *values, nn_size_t len) {
if(len > NN_MAX_SIGNAL_VALS) return "too many values";
if(len == 0) return "missing event";
// no OOM for you hehe
if(nn_measurePacketSize(values, len) > NN_MAX_SIGNAL_SIZE) {
return "too big";
}
if(computer->signalCount == NN_MAX_SIGNALS) return "too many signals";
computer->signals[computer->signalCount].len = len;
for(nn_size_t i = 0; i < len; i++) {
computer->signals[computer->signalCount].values[i] = values[i];
}
computer->signalCount++;
return NULL;
}
nn_value nn_fetchSignalValue(nn_computer *computer, nn_size_t index) {
if(computer->signalCount == 0) return nn_values_nil();
nn_signal *p = computer->signals;
if(index >= p->len) return nn_values_nil();
return p->values[index];
}
nn_size_t nn_signalSize(nn_computer *computer) {
if(computer->signalCount == 0) return 0;
return computer->signals[0].len;
}
void nn_popSignal(nn_computer *computer) {
if(computer->signalCount == 0) return;
nn_signal *p = computer->signals;
for(nn_size_t i = 0; i < p->len; i++) {
nn_values_drop(p->values[i]);
}
for(nn_size_t i = 1; i < computer->signalCount; i++) {
computer->signals[i-1] = computer->signals[i];
}
computer->signalCount--;
}
const char *nn_addUser(nn_computer *computer, const char *name) {
if(computer->userCount == NN_MAX_USERS) return "too many users";
char *user = nn_strdup(&computer->universe->ctx.allocator, name);
if(user == NULL) return "out of memory";
computer->users[computer->userCount] = user;
computer->userCount++;
return NULL;
}
void nn_deleteUser(nn_computer *computer, const char *name) {
nn_size_t j = 0;
for(nn_size_t i = 0; i < computer->userCount; i++) {
char *user = computer->users[i];
if(nn_strcmp(user, name) == 0) {
nn_deallocStr(&computer->universe->ctx.allocator, user);
} else {
computer->users[j] = user;
j++;
}
}
computer->userCount = j;
}
const char *nn_indexUser(nn_computer *computer, nn_size_t idx) {
if(idx >= computer->userCount) return NULL;
return computer->users[idx];
}
nn_bool_t nn_isUser(nn_computer *computer, const char *name) {
if(computer->userCount == 0) return true;
for(nn_size_t i = 0; i < computer->userCount; i++) {
if(nn_strcmp(computer->users[i], name) == 0) return true;
}
return false;
}
void nn_setCallBudget(nn_computer *computer, double callBudget) {
computer->callBudget = callBudget;
}
double nn_getCallBudget(nn_computer *computer) {
return computer->callBudget;
}
void nn_callCost(nn_computer *computer, double cost) {
computer->callCost += cost;
if(computer->callCost >= computer->callBudget) nn_triggerIndirect(computer);
}
double nn_getCallCost(nn_computer *computer) {
return computer->callCost;
}
nn_bool_t nn_isOverworked(nn_computer *computer) {
return computer->state == NN_STATE_OVERWORKED;
}
void nn_triggerIndirect(nn_computer *computer) {
computer->state = NN_STATE_OVERWORKED;
}
int nn_getState(nn_computer *computer) {
return computer->state;
}
void nn_setState(nn_computer *computer, int state) {
computer->state = state;
}
void nn_setEnergyInfo(nn_computer *computer, double energy, double capacity) {
computer->energy = energy;
computer->maxEnergy = capacity;
}
double nn_getEnergy(nn_computer *computer) {
return computer->energy;
}
double nn_getMaxEnergy(nn_computer *computer) {
return computer->maxEnergy;
}
void nn_removeEnergy(nn_computer *computer, double energy) {
if(computer->energy < energy) {
// blackout
computer->energy = 0;
computer->state = NN_STATE_BLACKOUT;
return;
}
computer->energy -= energy;
}
void nn_addEnergy(nn_computer *computer, double amount) {
if(computer->maxEnergy - computer->energy < amount) {
computer->energy = computer->maxEnergy;
return;
}
computer->energy += amount;
}
double nn_getTemperature(nn_computer *computer) {
return computer->temperature;
}
double nn_getThermalCoefficient(nn_computer *computer) {
return computer->temperatureCoefficient;
}
double nn_getRoomTemperature(nn_computer *computer) {
return computer->roomTemperature;
}
void nn_setTemperature(nn_computer *computer, double temperature) {
computer->temperature = temperature;
if(computer->temperature < computer->roomTemperature) computer->temperature = computer->roomTemperature;
}
void nn_setTemperatureCoefficient(nn_computer *computer, double coefficient) {
computer->temperatureCoefficient = coefficient;
}
void nn_setRoomTemperature(nn_computer *computer, double roomTemperature) {
computer->roomTemperature = roomTemperature;
if(computer->temperature < computer->roomTemperature) computer->temperature = computer->roomTemperature;
}
void nn_addHeat(nn_computer *computer, double heat) {
computer->temperature += heat * computer->temperatureCoefficient;
if(computer->temperature < computer->roomTemperature) computer->temperature = computer->roomTemperature;
}
void nn_removeHeat(nn_computer *computer, double heat) {
computer->temperature -= heat;
if(computer->temperature < computer->roomTemperature) computer->temperature = computer->roomTemperature;
}
nn_bool_t nn_isOverheating(nn_computer *computer) {
return computer->temperature > NN_OVERHEAT_MIN;
}
const char *nn_getError(nn_computer *computer) {
return computer->err;
}
void nn_clearError(nn_computer *computer) {
if(computer->allocatedError) {
nn_deallocStr(&computer->universe->ctx.allocator, computer->err);
}
computer->err = NULL;
computer->allocatedError = false;
}
void nn_setError(nn_computer *computer, const char *err) {
nn_clearError(computer);
char *copy = nn_strdup(&computer->universe->ctx.allocator, err);
if(copy == NULL) {
nn_setCError(computer, "out of memory");
return;
}
computer->err = copy;
computer->allocatedError = true;
}
void nn_setCError(nn_computer *computer, const char *err) {
nn_clearError(computer);
// we pinky promise this is safe
computer->err = (char *)err;
computer->allocatedError = false;
}
nn_component *nn_newComponent(nn_computer *computer, nn_address address, int slot, nn_componentTable *table, void *userdata) {
nn_component *c = NULL;
for(nn_size_t i = 0; i < computer->componentLen; i++) {
if(computer->components[i].address == NULL) {
c = computer->components + i;
break;
}
}
if(c == NULL) {
if(computer->componentLen == computer->componentCap) return NULL; // too many
c = computer->components + computer->componentLen;
computer->componentLen++;
}
if(address == NULL) {
c->address = nn_randomUUID(&computer->universe->ctx);
} else {
c->address = nn_strdup(&computer->universe->ctx.allocator, address);
}
if(c->address == NULL) return NULL;
c->table = table;
c->slot = slot;
c->computer = computer;
if(table->constructor == NULL) {
c->statePtr = userdata;
} else {
c->statePtr = table->constructor(table->userdata, userdata);
}
return c;
}
void nn_removeComponent(nn_computer *computer, nn_address address) {
for(nn_size_t i = 0; i < computer->componentLen; i++) {
if(nn_strcmp(computer->components[i].address, address) == 0) {
nn_destroyComponent(computer->components + i);
}
}
}
void nn_destroyComponent(nn_component *component) {
nn_deallocStr(&component->computer->universe->ctx.allocator, component->address);
if(component->table->destructor != NULL) {
component->table->destructor(component->table->userdata, component, component->statePtr);
}
component->address = NULL; // marks component as freed
}
nn_component *nn_findComponent(nn_computer *computer, nn_address address) {
for(nn_size_t i = 0; i < computer->componentLen; i++) {
if(computer->components[i].address == NULL) continue; // empty slot
if(nn_strcmp(computer->components[i].address, address) == 0) {
return computer->components + i;
}
}
return NULL;
}
nn_component *nn_iterComponent(nn_computer *computer, nn_size_t *internalIndex) {
for(nn_size_t i = *internalIndex; i < computer->componentLen; i++) {
if(computer->components[i].address == NULL) continue;
*internalIndex = i+1;
return computer->components + i;
}
return NULL;
}
void nn_resetCall(nn_computer *computer) {
for(nn_size_t i = 0; i < computer->argc; i++) {
nn_values_drop(computer->args[i]);
}
for(nn_size_t i = 0; i < computer->retc; i++) {
nn_values_drop(computer->rets[i]);
}
computer->argc = 0;
computer->retc = 0;
}
void nn_addArgument(nn_computer *computer, nn_value arg) {
if(computer->argc == NN_MAX_ARGS) return;
computer->args[computer->argc] = arg;
computer->argc++;
}
void nn_return(nn_computer *computer, nn_value val) {
if(computer->retc == NN_MAX_RETS) return;
computer->rets[computer->retc] = val;
computer->retc++;
}
nn_value nn_getArgument(nn_computer *computer, nn_size_t idx) {
if(idx >= computer->argc) return nn_values_nil();
return computer->args[idx];
}
nn_value nn_getReturn(nn_computer *computer, nn_size_t idx) {
if(idx >= computer->retc) return nn_values_nil();
return computer->rets[idx];
}
nn_size_t nn_getArgumentCount(nn_computer *computer) {
return computer->argc;
}
nn_size_t nn_getReturnCount(nn_computer *computer) {
return computer->retc;
}
char *nn_serializeProgram(nn_computer *computer, nn_Alloc *alloc, nn_size_t *len) {
return computer->arch->serialize(computer, alloc, computer->archState, computer->arch->userdata, len);
}
void nn_deserializeProgram(nn_computer *computer, const char *memory, nn_size_t len) {
computer->arch->deserialize(computer, memory, len, computer->archState, computer->arch->userdata);
}
nn_Context *nn_getComputerContext(nn_computer *computer) {
return &computer->universe->ctx;
}
nn_guard *nn_getComputerLock(nn_computer *computer) {
return computer->lock;
}
void nn_return_nil(nn_computer *computer) {
nn_return(computer, nn_values_nil());
}
void nn_return_integer(nn_computer *computer, nn_integer_t integer) {
nn_return(computer, nn_values_integer(integer));
}
void nn_return_number(nn_computer *computer, double number) {
nn_return(computer, nn_values_number(number));
}
void nn_return_boolean(nn_computer *computer, nn_bool_t boolean) {
nn_return(computer, nn_values_boolean(boolean));
}
void nn_return_cstring(nn_computer *computer, const char *cstr) {
nn_return(computer, nn_values_cstring(cstr));
}
void nn_return_string(nn_computer *computer, const char *str, nn_size_t len) {
nn_value val = nn_values_string(&computer->universe->ctx.allocator, str, len);
if(val.tag == NN_VALUE_NIL) {
nn_setCError(computer, "out of memory");
}
nn_return(computer, val);
}
nn_value nn_return_array(nn_computer *computer, nn_size_t len) {
nn_value val = nn_values_array(&computer->universe->ctx.allocator, len);
if(val.tag == NN_VALUE_NIL) {
nn_setCError(computer, "out of memory");
}
nn_return(computer, val);
return val;
}
nn_value nn_return_table(nn_computer *computer, nn_size_t len) {
nn_value val = nn_values_table(&computer->universe->ctx.allocator, len);
if(val.tag == NN_VALUE_NIL) {
nn_setCError(computer, "out of memory");
}
nn_return(computer, val);
return val;
}
void nn_return_resource(nn_computer *computer, nn_size_t userdata) {
nn_return(computer, nn_values_resource(userdata));
}
nn_bool_t nn_wakeupMatches(nn_value *values, nn_size_t valueLen, const char *wakeUp, nn_bool_t fuzzy) {
if(valueLen == 0) return false;
nn_value header = values[0];
const char *headerStr = nn_toCString(header);
if(fuzzy) {
return nn_strbegin(headerStr, wakeUp);
} else {
return nn_strcmp(headerStr, wakeUp) == 0;
}
}
const char *nn_pushNetworkMessage(nn_computer *computer, nn_address receiver, nn_address sender, nn_size_t port, double distance, nn_value *values, nn_size_t valueLen) {
nn_Alloc *alloc = &computer->universe->ctx.allocator;
nn_value buffer[valueLen + 5];
buffer[0] = nn_values_cstring("modem_message");
buffer[1] = nn_values_string(alloc, receiver, nn_strlen(receiver));
buffer[2] = nn_values_string(alloc, sender, nn_strlen(sender));
buffer[3] = nn_values_integer(port);
buffer[4] = nn_values_number(distance);
for(nn_size_t i = 0; i < valueLen; i++) {
buffer[i + 5] = nn_values_retain(values[i]);
}
return nn_pushSignal(computer, buffer, valueLen + 5);
}
static nn_resource_t *nn_resource_find(nn_computer *computer, nn_size_t id) {
for(nn_size_t i = 0; i < NN_MAX_CONCURRENT_RESOURCES; i++) {
if(computer->resources[i].id == id) {
return computer->resources + i;
}
}
return NULL;
}
nn_size_t nn_resource_allocate(nn_computer *computer, void *userdata, nn_resourceTable_t *table) {
nn_size_t i = 0;
for(nn_size_t j = 0; j < NN_MAX_CONCURRENT_RESOURCES; j++) {
if(computer->resources[j].id == NN_NULL_RESOURCE) {
i = j;
goto slotFound;
}
}
return NN_NULL_RESOURCE;
slotFound:
computer->rid++;
nn_size_t rid = computer->rid;
computer->resources[i] = (nn_resource_t) {
.id = rid,
.ptr = userdata,
.table = table,
};
return rid;
}
void nn_resource_release(nn_computer *computer, nn_size_t id) {
nn_resource_t *res = nn_resource_find(computer, id);
if(res == NULL) return;
res->id = NN_NULL_RESOURCE;
if(res->table->dtor != NULL) {
res->table->dtor(res->ptr);
}
}
nn_resourceTable_t *nn_resource_fetchTable(nn_computer *computer, nn_size_t resourceID) {
nn_resource_t *res = nn_resource_find(computer, resourceID);
if(res == NULL) return NULL;
return res->table;
}
nn_bool_t nn_resource_invoke(nn_computer *computer, nn_size_t resourceID, const char *method) {
nn_resource_t *res = nn_resource_find(computer, resourceID);
if(res == NULL) return false;
nn_resourceTable_t *t = res->table;
for(nn_size_t i = 0; i < t->methodCount; i++) {
nn_resourceMethod_t m = t->methods[i];
if(nn_strcmp(m.name, method) != 0) continue;
if(m.condition != NULL) {
if(!m.condition(res->ptr, m.userdata)) continue;
}
m.callback(res->ptr, m.userdata, computer);
return true;
}
return false;
}
// returns the name, and NULL for out of bounds
const char *nn_resource_nextMethodInfo(nn_computer *computer, nn_size_t id, const char **doc, nn_size_t *idx) {
nn_resource_t *res = nn_resource_find(computer, id);
if(res == NULL) return false;
nn_resourceTable_t *t = res->table;
for(nn_size_t i = *idx; i < t->methodCount; i++) {
nn_resourceMethod_t method = t->methods[i];
if(method.condition != NULL) {
if(!method.condition(res->ptr, method.userdata)) {
continue;
}
}
*idx = i + 1;
*doc = method.doc;
return method.name;
}
return NULL;
}
nn_deviceInfoList_t *nn_getComputerDeviceInfoList(nn_computer *computer) {
return computer->deviceInfo;
}
void nn_computer_clearBeep(nn_computer *computer) {
computer->hasBeep = false;
}
void nn_computer_setBeep(nn_computer *computer, double frequency, double duration, double volume) {
computer->hasBeep = true;
computer->beepFrequency = frequency;
computer->beepDuration = duration;
computer->beepVolume = volume;
}
nn_bool_t nn_computer_getBeep(nn_computer *computer, double *frequency, double *duration, double *volume) {
if(frequency != NULL) *frequency = computer->beepFrequency;
if(duration != NULL) *duration = computer->beepDuration;
if(volume != NULL) *volume = computer->beepVolume;
return computer->hasBeep;
}

View File

@@ -1,60 +0,0 @@
#ifndef NEONUCLEUS_COMPUTER_H
#define NEONUCLEUS_COMPUTER_H
#include "neonucleus.h"
typedef struct nn_signal {
nn_size_t len;
nn_value values[NN_MAX_SIGNAL_VALS];
} nn_signal;
typedef struct nn_resource_t {
nn_size_t id;
void *ptr;
nn_resourceTable_t *table;
} nn_resource_t;
typedef struct nn_computer {
char state;
nn_bool_t hasBeep;
nn_bool_t allocatedError;
char *err;
void *userdata;
nn_guard *lock;
nn_component *components;
nn_size_t componentLen;
nn_size_t componentCap;
nn_value args[NN_MAX_ARGS];
nn_size_t argc;
nn_value rets[NN_MAX_RETS];
nn_size_t retc;
nn_architecture *arch; // btw
void *archState;
nn_architecture *nextArch;
nn_architecture *supportedArch[NN_MAX_ARCHITECTURES];
nn_size_t supportedArchCount;
double timeOffset;
nn_universe *universe;
char *users[NN_MAX_USERS];
nn_size_t userCount;
double energy;
double maxEnergy;
nn_signal signals[NN_MAX_SIGNALS];
nn_size_t signalCount;
nn_size_t memoryTotal;
nn_address address;
nn_address tmpAddress;
double temperature;
double temperatureCoefficient;
double roomTemperature;
double callCost;
double callBudget;
nn_size_t rid;
nn_resource_t resources[NN_MAX_CONCURRENT_RESOURCES];
nn_deviceInfoList_t *deviceInfo;
double beepFrequency;
double beepDuration;
double beepVolume;
} nn_computer;
#endif

View File

@@ -1,20 +0,0 @@
const std = @import("std");
const c = @cImport({
@cInclude("neonucleus.h");
});
pub export fn nn_data_crc32(inBuf: [*]const u8, len: usize, outBuf: [*]u8) void {
var digest = std.hash.Crc32.hash(inBuf[0..len]);
digest = std.mem.nativeToLittle(u32, digest);
const digestBuf: [4]u8 = @bitCast(digest);
std.mem.copyForwards(u8, outBuf[0..4], &digestBuf);
}
pub export fn nn_data_md5(inBuf: [*]const u8, len: usize, outBuf: [*]u8) void {
std.crypto.hash.Md5.hash(inBuf[0..len], @ptrCast(outBuf), .{});
}
pub export fn nn_data_sha256(inBuf: [*]const u8, len: usize, outBuf: [*]u8) void {
std.crypto.hash.sha2.Sha256.hash(inBuf[0..len], @ptrCast(outBuf), .{});
}

View File

@@ -1,130 +0,0 @@
#include "neonucleus.h"
typedef struct nn_deviceInfoPair_t {
char *key;
char *value;
} nn_deviceInfoPair_t;
typedef struct nn_deviceInfo_t {
nn_deviceInfoPair_t *pairs;
nn_size_t len;
nn_size_t capacity;
char *address;
nn_Alloc alloc;
} nn_deviceInfo_t;
typedef struct nn_deviceInfoList_t {
nn_Context ctx;
nn_deviceInfo_t *info;
nn_size_t len;
nn_size_t cap;
} nn_deviceInfoList_t;
nn_deviceInfoList_t *nn_newDeviceInfoList(nn_Context *ctx, nn_size_t preallocate) {
nn_Alloc *alloc = &ctx->allocator;
nn_deviceInfoList_t *list = nn_alloc(alloc, sizeof(nn_deviceInfoList_t));
if(list == NULL) return NULL;
list->ctx = *ctx;
list->len = 0;
list->cap = preallocate;
list->info = nn_alloc(alloc, sizeof(nn_deviceInfo_t) * list->cap);
if(list->info == NULL) {
nn_dealloc(alloc, list, sizeof(nn_deviceInfoList_t));
return NULL;
}
return list;
}
static void nn_deleteDeviceInfo(nn_Context *ctx, nn_deviceInfo_t *info) {
nn_Alloc *alloc = &ctx->allocator;
nn_deallocStr(alloc, info->address);
nn_dealloc(alloc, info->pairs, sizeof(nn_deviceInfoPair_t) * info->capacity);
}
void nn_deleteDeviceInfoList(nn_deviceInfoList_t *list) {
for(nn_size_t i = 0; i < list->len; i++) {
nn_deleteDeviceInfo(&list->ctx, &list->info[i]);
}
nn_Alloc alloc = list->ctx.allocator;
nn_dealloc(&alloc, list->info, sizeof(nn_deviceInfo_t) * list->cap);
nn_dealloc(&alloc, list, sizeof(nn_deviceInfoList_t));
}
nn_deviceInfo_t *nn_addDeviceInfo(nn_deviceInfoList_t *list, nn_address address, nn_size_t maxKeys) {
if(list->len == list->cap) {
nn_size_t neededCap = list->cap;
if(neededCap < 1) neededCap = 1;
while(neededCap < (list->len + 1)) neededCap *= 2;
nn_deviceInfo_t *newBuf = nn_resize(&list->ctx.allocator, list->info, sizeof(nn_deviceInfo_t) * list->cap, sizeof(nn_deviceInfo_t) * neededCap);
if(newBuf == NULL) return NULL;
list->cap = neededCap;
list->info = newBuf;
}
nn_deviceInfoPair_t *pairs = nn_alloc(&list->ctx.allocator, sizeof(nn_deviceInfoPair_t) * maxKeys);
if(pairs == NULL) return NULL;
nn_size_t i = list->len;
list->info[i] = (nn_deviceInfo_t) {
.alloc = list->ctx.allocator,
.pairs = pairs,
.len = 0,
.address = address == NULL ? nn_randomUUID(&list->ctx) : nn_strdup(&list->ctx.allocator, address), // TODO: handle OOM
.capacity = maxKeys,
};
list->len++;
return list->info + i;
}
void nn_removeDeviceInfo(nn_deviceInfoList_t *list, const char *address) {
nn_size_t j = 0;
for(nn_size_t i = 0; i < list->len; i++) {
if(nn_strcmp(list->info[i].address, address) == 0) {
nn_deleteDeviceInfo(&list->ctx, &list->info[i]);
} else {
list->info[j] = list->info[i];
j++;
}
}
list->len = j;
}
nn_bool_t nn_registerDeviceKey(nn_deviceInfo_t *deviceInfo, const char *key, const char *value) {
if(deviceInfo->len == deviceInfo->capacity) return false;
nn_size_t i = deviceInfo->len;
nn_Alloc *alloc = &deviceInfo->alloc;
// TODO: handle OOM
deviceInfo->pairs[i].key = nn_strdup(alloc, key);
deviceInfo->pairs[i].value = nn_strdup(alloc, value);
deviceInfo->len++;
return true;
}
nn_deviceInfo_t *nn_getDeviceInfoAt(nn_deviceInfoList_t *list, nn_size_t idx) {
if(idx >= list->len) return NULL;
return &list->info[idx];
}
const char *nn_getDeviceInfoAddress(nn_deviceInfo_t *deviceInfo) {
return deviceInfo->address;
}
nn_size_t nn_getDeviceCount(nn_deviceInfoList_t *list) {
return list->len;
}
const char *nn_iterateDeviceInfoKeys(nn_deviceInfo_t *deviceInfo, nn_size_t idx, const char **value) {
if(idx >= deviceInfo->len) return NULL;
nn_deviceInfoPair_t pair = deviceInfo->pairs[idx];
if(value != NULL) *value = pair.value;
return pair.key;
}
nn_size_t nn_getDeviceKeyCount(nn_deviceInfo_t *deviceInfo) {
return deviceInfo->len;
}

File diff suppressed because it is too large Load Diff

View File

View File

@@ -1,86 +0,0 @@
#include "neonucleus.h"
#ifndef NN_BAREMETAL
#include "tinycthread.h"
static nn_bool_t nni_libcLock(void *_, mtx_t *lock, int action, int flags) {
if(action == NN_LOCK_INIT) {
mtx_init(lock, mtx_recursive);
} else if(action == NN_LOCK_DEINIT) {
mtx_destroy(lock);
} else if(action == NN_LOCK_RETAIN) {
if(flags & NN_LOCK_IMMEDIATE) {
return mtx_trylock(lock) == thrd_success;
}
return mtx_lock(lock);
} else if(action == NN_LOCK_RELEASE) {
mtx_unlock(lock);
}
return NN_TRUE;
}
nn_LockManager nn_libcMutex(void) {
nn_LockManager mgr = {};
mgr.lockSize = sizeof(mtx_t);
mgr.userdata = NULL;
mgr.proc = (nn_LockProc *)nni_libcLock;
return mgr;
}
#endif
static nn_bool_t nni_noLock(void *_, void *__, int action, int flags) {
return NN_TRUE;
}
nn_LockManager nn_noMutex(void) {
return (nn_LockManager) {
.userdata = NULL,
.lockSize = 0,
.proc = (nn_LockProc *)nni_noLock,
};
}
nn_guard *nn_newGuard(nn_Context *ctx) {
nn_guard *g = nn_alloc(&ctx->allocator, ctx->lockManager.lockSize);
if(g == NULL) return NULL;
ctx->lockManager.proc(ctx->lockManager.userdata, g, NN_LOCK_INIT, NN_LOCK_DEFAULT);
return g;
}
void nn_lock(nn_Context *context, nn_guard *guard) {
if(guard == NULL) return;
context->lockManager.proc(context->lockManager.userdata, guard, NN_LOCK_RETAIN, NN_LOCK_DEFAULT);
}
nn_bool_t nn_tryLock(nn_Context *context, nn_guard *guard) {
if(guard == NULL) return NN_TRUE;
return context->lockManager.proc(context->lockManager.userdata, guard, NN_LOCK_RETAIN, NN_LOCK_IMMEDIATE);
}
void nn_unlock(nn_Context *context, nn_guard *guard) {
if(guard == NULL) return;
context->lockManager.proc(context->lockManager.userdata, guard, NN_LOCK_RELEASE, NN_LOCK_DEFAULT);
}
void nn_deleteGuard(nn_Context *context, nn_guard *guard) {
if(guard == NULL) return;
context->lockManager.proc(context->lockManager.userdata, guard, NN_LOCK_DEINIT, NN_LOCK_DEFAULT);
nn_dealloc(&context->allocator, guard, context->lockManager.lockSize);
}
void nn_addRef(nn_refc *refc, nn_size_t count) {
(*refc) += count;
}
void nn_incRef(nn_refc *refc) {
nn_addRef(refc, 1);
}
nn_bool_t nn_removeRef(nn_refc *refc, nn_size_t count) {
return ((*refc) -= count) == 0;
}
nn_bool_t nn_decRef(nn_refc *refc) {
return nn_removeRef(refc, 1);
}

View File

@@ -281,6 +281,7 @@ ne_FsState *ne_newFS(const char *path, bool readonly) {
}
sprintf(fs->path, "data%c%s", NE_PATHSEP, path);
fs->isReadonly = readonly;
fs->dir = NULL;
return fs;
}
@@ -1236,9 +1237,9 @@ cleanup:;
nn_destroyComputer(c);
nn_destroyComponentState(sandstate);
nn_destroyComponentState(etype);
nn_destroyComponentState(gputype);
nn_destroyComponentState(scrtype);
nn_destroyComponentState(keytype);
nn_destroyComponentState(gputype);
nn_destroyComponentState(vdriveState);
for(size_t i = 0; i < 5; i++) nn_destroyComponentState(fstype[i]);
ne_dropScreenBuf(scrbuf);

View File

@@ -1012,7 +1012,7 @@ size_t nn_getTotalMemory(nn_Computer *computer) {
}
size_t nn_getFreeMemory(nn_Computer *computer) {
if(computer->state == NN_BOOTUP) return 0;
if(computer->state == NN_BOOTUP) return computer->totalMemory;
nn_ArchitectureRequest req;
req.computer = computer;
req.action = NN_ARCH_FREEMEM;
@@ -1031,6 +1031,45 @@ double nn_getUptime(nn_Computer *computer) {
return nn_currentTime(&computer->universe->ctx) - computer->creationTimestamp;
}
nn_Exit nn_deserializeComputer(nn_Computer *computer, const char *buf, size_t buflen) {
nn_ArchitectureRequest req;
req.computer = computer;
req.action = NN_ARCH_DESERIALIZE;
req.globalState = computer->arch.state;
req.localState = computer->archState;
req.memIn = buf;
req.memLen = buflen;
return computer->arch.handler(&req);
}
nn_Exit nn_serializeComputer(nn_Computer *computer, char **buf, size_t *buflen) {
nn_ArchitectureRequest req;
req.computer = computer;
req.action = NN_ARCH_SERIALIZE;
req.globalState = computer->arch.state;
req.localState = computer->archState;
nn_Exit e = computer->arch.handler(&req);
if(e) return e;
*buf = req.memOut;
*buflen = req.memLen;
return NN_OK;
}
nn_Exit nn_freeSerializedComputer(nn_Computer *computer, char *buf, size_t buflen) {
nn_ArchitectureRequest req;
req.computer = computer;
req.action = NN_ARCH_DROPSERIALIZED;
req.globalState = computer->arch.state;
req.localState = computer->archState;
req.memOut = buf;
req.memLen = buflen;
return computer->arch.handler(&req);
}
void nn_setError(nn_Computer *computer, const char *s) {
nn_setLError(computer, s, nn_strlen(s));

File diff suppressed because it is too large Load Diff

View File

@@ -1,29 +0,0 @@
#include "resource.h"
nn_resourceTable_t *nn_resource_newTable(nn_Context *ctx, nn_resourceDestructor_t *dtor) {
nn_resourceTable_t *t = nn_alloc(&ctx->allocator, sizeof(nn_resourceTable_t));
if(t == NULL) return NULL;
t->dtor = dtor;
t->methodCount = 0;
return t;
}
nn_resourceMethod_t *nn_resource_addMethod(nn_resourceTable_t *table, const char *methodName, nn_resourceMethodCallback_t *method, const char *doc) {
if(table->methodCount == NN_MAX_METHODS) return NULL;
nn_resourceMethod_t *m = &table->methods[table->methodCount];
table->methodCount++;
nn_Alloc *a = &table->ctx.allocator;
m->name = nn_strdup(a, methodName);
m->doc = nn_strdup(a, doc);
m->callback = method;
m->condition = NULL;
return m;
}
void nn_resource_setUserdata(nn_resourceMethod_t *method, void *methodUserdata) {
method->userdata = methodUserdata;
}
void nn_resource_setCondition(nn_resourceMethod_t *method, nn_resourceMethodCondition_t *methodCondition) {
method->condition = methodCondition;
}

View File

@@ -1,21 +0,0 @@
#ifndef NN_RESOURCE
#define NN_RESOURCE
#include "neonucleus.h"
typedef struct nn_resourceMethod_t {
const char *name;
const char *doc;
void *userdata;
nn_resourceMethodCallback_t *callback;
nn_resourceMethodCondition_t *condition;
} nn_resourceMethod_t;
typedef struct nn_resourceTable_t {
nn_Context ctx;
nn_resourceDestructor_t *dtor;
nn_size_t methodCount;
nn_resourceMethod_t methods[NN_MAX_METHODS];
} nn_resourceTable_t;
#endif

View File

@@ -1,455 +0,0 @@
-- sandbox stuff
local function copy(v, p)
if type(v) == "table" then
local t = {}
for key, val in pairs(v) do
t[key] = copy(val)
end
if p then
for key, val in pairs(p) do
t[key] = copy(val)
end
end
return t
else
return v
end
end
local function spcall(f, ...)
local t = {pcall(f, ...)}
if t[1] then
return table.unpack(t, 2)
end
return nil, t[2]
end
local function nextDeadline()
return computer.uptime() + 5
end
local bubbleYield = false
local timeout = nextDeadline()
local function tooLong()
return computer.uptime() >= timeout
end
local tooLongWithoutYielding = "too long without yielding"
local function nextHeatUp()
return computer.uptime() + math.random() * 2 + 0.1
end
local heatInc = nextHeatUp()
debug.sethook(function()
if computer.uptime() >= heatInc then
heatInc = nextHeatUp()
computer.addHeat(math.random() * 3)
end
if tooLong() and not bubbleYield then
bubbleYield = true
error(tooLongWithoutYielding) -- here it is an actual string
end
end, "c", 100000) -- no bogo mips, the check is cheap anyways
local function resume(co, val1, ...)
while true do
local t = {coroutine.resume(co, val1, ...)}
if bubbleYield then -- yield was meaningless
coroutine.yield() -- carry through
else
return table.unpack(t) -- yield the user cares about
end
end
end
local function yield()
bubbleYield = true
coroutine.yield()
end
local function ensureYields()
if bubbleYield then
coroutine.yield()
end
end
local function checkArg(idx, v, ...)
local bad = true
local n = select("#", ...)
for i=1,n do
local t = select(i, ...)
if type(v) == t then bad = false break end
end
if not bad then return end
local msg = string.format("bad argument #%d (%s expected, got %s)", idx, table.concat({...}, " or "), type(v))
error(msg, 3)
end
local libcomponent
local componentProxy = {
__pairs = function(self)
local method
return function()
method = next(self, method)
if method then
return method, self[method]
end
end
end,
}
local componentCallback = {
__call = function(self, ...)
return libcomponent.invoke(self.address, self.name, ...)
end,
__tostring = function(self)
return libcomponent.doc(self.address, self.name) or "undocumented"
end
}
libcomponent = {
invoke = function(addr, method, ...)
checkArg(1, addr, "string")
checkArg(2, method, "string")
while true do
local r = {pcall(component.invoke, addr, method, ...)}
computer.clearError()
-- in this situation, either the temperature is above 100 C and we throttle
-- or the call budget has been filled and we dont care
if computer.isOverheating() or computer.isOverworked() then
local ok = pcall(yield)
assert(ok, "component explicitly requested to be suspended")
end
if computer.getState() == states.blackout then
-- oops, powerout
local ok = pcall(yield)
assert(ok, "blackout")
end
if computer.getState() == states.busy then
-- busy gets to try again
computer.setState(states.running)
else
if r[1] then
return table.unpack(r, 2)
end
return nil, r[2]
end
end
end,
list = function(filter, exact)
checkArg(1, filter, "string", "nil")
local t = component.list()
local list = t
if filter then
list = {}
for addr, kind in pairs(t) do
if type(exact) == "boolean" and exact then
if kind == filter then
list[addr] = kind
end
elseif rawequal(exact, "pattern") then
if string.match(kind, filter) then
list[addr] = kind
end
else
if string.find(kind, filter, nil, true) then
list[addr] = kind
end
end
end
end
local key = nil
return setmetatable(list, {
__call = function()
key = next(list, key)
if key then
return key, list[key]
end
end,
})
end,
methods = component.methods,
fields = component.fields,
doc = component.doc,
slot = component.slot,
type = component.type,
proxy = function(addr)
checkArg(1, addr, "string")
if not component.type(addr) then return nil, "no such component" end
local proxy = setmetatable({
address = addr,
type = component.type(addr),
slot = component.slot(addr),
fields = {},
}, componentProxy)
local methods = component.methods(addr)
for method in pairs(methods) do
proxy[method] = setmetatable({address = addr, name = method}, componentCallback)
end
return proxy
end,
}
local libcomputer = {
isRobot = function()
return libcomponent.list("robot", true) ~= nil
end,
address = computer.address,
tmpAddress = computer.tmpAddress,
usedMemory = computer.usedMemory,
freeMemory = computer.freeMemory,
totalMemory = computer.totalMemory,
uptime = computer.uptime,
energy = computer.energy,
maxEnergy = computer.maxEnergy,
users = computer.users,
-- these 2 are not actually implemented
-- TODO: implement them
addUser = computer.addUser,
removeUser = computer.removeUser,
shutdown = function(reboot)
computer.setState(reboot and states.REPEAT or states.closing)
yield()
end,
pushSignal = computer.pushSignal,
pullSignal = function(timeout)
local deadline = computer.uptime() + (type(timeout) == "number" and timeout or math.huge)
repeat
yield() -- give executor a chance to give us stuff
local s = table.pack(computer.popSignal())
if s.n > 0 then
return table.unpack(s)
end
until computer.uptime() >= deadline
end,
beep = computer.beep,
getDeviceInfo = computer.getDeviceInfo,
getProgramLocations = function()
return {} -- yup
end,
getArchitectures = computer.getArchitectures,
getArchitecture = computer.getArchitecture,
setArchitecture = function(...)
computer.setArchitecture(...) -- also sets state to SWITCH
yield()
end,
getTemperature = computer.getTemperature,
}
local sandbox
sandbox = {
assert = assert,
error = error,
getmetatable = function(t)
if type(t) == "string" then -- HUGE security problem
return nil -- fixed
end
return getmetatable(t)
end,
ipairs = ipairs,
load = function(ld, source, _, env) -- mode is ignored as bytecode is just fully illegal for now
return load(ld, source, "t", env or sandbox)
end,
next = next,
pairs = pairs,
pcall = function(...)
if tooLong() then
yield()
return false, tooLongWithoutYielding
end
local t = {pcall(...)}
ensureYields() -- if it took too long, this will make it yield
return table.unpack(t)
end,
rawequal = rawequal,
rawget = rawget,
rawlen = rawlen,
rawset = rawset,
select = select,
setmetatable = function(t, mt)
if type(mt) ~= "table" then
return setmetatable(t, mt)
end
-- we do mutate the metatable but this field shouldn't exist anyways
mt.__gc = nil
return setmetatable(t, mt)
end,
tonumber = tonumber,
tostring = tostring,
type = type,
_VERSION = _VERSION,
xpcall = function(f, msgh, ...)
checkArg(1, f, "function")
checkArg(2, msgh, "function")
-- to prevent infinite loops we simply terminate the error handler if it took too long.
local function errorCapture(ff, ...)
--ensureYields() -- you can't yield in xpcall...
-- Immediately dont care
if tooLong() then
return nil, tooLongWithoutYielding
end
-- This would mean you shutdown in the errorCapture.
-- In vanilla OC, that does nothing.
-- In here, it returns a suspended error and then yields eventually.
if bubbleYield then
return nil, "suspended"
end
return xpcall(ff, function(...)
if tooLong() then
return tooLongWithoutYielding
else
return select(2, errorCapture(msgh, ...))
end
end, ...)
end
local t = {errorCapture(f, ...)}
pcall(ensureYields) -- it can fail if we are doing xpcall in xpcall.
return table.unpack(t)
end,
coroutine = {
create = coroutine.create,
resume = resume,
running = coroutine.running,
status = coroutine.status,
yield = coroutine.yield,
wrap = function(f)
-- uses the correct resume
local co = coroutine.create(f)
return function(...)
local result = {resume(co, ...)}
if result[1] then
return table.unpack(result, 2)
else
error(result[2], 0)
end
end
end,
isyieldable = coroutine.isyieldable,
},
string = copy(string),
table = copy(table),
math = copy(math, {
-- patch table
atan2 = math.atan,
ldexp = function(a, e) return a * (2.0 ^ e) end,
pow = function(a, b) return a ^ b end,
}),
os = {
clock = os.clock,
date = os.date,
difftime = function(t2, t1) return t2 - t1 end, -- thanks UNIX
time = function(t)
checkArg(1, t, "table", "nil")
return os.time(t)
end,
},
debug = {
getinfo = function(...)
local result = debug.getinfo(...)
if result then
return {
source = result.source,
short_src = result.short_src,
linedefined = result.linedefined,
lastlinedefined = result.lastlinedefined,
what = result.what,
currentline = result.currentline,
nups = result.nups,
nparams = result.nparams,
isvararg = result.isvararg,
name = result.name,
namewhat = result.namewhat,
istailcall = result.istailcall,
-- believe it or not, this IS NOT safe.
-- They may use this to re-call machine.lua which would reset the hook and timeout.
-- TODO: make this safe.
--func = result.func,
}
end
end,
traceback = debug.traceback,
-- we only allow the first return, aka not the value.
-- Otherwise, some dumb shmuck could do nasty stuff.
-- TODO: make them not need this.
getlocal = function(...) return (debug.getlocal(...)) end,
getupvalue = function(...) return (debug.getupvalue(...)) end,
},
utf8 = copy(utf8),
unicode = copy(unicode, {
isWide = function(s)
local c = unicode.sub(s, 1, 1)
return unicode.wlen(c) > unicode.len(c)
end,
upper = string.upper,
lower = string.lower,
wtrunc = function (str,space)
space = space - 1
return unicode.sub(str, 1, space)
end,
}),
checkArg = checkArg,
component = libcomponent,
computer = libcomputer,
debugprint = print,
}
sandbox._G = sandbox
local function bootstrap()
local eeprom = libcomponent.list("eeprom")()
assert(eeprom, "no eeprom")
local code = libcomponent.invoke(eeprom, "get")
assert(code and #code > 0, "empty eeprom")
return assert(load(code, "=bios", "t", sandbox))
end
coroutine.yield() -- startup delay
local f = bootstrap()
local co = coroutine.create(f)
local gcInterval = 0.25
local lastGC = computer.uptime()
while true do
timeout = nextDeadline()
bubbleYield = false
if computer.uptime() - lastGC >= gcInterval then
collectgarbage("collect")
lastGC = computer.uptime()
end
local ok, err = coroutine.resume(co)
if not ok then
error(debug.traceback(co, err), 0)
elseif coroutine.status(co) == "dead" then
error("computer halted", 0)
else
coroutine.yield()
end
end

View File

@@ -1,789 +0,0 @@
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "neonucleus.h"
char *testLuaSandbox = NULL;
#if LUA_VERSION_NUM == 502
#include <math.h>
// monkey patching
bool lua_isinteger(lua_State *L, int i) {
if(lua_type(L, i) != LUA_TNUMBER) return false;
double x = lua_tonumber(L, i);
if(isinf(x)) return false;
if(isnan(x)) return false;
return trunc(x) == x;
}
void lua_seti(lua_State *L, int arr, int i) {
lua_rawseti(L, arr, i);
}
#endif
typedef struct testLuaArch {
lua_State *L;
nn_computer *computer;
size_t memoryUsed;
} testLuaArch;
testLuaArch *testLuaArch_get(lua_State *L) {
lua_getfield(L, LUA_REGISTRYINDEX, "archPtr");
testLuaArch *arch = lua_touserdata(L, -1);
lua_pop(L, 1);
return arch;
}
nn_Alloc *testLuaArch_getAlloc(lua_State *L) {
lua_getfield(L, LUA_REGISTRYINDEX, "archPtr");
testLuaArch *arch = lua_touserdata(L, -1);
lua_pop(L, 1);
return nn_getAllocator(nn_getUniverse(arch->computer));
}
const char *testLuaArch_pushlstring(lua_State *L, const char *s, size_t len) {
if (lua_checkstack(L, 1) == 0) {
return NULL;
}
testLuaArch* arch = testLuaArch_get(L);
size_t freeSpace = nn_getComputerMemoryTotal(arch->computer) - arch->memoryUsed;
if ((len * 2 + 64) > freeSpace) { // dk how much space this really needs and its unstable so :/
return NULL;
}
return lua_pushlstring(L, s, len);
}
const char *testLuaArch_pushstring(lua_State *L, const char *s) {
size_t len = strlen(s);
return testLuaArch_pushlstring(L, s, len);
}
void *testLuaArch_alloc(testLuaArch *arch, void *ptr, size_t osize, size_t nsize) {
nn_Alloc *alloc = nn_getAllocator(nn_getUniverse(arch->computer));
if(nsize == 0) {
arch->memoryUsed -= osize;
nn_dealloc(alloc, ptr, osize);
return NULL;
} else {
size_t actualOldSize = osize;
if(ptr == NULL) actualOldSize = 0;
if(arch->memoryUsed - actualOldSize + nsize > nn_getComputerMemoryTotal(arch->computer)) {
return NULL; // OOM condition
}
arch->memoryUsed -= actualOldSize;
arch->memoryUsed += nsize;
return nn_resize(alloc, ptr, actualOldSize, nsize);
}
}
nn_computer *testLuaArch_getComputer(lua_State *L) {
return testLuaArch_get(L)->computer;
}
static nn_value testLuaArch_getValue(lua_State *L, int index) {
int type = lua_type(L, index);
nn_Alloc *alloc = testLuaArch_getAlloc(L);
if(type == LUA_TBOOLEAN) {
return nn_values_boolean(lua_toboolean(L, index));
}
if(lua_isnoneornil(L, index)) {
return nn_values_nil();
}
if(type == LUA_TSTRING) {
size_t l = 0;
const char *s = lua_tolstring(L, index, &l);
return nn_values_string(alloc, s, l);
}
if(type == LUA_TNUMBER && lua_isinteger(L, index)) {
return nn_values_integer(lua_tointeger(L, index));
}
if(type == LUA_TNUMBER && lua_isnumber(L, index)) {
return nn_values_number(lua_tonumber(L, index));
}
//TODO: bring it back once I make everything else not leak memory
//luaL_argcheck(L, false, index, luaL_typename(L, index));
return nn_values_nil();
}
static void testLuaArch_pushValue(lua_State *L, nn_value val) {
int t = nn_values_getType(val);
if(t == NN_VALUE_NIL) {
lua_pushnil(L);
return;
}
if(t == NN_VALUE_INT) {
lua_pushinteger(L, val.integer);
return;
}
if(t == NN_VALUE_NUMBER) {
lua_pushnumber(L, val.number);
return;
}
if(t == NN_VALUE_BOOL) {
lua_pushboolean(L, val.boolean);
return;
}
if(t == NN_VALUE_STR) {
lua_pushlstring(L, val.string->data, val.string->len);
return;
}
if(t == NN_VALUE_CSTR) {
lua_pushstring(L, val.cstring);
return;
}
if(t == NN_VALUE_ARRAY) {
nn_array *arr = val.array;
lua_createtable(L, arr->len, 0);
int luaVal = lua_gettop(L);
for(size_t i = 0; i < arr->len; i++) {
testLuaArch_pushValue(L, arr->values[i]);
lua_seti(L, luaVal, i+1);
}
return;
}
if(t == NN_VALUE_TABLE) {
nn_table *tbl = val.table;
lua_createtable(L, 0, tbl->len);
int luaVal = lua_gettop(L);
for(size_t i = 0; i < tbl->len; i++) {
testLuaArch_pushValue(L, tbl->pairs[i].key);
testLuaArch_pushValue(L, tbl->pairs[i].val);
lua_settable(L, luaVal);
}
return;
}
luaL_error(L, "invalid return type: %d", t);
}
static int testLuaArch_computer_clearError(lua_State *L) {
testLuaArch *s = testLuaArch_get(L);
nn_clearError(s->computer);
return 0;
}
static int testLuaArch_computer_usedMemory(lua_State *L) {
testLuaArch *s = testLuaArch_get(L);
lua_pushinteger(L, s->memoryUsed);
return 1;
}
static int testLuaArch_computer_freeMemory(lua_State *L) {
testLuaArch *s = testLuaArch_get(L);
lua_pushinteger(L, nn_getComputerMemoryTotal(s->computer) - s->memoryUsed);
return 1;
}
static int testLuaArch_computer_totalMemory(lua_State *L) {
testLuaArch *s = testLuaArch_get(L);
lua_pushinteger(L, nn_getComputerMemoryTotal(s->computer));
return 1;
}
static int testLuaArch_computer_address(lua_State *L) {
nn_computer *c = testLuaArch_getComputer(L);
lua_pushstring(L, nn_getComputerAddress(c));
return 1;
}
static int testLuaArch_computer_tmpAddress(lua_State *L) {
nn_computer *c = testLuaArch_getComputer(L);
lua_pushstring(L, nn_getTmpAddress(c));
return 1;
}
static int testLuaArch_computer_uptime(lua_State *L) {
nn_computer *c = testLuaArch_getComputer(L);
lua_pushnumber(L, nn_getUptime(c));
return 1;
}
// TODO: beep
static int testLuaArch_computer_beep(lua_State *L) {
nn_computer *c = testLuaArch_getComputer(L);
// defaults
double frequency = 200;
double duration = 0.25;
double volume = 1;
if(lua_type(L, 1) == LUA_TNUMBER) {
frequency = lua_tonumber(L, 1);
}
if(lua_type(L, 2) == LUA_TNUMBER) {
duration = lua_tonumber(L, 2);
}
if(lua_type(L, 3) == LUA_TNUMBER) {
volume = lua_tonumber(L, 3);
}
nn_computer_setBeep(c, frequency, duration, volume);
return 0;
}
static int testLuaArch_computer_energy(lua_State *L) {
nn_computer *c = testLuaArch_getComputer(L);
lua_pushnumber(L, nn_getEnergy(c));
return 1;
}
static int testLuaArch_computer_maxEnergy(lua_State *L) {
nn_computer *c = testLuaArch_getComputer(L);
lua_pushnumber(L, nn_getEnergy(c));
return 1;
}
static int testLuaArch_computer_getArchitecture(lua_State *L) {
nn_computer *c = testLuaArch_getComputer(L);
lua_pushstring(L, nn_getArchitecture(c)->archName);
return 1;
}
static int testLuaArch_computer_getArchitectures(lua_State *L) {
nn_computer *c = testLuaArch_getComputer(L);
lua_createtable(L, 3, 0);
int arr = lua_gettop(L);
size_t i = 0;
while(true) {
nn_architecture *arch = nn_getSupportedArchitecture(c, i);
if(arch == NULL) break;
i++;
lua_pushstring(L, arch->archName);
lua_seti(L, arr, i);
}
return 1;
}
static int testLuaArch_computer_setArchitecture(lua_State *L) {
nn_computer *c = testLuaArch_getComputer(L);
const char *requested = luaL_checkstring(L, 1);
for(size_t i = 0;; i++) {
nn_architecture *arch = nn_getSupportedArchitecture(c, i);
if(arch == NULL) break;
if(strcmp(arch->archName, requested) == 0) {
nn_setState(c, NN_STATE_SWITCH);
nn_setNextArchitecture(c, arch);
return 0;
}
}
luaL_error(L, "unsupported architecture: %s", requested);
return 0;
}
static int testLuaArch_computer_isOverworked(lua_State *L) {
nn_computer *c = testLuaArch_getComputer(L);
lua_pushboolean(L, nn_isOverworked(c));
return 1;
}
static int testLuaArch_computer_isOverheating(lua_State *L) {
nn_computer *c = testLuaArch_getComputer(L);
lua_pushboolean(L, nn_isOverheating(c));
return 1;
}
static int testLuaArch_computer_getTemperature(lua_State *L) {
nn_computer *c = testLuaArch_getComputer(L);
lua_pushnumber(L, nn_getTemperature(c));
return 1;
}
static int testLuaArch_computer_addHeat(lua_State *L) {
nn_computer *c = testLuaArch_getComputer(L);
double n = luaL_checknumber(L, 1);
nn_addHeat(c, n);
return 0;
}
static int testLuaArch_computer_pushSignal(lua_State *L) {
nn_computer *c = testLuaArch_getComputer(L);
luaL_checkstring(L, 1);
int argc = lua_gettop(L);
if(argc > NN_MAX_ARGS) luaL_error(L, "too many arguments");
nn_value args[argc];
for(size_t i = 0; i < argc; i++) {
args[i] = testLuaArch_getValue(L, i+1);
}
const char *err = nn_pushSignal(c, args, argc);
if(err != NULL) {
for(size_t i = 0; i < argc; i++) {
nn_values_drop(args[i]);
}
luaL_error(L, "%s", err);
return 0;
}
return 0;
}
static int testLuaArch_computer_popSignal(lua_State *L) {
nn_computer *c = testLuaArch_getComputer(L);
size_t retc = nn_signalSize(c);
for(size_t i = 0; i < retc; i++) {
testLuaArch_pushValue(L, nn_fetchSignalValue(c, i));
}
nn_popSignal(c);
return retc;
}
static int testLuaArch_computer_users(lua_State *L) {
nn_computer *c = testLuaArch_getComputer(L);
size_t i = 0;
while(true) {
const char *name = nn_indexUser(c, i);
if(name == NULL) break;
lua_pushstring(L, name);
i++;
}
return i;
}
static int testLuaArch_computer_getState(lua_State *L) {
nn_computer *c = testLuaArch_getComputer(L);
lua_pushinteger(L, nn_getState(c));
return 1;
}
static int testLuaArch_computer_setState(lua_State *L) {
nn_computer *c = testLuaArch_getComputer(L);
int s = luaL_checkinteger(L, 1);
nn_setState(c, s);
return 1;
}
static int testLuaArch_computer_getDeviceInfo(lua_State *L) {
nn_computer *c = testLuaArch_getComputer(L);
nn_deviceInfoList_t *list = nn_getComputerDeviceInfoList(c);
nn_size_t deviceCount = nn_getDeviceCount(list);
lua_createtable(L, 0, deviceCount);
int infoTable = lua_gettop(L);
for(nn_size_t i = 0; i < deviceCount; i++) {
nn_deviceInfo_t *info = nn_getDeviceInfoAt(list, i);
lua_createtable(L, 0, 16);
int deviceTable = lua_gettop(L);
nn_size_t j = 0;
while(true) {
const char *value = NULL;
const char *key = nn_iterateDeviceInfoKeys(info, j, &value);
j++;
if(key == NULL) break;
lua_pushstring(L, value);
lua_setfield(L, deviceTable, key);
}
lua_setfield(L, infoTable, nn_getDeviceInfoAddress(info));
}
return 1;
}
static int testLuaArch_component_list(lua_State *L) {
nn_computer *c = testLuaArch_getComputer(L);
lua_createtable(L, 0, 10);
size_t iter = 0;
int list = lua_gettop(L);
while(true) {
nn_component *component = nn_iterComponent(c, &iter);
if(component == NULL) break;
nn_componentTable *table = nn_getComponentTable(component);
nn_address addr = nn_getComponentAddress(component);
const char *type = nn_getComponentType(table);
lua_pushstring(L, type);
lua_setfield(L, list, addr);
}
return 1;
}
static int testLuaArch_component_doc(lua_State *L) {
nn_computer *c = testLuaArch_getComputer(L);
const char *addr = luaL_checkstring(L, 1);
const char *method = luaL_checkstring(L, 2);
nn_component *component = nn_findComponent(c, (char *)addr);
if(component == NULL) {
lua_pushnil(L);
lua_pushstring(L, "no such component");
return 2;
}
const char *doc = nn_methodDoc(nn_getComponentTable(component), method);
if(doc == NULL) {
lua_pushnil(L);
} else {
lua_pushstring(L, doc);
}
return 1;
}
static int testLuaArch_component_fields(lua_State *L) {
lua_createtable(L, 0, 0);
return 1;
}
static int testLuaArch_component_methods(lua_State *L) {
nn_computer *c = testLuaArch_getComputer(L);
const char *addr = luaL_checkstring(L, 1);
nn_component *component = nn_findComponent(c, (char *)addr);
if(component == NULL) {
lua_pushnil(L);
lua_pushstring(L, "no such component");
return 2;
}
nn_componentTable *table = nn_getComponentTable(component);
lua_createtable(L, 0, 0);
int methods = lua_gettop(L);
size_t i = 0;
while(true) {
bool direct = false;
const char *name = nn_getTableMethod(table, i, &direct);
if(name == NULL) break;
i++;
if(!nn_isMethodEnabled(component, name)) continue;
lua_pushboolean(L, direct);
lua_setfield(L, methods, name);
}
return 1;
}
static int testLuaArch_component_slot(lua_State *L) {
nn_computer *c = testLuaArch_getComputer(L);
const char *addr = luaL_checkstring(L, 1);
nn_component *component = nn_findComponent(c, (char *)addr);
if(component == NULL) {
lua_pushnil(L);
lua_pushstring(L, "no such component");
return 2;
}
lua_pushinteger(L, nn_getComponentSlot(component));
return 1;
}
static int testLuaArch_component_type(lua_State *L) {
nn_computer *c = testLuaArch_getComputer(L);
const char *addr = luaL_checkstring(L, 1);
nn_component *component = nn_findComponent(c, (char *)addr);
if(component == NULL) {
lua_pushnil(L);
lua_pushstring(L, "no such component");
return 2;
}
lua_pushstring(L, nn_getComponentType(nn_getComponentTable(component)));
return 1;
}
static int testLuaArch_component_invoke(lua_State *L) {
nn_computer *c = testLuaArch_getComputer(L);
const char *addr = luaL_checkstring(L, 1);
const char *method = luaL_checkstring(L, 2);
int argc = lua_gettop(L) - 2;
nn_component *component = nn_findComponent(c, (char *)addr);
if(component == NULL) {
lua_pushnil(L);
lua_pushstring(L, "no such component");
return 2;
}
nn_resetCall(c);
for(size_t i = 0; i < argc; i++) {
nn_addArgument(c, testLuaArch_getValue(L, 3 + i));
}
if(!nn_invokeComponentMethod(component, method)) {
nn_resetCall(c);
lua_pushnil(L);
lua_pushstring(L, "no such method");
return 2;
}
if(nn_getError(c) != NULL) {
nn_resetCall(c);
luaL_error(L, "%s", nn_getError(c));
}
size_t retc = nn_getReturnCount(c);
for(size_t i = 0; i < retc; i++) {
testLuaArch_pushValue(L, nn_getReturn(c, i));
}
nn_resetCall(c);
return retc;
}
int testLuaArch_unicode_sub(lua_State *L) {
const char *s = luaL_checkstring(L, 1);
nn_Alloc *alloc = testLuaArch_getAlloc(L);
int start = luaL_checkinteger(L, 2);
int len = nn_unicode_lenPermissive(s);
if(len < 0) {
luaL_error(L, "length overflow");
}
int stop = len;
if(lua_isinteger(L, 3)) {
stop = luaL_checkinteger(L, 3);
}
// OpenOS does this...
if(len == 0) {
lua_pushstring(L, "");
return 1;
}
if(start == 0) start = 1;
if(stop == 0) {
lua_pushstring(L, "");
return 1;
}
if(start < 0) start = len + start + 1;
if(stop < 0) stop = len + stop + 1;
if(stop >= len) {
stop = len;
}
if(start > stop) {
lua_pushstring(L, "");
return 1;
}
nn_size_t startByte = nn_unicode_indexPermissive(s, start - 1);
nn_size_t termByte = nn_unicode_indexPermissive(s, stop);
const char *res = testLuaArch_pushlstring(L, s + startByte, termByte - startByte);
if (!res) {
luaL_error(L, "out of memory");
}
return 1;
}
int testLuaArch_unicode_char(lua_State *L) {
int argc = lua_gettop(L);
nn_Alloc *alloc = testLuaArch_getAlloc(L);
unsigned int *codepoints = nn_alloc(alloc, sizeof(unsigned int) * argc);
if(codepoints == NULL) {
luaL_error(L, "out of memory");
return 0; // tell lsp to shut the fuck up
}
for(int i = 0; i < argc; i++) {
int idx = i + 1;
if(!lua_isinteger(L, idx)) {
nn_dealloc(alloc, codepoints, sizeof(unsigned int) * argc);
luaL_argerror(L, idx, "integer expected");
return 0;
}
codepoints[i] = lua_tointeger(L, idx);
}
char *s = nn_unicode_char(alloc, codepoints, argc);
const char *res = testLuaArch_pushstring(L, s);
nn_deallocStr(alloc, s);
nn_dealloc(alloc, codepoints, sizeof(unsigned int) * argc);
if (!res) {
luaL_error(L, "out of memory");
}
return 1;
}
int testLuaArch_unicode_len(lua_State *L) {
const char *s = luaL_checkstring(L, 1);
lua_pushinteger(L, nn_unicode_lenPermissive(s));
return 1;
}
int testLuaArch_unicode_wlen(lua_State *L) {
const char *s = luaL_checkstring(L, 1);
lua_pushinteger(L, nn_unicode_lenPermissive(s));
return 1;
}
void testLuaArch_loadEnv(lua_State *L) {
lua_createtable(L, 0, 10);
int computer = lua_gettop(L);
lua_pushcfunction(L, testLuaArch_computer_clearError);
lua_setfield(L, computer, "clearError");
lua_pushcfunction(L, testLuaArch_computer_usedMemory);
lua_setfield(L, computer, "usedMemory");
lua_pushcfunction(L, testLuaArch_computer_freeMemory);
lua_setfield(L, computer, "freeMemory");
lua_pushcfunction(L, testLuaArch_computer_totalMemory);
lua_setfield(L, computer, "totalMemory");
lua_pushcfunction(L, testLuaArch_computer_address);
lua_setfield(L, computer, "address");
lua_pushcfunction(L, testLuaArch_computer_tmpAddress);
lua_setfield(L, computer, "tmpAddress");
lua_pushcfunction(L, testLuaArch_computer_uptime);
lua_setfield(L, computer, "uptime");
lua_pushcfunction(L, testLuaArch_computer_beep);
lua_setfield(L, computer, "beep");
lua_pushcfunction(L, testLuaArch_computer_energy);
lua_setfield(L, computer, "energy");
lua_pushcfunction(L, testLuaArch_computer_maxEnergy);
lua_setfield(L, computer, "maxEnergy");
lua_pushcfunction(L, testLuaArch_computer_getArchitecture);
lua_setfield(L, computer, "getArchitecture");
lua_pushcfunction(L, testLuaArch_computer_getArchitectures);
lua_setfield(L, computer, "getArchitectures");
lua_pushcfunction(L, testLuaArch_computer_setArchitecture);
lua_setfield(L, computer, "setArchitecture");
lua_pushcfunction(L, testLuaArch_computer_isOverworked);
lua_setfield(L, computer, "isOverworked");
lua_pushcfunction(L, testLuaArch_computer_isOverheating);
lua_setfield(L, computer, "isOverheating");
lua_pushcfunction(L, testLuaArch_computer_getTemperature);
lua_setfield(L, computer, "getTemperature");
lua_pushcfunction(L, testLuaArch_computer_addHeat);
lua_setfield(L, computer, "addHeat");
lua_pushcfunction(L, testLuaArch_computer_pushSignal);
lua_setfield(L, computer, "pushSignal");
lua_pushcfunction(L, testLuaArch_computer_popSignal);
lua_setfield(L, computer, "popSignal");
lua_pushcfunction(L, testLuaArch_computer_users);
lua_setfield(L, computer, "users");
lua_pushcfunction(L, testLuaArch_computer_getState);
lua_setfield(L, computer, "getState");
lua_pushcfunction(L, testLuaArch_computer_setState);
lua_setfield(L, computer, "setState");
lua_pushcfunction(L, testLuaArch_computer_getDeviceInfo);
lua_setfield(L, computer, "getDeviceInfo");
lua_setglobal(L, "computer");
lua_createtable(L, 0, 10);
int component = lua_gettop(L);
lua_pushcfunction(L, testLuaArch_component_list);
lua_setfield(L, component, "list");
lua_pushcfunction(L, testLuaArch_component_doc);
lua_setfield(L, component, "doc");
lua_pushcfunction(L, testLuaArch_component_fields);
lua_setfield(L, component, "fields");
lua_pushcfunction(L, testLuaArch_component_methods);
lua_setfield(L, component, "methods");
lua_pushcfunction(L, testLuaArch_component_invoke);
lua_setfield(L, component, "invoke");
lua_pushcfunction(L, testLuaArch_component_slot);
lua_setfield(L, component, "slot");
lua_pushcfunction(L, testLuaArch_component_type);
lua_setfield(L, component, "type");
lua_setglobal(L, "component");
lua_createtable(L, 0, 7);
int states = lua_gettop(L);
lua_pushinteger(L, NN_STATE_SETUP);
lua_setfield(L, states, "setup");
lua_pushinteger(L, NN_STATE_RUNNING);
lua_setfield(L, states, "running");
lua_pushinteger(L, NN_STATE_BUSY);
lua_setfield(L, states, "busy");
lua_pushinteger(L, NN_STATE_BLACKOUT);
lua_setfield(L, states, "blackout");
lua_pushinteger(L, NN_STATE_CLOSING);
lua_setfield(L, states, "closing");
lua_pushinteger(L, NN_STATE_REPEAT);
lua_setfield(L, states, "REPEAT");
lua_pushinteger(L, NN_STATE_SWITCH);
lua_setfield(L, states, "switch");
lua_setglobal(L, "states");
lua_createtable(L, 0, 20);
int unicode = lua_gettop(L);
lua_pushcfunction(L, testLuaArch_unicode_sub);
lua_setfield(L, unicode, "sub");
lua_pushcfunction(L, testLuaArch_unicode_len);
lua_setfield(L, unicode, "len");
lua_pushcfunction(L, testLuaArch_unicode_wlen);
lua_setfield(L, unicode, "wlen");
lua_pushcfunction(L, testLuaArch_unicode_char);
lua_setfield(L, unicode, "char");
lua_setglobal(L, "unicode");
}
testLuaArch *testLuaArch_setup(nn_computer *computer, void *_) {
nn_Alloc *alloc = nn_getAllocator(nn_getUniverse(computer));
testLuaArch *s = nn_alloc(alloc, sizeof(testLuaArch));
if(s == NULL) return NULL;
s->memoryUsed = 0;
s->computer = computer;
lua_State *L = lua_newstate((void *)testLuaArch_alloc, s);
assert(L != NULL);
luaL_openlibs(L);
lua_pushlightuserdata(L, s);
lua_setfield(L, LUA_REGISTRYINDEX, "archPtr");
s->L = L;
testLuaArch_loadEnv(L);
if(luaL_loadbufferx(L, testLuaSandbox, strlen(testLuaSandbox), "=machine.lua", "t") != LUA_OK) {
lua_close(L);
nn_dealloc(alloc, s, sizeof(testLuaArch));
return NULL;
}
return s;
}
void testLuaArch_teardown(nn_computer *computer, testLuaArch *arch, void *_) {
nn_Alloc *alloc = nn_getAllocator(nn_getUniverse(computer));
lua_close(arch->L);
nn_dealloc(alloc, arch, sizeof(testLuaArch));
}
void testLuaArch_tick(nn_computer *computer, testLuaArch *arch, void *_) {
int ret = 0;
#if LUA_VERSION_NUM == 504
int res = lua_resume(arch->L, NULL, 0, &ret);
#endif
#if LUA_VERSION_NUM == 503
int res = lua_resume(arch->L, NULL, 0);
#endif
#if LUA_VERSION_NUM == 502
int res = lua_resume(arch->L, NULL, 0);
#endif
if(res == LUA_OK) {
// machine halted, this is no good
lua_pop(arch->L, ret);
nn_setCError(computer, "machine halted");
} else if(res == LUA_YIELD) {
lua_pop(arch->L, ret);
} else {
const char *s = lua_tostring(arch->L, -1);
nn_setError(computer, s);
lua_pop(arch->L, ret);
}
}
size_t testLuaArch_getMemoryUsage(nn_computer *computer, testLuaArch *arch, void *_) {
return arch->memoryUsed;
}
char *testLuaArch_serialize(nn_computer *computer, nn_Alloc *alloc, testLuaArch *arch, void *_, size_t *len) {
*len = 0;
return NULL;
}
void testLuaArch_deserialize(nn_computer *computer, const char *data, size_t len, testLuaArch *arch, void *_) {}
nn_architecture testLuaArchTable = {
.archName = "Lua Test",
.userdata = NULL,
.setup = (void *)testLuaArch_setup,
.teardown = (void *)testLuaArch_teardown,
.tick = (void *)testLuaArch_tick,
.getMemoryUsage = (void*)testLuaArch_getMemoryUsage,
};
nn_architecture *testLuaArch_getArchitecture(const char *sandboxPath) {
if(testLuaSandbox == NULL) {
FILE *f = fopen(sandboxPath, "r");
if(f == NULL) return NULL;
fseek(f, 0, SEEK_END);
size_t l = ftell(f);
testLuaSandbox = malloc(l+1);
if(testLuaSandbox == NULL) {
fclose(f);
return NULL;
}
fseek(f, 0, SEEK_SET);
fread(testLuaSandbox, sizeof(char), l, f);
testLuaSandbox[l] = '\0';
fclose(f);
}
return &testLuaArchTable;
}

View File

@@ -1,7 +0,0 @@
#ifndef TEST_LUA_ARCH
#define TEST_LUA_ARCH
#include "neonucleus.h"
nn_architecture *testLuaArch_getArchitecture(const char *sandboxPath);
#endif

View File

@@ -1,934 +0,0 @@
// Not created by NeoNucleus project authors.
// It was not modified aside from the writing of this comment.
/* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*-
Copyright (c) 2012 Marcus Geelnard
Copyright (c) 2013-2016 Evan Nemerson
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#include "tinycthread.h"
#include <stdlib.h>
/* Platform specific includes */
#if defined(_TTHREAD_POSIX_)
#include <signal.h>
#include <sched.h>
#include <unistd.h>
#include <sys/time.h>
#include <errno.h>
#elif defined(_TTHREAD_WIN32_)
#include <process.h>
#include <sys/timeb.h>
#endif
/* Standard, good-to-have defines */
#ifndef NULL
#define NULL (void*)0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifdef __cplusplus
extern "C" {
#endif
int mtx_init(mtx_t *mtx, int type)
{
#if defined(_TTHREAD_WIN32_)
mtx->mAlreadyLocked = FALSE;
mtx->mRecursive = type & mtx_recursive;
mtx->mTimed = type & mtx_timed;
if (!mtx->mTimed)
{
InitializeCriticalSection(&(mtx->mHandle.cs));
}
else
{
mtx->mHandle.mut = CreateMutex(NULL, FALSE, NULL);
if (mtx->mHandle.mut == NULL)
{
return thrd_error;
}
}
return thrd_success;
#else
int ret;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
if (type & mtx_recursive)
{
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
}
ret = pthread_mutex_init(mtx, &attr);
pthread_mutexattr_destroy(&attr);
return ret == 0 ? thrd_success : thrd_error;
#endif
}
void mtx_destroy(mtx_t *mtx)
{
#if defined(_TTHREAD_WIN32_)
if (!mtx->mTimed)
{
DeleteCriticalSection(&(mtx->mHandle.cs));
}
else
{
CloseHandle(mtx->mHandle.mut);
}
#else
pthread_mutex_destroy(mtx);
#endif
}
int mtx_lock(mtx_t *mtx)
{
#if defined(_TTHREAD_WIN32_)
if (!mtx->mTimed)
{
EnterCriticalSection(&(mtx->mHandle.cs));
}
else
{
switch (WaitForSingleObject(mtx->mHandle.mut, INFINITE))
{
case WAIT_OBJECT_0:
break;
case WAIT_ABANDONED:
default:
return thrd_error;
}
}
if (!mtx->mRecursive)
{
while(mtx->mAlreadyLocked) Sleep(1); /* Simulate deadlock... */
mtx->mAlreadyLocked = TRUE;
}
return thrd_success;
#else
return pthread_mutex_lock(mtx) == 0 ? thrd_success : thrd_error;
#endif
}
int mtx_timedlock(mtx_t *mtx, const struct timespec *ts)
{
#if defined(_TTHREAD_WIN32_)
struct timespec current_ts;
DWORD timeoutMs;
if (!mtx->mTimed)
{
return thrd_error;
}
timespec_get(&current_ts, TIME_UTC);
if ((current_ts.tv_sec > ts->tv_sec) || ((current_ts.tv_sec == ts->tv_sec) && (current_ts.tv_nsec >= ts->tv_nsec)))
{
timeoutMs = 0;
}
else
{
timeoutMs = (DWORD)(ts->tv_sec - current_ts.tv_sec) * 1000;
timeoutMs += (ts->tv_nsec - current_ts.tv_nsec) / 1000000;
timeoutMs += 1;
}
/* TODO: the timeout for WaitForSingleObject doesn't include time
while the computer is asleep. */
switch (WaitForSingleObject(mtx->mHandle.mut, timeoutMs))
{
case WAIT_OBJECT_0:
break;
case WAIT_TIMEOUT:
return thrd_timedout;
case WAIT_ABANDONED:
default:
return thrd_error;
}
if (!mtx->mRecursive)
{
while(mtx->mAlreadyLocked) Sleep(1); /* Simulate deadlock... */
mtx->mAlreadyLocked = TRUE;
}
return thrd_success;
#elif defined(_POSIX_TIMEOUTS) && (_POSIX_TIMEOUTS >= 200112L) && defined(_POSIX_THREADS) && (_POSIX_THREADS >= 200112L)
switch (pthread_mutex_timedlock(mtx, ts)) {
case 0:
return thrd_success;
case ETIMEDOUT:
return thrd_timedout;
default:
return thrd_error;
}
#else
int rc;
struct timespec cur, dur;
/* Try to acquire the lock and, if we fail, sleep for 5ms. */
while ((rc = pthread_mutex_trylock (mtx)) == EBUSY) {
timespec_get(&cur, TIME_UTC);
if ((cur.tv_sec > ts->tv_sec) || ((cur.tv_sec == ts->tv_sec) && (cur.tv_nsec >= ts->tv_nsec)))
{
break;
}
dur.tv_sec = ts->tv_sec - cur.tv_sec;
dur.tv_nsec = ts->tv_nsec - cur.tv_nsec;
if (dur.tv_nsec < 0)
{
dur.tv_sec--;
dur.tv_nsec += 1000000000;
}
if ((dur.tv_sec != 0) || (dur.tv_nsec > 5000000))
{
dur.tv_sec = 0;
dur.tv_nsec = 5000000;
}
nanosleep(&dur, NULL);
}
switch (rc) {
case 0:
return thrd_success;
case ETIMEDOUT:
case EBUSY:
return thrd_timedout;
default:
return thrd_error;
}
#endif
}
int mtx_trylock(mtx_t *mtx)
{
#if defined(_TTHREAD_WIN32_)
int ret;
if (!mtx->mTimed)
{
ret = TryEnterCriticalSection(&(mtx->mHandle.cs)) ? thrd_success : thrd_busy;
}
else
{
ret = (WaitForSingleObject(mtx->mHandle.mut, 0) == WAIT_OBJECT_0) ? thrd_success : thrd_busy;
}
if ((!mtx->mRecursive) && (ret == thrd_success))
{
if (mtx->mAlreadyLocked)
{
LeaveCriticalSection(&(mtx->mHandle.cs));
ret = thrd_busy;
}
else
{
mtx->mAlreadyLocked = TRUE;
}
}
return ret;
#else
return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy;
#endif
}
int mtx_unlock(mtx_t *mtx)
{
#if defined(_TTHREAD_WIN32_)
mtx->mAlreadyLocked = FALSE;
if (!mtx->mTimed)
{
LeaveCriticalSection(&(mtx->mHandle.cs));
}
else
{
if (!ReleaseMutex(mtx->mHandle.mut))
{
return thrd_error;
}
}
return thrd_success;
#else
return pthread_mutex_unlock(mtx) == 0 ? thrd_success : thrd_error;;
#endif
}
#if defined(_TTHREAD_WIN32_)
#define _CONDITION_EVENT_ONE 0
#define _CONDITION_EVENT_ALL 1
#endif
int cnd_init(cnd_t *cond)
{
#if defined(_TTHREAD_WIN32_)
cond->mWaitersCount = 0;
/* Init critical section */
InitializeCriticalSection(&cond->mWaitersCountLock);
/* Init events */
cond->mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL);
if (cond->mEvents[_CONDITION_EVENT_ONE] == NULL)
{
cond->mEvents[_CONDITION_EVENT_ALL] = NULL;
return thrd_error;
}
cond->mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL);
if (cond->mEvents[_CONDITION_EVENT_ALL] == NULL)
{
CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]);
cond->mEvents[_CONDITION_EVENT_ONE] = NULL;
return thrd_error;
}
return thrd_success;
#else
return pthread_cond_init(cond, NULL) == 0 ? thrd_success : thrd_error;
#endif
}
void cnd_destroy(cnd_t *cond)
{
#if defined(_TTHREAD_WIN32_)
if (cond->mEvents[_CONDITION_EVENT_ONE] != NULL)
{
CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]);
}
if (cond->mEvents[_CONDITION_EVENT_ALL] != NULL)
{
CloseHandle(cond->mEvents[_CONDITION_EVENT_ALL]);
}
DeleteCriticalSection(&cond->mWaitersCountLock);
#else
pthread_cond_destroy(cond);
#endif
}
int cnd_signal(cnd_t *cond)
{
#if defined(_TTHREAD_WIN32_)
int haveWaiters;
/* Are there any waiters? */
EnterCriticalSection(&cond->mWaitersCountLock);
haveWaiters = (cond->mWaitersCount > 0);
LeaveCriticalSection(&cond->mWaitersCountLock);
/* If we have any waiting threads, send them a signal */
if(haveWaiters)
{
if (SetEvent(cond->mEvents[_CONDITION_EVENT_ONE]) == 0)
{
return thrd_error;
}
}
return thrd_success;
#else
return pthread_cond_signal(cond) == 0 ? thrd_success : thrd_error;
#endif
}
int cnd_broadcast(cnd_t *cond)
{
#if defined(_TTHREAD_WIN32_)
int haveWaiters;
/* Are there any waiters? */
EnterCriticalSection(&cond->mWaitersCountLock);
haveWaiters = (cond->mWaitersCount > 0);
LeaveCriticalSection(&cond->mWaitersCountLock);
/* If we have any waiting threads, send them a signal */
if(haveWaiters)
{
if (SetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0)
{
return thrd_error;
}
}
return thrd_success;
#else
return pthread_cond_broadcast(cond) == 0 ? thrd_success : thrd_error;
#endif
}
#if defined(_TTHREAD_WIN32_)
static int _cnd_timedwait_win32(cnd_t *cond, mtx_t *mtx, DWORD timeout)
{
DWORD result;
int lastWaiter;
/* Increment number of waiters */
EnterCriticalSection(&cond->mWaitersCountLock);
++ cond->mWaitersCount;
LeaveCriticalSection(&cond->mWaitersCountLock);
/* Release the mutex while waiting for the condition (will decrease
the number of waiters when done)... */
mtx_unlock(mtx);
/* Wait for either event to become signaled due to cnd_signal() or
cnd_broadcast() being called */
result = WaitForMultipleObjects(2, cond->mEvents, FALSE, timeout);
if (result == WAIT_TIMEOUT)
{
/* The mutex is locked again before the function returns, even if an error occurred */
mtx_lock(mtx);
return thrd_timedout;
}
else if (result == WAIT_FAILED)
{
/* The mutex is locked again before the function returns, even if an error occurred */
mtx_lock(mtx);
return thrd_error;
}
/* Check if we are the last waiter */
EnterCriticalSection(&cond->mWaitersCountLock);
-- cond->mWaitersCount;
lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) &&
(cond->mWaitersCount == 0);
LeaveCriticalSection(&cond->mWaitersCountLock);
/* If we are the last waiter to be notified to stop waiting, reset the event */
if (lastWaiter)
{
if (ResetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0)
{
/* The mutex is locked again before the function returns, even if an error occurred */
mtx_lock(mtx);
return thrd_error;
}
}
/* Re-acquire the mutex */
mtx_lock(mtx);
return thrd_success;
}
#endif
int cnd_wait(cnd_t *cond, mtx_t *mtx)
{
#if defined(_TTHREAD_WIN32_)
return _cnd_timedwait_win32(cond, mtx, INFINITE);
#else
return pthread_cond_wait(cond, mtx) == 0 ? thrd_success : thrd_error;
#endif
}
int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts)
{
#if defined(_TTHREAD_WIN32_)
struct timespec now;
if (timespec_get(&now, TIME_UTC) == TIME_UTC)
{
unsigned long long nowInMilliseconds = now.tv_sec * 1000 + now.tv_nsec / 1000000;
unsigned long long tsInMilliseconds = ts->tv_sec * 1000 + ts->tv_nsec / 1000000;
DWORD delta = (tsInMilliseconds > nowInMilliseconds) ?
(DWORD)(tsInMilliseconds - nowInMilliseconds) : 0;
return _cnd_timedwait_win32(cond, mtx, delta);
}
else
return thrd_error;
#else
int ret;
ret = pthread_cond_timedwait(cond, mtx, ts);
if (ret == ETIMEDOUT)
{
return thrd_timedout;
}
return ret == 0 ? thrd_success : thrd_error;
#endif
}
#if defined(_TTHREAD_WIN32_)
struct TinyCThreadTSSData {
void* value;
tss_t key;
struct TinyCThreadTSSData* next;
};
static tss_dtor_t _tinycthread_tss_dtors[1088] = { NULL, };
static _Thread_local struct TinyCThreadTSSData* _tinycthread_tss_head = NULL;
static _Thread_local struct TinyCThreadTSSData* _tinycthread_tss_tail = NULL;
static void _tinycthread_tss_cleanup (void);
static void _tinycthread_tss_cleanup (void) {
struct TinyCThreadTSSData* data;
int iteration;
unsigned int again = 1;
void* value;
for (iteration = 0 ; iteration < TSS_DTOR_ITERATIONS && again > 0 ; iteration++)
{
again = 0;
for (data = _tinycthread_tss_head ; data != NULL ; data = data->next)
{
if (data->value != NULL)
{
value = data->value;
data->value = NULL;
if (_tinycthread_tss_dtors[data->key] != NULL)
{
again = 1;
_tinycthread_tss_dtors[data->key](value);
}
}
}
}
while (_tinycthread_tss_head != NULL) {
data = _tinycthread_tss_head->next;
free (_tinycthread_tss_head);
_tinycthread_tss_head = data;
}
_tinycthread_tss_head = NULL;
_tinycthread_tss_tail = NULL;
}
static void NTAPI _tinycthread_tss_callback(PVOID h, DWORD dwReason, PVOID pv)
{
(void)h;
(void)pv;
if (_tinycthread_tss_head != NULL && (dwReason == DLL_THREAD_DETACH || dwReason == DLL_PROCESS_DETACH))
{
_tinycthread_tss_cleanup();
}
}
#if defined(_MSC_VER)
#ifdef _M_X64
#pragma const_seg(".CRT$XLB")
#else
#pragma data_seg(".CRT$XLB")
#endif
PIMAGE_TLS_CALLBACK p_thread_callback = _tinycthread_tss_callback;
#ifdef _M_X64
#pragma data_seg()
#else
#pragma const_seg()
#endif
#else
PIMAGE_TLS_CALLBACK p_thread_callback __attribute__((section(".CRT$XLB"))) = _tinycthread_tss_callback;
#endif
#endif /* defined(_TTHREAD_WIN32_) */
/** Information to pass to the new thread (what to run). */
typedef struct {
thrd_start_t mFunction; /**< Pointer to the function to be executed. */
void * mArg; /**< Function argument for the thread function. */
} _thread_start_info;
/* Thread wrapper function. */
#if defined(_TTHREAD_WIN32_)
static DWORD WINAPI _thrd_wrapper_function(LPVOID aArg)
#elif defined(_TTHREAD_POSIX_)
static void * _thrd_wrapper_function(void * aArg)
#endif
{
thrd_start_t fun;
void *arg;
int res;
/* Get thread startup information */
_thread_start_info *ti = (_thread_start_info *) aArg;
fun = ti->mFunction;
arg = ti->mArg;
/* The thread is responsible for freeing the startup information */
free((void *)ti);
/* Call the actual client thread function */
res = fun(arg);
#if defined(_TTHREAD_WIN32_)
if (_tinycthread_tss_head != NULL)
{
_tinycthread_tss_cleanup();
}
return (DWORD)res;
#else
return (void*)(intptr_t)res;
#endif
}
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
{
/* Fill out the thread startup information (passed to the thread wrapper,
which will eventually free it) */
_thread_start_info* ti = (_thread_start_info*)malloc(sizeof(_thread_start_info));
if (ti == NULL)
{
return thrd_nomem;
}
ti->mFunction = func;
ti->mArg = arg;
/* Create the thread */
#if defined(_TTHREAD_WIN32_)
*thr = CreateThread(NULL, 0, _thrd_wrapper_function, (LPVOID) ti, 0, NULL);
#elif defined(_TTHREAD_POSIX_)
if(pthread_create(thr, NULL, _thrd_wrapper_function, (void *)ti) != 0)
{
*thr = 0;
}
#endif
/* Did we fail to create the thread? */
if(!*thr)
{
free(ti);
return thrd_error;
}
return thrd_success;
}
thrd_t thrd_current(void)
{
#if defined(_TTHREAD_WIN32_)
return GetCurrentThread();
#else
return pthread_self();
#endif
}
int thrd_detach(thrd_t thr)
{
#if defined(_TTHREAD_WIN32_)
/* https://stackoverflow.com/questions/12744324/how-to-detach-a-thread-on-windows-c#answer-12746081 */
return CloseHandle(thr) != 0 ? thrd_success : thrd_error;
#else
return pthread_detach(thr) == 0 ? thrd_success : thrd_error;
#endif
}
int thrd_equal(thrd_t thr0, thrd_t thr1)
{
#if defined(_TTHREAD_WIN32_)
return GetThreadId(thr0) == GetThreadId(thr1);
#else
return pthread_equal(thr0, thr1);
#endif
}
void thrd_exit(int res)
{
#if defined(_TTHREAD_WIN32_)
if (_tinycthread_tss_head != NULL)
{
_tinycthread_tss_cleanup();
}
ExitThread((DWORD)res);
#else
pthread_exit((void*)(intptr_t)res);
#endif
}
int thrd_join(thrd_t thr, int *res)
{
#if defined(_TTHREAD_WIN32_)
DWORD dwRes;
if (WaitForSingleObject(thr, INFINITE) == WAIT_FAILED)
{
return thrd_error;
}
if (res != NULL)
{
if (GetExitCodeThread(thr, &dwRes) != 0)
{
*res = (int) dwRes;
}
else
{
return thrd_error;
}
}
CloseHandle(thr);
#elif defined(_TTHREAD_POSIX_)
void *pres;
if (pthread_join(thr, &pres) != 0)
{
return thrd_error;
}
if (res != NULL)
{
*res = (int)(intptr_t)pres;
}
#endif
return thrd_success;
}
int thrd_sleep(const struct timespec *duration, struct timespec *remaining)
{
#if !defined(_TTHREAD_WIN32_)
int res = nanosleep(duration, remaining);
if (res == 0) {
return 0;
} else if (errno == EINTR) {
return -1;
} else {
return -2;
}
#else
struct timespec start;
DWORD t;
timespec_get(&start, TIME_UTC);
t = SleepEx((DWORD)(duration->tv_sec * 1000 +
duration->tv_nsec / 1000000 +
(((duration->tv_nsec % 1000000) == 0) ? 0 : 1)),
TRUE);
if (t == 0) {
return 0;
} else {
if (remaining != NULL) {
timespec_get(remaining, TIME_UTC);
remaining->tv_sec -= start.tv_sec;
remaining->tv_nsec -= start.tv_nsec;
if (remaining->tv_nsec < 0)
{
remaining->tv_nsec += 1000000000;
remaining->tv_sec -= 1;
}
}
return (t == WAIT_IO_COMPLETION) ? -1 : -2;
}
#endif
}
void thrd_yield(void)
{
#if defined(_TTHREAD_WIN32_)
Sleep(0);
#else
sched_yield();
#endif
}
int tss_create(tss_t *key, tss_dtor_t dtor)
{
#if defined(_TTHREAD_WIN32_)
*key = TlsAlloc();
if (*key == TLS_OUT_OF_INDEXES)
{
return thrd_error;
}
_tinycthread_tss_dtors[*key] = dtor;
#else
if (pthread_key_create(key, dtor) != 0)
{
return thrd_error;
}
#endif
return thrd_success;
}
void tss_delete(tss_t key)
{
#if defined(_TTHREAD_WIN32_)
struct TinyCThreadTSSData* data = (struct TinyCThreadTSSData*) TlsGetValue (key);
struct TinyCThreadTSSData* prev = NULL;
if (data != NULL)
{
if (data == _tinycthread_tss_head)
{
_tinycthread_tss_head = data->next;
}
else
{
prev = _tinycthread_tss_head;
if (prev != NULL)
{
while (prev->next != data)
{
prev = prev->next;
}
}
}
if (data == _tinycthread_tss_tail)
{
_tinycthread_tss_tail = prev;
}
free (data);
}
_tinycthread_tss_dtors[key] = NULL;
TlsFree(key);
#else
pthread_key_delete(key);
#endif
}
void *tss_get(tss_t key)
{
#if defined(_TTHREAD_WIN32_)
struct TinyCThreadTSSData* data = (struct TinyCThreadTSSData*)TlsGetValue(key);
if (data == NULL)
{
return NULL;
}
return data->value;
#else
return pthread_getspecific(key);
#endif
}
int tss_set(tss_t key, void *val)
{
#if defined(_TTHREAD_WIN32_)
struct TinyCThreadTSSData* data = (struct TinyCThreadTSSData*)TlsGetValue(key);
if (data == NULL)
{
data = (struct TinyCThreadTSSData*)malloc(sizeof(struct TinyCThreadTSSData));
if (data == NULL)
{
return thrd_error;
}
data->value = NULL;
data->key = key;
data->next = NULL;
if (_tinycthread_tss_tail != NULL)
{
_tinycthread_tss_tail->next = data;
}
else
{
_tinycthread_tss_tail = data;
}
if (_tinycthread_tss_head == NULL)
{
_tinycthread_tss_head = data;
}
if (!TlsSetValue(key, data))
{
free (data);
return thrd_error;
}
}
data->value = val;
#else
if (pthread_setspecific(key, val) != 0)
{
return thrd_error;
}
#endif
return thrd_success;
}
#if defined(_TTHREAD_EMULATE_TIMESPEC_GET_)
int _tthread_timespec_get(struct timespec *ts, int base)
{
#if defined(_TTHREAD_WIN32_)
struct _timeb tb;
#elif !defined(CLOCK_REALTIME)
struct timeval tv;
#endif
if (base != TIME_UTC)
{
return 0;
}
#if defined(_TTHREAD_WIN32_)
_ftime_s(&tb);
ts->tv_sec = (time_t)tb.time;
ts->tv_nsec = 1000000L * (long)tb.millitm;
#elif defined(CLOCK_REALTIME)
base = (clock_gettime(CLOCK_REALTIME, ts) == 0) ? base : 0;
#else
gettimeofday(&tv, NULL);
ts->tv_sec = (time_t)tv.tv_sec;
ts->tv_nsec = 1000L * (long)tv.tv_usec;
#endif
return base;
}
#endif /* _TTHREAD_EMULATE_TIMESPEC_GET_ */
#if defined(_TTHREAD_WIN32_)
void call_once(once_flag *flag, void (*func)(void))
{
/* The idea here is that we use a spin lock (via the
InterlockedCompareExchange function) to restrict access to the
critical section until we have initialized it, then we use the
critical section to block until the callback has completed
execution. */
while (flag->status < 3)
{
switch (flag->status)
{
case 0:
if (InterlockedCompareExchange (&(flag->status), 1, 0) == 0) {
InitializeCriticalSection(&(flag->lock));
EnterCriticalSection(&(flag->lock));
flag->status = 2;
func();
flag->status = 3;
LeaveCriticalSection(&(flag->lock));
return;
}
break;
case 1:
break;
case 2:
EnterCriticalSection(&(flag->lock));
LeaveCriticalSection(&(flag->lock));
break;
}
}
}
#endif /* defined(_TTHREAD_WIN32_) */
#ifdef __cplusplus
}
#endif

View File

@@ -1,479 +0,0 @@
/* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*-
Copyright (c) 2012 Marcus Geelnard
Copyright (c) 2013-2016 Evan Nemerson
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#ifndef _TINYCTHREAD_H_
#define _TINYCTHREAD_H_
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file
* @mainpage TinyCThread API Reference
*
* @section intro_sec Introduction
* TinyCThread is a minimal, portable implementation of basic threading
* classes for C.
*
* They closely mimic the functionality and naming of the C11 standard, and
* should be easily replaceable with the corresponding standard variants.
*
* @section port_sec Portability
* The Win32 variant uses the native Win32 API for implementing the thread
* classes, while for other systems, the POSIX threads API (pthread) is used.
*
* @section misc_sec Miscellaneous
* The following special keywords are available: #_Thread_local.
*
* For more detailed information, browse the different sections of this
* documentation. A good place to start is:
* tinycthread.h.
*/
/* Which platform are we on? */
#if !defined(_TTHREAD_PLATFORM_DEFINED_)
#if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__)
#define _TTHREAD_WIN32_
#else
#define _TTHREAD_POSIX_
#endif
#define _TTHREAD_PLATFORM_DEFINED_
#endif
/* Activate some POSIX functionality (e.g. clock_gettime and recursive mutexes) */
#if defined(_TTHREAD_POSIX_)
#undef _FEATURES_H
#if !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif
#if !defined(_POSIX_C_SOURCE) || ((_POSIX_C_SOURCE - 0) < 199309L)
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 199309L
#endif
#if !defined(_XOPEN_SOURCE) || ((_XOPEN_SOURCE - 0) < 500)
#undef _XOPEN_SOURCE
#define _XOPEN_SOURCE 500
#endif
#define _XPG6
#endif
/* Generic includes */
#include <time.h>
/* Platform specific includes */
#if defined(_TTHREAD_POSIX_)
#include <pthread.h>
#elif defined(_TTHREAD_WIN32_)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#define __UNDEF_LEAN_AND_MEAN
#endif
#include <windows.h>
#ifdef __UNDEF_LEAN_AND_MEAN
#undef WIN32_LEAN_AND_MEAN
#undef __UNDEF_LEAN_AND_MEAN
#endif
#endif
/* Compiler-specific information */
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
#define TTHREAD_NORETURN _Noreturn
#elif defined(__GNUC__)
#define TTHREAD_NORETURN __attribute__((__noreturn__))
#else
#define TTHREAD_NORETURN
#endif
/* If TIME_UTC is missing, provide it and provide a wrapper for
timespec_get. */
#ifndef TIME_UTC
#define TIME_UTC 1
#define _TTHREAD_EMULATE_TIMESPEC_GET_
#if defined(_TTHREAD_WIN32_)
struct _tthread_timespec {
time_t tv_sec;
long tv_nsec;
};
#define timespec _tthread_timespec
#endif
int _tthread_timespec_get(struct timespec *ts, int base);
#define timespec_get _tthread_timespec_get
#endif
/** TinyCThread version (major number). */
#define TINYCTHREAD_VERSION_MAJOR 1
/** TinyCThread version (minor number). */
#define TINYCTHREAD_VERSION_MINOR 2
/** TinyCThread version (full version). */
#define TINYCTHREAD_VERSION (TINYCTHREAD_VERSION_MAJOR * 100 + TINYCTHREAD_VERSION_MINOR)
/**
* @def _Thread_local
* Thread local storage keyword.
* A variable that is declared with the @c _Thread_local keyword makes the
* value of the variable local to each thread (known as thread-local storage,
* or TLS). Example usage:
* @code
* // This variable is local to each thread.
* _Thread_local int variable;
* @endcode
* @note The @c _Thread_local keyword is a macro that maps to the corresponding
* compiler directive (e.g. @c __declspec(thread)).
* @note This directive is currently not supported on Mac OS X (it will give
* a compiler error), since compile-time TLS is not supported in the Mac OS X
* executable format. Also, some older versions of MinGW (before GCC 4.x) do
* not support this directive, nor does the Tiny C Compiler.
* @hideinitializer
*/
#if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201102L)) && !defined(_Thread_local)
#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__)
#define _Thread_local __thread
#else
#define _Thread_local __declspec(thread)
#endif
#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && (((__GNUC__ << 8) | __GNUC_MINOR__) < ((4 << 8) | 9))
#define _Thread_local __thread
#endif
/* Macros */
#if defined(_TTHREAD_WIN32_)
#define TSS_DTOR_ITERATIONS (4)
#else
#define TSS_DTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS
#endif
/* Function return values */
#define thrd_error 0 /**< The requested operation failed */
#define thrd_success 1 /**< The requested operation succeeded */
#define thrd_timedout 2 /**< The time specified in the call was reached without acquiring the requested resource */
#define thrd_busy 3 /**< The requested operation failed because a tesource requested by a test and return function is already in use */
#define thrd_nomem 4 /**< The requested operation failed because it was unable to allocate memory */
/* Mutex types */
#define mtx_plain 0
#define mtx_timed 1
#define mtx_recursive 2
/* Mutex */
#if defined(_TTHREAD_WIN32_)
typedef struct {
union {
CRITICAL_SECTION cs; /* Critical section handle (used for non-timed mutexes) */
HANDLE mut; /* Mutex handle (used for timed mutex) */
} mHandle; /* Mutex handle */
int mAlreadyLocked; /* TRUE if the mutex is already locked */
int mRecursive; /* TRUE if the mutex is recursive */
int mTimed; /* TRUE if the mutex is timed */
} mtx_t;
#else
typedef pthread_mutex_t mtx_t;
#endif
/** Create a mutex object.
* @param mtx A mutex object.
* @param type Bit-mask that must have one of the following six values:
* @li @c mtx_plain for a simple non-recursive mutex
* @li @c mtx_timed for a non-recursive mutex that supports timeout
* @li @c mtx_plain | @c mtx_recursive (same as @c mtx_plain, but recursive)
* @li @c mtx_timed | @c mtx_recursive (same as @c mtx_timed, but recursive)
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int mtx_init(mtx_t *mtx, int type);
/** Release any resources used by the given mutex.
* @param mtx A mutex object.
*/
void mtx_destroy(mtx_t *mtx);
/** Lock the given mutex.
* Blocks until the given mutex can be locked. If the mutex is non-recursive, and
* the calling thread already has a lock on the mutex, this call will block
* forever.
* @param mtx A mutex object.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int mtx_lock(mtx_t *mtx);
/** Lock the given mutex, or block until a specific point in time.
* Blocks until either the given mutex can be locked, or the specified TIME_UTC
* based time.
* @param mtx A mutex object.
* @param ts A UTC based calendar time
* @return @ref The mtx_timedlock function returns thrd_success on success, or
* thrd_timedout if the time specified was reached without acquiring the
* requested resource, or thrd_error if the request could not be honored.
*/
int mtx_timedlock(mtx_t *mtx, const struct timespec *ts);
/** Try to lock the given mutex.
* The specified mutex shall support either test and return or timeout. If the
* mutex is already locked, the function returns without blocking.
* @param mtx A mutex object.
* @return @ref thrd_success on success, or @ref thrd_busy if the resource
* requested is already in use, or @ref thrd_error if the request could not be
* honored.
*/
int mtx_trylock(mtx_t *mtx);
/** Unlock the given mutex.
* @param mtx A mutex object.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int mtx_unlock(mtx_t *mtx);
/* Condition variable */
#if defined(_TTHREAD_WIN32_)
typedef struct {
HANDLE mEvents[2]; /* Signal and broadcast event HANDLEs. */
unsigned int mWaitersCount; /* Count of the number of waiters. */
CRITICAL_SECTION mWaitersCountLock; /* Serialize access to mWaitersCount. */
} cnd_t;
#else
typedef pthread_cond_t cnd_t;
#endif
/** Create a condition variable object.
* @param cond A condition variable object.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int cnd_init(cnd_t *cond);
/** Release any resources used by the given condition variable.
* @param cond A condition variable object.
*/
void cnd_destroy(cnd_t *cond);
/** Signal a condition variable.
* Unblocks one of the threads that are blocked on the given condition variable
* at the time of the call. If no threads are blocked on the condition variable
* at the time of the call, the function does nothing and return success.
* @param cond A condition variable object.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int cnd_signal(cnd_t *cond);
/** Broadcast a condition variable.
* Unblocks all of the threads that are blocked on the given condition variable
* at the time of the call. If no threads are blocked on the condition variable
* at the time of the call, the function does nothing and return success.
* @param cond A condition variable object.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int cnd_broadcast(cnd_t *cond);
/** Wait for a condition variable to become signaled.
* The function atomically unlocks the given mutex and endeavors to block until
* the given condition variable is signaled by a call to cnd_signal or to
* cnd_broadcast. When the calling thread becomes unblocked it locks the mutex
* before it returns.
* @param cond A condition variable object.
* @param mtx A mutex object.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int cnd_wait(cnd_t *cond, mtx_t *mtx);
/** Wait for a condition variable to become signaled.
* The function atomically unlocks the given mutex and endeavors to block until
* the given condition variable is signaled by a call to cnd_signal or to
* cnd_broadcast, or until after the specified time. When the calling thread
* becomes unblocked it locks the mutex before it returns.
* @param cond A condition variable object.
* @param mtx A mutex object.
* @param xt A point in time at which the request will time out (absolute time).
* @return @ref thrd_success upon success, or @ref thrd_timeout if the time
* specified in the call was reached without acquiring the requested resource, or
* @ref thrd_error if the request could not be honored.
*/
int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts);
/* Thread */
#if defined(_TTHREAD_WIN32_)
typedef HANDLE thrd_t;
#else
typedef pthread_t thrd_t;
#endif
/** Thread start function.
* Any thread that is started with the @ref thrd_create() function must be
* started through a function of this type.
* @param arg The thread argument (the @c arg argument of the corresponding
* @ref thrd_create() call).
* @return The thread return value, which can be obtained by another thread
* by using the @ref thrd_join() function.
*/
typedef int (*thrd_start_t)(void *arg);
/** Create a new thread.
* @param thr Identifier of the newly created thread.
* @param func A function pointer to the function that will be executed in
* the new thread.
* @param arg An argument to the thread function.
* @return @ref thrd_success on success, or @ref thrd_nomem if no memory could
* be allocated for the thread requested, or @ref thrd_error if the request
* could not be honored.
* @note A threads identifier may be reused for a different thread once the
* original thread has exited and either been detached or joined to another
* thread.
*/
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg);
/** Identify the calling thread.
* @return The identifier of the calling thread.
*/
thrd_t thrd_current(void);
/** Dispose of any resources allocated to the thread when that thread exits.
* @return thrd_success, or thrd_error on error
*/
int thrd_detach(thrd_t thr);
/** Compare two thread identifiers.
* The function determines if two thread identifiers refer to the same thread.
* @return Zero if the two thread identifiers refer to different threads.
* Otherwise a nonzero value is returned.
*/
int thrd_equal(thrd_t thr0, thrd_t thr1);
/** Terminate execution of the calling thread.
* @param res Result code of the calling thread.
*/
TTHREAD_NORETURN void thrd_exit(int res);
/** Wait for a thread to terminate.
* The function joins the given thread with the current thread by blocking
* until the other thread has terminated.
* @param thr The thread to join with.
* @param res If this pointer is not NULL, the function will store the result
* code of the given thread in the integer pointed to by @c res.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int thrd_join(thrd_t thr, int *res);
/** Put the calling thread to sleep.
* Suspend execution of the calling thread.
* @param duration Interval to sleep for
* @param remaining If non-NULL, this parameter will hold the remaining
* time until time_point upon return. This will
* typically be zero, but if the thread was woken up
* by a signal that is not ignored before duration was
* reached @c remaining will hold a positive time.
* @return 0 (zero) on successful sleep, -1 if an interrupt occurred,
* or a negative value if the operation fails.
*/
int thrd_sleep(const struct timespec *duration, struct timespec *remaining);
/** Yield execution to another thread.
* Permit other threads to run, even if the current thread would ordinarily
* continue to run.
*/
void thrd_yield(void);
/* Thread local storage */
#if defined(_TTHREAD_WIN32_)
typedef DWORD tss_t;
#else
typedef pthread_key_t tss_t;
#endif
/** Destructor function for a thread-specific storage.
* @param val The value of the destructed thread-specific storage.
*/
typedef void (*tss_dtor_t)(void *val);
/** Create a thread-specific storage.
* @param key The unique key identifier that will be set if the function is
* successful.
* @param dtor Destructor function. This can be NULL.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
* @note On Windows, the @c dtor will definitely be called when
* appropriate for threads created with @ref thrd_create. It will be
* called for other threads in most cases, the possible exception being
* for DLLs loaded with LoadLibraryEx. In order to be certain, you
* should use @ref thrd_create whenever possible.
*/
int tss_create(tss_t *key, tss_dtor_t dtor);
/** Delete a thread-specific storage.
* The function releases any resources used by the given thread-specific
* storage.
* @param key The key that shall be deleted.
*/
void tss_delete(tss_t key);
/** Get the value for a thread-specific storage.
* @param key The thread-specific storage identifier.
* @return The value for the current thread held in the given thread-specific
* storage.
*/
void *tss_get(tss_t key);
/** Set the value for a thread-specific storage.
* @param key The thread-specific storage identifier.
* @param val The value of the thread-specific storage to set for the current
* thread.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int tss_set(tss_t key, void *val);
#if defined(_TTHREAD_WIN32_)
typedef struct {
LONG volatile status;
CRITICAL_SECTION lock;
} once_flag;
#define ONCE_FLAG_INIT {0,}
#else
#define once_flag pthread_once_t
#define ONCE_FLAG_INIT PTHREAD_ONCE_INIT
#endif
/** Invoke a callback exactly once
* @param flag Flag used to ensure the callback is invoked exactly
* once.
* @param func Callback to invoke.
*/
#if defined(_TTHREAD_WIN32_)
void call_once(once_flag *flag, void (*func)(void));
#else
#define call_once(flag,func) pthread_once(flag,func)
#endif
#ifdef __cplusplus
}
#endif
#endif /* _TINYTHREAD_H_ */

View File

@@ -1,435 +0,0 @@
#include "neonucleus.h"
// both tables copied from: https://github.com/MightyPirates/OpenComputers/blob/52da41b5e171b43fea80342dc75d808f97a0f797/src/main/scala/li/cil/oc/util/FontUtils.scala
static const unsigned char nn_unicode_charWidth_table[] = {
16, 16, 16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 16, 33, 16, 16, 16, 34, 35, 36,
37, 38, 39, 40, 16, 16, 41, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 42, 43, 16, 16, 44, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 45, 16, 46, 47, 48, 49, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 50, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 51, 16, 16, 52,
53, 16, 54, 55, 56, 16, 16, 16, 16, 16, 16, 57, 16, 16, 58, 16, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,
69, 70, 16, 71, 72, 73, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 74, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 75, 76, 16, 16, 16, 77, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 78, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 79, 80, 16, 16, 16, 16, 16, 16, 16, 81, 16, 16, 16, 16, 16, 82, 83, 84, 16, 16, 16, 16, 16, 85,
86, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 248, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 254, 255, 255, 255, 255, 191, 182, 0, 0, 0, 0, 0, 0, 0, 63, 0, 255, 23, 0, 0, 0, 0, 0, 248, 255,
255, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 191, 159, 61, 0, 0, 0, 128, 2, 0, 0, 0, 255, 255, 255,
7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 1, 0, 0, 0, 0, 0, 0, 248, 15, 32, 0, 0, 192, 251, 239, 62, 0, 0,
0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 255, 255, 255,
255, 7, 0, 0, 0, 0, 0, 0, 20, 254, 33, 254, 0, 12, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 16, 30, 32, 0, 0, 12, 0, 0,
64, 6, 0, 0, 0, 0, 0, 0, 16, 134, 57, 2, 0, 0, 0, 35, 0, 6, 0, 0, 0, 0, 0, 0, 16, 190, 33, 0, 0, 12, 0, 0,
252, 2, 0, 0, 0, 0, 0, 0, 144, 30, 32, 64, 0, 12, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 17,
0, 0, 0, 0, 0, 0, 192, 193, 61, 96, 0, 12, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 144, 64, 48, 0, 0, 12, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 24, 30, 32, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
242, 7, 128, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 242, 31, 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 160,
2, 0, 0, 0, 0, 0, 0, 254, 127, 223, 224, 255, 254, 255, 255, 255, 31, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 224, 253, 102, 0, 0, 0, 195, 1, 0, 30, 0, 100, 32, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 0, 0, 0, 28, 0, 0, 0, 12, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 176, 63, 64, 254,
15, 32, 0, 0, 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 135, 1, 4, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
128, 9, 0, 0, 0, 0, 0, 0, 64, 127, 229, 31, 248, 159, 0, 0, 0, 0, 0, 0, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0,
15, 0, 0, 0, 0, 0, 208, 23, 4, 0, 0, 0, 0, 248, 15, 0, 3, 0, 0, 0, 60, 59, 0, 0, 0, 0, 0, 0, 64, 163, 3, 0, 0,
0, 0, 0, 0, 240, 207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 247, 255, 253, 33, 16,
3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255,
251, 0, 248, 0, 0, 0, 124, 0, 0, 0, 0, 0, 0, 223, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
255, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 128, 247, 63, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 68, 8, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 255, 255, 3, 128, 0, 0, 0, 0, 192, 63, 0, 0, 128, 255, 3, 0,
0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 200, 51, 0, 0, 0, 0, 32, 0, 0,
0, 0, 0, 0, 0, 0, 126, 102, 0, 8, 16, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 157, 193, 2, 0, 0, 0, 0, 48, 64, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 33, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0,
64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 255,
255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, 240, 0,
0, 0, 0, 0, 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 240, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 255, 127, 0, 0, 0, 0, 0, 0, 128,
3, 0, 0, 0, 0, 0, 120, 38, 0, 32, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 128, 239, 31, 0, 0, 0, 0, 0, 0, 0, 8, 0, 3, 0,
0, 0, 0, 0, 192, 127, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 211, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 128, 248, 7, 0, 0, 3, 0, 0, 0, 0, 0, 0, 24, 1, 0, 0, 0, 192, 31, 31, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 255, 92, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 133, 13, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 176, 1, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
248, 167, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 188, 15, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 6, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 240, 12, 1, 0, 0, 0, 254, 7, 0, 0, 0, 0, 248, 121, 128, 0, 126, 14, 0, 0, 0, 0, 0, 252,
127, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 255,
255, 252, 109, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 126, 180, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 255,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 0, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 128, 7, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 3, 248, 255, 231, 15, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
255, 255, 255, 255, 127, 248, 255, 255, 255, 255, 255, 31, 32, 0, 16, 0, 0, 248, 254, 255, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 249, 219, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 7, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static const unsigned char nn_unicode_charWidth_wide_table[] = {
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 18, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 19, 16, 20, 21, 22, 16, 16, 16, 23, 16, 16, 24, 25, 26, 27, 28, 17,
17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 29,
17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
17, 17, 17, 17, 17, 17, 17, 17, 30, 16, 16, 16, 16, 31, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
17, 17, 17, 17, 17, 17, 17, 32, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 16, 16, 16, 33,
34, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 35, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
17, 17, 17, 17, 17, 17, 36, 17, 17, 37, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 38, 39, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 40, 41, 42, 43, 44, 45, 46, 47, 16, 48, 49, 16, 16, 16, 16,
16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 0, 0, 0, 80, 184, 0, 0, 0, 0, 0, 0, 0, 224,
0, 0, 0, 1, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 251, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 15, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 63, 0, 0, 0, 255, 15, 255, 255, 255, 255,
255, 255, 255, 127, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127, 254, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 224, 255, 255, 255, 255, 255, 254, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 127, 255, 255, 255, 255, 255, 7, 255, 255, 255, 255, 15, 0,
255, 255, 255, 255, 255, 127, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 31, 255, 255, 255, 255, 255, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
255, 255, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 3, 0, 0, 255, 255, 255, 255, 247, 255, 127, 15, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 7, 0, 255, 255, 255, 127, 0, 0, 0, 0, 0,
0, 7, 0, 240, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 254, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 255, 255, 255,
255, 255, 15, 255, 1, 3, 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
1, 224, 191, 255, 255, 255, 255, 255, 255, 255, 255, 223, 255, 255, 15, 0, 255, 255, 255, 255,
255, 135, 15, 0, 255, 255, 17, 255, 255, 255, 255, 255, 255, 255, 255, 127, 253, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
159, 255, 255, 255, 255, 255, 255, 255, 63, 0, 120, 255, 255, 255, 0, 0, 4, 0, 0, 96, 0, 16, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255,
255, 255, 255, 255, 255, 255, 63, 16, 39, 0, 0, 24, 240, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 15, 0,
0, 0, 224, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 123, 252, 255, 255, 255,
255, 231, 199, 255, 255, 255, 231, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 15, 7, 7, 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static nn_bool_t nn_unicode_is_continuation(unsigned char byte) {
return (byte >> 6) == 0b10;
}
nn_bool_t nn_unicode_isValidCodepoint(const char *s) {
if(s[0] <= 0x7F) {
return true;
} else if((s[0] >> 5) == 0b110) {
if (!nn_unicode_is_continuation(s[1])) {
return false;
}
} else if((s[0] >> 4) == 0b1110) {
if (!nn_unicode_is_continuation(s[1])) {
return false;
}
if (!nn_unicode_is_continuation(s[2])) {
return false;
}
} else if((s[0] >> 3) == 0b11110) {
if (!nn_unicode_is_continuation(s[1])) {
return false;
}
if (!nn_unicode_is_continuation(s[2])) {
return false;
}
if (!nn_unicode_is_continuation(s[3])) {
return false;
}
} else {
return false;
}
return true;
}
nn_bool_t nn_unicode_validate(const char *b) {
const unsigned char* s = (const unsigned char*)b;
while (*s) {
if(s[0] <= 0x7F) {
s++;
} else if((s[0] >> 5) == 0b110) {
if (!nn_unicode_is_continuation(s[1])) {
return false;
}
s += 2;
} else if((s[0] >> 4) == 0b1110) {
if (!nn_unicode_is_continuation(s[1])) {
return false;
}
if (!nn_unicode_is_continuation(s[2])) {
return false;
}
s += 3;
} else if((s[0] >> 3) == 0b11110) {
if (!nn_unicode_is_continuation(s[1])) {
return false;
}
if (!nn_unicode_is_continuation(s[2])) {
return false;
}
if (!nn_unicode_is_continuation(s[3])) {
return false;
}
s += 4;
} else {
return false;
}
}
return true;
}
// A general unicode library, which assumes unicode encoding.
// It is used to power the Lua architecture's Unicode API re-implementation.
// It can also just be used to deal with unicode.
char *nn_unicode_char(nn_Alloc *alloc, unsigned int *codepoints, nn_size_t codepointCount) {
nn_size_t len = 0;
for (nn_size_t i = 0; i < codepointCount; i++) {
unsigned int codepoint = codepoints[i];
len += nn_unicode_codepointSize(codepoint);
}
char *buf = nn_alloc(alloc, len+1);
if (buf == NULL) return buf;
nn_size_t j = 0;
for (nn_size_t i = 0; i < codepointCount; i++) {
int codepoint = codepoints[i];
nn_size_t codepointLen = 0;
char c[NN_MAXIMUM_UNICODE_BUFFER];
nn_unicode_codepointToChar(c, codepoint, &codepointLen);
nn_memcpy(buf + j, c, codepointLen);
j += codepointLen;
}
buf[j] = '\0';
return buf;
}
unsigned int *nn_unicode_codepoints(nn_Alloc *alloc, const char *s, nn_size_t *len) {
nn_size_t l = nn_unicode_len(s);
unsigned int *buf = nn_alloc(alloc, sizeof(unsigned int) * l);
if(buf == NULL) return NULL;
if(len != NULL) *len = l;
nn_size_t cur = 0;
nn_size_t bufidx = 0;
while(s[cur] != 0) {
unsigned int point = nn_unicode_codepointAt(s, cur);
cur += nn_unicode_codepointSize(point);
buf[bufidx++] = point;
}
return buf;
}
nn_size_t nn_unicode_len(const char *b) {
nn_size_t count = 0;
const unsigned char* s = (const unsigned char*)b;
while (*s) {
count++;
if(s[0] <= 0x7F) {
s++;
} else if((s[0] >> 5) == 0b110) {
s += 2;
} else if((s[0] >> 4) == 0b1110) {
s += 3;
} else if((s[0] >> 3) == 0b11110) {
s += 4;
}
}
return count;
}
unsigned int nn_unicode_codepointAt(const char *s, nn_size_t byteOffset) {
unsigned int point = 0;
const unsigned char *b = (const unsigned char *)s + byteOffset;
const unsigned char subpartMask = 0b111111;
// look into nn_unicode_codepointToChar as well.
if(b[0] <= 0x7F) {
return b[0];
} else if((b[0] >> 5) == 0b110) {
point += ((unsigned int)(b[0] & 0b11111)) << 6;
point += ((unsigned int)(b[1] & subpartMask));
} else if((b[0] >> 4) == 0b1110) {
point += ((unsigned int)(b[0] & 0b1111)) << 12;
point += ((unsigned int)(b[1] & subpartMask)) << 6;
point += ((unsigned int)(b[2] & subpartMask));
} else if((b[0] >> 3) == 0b11110) {
point += ((unsigned int)(b[0] & 0b111)) << 18;
point += ((unsigned int)(b[1] & subpartMask)) << 12;
point += ((unsigned int)(b[2] & subpartMask)) << 6;
point += ((unsigned int)(b[3] & subpartMask));
}
return point;
}
nn_size_t nn_unicode_codepointSize(unsigned int codepoint) {
if (codepoint <= 0x007f) {
return 1;
} else if (codepoint <= 0x07ff) {
return 2;
} else if (codepoint <= 0xffff) {
return 3;
} else if (codepoint <= 0x10ffff) {
return 4;
}
return 1;
}
void nn_unicode_codepointToChar(char *buffer, unsigned int codepoint, nn_size_t *len) {
nn_size_t codepointSize = nn_unicode_codepointSize(codepoint);
if(len != NULL) *len = codepointSize;
nn_memset(buffer, 0, 4); // Clear static array
if (codepointSize == 1) {
buffer[0] = (char)codepoint;
} else if (codepointSize == 2) {
buffer[0] = 0b11000000 + ((codepoint >> 6) & 0b11111);
buffer[1] = 0b10000000 + (codepoint & 0b111111);
} else if (codepointSize == 3) {
buffer[0] = 0b11100000 + ((codepoint >> 12) & 0b1111);
buffer[1] = 0b10000000 + ((codepoint >> 6) & 0b111111);
buffer[2] = 0b10000000 + (codepoint & 0b111111);
} else if (codepointSize == 4) {
buffer[0] = 0b11110000 + ((codepoint >> 18) & 0b111);
buffer[1] = 0b10000000 + ((codepoint >> 12) & 0b111111);
buffer[2] = 0b10000000 + ((codepoint >> 6) & 0b111111);
buffer[3] = 0b10000000 + (codepoint & 0b111111);
}
}
// copied straight from opencomputers and musl's libc
// https://github.com/MightyPirates/OpenComputers/blob/52da41b5e171b43fea80342dc75d808f97a0f797/src/main/scala/li/cil/oc/util/FontUtils.scala#L205
// https://git.musl-libc.org/cgit/musl/tree/src/ctype/wcwidth.c
nn_size_t nn_unicode_charWidth(unsigned int codepoint) {
if (codepoint < 0xff) {
if (((codepoint + 1) & 0x7f) >= 0x21) {
return 1;
} else {
return 0;
}
} else if ((codepoint & 0xfffeffff) < 0xfffe) {
if ((nn_unicode_charWidth_table[nn_unicode_charWidth_table[codepoint>>8]*32+((codepoint&255)>>3)]>>(codepoint&7))&1)
return 0;
if ((nn_unicode_charWidth_wide_table[nn_unicode_charWidth_wide_table[codepoint>>8]*32+((codepoint&255)>>3)]>>(codepoint&7))&1)
return 2;
return 1;
} else if (codepoint-0x20000 < 0x20000) {
return 2;
} else if (codepoint == 0xe0001 || codepoint-0xe0020 < 0x5f || codepoint-0xe0100 < 0xef) {
return 0;
}
return 1;
}
nn_size_t nn_unicode_wlen(const char *s) {
nn_size_t wlen = 0;
while (*s) {
unsigned int codepoint = nn_unicode_codepointAt(s, 0);
nn_size_t codepointSize = nn_unicode_codepointSize(codepoint);
wlen += nn_unicode_charWidth(codepoint);
s += codepointSize;
}
return wlen;
}
// NOT IMPLEMENTED YET
unsigned int nn_unicode_upperCodepoint(unsigned int codepoint);
char *nn_unicode_upper(nn_Alloc *alloc, const char *s);
unsigned int nn_unicode_lowerCodepoint(unsigned int codepoint);
char *nn_unicode_lower(nn_Alloc *alloc, const char *s);
unsigned int nn_unicode_nextCodepointPermissive(const char *s, nn_size_t *index) {
nn_size_t i = *index;
if(nn_unicode_isValidCodepoint(s + i)) {
// TODO: handle edge-case where suboptimial encoding is used
unsigned int p = nn_unicode_codepointAt(s, i);
*index = i + nn_unicode_codepointSize(p);
return p;
}
unsigned int p = (unsigned char)s[i];
*index = i + 1;
return p;
}
nn_size_t nn_unicode_lenPermissive(const char *b) {
nn_size_t len = 0;
nn_size_t cur = 0;
while(b[cur]) {
nn_unicode_nextCodepointPermissive(b, &cur);
len++;
}
return len;
}
nn_size_t nn_unicode_wlenPermissive(const char *s) {
nn_size_t wlen = 0;
nn_size_t cur = 0;
while (s[cur]) {
unsigned int codepoint = nn_unicode_nextCodepointPermissive(s, &cur);
wlen += nn_unicode_charWidth(codepoint);
}
return wlen;
}
nn_intptr_t nn_unicode_indexPermissive(const char *s, nn_size_t codepointIndex) {
nn_size_t bytes = 0;
while(true) {
if(codepointIndex == 0) return bytes;
nn_unicode_nextCodepointPermissive(s, &bytes);
codepointIndex--;
}
}

View File

@@ -1,75 +0,0 @@
#include "neonucleus.h"
#include "universe.h"
nn_universe *nn_newUniverse(nn_Context ctx) {
nn_universe *u = nn_alloc(&ctx.allocator, sizeof(nn_universe));
if(u == NULL) return u;
u->ctx = ctx;
// we leave udata uninitialized because it does not matter
u->udataLen = 0;
return u;
}
nn_Context *nn_getContext(nn_universe *universe) {
return &universe->ctx;
}
nn_Alloc *nn_getAllocator(nn_universe *universe) {
return &universe->ctx.allocator;
}
nn_Clock *nn_getClock(nn_universe *universe) {
return &universe->ctx.clock;
}
nn_LockManager *nn_getLockManager(nn_universe *universe) {
return &universe->ctx.lockManager;
}
nn_Rng *nn_getRng(nn_universe *universe) {
return &universe->ctx.rng;
}
void nn_unsafeDeleteUniverse(nn_universe *universe) {
for(nn_size_t i = 0; i < universe->udataLen; i++) {
nn_deallocStr(&universe->ctx.allocator, universe->udata[i].name);
}
nn_dealloc(&universe->ctx.allocator, universe, sizeof(nn_universe));
}
void *nn_queryUserdata(nn_universe *universe, const char *name) {
for(nn_size_t i = 0; i < universe->udataLen; i++) {
if(nn_strcmp(universe->udata[i].name, name) == 0) {
return universe->udata[i].userdata;
}
}
return NULL;
}
void nn_storeUserdata(nn_universe *universe, const char *name, void *data) {
if(universe->udataLen == NN_MAX_USERDATA) return; // prevent overflow
nn_size_t idx = universe->udataLen;
char *allocName = nn_strdup(&universe->ctx.allocator, name);
if(allocName == NULL) return;
universe->udata[idx].name = allocName;
universe->udata[idx].userdata = data;
universe->udataLen++;
}
double nn_getTime(nn_universe *universe) {
return universe->ctx.clock.proc(universe->ctx.clock.userdata);
}
void nn_loadCoreComponentTables(nn_universe *universe) {
nn_loadEepromTable(universe);
nn_loadFilesystemTable(universe);
nn_loadDriveTable(universe);
nn_loadScreenTable(universe);
nn_loadGraphicsCardTable(universe);
nn_loadKeyboardTable(universe);
nn_loadModemTable(universe);
nn_loadTunnelTable(universe);
nn_loadDiskDriveTable(universe);
}

View File

@@ -1,17 +0,0 @@
#ifndef NEONUCLEUS_UNIVERSE_H
#define NEONUCLEUS_UNIVERSE_H
#include "neonucleus.h"
typedef struct nn_universe_udata {
char *name;
void *userdata;
} nn_universe_udata;
typedef struct nn_universe {
nn_Context ctx;
nn_universe_udata udata[NN_MAX_USERDATA];
nn_size_t udataLen;
} nn_universe;
#endif

View File

@@ -1,462 +0,0 @@
#include "neonucleus.h"
#ifndef NN_BAREMETAL
#ifdef NN_POSIX
#include <time.h>
#else
#include <time.h>
#include <windows.h>
#endif
#endif
void *nn_alloc(nn_Alloc *alloc, nn_size_t size) {
if(size == 0) return alloc->proc;
return alloc->proc(alloc->userdata, NULL, 0, size, NULL);
}
void *nn_resize(nn_Alloc *alloc, void *memory, nn_size_t oldSize, nn_size_t newSize) {
if(oldSize == newSize) return memory;
if(newSize == 0) {
nn_dealloc(alloc, memory, oldSize);
return alloc->proc;
}
if(memory == NULL) {
return nn_alloc(alloc, newSize);
}
if(memory == alloc->proc) {
if(newSize == 0) return memory;
return nn_alloc(alloc, newSize);
}
return alloc->proc(alloc->userdata, memory, oldSize, newSize, NULL);
}
void nn_dealloc(nn_Alloc *alloc, void *memory, nn_size_t size) {
if(memory == NULL) return; // matches free()
if(memory == alloc->proc) return; // 0-sized memory
alloc->proc(alloc->userdata, memory, size, 0, NULL);
}
#ifndef NN_BAREMETAL
#include <stdlib.h>
static void *nn_libcAllocProc(void *_, void *ptr, nn_size_t oldSize, nn_size_t newSize, void *__) {
if(newSize == 0) {
//printf("Freed %lu bytes from %p\n", oldSize, ptr);
free(ptr);
return NULL;
} else {
void *rptr = realloc(ptr, newSize);
//printf("Allocated %lu bytes for %p\n", newSize - oldSize, rptr);
return rptr;
}
}
nn_Alloc nn_libcAllocator(void) {
return (nn_Alloc) {
.userdata = NULL,
.proc = nn_libcAllocProc,
};
}
static nn_size_t nni_rand(void *userdata) {
return rand();
}
nn_Rng nn_libcRng(void) {
srand(time(NULL));
return (nn_Rng) {
.userdata = NULL,
.maximum = RAND_MAX,
.proc = nni_rand,
};
}
nn_Context nn_libcContext(void) {
return (nn_Context) {
.allocator = nn_libcAllocator(),
.clock = nn_libcRealTime(),
.lockManager = nn_libcMutex(),
.rng = nn_libcRng(),
};
}
#endif
// Utilities, both internal and external
char *nn_strdup(nn_Alloc *alloc, const char *s) {
nn_size_t l = nn_strlen(s);
char *m = nn_alloc(alloc, l+1);
if(m == NULL) return m;
return nn_strcpy(m, s);
}
void *nn_memdup(nn_Alloc *alloc, const void *buf, nn_size_t len) {
char *m = nn_alloc(alloc, len);
if(m == NULL) return m;
nn_memcpy(m, buf, len);
return m;
}
void nn_deallocStr(nn_Alloc *alloc, char *s) {
if(s == NULL) return;
nn_dealloc(alloc, s, nn_strlen(s)+1);
}
static void nni_randomHex(nn_Context *ctx, char *buf, nn_size_t len) {
const char *hex = "0123456789abcdef";
for(nn_size_t i = 0; i < len; i++) {
int r = nn_rand(&ctx->rng) % 16;
buf[i] = hex[r];
}
}
nn_address nn_randomUUID(nn_Context *ctx) {
nn_address addr = nn_alloc(&ctx->allocator, 37);
if(addr == NULL) return NULL;
nni_randomHex(ctx, addr + 0, 8);
addr[8] = '-';
nni_randomHex(ctx, addr + 9, 4);
addr[13] = '-';
nni_randomHex(ctx, addr + 14, 4);
addr[18] = '-';
nni_randomHex(ctx, addr + 19, 4);
addr[23] = '-';
nni_randomHex(ctx, addr + 24, 12);
addr[36] = '\0';
// UUIDv4 variant 1
addr[14] = '4';
addr[19] = '1';
return addr;
}
nn_size_t nn_rand(nn_Rng *rng) {
return rng->proc(rng->userdata);
}
// returns from 0 to 1 (inclusive)
double nn_randf(nn_Rng *rng) {
double x = nn_rand(rng);
return x / rng->maximum;
}
// returns from 0 to 1 (exclusive)
double nn_randfe(nn_Rng *rng) {
double x = nn_rand(rng);
if(x >= rng->maximum) return 0;
return x / rng->maximum;
}
#ifndef NN_BAREMETAL
#ifdef NN_POSIX
static double nni_realTime(void) {
struct timespec time;
if(clock_gettime(CLOCK_MONOTONIC, &time) < 0) return 0; // oh no
return time.tv_sec + ((double)time.tv_nsec) / 1e9;
}
#else
static double nni_realTime(void) {
LARGE_INTEGER frequency = {0};
if(!QueryPerformanceFrequency(&frequency)) return 0;
LARGE_INTEGER now = {0};
if(!QueryPerformanceCounter(&now)) return 0;
return (double)now.QuadPart / frequency.QuadPart;
}
#endif
double nni_realTimeClock(void *_) {
return nni_realTime();
}
nn_Clock nn_libcRealTime(void) {
return (nn_Clock) {
.userdata = NULL,
.proc = nni_realTimeClock,
};
}
#endif
// TODO: use OKLAB the color space for more accurate results.
typedef struct nn_rgbColor {
double r, g, b;
} nn_rgbColor;
nn_rgbColor nni_splitColorToRgb(int color) {
double r = (color & 0xFF0000) >> 16;
double g = (color & 0x00FF00) >> 8;
double b = color & 0x0000FF;
int max = 0xFF;
return (nn_rgbColor) {
.r = r / max,
.g = g / max,
.b = b / max,
};
}
double nn_colorDistance(int colorA, int colorB) {
if(colorA == colorB) return 0;
nn_rgbColor a = nni_splitColorToRgb(colorA);
nn_rgbColor b = nni_splitColorToRgb(colorB);
nn_rgbColor delta;
delta.r = a.r - b.r;
delta.g = a.g - b.g;
delta.b = a.b - b.b;
return 0.2126 * delta.r*delta.r + 0.7152 * delta.g*delta.g + 0.0722 * delta.b*delta.b;
}
int nn_mapColor(int color, int *palette, int paletteSize) {
if(paletteSize <= 0) return color;
int bestColor = palette[0];
double fitness = nn_colorDistance(color, bestColor);
for(int i = 1; i < paletteSize; i++) {
double dist = nn_colorDistance(color, palette[i]);
if(dist < fitness) {
bestColor = palette[i];
fitness = dist;
}
if(bestColor == color) return color;
}
return bestColor;
}
void nn_memset(void *buf, unsigned char byte, nn_size_t len) {
if(buf == NULL) return;
unsigned char *bytes = buf;
for(nn_size_t i = 0; i < len; i++) bytes[i] = byte;
}
void nn_memcpy(void *dest, const void *src, nn_size_t len) {
if(dest == NULL) return;
if(src == NULL) return;
if(len == 0) return;
char *destBytes = dest;
const char *srcBytes = src;
for(nn_size_t i = 0; i < len; i++) {
destBytes[i] = srcBytes[i];
}
}
char *nn_strcpy(char *dest, const char *src) {
if(dest == NULL) return dest;
nn_size_t i = 0;
while(src[i]) {
dest[i] = src[i];
i++;
}
dest[i] = 0;
return dest;
}
int nn_strcmp(const char *a, const char *b) {
nn_size_t i = 0;
while(NN_TRUE) {
unsigned char ca = a[i];
unsigned char cb = b[i];
if(ca < cb) {
return -1;
}
if(ca > cb) {
return -1;
}
if(ca == 0 && cb == 0) { // reached terminator
return 0;
}
i++;
}
}
const char *nn_strchr(const char *str, int ch) {
if(str == NULL) return NULL;
nn_size_t i = 0;
while(NN_TRUE) {
if(str[i] == ch) return str + i;
if(str[i] == 0) return NULL;
i++;
}
}
nn_size_t nn_strlen(const char *a) {
if(a == NULL) return 0;
nn_size_t l = 0;
while(a[l]) l++;
return l;
}
nn_bool_t nn_strbegin(const char *s, const char *prefix) {
nn_size_t i = 0;
while(true) {
if(prefix[i] == 0) return true; // prefix over, it matched
if(s[i] == 0) return false; // string over, it didn't match
if(s[i] != prefix[i]) return false;
i++;
}
}
nn_bool_t nn_error_isEmpty(nn_errorbuf_t buf) {
if(buf == NULL) return true;
return buf[0] == 0;
}
void nn_error_write(nn_errorbuf_t buf, const char *s) {
if(buf == NULL) return;
for(nn_size_t i = 0; i < NN_MAX_ERROR_BUFFER; i++) {
buf[i] = s[i];
if(s[i] == 0) break;
}
// just to be sure
buf[NN_MAX_ERROR_BUFFER-1] = 0;
}
void nn_error_clear(nn_errorbuf_t buf) {
if(buf == NULL) return;
buf[0] = 0;
}
nn_bool_t nn_path_hasSlash(const char *path) {
while(*path) {
if(*path == '/') return true;
path++;
}
return false;
}
nn_size_t nn_path_firstSlash(const char *path) {
for(nn_size_t i = 0; path[i]; i++) {
if(path[i] == '/') return i;
}
return 0; // should never happen
}
nn_size_t nn_path_lastSlash(const char *path) {
nn_size_t slash = 0;
for(nn_size_t i = 0; path[i]; i++) {
if(path[i] == '/') slash = i;
}
return slash;
}
// returns whether it is the last name
nn_bool_t nn_path_firstName(const char *path, char firstDirectory[NN_MAX_PATH], char subpath[NN_MAX_PATH]) {
if(!nn_path_hasSlash(path)) {
nn_strcpy(firstDirectory, path);
nn_strcpy(subpath, "");
return true; // end
}
nn_size_t slash = nn_path_firstSlash(path);
nn_memcpy(firstDirectory, path, slash);
firstDirectory[slash] = 0;
nn_strcpy(subpath, path + slash + 1);
return false;
}
// returns whether it is the only name
nn_bool_t nn_path_lastName(const char *path, char name[NN_MAX_PATH], char parent[NN_MAX_PATH]) {
if(!nn_path_hasSlash(path)) {
nn_strcpy(name, path);
nn_strcpy(parent, "");
return true; // end
}
nn_size_t slash = nn_path_lastSlash(path);
nn_strcpy(name, path + slash + 1);
nn_memcpy(parent, path, slash);
parent[slash] = 0;
return false;
}
const char *nn_path_illegal = "\"\\:*?<>|";
nn_bool_t nn_path_isValid(const char *path) {
// if we don't check for these, we will be FUCKED
for(nn_size_t i = 0; nn_path_illegal[i] != '\0'; i++) {
if(nn_strchr(path, nn_path_illegal[i]) != NULL) return false;
}
return nn_strlen(path) < NN_MAX_PATH; // less then because we depend on the terminator
}
static nn_bool_t nni_path_isAllDots(const char *path, nn_size_t len) {
for(nn_size_t i = 0; i < len; i++) {
if(path[i] != '.') return false;
}
return true;
}
nn_bool_t nn_path_canonical(const char *path, char canonical[NN_MAX_PATH]) {
// attempts to convert a random barely legal path
// if this shit is ever bugged and a sandbox escape is done
// by tricking it into sneaking some .. in there
// !!!! WE WILL BE FUCKED, THE SERVER WILL BE HACKED, AND WE WILL DIE !!!!
if(!nn_path_isValid(path)) {
// HELL NO
return true;
}
// 0'd out because it fills it up with terminators, simplifying the rest of the code
// in theory this is suboptimal, however, I'm lazy
nn_memset(canonical, 0, NN_MAX_PATH);
nn_size_t ptr = 0;
nn_size_t i = 0;
// TODO: burn it with fire and get banned from programming
while(true) {
while(path[i] == '/') i++; // just do not ask
if(path[i] == 0) break;
const char *subpath = path + i;
nn_size_t len = nn_path_firstSlash(subpath);
if(len == 0) {
len = nn_strlen(path) - i;
}
if(nni_path_isAllDots(subpath, len)) {
// we don't actually resolve them because they shouldn't be there
// to begin with
i += len;
continue;
}
if(ptr == 0) {
// at the start
nn_memcpy(canonical, subpath, len);
ptr = len;
i += len;
continue;
}
// just append to it
canonical[ptr] = '/';
ptr++;
nn_memcpy(canonical + ptr, subpath, len);
ptr += len;
i += len;
continue;
}
return false;
}

View File

@@ -1,257 +0,0 @@
#include "neonucleus.h"
nn_value nn_values_nil(void) {
return (nn_value) {.tag = NN_VALUE_NIL};
}
nn_value nn_values_integer(nn_integer_t integer) {
return (nn_value) {.tag = NN_VALUE_INT, .integer = integer};
}
nn_value nn_values_number(double num) {
return (nn_value) {.tag = NN_VALUE_NUMBER, .number = num};
}
nn_value nn_values_boolean(nn_bool_t boolean) {
return (nn_value) {.tag = NN_VALUE_BOOL, .boolean = boolean};
}
nn_value nn_values_cstring(const char *string) {
return (nn_value) {.tag = NN_VALUE_CSTR, .cstring = string};
}
nn_value nn_values_string(nn_Alloc *alloc, const char *string, nn_size_t len) {
char *buf = nn_alloc(alloc, len+1);
if(buf == NULL) {
return nn_values_nil();
}
nn_memcpy(buf, string, len);
buf[len] = '\0';
nn_string *s = nn_alloc(alloc, sizeof(nn_string));
if(s == NULL) {
nn_dealloc(alloc, buf, len+1);
return nn_values_nil();
}
s->data = buf;
s->len = len;
s->refc = 1;
s->alloc = *alloc;
return (nn_value) {.tag = NN_VALUE_STR, .string = s};
}
nn_value nn_values_array(nn_Alloc *alloc, nn_size_t len) {
nn_array *arr = nn_alloc(alloc, sizeof(nn_array));
if(arr == NULL) {
return nn_values_nil();
}
arr->alloc = *alloc;
arr->refc = 1;
arr->len = len;
nn_value *values = nn_alloc(alloc, sizeof(nn_value) * len);
if(values == NULL) {
nn_dealloc(alloc, arr, sizeof(nn_array));
return nn_values_nil();
}
for(nn_size_t i = 0; i < len; i++) {
values[i] = nn_values_nil();
}
arr->values = values;
return (nn_value) {.tag = NN_VALUE_ARRAY, .array = arr};
}
nn_value nn_values_table(nn_Alloc *alloc, nn_size_t pairCount) {
nn_table *table = nn_alloc(alloc, sizeof(nn_table));
if(table == NULL) {
return nn_values_nil();
}
table->alloc = *alloc;
table->refc = 1;
table->len = pairCount;
nn_pair *pairs = nn_alloc(alloc, sizeof(nn_pair) * pairCount);
if(pairs == NULL) {
nn_dealloc(alloc, table, sizeof(nn_table));
return nn_values_nil();
}
for(nn_size_t i = 0; i < pairCount; i++) {
pairs[i].key = nn_values_nil();
pairs[i].val = nn_values_nil();
}
table->pairs = pairs;
return (nn_value) {.tag = NN_VALUE_TABLE, .table = table};
}
nn_value nn_values_resource(nn_size_t id) {
return (nn_value) {
.tag = NN_VALUE_RESOURCE,
.resourceID = id,
};
}
nn_size_t nn_values_getType(nn_value val) {
return val.tag;
}
nn_value nn_values_retain(nn_value val) {
if(val.tag == NN_VALUE_STR) {
val.string->refc++;
} else if(val.tag == NN_VALUE_ARRAY) {
val.array->refc++;
} else if(val.tag == NN_VALUE_TABLE) {
val.table->refc++;
}
return val;
}
void nn_values_drop(nn_value val) {
if(val.tag == NN_VALUE_STR) {
val.string->refc--;
if(val.string->refc == 0) {
nn_Alloc *a = &val.string->alloc;
nn_dealloc(a, val.string->data, val.string->len + 1);
nn_dealloc(a, val.string, sizeof(nn_string));
}
} else if(val.tag == NN_VALUE_ARRAY) {
val.array->refc--;
if(val.array->refc == 0) {
for(nn_size_t i = 0; i < val.array->len; i++) {
nn_values_drop(val.array->values[i]);
}
nn_Alloc *a = &val.array->alloc;
nn_dealloc(a, val.array->values, sizeof(nn_value) * val.array->len);
nn_dealloc(a, val.array, sizeof(nn_array));
}
} else if(val.tag == NN_VALUE_TABLE) {
val.table->refc--;
if(val.table->refc == 0) {
for(nn_size_t i = 0; i < val.table->len; i++) {
nn_values_drop(val.table->pairs[i].key);
nn_values_drop(val.table->pairs[i].val);
}
nn_Alloc *a = &val.table->alloc;
nn_dealloc(a, val.table->pairs, sizeof(nn_pair) * val.table->len);
nn_dealloc(a, val.table, sizeof(nn_table));
}
}
}
void nn_values_dropAll(nn_value *values, nn_size_t len) {
for(nn_size_t i = 0; i < len; i++) {
nn_values_drop(values[i]);
}
}
void nn_values_set(nn_value arr, nn_size_t idx, nn_value val) {
if(arr.tag != NN_VALUE_ARRAY) return;
if(idx >= arr.array->len) return;
nn_values_drop(arr.array->values[idx]);
arr.array->values[idx] = val;
}
nn_value nn_values_get(nn_value arr, nn_size_t idx) {
if(arr.tag != NN_VALUE_ARRAY) return nn_values_nil();
if(idx >= arr.array->len) return nn_values_nil();
return arr.array->values[idx];
}
void nn_values_setPair(nn_value obj, nn_size_t idx, nn_value key, nn_value val) {
if(obj.tag != NN_VALUE_TABLE) return;
if(idx >= obj.table->len) return;
nn_values_drop(obj.table->pairs[idx].key);
nn_values_drop(obj.table->pairs[idx].val);
obj.table->pairs[idx].key = key;
obj.table->pairs[idx].val = val;
}
nn_pair nn_values_getPair(nn_value obj, nn_size_t idx) {
nn_pair badPair = {.key = nn_values_nil(), .val = nn_values_nil()};
if(obj.tag != NN_VALUE_TABLE) return badPair;
if(idx >= obj.table->len) return badPair;
return obj.table->pairs[idx];
}
nn_integer_t nn_toInt(nn_value val) {
if(val.tag == NN_VALUE_INT) return val.integer;
if(val.tag == NN_VALUE_NUMBER) return val.number;
return 0;
}
double nn_toNumber(nn_value val) {
if(val.tag == NN_VALUE_INT) return val.integer;
if(val.tag == NN_VALUE_NUMBER) return val.number;
return 0;
}
nn_bool_t nn_toBoolean(nn_value val) {
if(val.tag == NN_VALUE_NIL) return false;
if(val.tag == NN_VALUE_BOOL) return val.boolean;
return true;
}
const char *nn_toCString(nn_value val) {
if(val.tag == NN_VALUE_CSTR) return val.cstring;
if(val.tag == NN_VALUE_STR) return val.string->data;
return NULL;
}
const char *nn_toString(nn_value val, nn_size_t *len) {
nn_size_t l = 0;
const char *c = NULL;
if(val.tag == NN_VALUE_CSTR) {
c = val.cstring;
l = nn_strlen(c);
}
if(val.tag == NN_VALUE_STR) {
c = val.string->data;
l = val.string->len;
}
if(len != NULL) *len = l;
return c;
}
nn_integer_t nn_toIntOr(nn_value val, nn_integer_t defaultVal) {
if(val.tag == NN_VALUE_INT) return val.integer;
if(val.tag == NN_VALUE_NUMBER) return val.number;
return defaultVal;
}
double nn_toNumberOr(nn_value val, double defaultVal) {
if(val.tag == NN_VALUE_INT) return val.integer;
if(val.tag == NN_VALUE_NUMBER) return val.number;
return defaultVal;
}
nn_bool_t nn_toBooleanOr(nn_value val, nn_bool_t defaultVal) {
if(val.tag == NN_VALUE_BOOL) return val.boolean;
return defaultVal;
}
nn_size_t nn_measurePacketSize(nn_value *vals, nn_size_t len) {
nn_size_t size = 0;
for(nn_size_t i = 0; i < len; i++) {
nn_value val = vals[i];
size += 2;
if(val.tag == NN_VALUE_INT || val.tag == NN_VALUE_NUMBER) {
size += 8;
} else if(val.tag == NN_VALUE_STR) {
nn_size_t len = val.string->len;
if(len == 0) len = 1; // ask OC
size += len;
} else if(val.tag == NN_VALUE_CSTR) {
nn_size_t len = nn_strlen(val.cstring);
if(len == 0) len = 1; // ask OC
size += len;
} else if(val.tag == NN_VALUE_BOOL || val.tag == NN_VALUE_NIL) {
size += 4;
} else {
// yeah no fuck off
// we abuse 2's complement
// TODO: NN_SIZE_MAX
return -1;
}
}
return size;
}