erase the old version
This commit is contained in:
78
TODO.md
78
TODO.md
@@ -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
|
||||
|
||||
@@ -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
|
||||
1681
rewrite/neonucleus.h
1681
rewrite/neonucleus.h
File diff suppressed because it is too large
Load Diff
150
src/component.c
150
src/component.c
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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, ¤t);
|
||||
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);
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
674
src/computer.c
674
src/computer.c
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
20
src/data.zig
20
src/data.zig
@@ -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), .{});
|
||||
}
|
||||
130
src/deviceInfo.c
130
src/deviceInfo.c
@@ -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;
|
||||
}
|
||||
1016
src/emulator.c
1016
src/emulator.c
File diff suppressed because it is too large
Load Diff
86
src/lock.c
86
src/lock.c
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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));
|
||||
2680
src/neonucleus.h
2680
src/neonucleus.h
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
455
src/sandbox.lua
455
src/sandbox.lua
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
#ifndef TEST_LUA_ARCH
|
||||
#define TEST_LUA_ARCH
|
||||
|
||||
#include "neonucleus.h"
|
||||
nn_architecture *testLuaArch_getArchitecture(const char *sandboxPath);
|
||||
|
||||
#endif
|
||||
@@ -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(¤t_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
|
||||
@@ -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 thread’s 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_ */
|
||||
435
src/unicode.c
435
src/unicode.c
@@ -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--;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
462
src/utils.c
462
src/utils.c
@@ -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;
|
||||
}
|
||||
257
src/value.c
257
src/value.c
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user