Compare commits
4 Commits
a6d7278735
...
5c905e6f77
| Author | SHA1 | Date | |
|---|---|---|---|
| 5c905e6f77 | |||
| 2275cbf47d | |||
| 79ea1c7f6f | |||
| 60b12ee507 |
@@ -124,6 +124,27 @@ static void luaArch_nnToLua(luaArch *arch, lua_State *L, size_t nnIdx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int luaArch_computer_beep(lua_State *L) {
|
static int luaArch_computer_beep(lua_State *L) {
|
||||||
|
if(lua_type(L, 1) == LUA_TSTRING) {
|
||||||
|
nn_MorseBeep beep = {.frequency = 1000, .beepDuration = 200, .volume = 1};
|
||||||
|
beep.pattern = lua_tostring(L, 1);
|
||||||
|
if(lua_isnumber(L, 2)) {
|
||||||
|
beep.frequency = lua_tonumber(L, 2);
|
||||||
|
}
|
||||||
|
if(lua_isnumber(L, 3)) {
|
||||||
|
beep.beepDuration = lua_tonumber(L, 3);
|
||||||
|
}
|
||||||
|
if(lua_isnumber(L, 4)) {
|
||||||
|
beep.volume = lua_tonumber(L, 4);
|
||||||
|
}
|
||||||
|
if(beep.frequency < 20) beep.frequency = 20;
|
||||||
|
if(beep.beepDuration < 0) beep.beepDuration = 0;
|
||||||
|
if(beep.volume < 0) beep.volume = 0;
|
||||||
|
if(beep.frequency > 20000) beep.frequency = 20000;
|
||||||
|
if(beep.beepDuration > 5) beep.beepDuration = 5;
|
||||||
|
if(beep.volume > 1) beep.volume = 1;
|
||||||
|
nn_beepComputerMorse(luaArch_from(L)->computer, beep);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
nn_Beep beep = {.frequency = 1000, .duration = 1, .volume = 1};
|
nn_Beep beep = {.frequency = 1000, .duration = 1, .volume = 1};
|
||||||
if(lua_isnumber(L, 1)) {
|
if(lua_isnumber(L, 1)) {
|
||||||
beep.frequency = lua_tonumber(L, 1);
|
beep.frequency = lua_tonumber(L, 1);
|
||||||
|
|||||||
22
src/main.c
22
src/main.c
@@ -500,6 +500,7 @@ typedef struct ne_memSand {
|
|||||||
char *buf;
|
char *buf;
|
||||||
size_t used;
|
size_t used;
|
||||||
size_t cap;
|
size_t cap;
|
||||||
|
size_t active;
|
||||||
} ne_memSand;
|
} ne_memSand;
|
||||||
|
|
||||||
void *ne_sandbox_alloc(void *state, void *memory, size_t oldSize, size_t newSize) {
|
void *ne_sandbox_alloc(void *state, void *memory, size_t oldSize, size_t newSize) {
|
||||||
@@ -509,12 +510,16 @@ void *ne_sandbox_alloc(void *state, void *memory, size_t oldSize, size_t newSize
|
|||||||
newSize = ne_alignAlloc(newSize, NN_ALLOC_ALIGN);
|
newSize = ne_alignAlloc(newSize, NN_ALLOC_ALIGN);
|
||||||
|
|
||||||
// never free
|
// never free
|
||||||
if(newSize == 0) return NULL;
|
if(newSize == 0) {
|
||||||
|
sand->active -= oldSize;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
if(memory == NULL) {
|
if(memory == NULL) {
|
||||||
if(sand->cap - sand->used < newSize) return NULL;
|
if(sand->cap - sand->used < newSize) return NULL;
|
||||||
// alloc new
|
// alloc new
|
||||||
void *mem = sand->buf + sand->used;
|
void *mem = sand->buf + sand->used;
|
||||||
sand->used += newSize;
|
sand->used += newSize;
|
||||||
|
sand->active += newSize;
|
||||||
return mem;
|
return mem;
|
||||||
}
|
}
|
||||||
// realloc
|
// realloc
|
||||||
@@ -522,6 +527,7 @@ void *ne_sandbox_alloc(void *state, void *memory, size_t oldSize, size_t newSize
|
|||||||
if(sand->cap - sand->used < newSize) return NULL;
|
if(sand->cap - sand->used < newSize) return NULL;
|
||||||
void *mem = sand->buf + sand->used;
|
void *mem = sand->buf + sand->used;
|
||||||
sand->used += newSize;
|
sand->used += newSize;
|
||||||
|
sand->active += newSize;
|
||||||
memcpy(mem, memory, oldSize);
|
memcpy(mem, memory, oldSize);
|
||||||
return mem;
|
return mem;
|
||||||
}
|
}
|
||||||
@@ -536,6 +542,10 @@ void ne_env(nn_EnvironmentRequest *req) {
|
|||||||
printf("beep: %f Hz %fs %.02f%%\n", req->beep.frequency, req->beep.duration, req->beep.volume*100);
|
printf("beep: %f Hz %fs %.02f%%\n", req->beep.frequency, req->beep.duration, req->beep.volume*100);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if(req->action == NN_ENV_BEEPMORSE) {
|
||||||
|
printf("morse beep: %s, %f Hz %fs %.02f%%\n", req->morseBeep.pattern, req->morseBeep.frequency, req->morseBeep.beepDuration, req->morseBeep.volume*100);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if(req->action == NN_ENV_DRAWENERGY) {
|
if(req->action == NN_ENV_DRAWENERGY) {
|
||||||
accumulatedEnergyCost += req->energy;
|
accumulatedEnergyCost += req->energy;
|
||||||
totalEnergyLoss += req->energy;
|
totalEnergyLoss += req->energy;
|
||||||
@@ -566,6 +576,7 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
ne_memSand sand;
|
ne_memSand sand;
|
||||||
sand.buf = NULL;
|
sand.buf = NULL;
|
||||||
|
sand.active = 0;
|
||||||
|
|
||||||
if(sandboxMem) {
|
if(sandboxMem) {
|
||||||
// 1 MiB pre-allocated to prevent erasing the free-list
|
// 1 MiB pre-allocated to prevent erasing the free-list
|
||||||
@@ -626,10 +637,8 @@ int main(int argc, char **argv) {
|
|||||||
char mainfspath[NN_MAX_PATH];
|
char mainfspath[NN_MAX_PATH];
|
||||||
snprintf(mainfspath, NN_MAX_PATH, "data/%s", mainDir);
|
snprintf(mainfspath, NN_MAX_PATH, "data/%s", mainDir);
|
||||||
nn_Component *managedfs = ncl_createFilesystem(u, NULL, mainfspath, &nn_defaultFilesystems[tier-1], true);
|
nn_Component *managedfs = ncl_createFilesystem(u, NULL, mainfspath, &nn_defaultFilesystems[tier-1], true);
|
||||||
//nn_Component *tmpfs = ncl_createTmpFS(u, NULL, &nn_defaultTmpFS, NCL_FILECOST_DEFAULT, false);
|
nn_Component *tmpfs = ncl_createTmpFS(u, NULL, &nn_defaultTmpFS, NCL_FILECOST_DEFAULT, false);
|
||||||
nn_Component *tmpfs = ncl_createFilesystem(u, NULL, "/tmp", &nn_defaultFilesystems[3], false);
|
|
||||||
nn_Component *testingfs = ncl_createFilesystem(u, NULL, "aux", &nn_defaultFilesystems[3], false);
|
nn_Component *testingfs = ncl_createFilesystem(u, NULL, "aux", &nn_defaultFilesystems[3], false);
|
||||||
// until TmpFS works
|
|
||||||
nn_Component *debugfs = ncl_createTmpFS(u, NULL, &nn_defaultFilesystems[3], NCL_FILECOST_DEFAULT, false);
|
nn_Component *debugfs = ncl_createTmpFS(u, NULL, &nn_defaultFilesystems[3], NCL_FILECOST_DEFAULT, false);
|
||||||
|
|
||||||
const char * const testDriveData =
|
const char * const testDriveData =
|
||||||
@@ -700,7 +709,7 @@ int main(int argc, char **argv) {
|
|||||||
ncl_mountKeyboard(scrstate, "mainKB");
|
ncl_mountKeyboard(scrstate, "mainKB");
|
||||||
|
|
||||||
// we assume server basically
|
// we assume server basically
|
||||||
nn_Computer *c = nn_createComputer(u, NULL, NULL, ramTotal, nn_defaultComponentLimits[tier-1] * 4, 256);
|
nn_Computer * volatile c = nn_createComputer(u, NULL, NULL, ramTotal, nn_defaultComponentLimits[tier-1] * 4, 256);
|
||||||
nn_Environment cEnv = {
|
nn_Environment cEnv = {
|
||||||
.userdata = NULL,
|
.userdata = NULL,
|
||||||
.handler = ne_env,
|
.handler = ne_env,
|
||||||
@@ -991,5 +1000,8 @@ cleanup:;
|
|||||||
if(eepromPath != NULL) free(eepromCode);
|
if(eepromPath != NULL) free(eepromCode);
|
||||||
CloseWindow();
|
CloseWindow();
|
||||||
free(sand.buf);
|
free(sand.buf);
|
||||||
|
if(sand.buf != NULL) {
|
||||||
|
printf("Leaked: %zu bytes\n", sand.active);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
759
src/ncomplib.c
759
src/ncomplib.c
@@ -404,7 +404,7 @@ bool ncl_mkdir(ncl_VFS vfs, const char *path) {
|
|||||||
return vfs.handler(&req);
|
return vfs.handler(&req);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ncl_splitParentName(const char *path, char parent[NN_MAX_PATH], char name[NN_MAX_PATH]) {
|
void ncl_splitParentName(const char *path, char parent[NN_MAX_PATH], char name[NN_MAX_PATH]) {
|
||||||
char buf[NN_MAX_PATH];
|
char buf[NN_MAX_PATH];
|
||||||
// use snprintf instead of strncpy cuz NULL terminator
|
// use snprintf instead of strncpy cuz NULL terminator
|
||||||
snprintf(buf, NN_MAX_PATH, "%s", path);
|
snprintf(buf, NN_MAX_PATH, "%s", path);
|
||||||
@@ -448,7 +448,6 @@ static bool ncl_copydir(ncl_VFS vfs, const char *from, const char *to) {
|
|||||||
snprintf(subpath, NN_MAX_PATH, "%s%c%s", from, vfs.pathsep, name);
|
snprintf(subpath, NN_MAX_PATH, "%s%c%s", from, vfs.pathsep, name);
|
||||||
char subdest[NN_MAX_PATH];
|
char subdest[NN_MAX_PATH];
|
||||||
snprintf(subdest, NN_MAX_PATH, "%s%c%s", to, vfs.pathsep, name);
|
snprintf(subdest, NN_MAX_PATH, "%s%c%s", to, vfs.pathsep, name);
|
||||||
printf("%s -> %s\n", subpath, subdest);
|
|
||||||
if(!ncl_copyto(vfs, subpath, subdest)) goto fail;
|
if(!ncl_copyto(vfs, subpath, subdest)) goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -522,6 +521,7 @@ struct ncl_ScreenState {
|
|||||||
nn_Context *ctx;
|
nn_Context *ctx;
|
||||||
nn_Lock *lock;
|
nn_Lock *lock;
|
||||||
nn_ScreenConfig conf;
|
nn_ScreenConfig conf;
|
||||||
|
size_t usage;
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
int viewportWidth;
|
int viewportWidth;
|
||||||
@@ -868,7 +868,7 @@ static nn_Exit ncl_fsHandler(nn_FSRequest *req) {
|
|||||||
return NN_EBADCALL;
|
return NN_EBADCALL;
|
||||||
}
|
}
|
||||||
char path[NN_MAX_PATH];
|
char path[NN_MAX_PATH];
|
||||||
ncl_fixPath(state, req->open.path, path);
|
ncl_fixPath(state, req->opendir, path);
|
||||||
void *dir = ncl_opendir(state->vfs, path);
|
void *dir = ncl_opendir(state->vfs, path);
|
||||||
if(dir == NULL) {
|
if(dir == NULL) {
|
||||||
nn_unlock(ctx, state->lock);
|
nn_unlock(ctx, state->lock);
|
||||||
@@ -1075,535 +1075,512 @@ nn_Component *ncl_createFilesystem(nn_Universe *universe, const char *address, c
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define NCL_INITFILECAP (8 * NN_KiB)
|
|
||||||
#define NCL_INITDIRCAP 8
|
|
||||||
|
|
||||||
#define NCL_MAXDIRSIZE 1024
|
|
||||||
#define NCL_MAXFILESIZE (1 * NN_GiB)
|
|
||||||
|
|
||||||
typedef struct ncl_TmpFile {
|
typedef struct ncl_TmpFile {
|
||||||
nn_Context *ctx;
|
size_t openHandles;
|
||||||
struct ncl_TmpFile *parent;
|
struct ncl_TmpFile *parent;
|
||||||
|
struct ncl_TmpFile *next;
|
||||||
bool isFile;
|
bool isFile;
|
||||||
size_t size;
|
|
||||||
size_t cap;
|
|
||||||
size_t openFD;
|
|
||||||
union {
|
union {
|
||||||
char *code;
|
struct ncl_TmpFile *files;
|
||||||
struct ncl_TmpFile **files;
|
struct {
|
||||||
|
char *data;
|
||||||
|
size_t datalen;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
char name[NN_MAX_PATH];
|
char *name;
|
||||||
} ncl_TmpFile;
|
} ncl_TmpFile;
|
||||||
|
|
||||||
typedef struct ncl_TmpFileDesc {
|
size_t ncl_segmentLen(const char *text) {
|
||||||
ncl_TmpFile *f;
|
size_t l = 0;
|
||||||
size_t off;
|
while(text[l]) {
|
||||||
|
if(text[l] == '/') break;
|
||||||
|
l++;
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct ncl_TmpFildes {
|
||||||
|
ncl_TmpFile *file;
|
||||||
char mode;
|
char mode;
|
||||||
} ncl_TmpFileDesc;
|
union {
|
||||||
|
size_t offset;
|
||||||
static ncl_TmpFile *ncl_allocTmpFile(nn_Context *ctx, const char *name, bool isFile) {
|
ncl_TmpFile *curEnt;
|
||||||
size_t cap = isFile ? NCL_INITFILECAP : NCL_INITDIRCAP;
|
};
|
||||||
ncl_TmpFile *f = nn_alloc(ctx, sizeof(*f));
|
} ncl_TmpFildes;
|
||||||
if(f == NULL) return NULL;
|
|
||||||
snprintf(f->name, NN_MAX_PATH, "%s", name);
|
|
||||||
f->ctx = ctx;
|
|
||||||
f->parent = NULL;
|
|
||||||
f->isFile = isFile;
|
|
||||||
f->openFD = 0;
|
|
||||||
f->size = 0;
|
|
||||||
f->cap = cap;
|
|
||||||
if(isFile) {
|
|
||||||
char *code = nn_alloc(ctx, cap);
|
|
||||||
if(code == NULL) {
|
|
||||||
nn_free(ctx, f, sizeof(*f));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
f->code = code;
|
|
||||||
} else {
|
|
||||||
ncl_TmpFile **files = nn_alloc(ctx, sizeof(ncl_TmpFile *) * cap);
|
|
||||||
if(files == NULL) {
|
|
||||||
nn_free(ctx, f, sizeof(*f));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
f->files = files;
|
|
||||||
}
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ncl_freeTmpFile(ncl_TmpFile *f) {
|
|
||||||
nn_Context *ctx = f->ctx;
|
|
||||||
if(f->isFile) {
|
|
||||||
nn_free(ctx, f->code, f->cap);
|
|
||||||
} else {
|
|
||||||
for(size_t i = 0; i < f->size; i++) {
|
|
||||||
ncl_freeTmpFile(f->files[i]);
|
|
||||||
}
|
|
||||||
nn_free(ctx, f->files, f->cap * sizeof(ncl_TmpFile *));
|
|
||||||
}
|
|
||||||
nn_free(ctx, f, sizeof(*f));
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool ncl_removeTmpFile(ncl_TmpFile *dir, ncl_TmpFile *ent) {
|
|
||||||
size_t j = 0;
|
|
||||||
for(size_t i = 0; i < dir->size; i++) {
|
|
||||||
if(dir->files[i] != ent) {
|
|
||||||
dir->files[j] = dir->files[i];
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bool removed = j < dir->size;
|
|
||||||
dir->size = j;
|
|
||||||
return removed;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool ncl_addTmpFile(ncl_TmpFile *dir, ncl_TmpFile *ent) {
|
|
||||||
nn_Context *ctx = dir->ctx;
|
|
||||||
if(dir->size == dir->cap) {
|
|
||||||
size_t newCap = dir->cap * 2;
|
|
||||||
if(newCap > NCL_MAXDIRSIZE) return false;
|
|
||||||
ncl_TmpFile **files = nn_realloc(ctx, dir->files, sizeof(ncl_TmpFile *) * dir->cap, sizeof(ncl_TmpFile *) * newCap);
|
|
||||||
if(files == NULL) return false;
|
|
||||||
dir->cap = newCap;
|
|
||||||
dir->files = files;
|
|
||||||
}
|
|
||||||
dir->files[dir->size] = ent;
|
|
||||||
dir->size++;
|
|
||||||
ent->parent = dir;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensures minimum file capacity
|
|
||||||
static bool ncl_tmpEnsureCap(ncl_TmpFile *file, size_t minCap) {
|
|
||||||
if(!file->isFile) return false;
|
|
||||||
if(minCap > NCL_MAXFILESIZE) return false;
|
|
||||||
if(file->cap < minCap) {
|
|
||||||
size_t newCap = file->cap;
|
|
||||||
while(newCap < minCap) newCap *= 2;
|
|
||||||
if(newCap > NCL_MAXFILESIZE) return false;
|
|
||||||
char *code = nn_realloc(file->ctx, file->code, file->cap, newCap);
|
|
||||||
if(code == NULL) return false;
|
|
||||||
file->code = code;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ncl_TmpFile *ncl_tmpGet(ncl_TmpFile *root, const char *path) {
|
|
||||||
if(root->isFile) return NULL;
|
|
||||||
char pathbuf[NN_MAX_PATH];
|
|
||||||
strncpy(pathbuf, path, NN_MAX_PATH-1);
|
|
||||||
|
|
||||||
if(pathbuf[0] == '\0') {
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
char *endOfParent = strchr(pathbuf, '/');
|
|
||||||
if(endOfParent == NULL) {
|
|
||||||
for(size_t i = 0; i < root->size; i++) {
|
|
||||||
if(strcmp(root->files[i]->name, pathbuf) == 0) return root->files[i];
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
*endOfParent = '\0';
|
|
||||||
const char *subpath = endOfParent + 1;
|
|
||||||
ncl_TmpFile *parent = ncl_tmpGet(root, pathbuf);
|
|
||||||
if(parent == NULL) return NULL;
|
|
||||||
return ncl_tmpGet(parent, subpath);
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t ncl_tmpSpaceUsedIn(ncl_TmpFile *f, size_t fileCost) {
|
|
||||||
if(f->isFile) return fileCost + f->size;
|
|
||||||
size_t spaceUsed = fileCost;
|
|
||||||
for(size_t i = 0; i < f->size; f++) {
|
|
||||||
spaceUsed += ncl_tmpSpaceUsedIn(f->files[i], fileCost);
|
|
||||||
}
|
|
||||||
return spaceUsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct ncl_TmpFS {
|
typedef struct ncl_TmpFS {
|
||||||
nn_Context *ctx;
|
nn_Context *ctx;
|
||||||
nn_Lock *lock;
|
nn_Lock *lock;
|
||||||
nn_Filesystem conf;
|
|
||||||
size_t usage;
|
|
||||||
size_t fileCost;
|
size_t fileCost;
|
||||||
size_t spaceUsed;
|
|
||||||
bool isReadonly;
|
bool isReadonly;
|
||||||
ncl_TmpFileDesc *fds[NN_MAX_OPENFILES];
|
ncl_TmpFildes fds[NN_MAX_OPENFILES];
|
||||||
|
size_t usage;
|
||||||
|
nn_Filesystem conf;
|
||||||
ncl_TmpFile *root;
|
ncl_TmpFile *root;
|
||||||
char label[NN_MAX_LABEL];
|
size_t spaceUsed;
|
||||||
size_t labellen;
|
size_t labellen;
|
||||||
|
char label[NN_MAX_LABEL];
|
||||||
} ncl_TmpFS;
|
} ncl_TmpFS;
|
||||||
|
|
||||||
static size_t ncl_tmpSpaceUsedCached(ncl_TmpFS *tmpfs) {
|
ncl_TmpFile *ncl_tmpGet(ncl_TmpFile *root, const char *path) {
|
||||||
if(tmpfs->spaceUsed == 0) {
|
if(root->isFile) return NULL;
|
||||||
tmpfs->spaceUsed = ncl_tmpSpaceUsedIn(tmpfs->root, tmpfs->fileCost);
|
if(path[0] == '\0') return root;
|
||||||
if(tmpfs->spaceUsed > tmpfs->conf.spaceTotal) tmpfs->spaceUsed = tmpfs->conf.spaceTotal;
|
ncl_TmpFile *iter = root->files;
|
||||||
|
size_t l = ncl_segmentLen(path);
|
||||||
|
while(iter) {
|
||||||
|
if(strncmp(iter->name, path, l) == 0 && iter->name[l] == '\0') {
|
||||||
|
// final one
|
||||||
|
if(path[l] == '\0') return iter;
|
||||||
|
return ncl_tmpGet(iter, path + l + 1);
|
||||||
|
}
|
||||||
|
iter = iter->next;
|
||||||
}
|
}
|
||||||
return tmpfs->spaceUsed;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t ncl_tmpSpaceFree(ncl_TmpFS *tmpfs) {
|
ncl_TmpFile *ncl_tmpAllocFile(nn_Context *ctx, const char *name, bool isFile) {
|
||||||
return tmpfs->conf.spaceTotal - ncl_tmpSpaceUsedCached(tmpfs);
|
ncl_TmpFile *f = nn_alloc(ctx, sizeof(*f));
|
||||||
|
if(f == NULL) return NULL;
|
||||||
|
f->name = nn_strdup(ctx, name);
|
||||||
|
if(f->name == NULL) {
|
||||||
|
nn_free(ctx, f, sizeof(*f));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
f->isFile = isFile;
|
||||||
|
if(isFile) {
|
||||||
|
f->data = NULL;
|
||||||
|
f->datalen = 0;
|
||||||
|
} else {
|
||||||
|
f->files = NULL;
|
||||||
|
}
|
||||||
|
f->next = NULL;
|
||||||
|
f->openHandles = 0;
|
||||||
|
f->parent = NULL;
|
||||||
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ncl_tmpMkdir(ncl_TmpFile *root, const char *path) {
|
void ncl_tmpFreeFile(nn_Context *ctx, ncl_TmpFile *f) {
|
||||||
if(root->isFile) return false;
|
if(f->isFile) {
|
||||||
printf("tmpfs mkdir: %s\n", path);
|
nn_free(ctx, f->data, f->datalen);
|
||||||
char parent[NN_MAX_PATH];
|
} else {
|
||||||
char name[NN_MAX_PATH];
|
ncl_TmpFile *iter = f->files;
|
||||||
ncl_splitParentName(path, parent, name);
|
while(iter) {
|
||||||
|
ncl_TmpFile *cur = iter;
|
||||||
|
iter = iter->next;
|
||||||
|
ncl_tmpFreeFile(ctx, cur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nn_strfree(ctx, f->name);
|
||||||
|
nn_free(ctx, f, sizeof(*f));
|
||||||
|
}
|
||||||
|
|
||||||
// non-recursive for now
|
size_t ncl_tmpSpaceUsedIn(ncl_TmpFS *fs, ncl_TmpFile *f) {
|
||||||
ncl_TmpFile *dir = ncl_tmpGet(root, path);
|
if(f->isFile) return fs->fileCost + f->datalen;
|
||||||
if(dir == NULL) return false; // TODO: mkdir
|
size_t space = fs->fileCost;
|
||||||
if(dir->isFile) return false;
|
ncl_TmpFile *iter = f->files;
|
||||||
|
while(iter) {
|
||||||
|
space += ncl_tmpSpaceUsedIn(fs, iter);
|
||||||
|
iter = iter->next;
|
||||||
|
}
|
||||||
|
return space;
|
||||||
|
}
|
||||||
|
|
||||||
ncl_TmpFile *f = ncl_allocTmpFile(root->ctx, name, false);
|
size_t ncl_tmpSpaceUsed(ncl_TmpFS *fs) {
|
||||||
if(!ncl_addTmpFile(dir, f)) {
|
if(fs->spaceUsed == 0) fs->spaceUsed = ncl_tmpSpaceUsedIn(fs, fs->root);
|
||||||
return false;
|
return fs->spaceUsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ncl_findTmpFildes(ncl_TmpFS *fs) {
|
||||||
|
for(size_t i = 0; i < NN_MAX_OPENFILES; i++) {
|
||||||
|
if(fs->fds[i].file == NULL) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NULL on success
|
||||||
|
const char *ncl_tmpMkdir(ncl_TmpFS *fs, ncl_TmpFile *root, const char *path) {
|
||||||
|
if(root->isFile) return "is a file";
|
||||||
|
if(path[0] == '\0') return NULL;
|
||||||
|
ncl_TmpFile *iter = root->files;
|
||||||
|
size_t l = ncl_segmentLen(path);
|
||||||
|
while(iter) {
|
||||||
|
if(strncmp(iter->name, path, l) == 0 && iter->name[l] == '\0') {
|
||||||
|
// final one
|
||||||
|
if(path[l] == '\0') return NULL;
|
||||||
|
return ncl_tmpMkdir(fs, iter, path + l + 1);
|
||||||
|
}
|
||||||
|
iter = iter->next;
|
||||||
|
}
|
||||||
|
if(fs->conf.spaceTotal - ncl_tmpSpaceUsed(fs) < fs->fileCost) {
|
||||||
|
return "out of space";
|
||||||
|
}
|
||||||
|
// shi, we gotta actually make shit
|
||||||
|
char dirname[NN_MAX_PATH];
|
||||||
|
memcpy(dirname, path, l);
|
||||||
|
dirname[l] = '\0';
|
||||||
|
ncl_TmpFile *dir = ncl_tmpAllocFile(fs->ctx, dirname, false);
|
||||||
|
if(dir == NULL) return "out of memory";
|
||||||
|
dir->parent = root;
|
||||||
|
dir->next = root->files;
|
||||||
|
root->files = dir;
|
||||||
|
fs->spaceUsed += fs->fileCost;
|
||||||
|
// final one
|
||||||
|
if(path[l] == '\0') return NULL;
|
||||||
|
return ncl_tmpMkdir(fs, dir, path + l + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ncl_tmpCanRemove(ncl_TmpFile *f) {
|
||||||
|
if(f->openHandles > 0) return false;
|
||||||
|
if(!f->isFile) {
|
||||||
|
ncl_TmpFile *iter = f->files;
|
||||||
|
while(iter) {
|
||||||
|
if(!ncl_tmpCanRemove(iter)) return false;
|
||||||
|
iter = iter->next;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ncl_tmpRemoveEnt(ncl_TmpFile *dir, ncl_TmpFile *ent) {
|
||||||
|
ncl_TmpFile **pIter = &dir->files;
|
||||||
|
while(*pIter) {
|
||||||
|
if(*pIter == ent) {
|
||||||
|
*pIter = ent->next;
|
||||||
|
ent->next = NULL;
|
||||||
|
ent->parent = NULL;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
pIter = &ent->next;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: check filedesc types, in case of a fuzzing attack
|
||||||
static nn_Exit ncl_tmpfsHandler(nn_FSRequest *req) {
|
static nn_Exit ncl_tmpfsHandler(nn_FSRequest *req) {
|
||||||
nn_Context *ctx = req->ctx;
|
nn_Context *ctx = req->ctx;
|
||||||
nn_Computer *C = req->computer;
|
nn_Computer *C = req->computer;
|
||||||
ncl_TmpFS *tmpfs = req->state;
|
ncl_TmpFS *tmpfs = req->state;
|
||||||
if(req->action == NN_FS_DROP) {
|
if(req->action == NN_FS_DROP) {
|
||||||
ncl_freeTmpFile(tmpfs->root);
|
ncl_tmpFreeFile(ctx, tmpfs->root);
|
||||||
for(size_t i = 0; i < NN_MAX_OPENFILES; i++) {
|
|
||||||
ncl_TmpFileDesc *desc = tmpfs->fds[i];
|
|
||||||
if(desc != NULL) {
|
|
||||||
nn_free(ctx, desc, sizeof(*desc));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nn_destroyLock(ctx, tmpfs->lock);
|
nn_destroyLock(ctx, tmpfs->lock);
|
||||||
nn_free(ctx, tmpfs, sizeof(*tmpfs));
|
nn_free(ctx, tmpfs, sizeof(*tmpfs));
|
||||||
return NN_OK;
|
return NN_OK;
|
||||||
}
|
}
|
||||||
if(req->action == NN_FS_SPACEUSED) {
|
if(req->action == NN_FS_SPACEUSED) {
|
||||||
nn_lock(ctx, tmpfs->lock);
|
nn_lock(ctx, tmpfs->lock);
|
||||||
req->spaceUsed = ncl_tmpSpaceUsedCached(tmpfs);
|
req->spaceUsed = ncl_tmpSpaceUsed(tmpfs);
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
return NN_OK;
|
return NN_OK;
|
||||||
}
|
}
|
||||||
if(req->action == NN_FS_GETLABEL) {
|
if(req->action == NN_FS_GETLABEL) {
|
||||||
nn_lock(ctx, tmpfs->lock);
|
nn_lock(ctx, tmpfs->lock);
|
||||||
memcpy(req->getlabel.buf, tmpfs->label, tmpfs->labellen);
|
|
||||||
req->getlabel.len = tmpfs->labellen;
|
req->getlabel.len = tmpfs->labellen;
|
||||||
|
memcpy(req->getlabel.buf, tmpfs->label, tmpfs->labellen);
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
return NN_OK;
|
return NN_OK;
|
||||||
}
|
}
|
||||||
if(req->action == NN_FS_SETLABEL) {
|
if(req->action == NN_FS_SETLABEL) {
|
||||||
nn_lock(ctx, tmpfs->lock);
|
nn_lock(ctx, tmpfs->lock);
|
||||||
if(req->setlabel.len > NN_MAX_LABEL) req->setlabel.len = NN_MAX_LABEL;
|
|
||||||
memcpy(tmpfs->label, req->setlabel.buf, req->setlabel.len);
|
|
||||||
tmpfs->labellen = req->setlabel.len;
|
tmpfs->labellen = req->setlabel.len;
|
||||||
|
memcpy(tmpfs->label, req->setlabel.buf, tmpfs->labellen);
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
return NN_OK;
|
return NN_OK;
|
||||||
}
|
}
|
||||||
if(req->action == NN_FS_ISRO) {
|
if(req->action == NN_FS_OPENDIR) {
|
||||||
nn_lock(ctx, tmpfs->lock);
|
nn_lock(ctx, tmpfs->lock);
|
||||||
req->isReadonly = tmpfs->isReadonly;
|
tmpfs->usage++;
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
int fd = ncl_findTmpFildes(tmpfs);
|
||||||
return NN_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(req->action == NN_FS_OPEN) {
|
|
||||||
nn_lock(ctx, tmpfs->lock);
|
|
||||||
char mode = req->open.mode[0];
|
|
||||||
if(mode != 'w' && mode != 'a') mode = 'r';
|
|
||||||
int fd = ncl_findFileDesc((void **)tmpfs->fds);
|
|
||||||
if(fd < 0) {
|
if(fd < 0) {
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
nn_setError(C, "too many files open at once");
|
nn_setError(C, "too many file descriptors");
|
||||||
return NN_EBADCALL;
|
return NN_EBADCALL;
|
||||||
}
|
}
|
||||||
if(mode != 'r' && tmpfs->isReadonly) {
|
ncl_TmpFile *dir = ncl_tmpGet(tmpfs->root, req->opendir);
|
||||||
|
if(dir == NULL) {
|
||||||
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
|
nn_setError(C, req->opendir);
|
||||||
|
return NN_EBADCALL;
|
||||||
|
}
|
||||||
|
if(dir->isFile) {
|
||||||
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
|
nn_setError(C, "not a directory");
|
||||||
|
return NN_EBADCALL;
|
||||||
|
}
|
||||||
|
dir->openHandles++;
|
||||||
|
tmpfs->fds[fd].file = dir;
|
||||||
|
tmpfs->fds[fd].mode = 'r';
|
||||||
|
tmpfs->fds[fd].curEnt = dir->files;
|
||||||
|
req->fd = fd;
|
||||||
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
|
return NN_OK;
|
||||||
|
}
|
||||||
|
if(req->action == NN_FS_CLOSEDIR || req->action == NN_FS_CLOSE) {
|
||||||
|
int fd = req->fd;
|
||||||
|
if(fd < 0 || fd >= NN_MAX_OPENFILES) {
|
||||||
|
nn_setError(C, "bad file descriptor");
|
||||||
|
return NN_EBADCALL;
|
||||||
|
}
|
||||||
|
nn_lock(ctx, tmpfs->lock);
|
||||||
|
if(tmpfs->fds[fd].file == NULL) {
|
||||||
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
|
nn_setError(C, "bad file descriptor");
|
||||||
|
return NN_EBADCALL;
|
||||||
|
}
|
||||||
|
tmpfs->fds[fd].file->openHandles--;
|
||||||
|
tmpfs->fds[fd].file = NULL;
|
||||||
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
|
return NN_OK;
|
||||||
|
}
|
||||||
|
if(req->action == NN_FS_READDIR) {
|
||||||
|
int fd = req->fd;
|
||||||
|
if(fd < 0 || fd >= NN_MAX_OPENFILES) {
|
||||||
|
nn_setError(C, "bad file descriptor");
|
||||||
|
return NN_EBADCALL;
|
||||||
|
}
|
||||||
|
nn_lock(ctx, tmpfs->lock);
|
||||||
|
ncl_TmpFildes *fildes = &tmpfs->fds[fd];
|
||||||
|
if(fildes->file == NULL) {
|
||||||
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
|
nn_setError(C, "bad file descriptor");
|
||||||
|
return NN_EBADCALL;
|
||||||
|
}
|
||||||
|
if(fildes->curEnt) {
|
||||||
|
if(fildes->curEnt->isFile) {
|
||||||
|
snprintf(req->readdir.buf, req->readdir.len, "%s", fildes->curEnt->name);
|
||||||
|
} else {
|
||||||
|
snprintf(req->readdir.buf, req->readdir.len, "%s/", fildes->curEnt->name);
|
||||||
|
}
|
||||||
|
req->readdir.len = strlen(req->readdir.buf);
|
||||||
|
fildes->curEnt = fildes->curEnt->next;
|
||||||
|
} else {
|
||||||
|
req->readdir.buf = NULL;
|
||||||
|
}
|
||||||
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
|
return NN_OK;
|
||||||
|
}
|
||||||
|
if(req->action == NN_FS_STAT) {
|
||||||
|
nn_lock(ctx, tmpfs->lock);
|
||||||
|
tmpfs->usage++;
|
||||||
|
ncl_TmpFile *f = ncl_tmpGet(tmpfs->root, req->stat.path);
|
||||||
|
if(f) {
|
||||||
|
req->stat.isDirectory = !f->isFile;
|
||||||
|
req->stat.size = f->isFile ? f->datalen : 0;
|
||||||
|
// TODO: tmp mtime
|
||||||
|
req->stat.lastModified = 0;
|
||||||
|
} else {
|
||||||
|
req->stat.path = NULL;
|
||||||
|
}
|
||||||
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
|
return NN_OK;
|
||||||
|
}
|
||||||
|
if(req->action == NN_FS_OPEN) {
|
||||||
|
nn_lock(ctx, tmpfs->lock);
|
||||||
|
tmpfs->usage++;
|
||||||
|
int fd = ncl_findTmpFildes(tmpfs);
|
||||||
|
if(fd < 0) {
|
||||||
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
|
nn_setError(C, "too many file descriptors");
|
||||||
|
return NN_EBADCALL;
|
||||||
|
}
|
||||||
|
const char *mode = req->open.mode;
|
||||||
|
if(mode[0] != 'r' && tmpfs->isReadonly) {
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
nn_setError(C, "is readonly");
|
nn_setError(C, "is readonly");
|
||||||
return NN_EBADCALL;
|
return NN_EBADCALL;
|
||||||
}
|
}
|
||||||
ncl_TmpFile *f = ncl_tmpGet(tmpfs->root, req->open.path);
|
ncl_TmpFile *f = ncl_tmpGet(tmpfs->root, req->open.path);
|
||||||
if(f == NULL) {
|
if(f == NULL) {
|
||||||
if(mode == 'r') {
|
if(mode[0] == 'r') {
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
nn_setError(C, req->open.path);
|
nn_setError(C, req->open.path);
|
||||||
return NN_EBADCALL;
|
return NN_EBADCALL;
|
||||||
}
|
}
|
||||||
if(ncl_tmpSpaceFree(tmpfs) < tmpfs->fileCost) {
|
if(ncl_tmpSpaceUsed(tmpfs) + tmpfs->fileCost > tmpfs->conf.spaceTotal) {
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
nn_setError(C, "out of space");
|
nn_setError(C, "out of space");
|
||||||
return NN_EBADCALL;
|
return NN_EBADCALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
char parent[NN_MAX_PATH];
|
char parent[NN_MAX_PATH];
|
||||||
char name[NN_MAX_PATH];
|
char name[NN_MAX_PATH];
|
||||||
ncl_splitParentName(req->open.path, parent, name);
|
ncl_splitParentName(req->open.path, parent, name);
|
||||||
|
|
||||||
ncl_TmpFile *dir = ncl_tmpGet(tmpfs->root, parent);
|
ncl_TmpFile *dir = ncl_tmpGet(tmpfs->root, parent);
|
||||||
if(dir == NULL) {
|
if(dir == NULL) {
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
nn_setError(C, req->open.path);
|
nn_setError(C, "no such directory");
|
||||||
return NN_EBADCALL;
|
return NN_EBADCALL;
|
||||||
}
|
}
|
||||||
if(dir->isFile) {
|
if(dir->isFile) {
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
nn_setError(C, "is a directory");
|
nn_setError(C, "not a directory");
|
||||||
return NN_EBADCALL;
|
return NN_EBADCALL;
|
||||||
}
|
}
|
||||||
f = ncl_allocTmpFile(ctx, name, true);
|
|
||||||
|
f = ncl_tmpAllocFile(ctx, name, true);
|
||||||
if(f == NULL) {
|
if(f == NULL) {
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
return NN_ENOMEM;
|
return NN_ENOMEM;
|
||||||
}
|
}
|
||||||
if(!ncl_addTmpFile(dir, f)) {
|
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
f->next = dir->files;
|
||||||
ncl_freeTmpFile(f);
|
f->parent = dir;
|
||||||
nn_setError(C, "too many directory entries");
|
dir->files = f;
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
tmpfs->spaceUsed += tmpfs->fileCost;
|
|
||||||
}
|
}
|
||||||
if(!f->isFile) {
|
if(!f->isFile) {
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
nn_setError(C, "is a directory");
|
nn_setError(C, "is a directory");
|
||||||
return NN_EBADCALL;
|
return NN_EBADCALL;
|
||||||
}
|
}
|
||||||
if(mode == 'w') f->size = 0;
|
if(mode[0] == 'w') {
|
||||||
ncl_TmpFileDesc *desc = nn_alloc(tmpfs->ctx, sizeof(*desc));
|
tmpfs->spaceUsed -= f->datalen;
|
||||||
if(desc == NULL) {
|
nn_free(ctx, f->data, f->datalen);
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
f->data = NULL;
|
||||||
return NN_ENOMEM;
|
f->datalen = 0;
|
||||||
}
|
}
|
||||||
desc->f = f;
|
if(mode[0] != 'r') {
|
||||||
desc->mode = mode;
|
// modify mtime here ig
|
||||||
desc->off = mode == 'a' ? f->size : 0;
|
|
||||||
tmpfs->fds[fd] = desc;
|
|
||||||
f->openFD++;
|
|
||||||
req->fd = fd;
|
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
|
||||||
return NN_OK;
|
|
||||||
}
|
|
||||||
if(req->action == NN_FS_CLOSE || req->action == NN_FS_CLOSEDIR) {
|
|
||||||
nn_lock(ctx, tmpfs->lock);
|
|
||||||
ncl_TmpFileDesc *desc = ncl_getFile((void **)tmpfs->fds, req->fd);
|
|
||||||
if(desc == NULL) {
|
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
|
||||||
nn_setError(C, "bad file descriptor");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
}
|
||||||
desc->f->openFD--;
|
f->openHandles++;
|
||||||
nn_free(ctx, desc, sizeof(*desc));
|
tmpfs->fds[fd].file = f;
|
||||||
tmpfs->fds[req->fd] = NULL;
|
tmpfs->fds[fd].mode = mode[0];
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
tmpfs->fds[fd].offset = mode[0] == 'a' ? f->datalen : 0;
|
||||||
return NN_OK;
|
|
||||||
}
|
|
||||||
if(req->action == NN_FS_READ) {
|
|
||||||
nn_lock(ctx, tmpfs->lock);
|
|
||||||
ncl_TmpFileDesc *desc = ncl_getFile((void **)tmpfs->fds, req->fd);
|
|
||||||
if(desc == NULL) {
|
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
|
||||||
nn_setError(C, "bad file descriptor");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
if(!desc->f->isFile) {
|
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
|
||||||
nn_setError(C, "bad file descriptor");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
size_t remaining = desc->f->size - desc->off;
|
|
||||||
if(remaining == 0) {
|
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
|
||||||
req->read.buf = NULL;
|
|
||||||
return NN_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(req->read.len > remaining) req->read.len = remaining;
|
|
||||||
memcpy(req->read.buf, desc->f->code + desc->off, req->read.len);
|
|
||||||
desc->off += req->read.len;
|
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
return NN_OK;
|
return NN_OK;
|
||||||
}
|
}
|
||||||
if(req->action == NN_FS_WRITE) {
|
if(req->action == NN_FS_WRITE) {
|
||||||
|
int fd = req->fd;
|
||||||
|
if(fd < 0 || fd >= NN_MAX_OPENFILES) {
|
||||||
|
nn_setError(C, "bad file descriptor");
|
||||||
|
return NN_EBADCALL;
|
||||||
|
}
|
||||||
nn_lock(ctx, tmpfs->lock);
|
nn_lock(ctx, tmpfs->lock);
|
||||||
if(ncl_tmpSpaceFree(tmpfs) < req->write.len) {
|
tmpfs->usage++;
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
if(tmpfs->fds[fd].file == NULL) {
|
||||||
nn_setError(C, "out of space");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
ncl_TmpFileDesc *desc = ncl_getFile((void **)tmpfs->fds, req->fd);
|
|
||||||
if(desc == NULL) {
|
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
nn_setError(C, "bad file descriptor");
|
nn_setError(C, "bad file descriptor");
|
||||||
return NN_EBADCALL;
|
return NN_EBADCALL;
|
||||||
}
|
}
|
||||||
if(!desc->f->isFile) {
|
ncl_TmpFildes *fildes = &tmpfs->fds[fd];
|
||||||
|
size_t capNeeded = fildes->offset + req->write.len;
|
||||||
|
if(capNeeded > fildes->file->datalen) {
|
||||||
|
char *data = nn_realloc(ctx, fildes->file->data, fildes->file->datalen, capNeeded);
|
||||||
|
if(data == NULL) {
|
||||||
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
|
return NN_ENOMEM;
|
||||||
|
}
|
||||||
|
fildes->file->data = data;
|
||||||
|
fildes->file->datalen = capNeeded;
|
||||||
|
}
|
||||||
|
memcpy(fildes->file->data + fildes->offset, req->write.buf, req->write.len);
|
||||||
|
fildes->offset += req->write.len;
|
||||||
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
|
return NN_OK;
|
||||||
|
}
|
||||||
|
if(req->action == NN_FS_READ) {
|
||||||
|
int fd = req->fd;
|
||||||
|
if(fd < 0 || fd >= NN_MAX_OPENFILES) {
|
||||||
|
nn_setError(C, "bad file descriptor");
|
||||||
|
return NN_EBADCALL;
|
||||||
|
}
|
||||||
|
nn_lock(ctx, tmpfs->lock);
|
||||||
|
tmpfs->usage++;
|
||||||
|
if(tmpfs->fds[fd].file == NULL) {
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
nn_setError(C, "bad file descriptor");
|
nn_setError(C, "bad file descriptor");
|
||||||
return NN_EBADCALL;
|
return NN_EBADCALL;
|
||||||
}
|
}
|
||||||
size_t minSizeNeeded = desc->off + req->write.len;
|
ncl_TmpFildes *fildes = &tmpfs->fds[fd];
|
||||||
if(!ncl_tmpEnsureCap(desc->f, minSizeNeeded)) {
|
size_t size = fildes->file->datalen;
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
if(fildes->offset >= size) {
|
||||||
nn_setError(C, "file too big");
|
req->read.buf = NULL;
|
||||||
return NN_EBADCALL;
|
} else {
|
||||||
|
size_t read = req->read.len;
|
||||||
|
if(read+fildes->offset > size) read = size - fildes->offset;
|
||||||
|
memcpy(req->read.buf, fildes->file->data + fildes->offset, read);
|
||||||
|
req->read.len = read;
|
||||||
|
fildes->offset += read;
|
||||||
}
|
}
|
||||||
memcpy(desc->f->code + desc->off, req->write.buf, req->write.len);
|
|
||||||
if(desc->f->size < minSizeNeeded) desc->f->size = minSizeNeeded;
|
|
||||||
desc->off = minSizeNeeded;
|
|
||||||
tmpfs->spaceUsed = 0;
|
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
return NN_OK;
|
return NN_OK;
|
||||||
}
|
}
|
||||||
if(req->action == NN_FS_SEEK) {
|
if(req->action == NN_FS_SEEK) {
|
||||||
|
int fd = req->fd;
|
||||||
|
if(fd < 0 || fd >= NN_MAX_OPENFILES) {
|
||||||
|
nn_setError(C, "bad file descriptor");
|
||||||
|
return NN_EBADCALL;
|
||||||
|
}
|
||||||
nn_lock(ctx, tmpfs->lock);
|
nn_lock(ctx, tmpfs->lock);
|
||||||
ncl_TmpFileDesc *desc = ncl_getFile((void **)tmpfs->fds, req->fd);
|
tmpfs->usage++;
|
||||||
if(desc == NULL) {
|
if(tmpfs->fds[fd].file == NULL) {
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
nn_setError(C, "bad file descriptor");
|
nn_setError(C, "bad file descriptor");
|
||||||
return NN_EBADCALL;
|
return NN_EBADCALL;
|
||||||
}
|
}
|
||||||
if(!desc->f->isFile) {
|
ncl_TmpFildes *fildes = &tmpfs->fds[fd];
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
int cur = fildes->offset;
|
||||||
nn_setError(C, "bad file descriptor");
|
int off = req->seek.off;
|
||||||
return NN_EBADCALL;
|
size_t size = fildes->file->datalen;
|
||||||
}
|
nn_FSWhence whence = req->seek.whence;
|
||||||
intptr_t newOff = desc->off;
|
switch(whence) {
|
||||||
size_t size = desc->f->size;
|
|
||||||
|
|
||||||
switch(req->seek.whence) {
|
|
||||||
case NN_SEEK_SET:
|
case NN_SEEK_SET:
|
||||||
newOff = req->seek.off;
|
cur = off;
|
||||||
break;
|
break;
|
||||||
case NN_SEEK_CUR:
|
case NN_SEEK_CUR:
|
||||||
newOff += req->seek.off;
|
cur += off;
|
||||||
break;
|
break;
|
||||||
case NN_SEEK_END:
|
case NN_SEEK_END:
|
||||||
newOff = size - req->seek.off;
|
cur = size - off;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if(cur < 0) cur = 0;
|
||||||
if(newOff < 0) newOff = 0;
|
if(cur > size) cur = size;
|
||||||
if(newOff > size) newOff = size;
|
fildes->offset = cur;
|
||||||
desc->off = newOff;
|
req->seek.off = cur;
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
|
||||||
return NN_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(req->action == NN_FS_OPENDIR) {
|
|
||||||
nn_lock(ctx, tmpfs->lock);
|
|
||||||
int fd = ncl_findFileDesc((void **)tmpfs->fds);
|
|
||||||
if(fd < 0) {
|
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
|
||||||
nn_setError(C, "too many files open at once");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
ncl_TmpFile *f = ncl_tmpGet(tmpfs->root, req->opendir);
|
|
||||||
if(f == NULL) {
|
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
|
||||||
nn_setError(C, req->open.path);
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
if(f->isFile) {
|
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
|
||||||
nn_setError(C, "not a directory");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
ncl_TmpFileDesc *desc = nn_alloc(tmpfs->ctx, sizeof(*desc));
|
|
||||||
if(desc == NULL) {
|
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
|
||||||
return NN_ENOMEM;
|
|
||||||
}
|
|
||||||
desc->f = f;
|
|
||||||
desc->mode = 'd';
|
|
||||||
desc->off = 0;
|
|
||||||
tmpfs->fds[fd] = desc;
|
|
||||||
f->openFD++;
|
|
||||||
req->fd = fd;
|
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
|
||||||
return NN_OK;
|
|
||||||
}
|
|
||||||
if(req->action == NN_FS_READDIR) {
|
|
||||||
nn_lock(ctx, tmpfs->lock);
|
|
||||||
ncl_TmpFileDesc *desc = ncl_getFile((void **)tmpfs->fds, req->fd);
|
|
||||||
if(desc == NULL) {
|
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
|
||||||
nn_setError(C, "bad file descriptor");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
if(desc->f->isFile) {
|
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
|
||||||
nn_setError(C, "bad file descriptor");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
if(desc->off == desc->f->size) {
|
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
|
||||||
req->readdir.buf = NULL;
|
|
||||||
return NN_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
ncl_TmpFile *ent = desc->f->files[desc->off];
|
|
||||||
if(ent->isFile) {
|
|
||||||
snprintf(req->readdir.buf, req->readdir.len, "%s", ent->name);
|
|
||||||
} else {
|
|
||||||
snprintf(req->readdir.buf, req->readdir.len, "%s/", ent->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
req->readdir.len = strlen(req->readdir.buf);
|
|
||||||
desc->off++;
|
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
|
||||||
return NN_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(req->action == NN_FS_STAT) {
|
|
||||||
nn_lock(ctx, tmpfs->lock);
|
|
||||||
ncl_TmpFile *f = ncl_tmpGet(tmpfs->root, req->stat.path);
|
|
||||||
if(f == NULL) {
|
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
|
||||||
req->stat.path = NULL;
|
|
||||||
return NN_OK;
|
|
||||||
}
|
|
||||||
req->stat.isDirectory = !f->isFile;
|
|
||||||
req->stat.size = f->isFile ? f->size : 0;
|
|
||||||
// TODO: give nn_Context a way to get UNIX timestamp
|
|
||||||
req->stat.lastModified = 0;
|
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
return NN_OK;
|
return NN_OK;
|
||||||
}
|
}
|
||||||
if(req->action == NN_FS_MKDIR) {
|
if(req->action == NN_FS_MKDIR) {
|
||||||
nn_lock(ctx, tmpfs->lock);
|
nn_lock(ctx, tmpfs->lock);
|
||||||
ncl_tmpMkdir(tmpfs->root, req->mkdir);
|
tmpfs->usage++;
|
||||||
|
const char *err = ncl_tmpMkdir(tmpfs, tmpfs->root, req->mkdir);
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
return NN_OK;
|
if(err != NULL) nn_setError(C, err);
|
||||||
|
return err == NULL ? NN_OK : NN_EBADCALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(req->action == NN_FS_RENAME) {
|
if(req->action == NN_FS_RENAME) {
|
||||||
|
if(req->rename.from[0] == '\0') {
|
||||||
|
nn_setError(C, "root is forbidden");
|
||||||
|
return NN_EBADCALL;
|
||||||
|
}
|
||||||
|
if(req->rename.to != NULL && req->rename.to[0] == '\0') {
|
||||||
|
nn_setError(C, "root is forbidden");
|
||||||
|
return NN_EBADCALL;
|
||||||
|
}
|
||||||
nn_lock(ctx, tmpfs->lock);
|
nn_lock(ctx, tmpfs->lock);
|
||||||
const char *fromPath = req->rename.from;
|
tmpfs->usage++;
|
||||||
const char *toPath = req->rename.to;
|
if(req->rename.to == NULL) {
|
||||||
|
ncl_TmpFile *ripBro = ncl_tmpGet(tmpfs->root, req->rename.from);
|
||||||
if(toPath == NULL) {
|
if(ripBro == NULL) {
|
||||||
ncl_TmpFile *f = ncl_tmpGet(tmpfs->root, fromPath);
|
|
||||||
if(f == NULL) {
|
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
nn_setError(C, "no such file or directory");
|
nn_setError(C, req->rename.from);
|
||||||
return NN_EBADCALL;
|
return NN_EBADCALL;
|
||||||
}
|
}
|
||||||
if(f->parent == NULL) {
|
if(!ncl_tmpCanRemove(ripBro)) {
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
nn_setError(C, "root is forbidden");
|
nn_setError(C, "contents are pinned");
|
||||||
return NN_EBADCALL;
|
return NN_EBADCALL;
|
||||||
}
|
}
|
||||||
if(f->openFD > 0) {
|
if(!ncl_tmpRemoveEnt(ripBro->parent, ripBro)) {
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
nn_setError(C, "entry is in use");
|
nn_setError(C, "horrendously corrupted state, gg");
|
||||||
return NN_EBADCALL;
|
return NN_EBADCALL;
|
||||||
}
|
}
|
||||||
ncl_removeTmpFile(f->parent, f);
|
tmpfs->spaceUsed -= ncl_tmpSpaceUsedIn(tmpfs, ripBro);
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
ncl_tmpFreeFile(ctx, ripBro);
|
||||||
ncl_freeTmpFile(f);
|
|
||||||
return NN_OK;
|
|
||||||
}
|
}
|
||||||
nn_unlock(ctx, tmpfs->lock);
|
nn_unlock(ctx, tmpfs->lock);
|
||||||
nn_setError(C, "rename is not implemented yet");
|
return NN_OK;
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
}
|
||||||
if(C) nn_setError(C, "tmpfs: not implemented yet");
|
if(C) nn_setError(C, "tmpfs: not implemented yet");
|
||||||
return NN_EBADCALL;
|
return NN_EBADCALL;
|
||||||
@@ -1620,8 +1597,9 @@ nn_Component *ncl_createTmpFS(nn_Universe *universe, const char *address, const
|
|||||||
nn_free(ctx, state, sizeof(*state));
|
nn_free(ctx, state, sizeof(*state));
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
state->root = ncl_allocTmpFile(ctx, "", false);
|
state->root = ncl_tmpAllocFile(ctx, "", false);
|
||||||
if(state->root == NULL) {
|
if(state->root == NULL) {
|
||||||
|
nn_destroyLock(ctx, state->lock);
|
||||||
nn_free(ctx, state, sizeof(*state));
|
nn_free(ctx, state, sizeof(*state));
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -1632,11 +1610,11 @@ nn_Component *ncl_createTmpFS(nn_Universe *universe, const char *address, const
|
|||||||
state->labellen = 0;
|
state->labellen = 0;
|
||||||
state->spaceUsed = 0;
|
state->spaceUsed = 0;
|
||||||
for(size_t i = 0; i < NN_MAX_OPENFILES; i++) {
|
for(size_t i = 0; i < NN_MAX_OPENFILES; i++) {
|
||||||
state->fds[i] = NULL;
|
state->fds[i].file = NULL;
|
||||||
}
|
}
|
||||||
nn_Component *c = nn_createFilesystem(universe, address, fs, state, ncl_tmpfsHandler);
|
nn_Component *c = nn_createFilesystem(universe, address, fs, state, ncl_tmpfsHandler);
|
||||||
if(c == NULL) {
|
if(c == NULL) {
|
||||||
ncl_freeTmpFile(state->root);
|
ncl_tmpFreeFile(ctx, state->root);
|
||||||
nn_destroyLock(ctx, state->lock);
|
nn_destroyLock(ctx, state->lock);
|
||||||
nn_free(ctx, state, sizeof(*state));
|
nn_free(ctx, state, sizeof(*state));
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -2165,9 +2143,11 @@ static void ncl_setRealScreenPixel(ncl_ScreenState *state, int x, int y, ncl_Scr
|
|||||||
y--;
|
y--;
|
||||||
|
|
||||||
state->pixels[x + y * state->conf.maxWidth] = pixel;
|
state->pixels[x + y * state->conf.maxWidth] = pixel;
|
||||||
|
state->usage++;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ncl_recomputeScreen(const ncl_ScreenState *state) {
|
static void ncl_recomputeScreen(ncl_ScreenState *state) {
|
||||||
|
state->usage++;
|
||||||
for(int y = 1; y <= state->height; y++) {
|
for(int y = 1; y <= state->height; y++) {
|
||||||
for(int x = 1; x <= state->width; x++) {
|
for(int x = 1; x <= state->width; x++) {
|
||||||
ncl_ScreenPixel *pixel = ncl_getRealScreenPixelPointer(state, x, y);
|
ncl_ScreenPixel *pixel = ncl_getRealScreenPixelPointer(state, x, y);
|
||||||
@@ -2336,6 +2316,7 @@ nn_Component *ncl_createScreen(nn_Universe *universe, const char *address, const
|
|||||||
screen->viewportHeight = screen->height;
|
screen->viewportHeight = screen->height;
|
||||||
screen->keyboardCount = 0;
|
screen->keyboardCount = 0;
|
||||||
screen->brightness = 1;
|
screen->brightness = 1;
|
||||||
|
screen->usage = 0;
|
||||||
|
|
||||||
ncl_resetScreen(screen);
|
ncl_resetScreen(screen);
|
||||||
|
|
||||||
@@ -2589,6 +2570,7 @@ static nn_Exit ncl_gpuHandler(nn_GPURequest *req) {
|
|||||||
scr->palette[idx] = req->palette.color;
|
scr->palette[idx] = req->palette.color;
|
||||||
scr->resolvedPalette[idx] =
|
scr->resolvedPalette[idx] =
|
||||||
nn_mapDepth(req->palette.color, scr->depth);
|
nn_mapDepth(req->palette.color, scr->depth);
|
||||||
|
scr->usage++;
|
||||||
ncl_unlockScreen(scr);
|
ncl_unlockScreen(scr);
|
||||||
return NN_OK;
|
return NN_OK;
|
||||||
}
|
}
|
||||||
@@ -3542,6 +3524,7 @@ void ncl_statComponent(nn_Component *component, ncl_ComponentStat *stat) {
|
|||||||
if(strcmp(ty, NCL_SCREEN) == 0) {
|
if(strcmp(ty, NCL_SCREEN) == 0) {
|
||||||
ncl_ScreenState *screen = state;
|
ncl_ScreenState *screen = state;
|
||||||
nn_lock(screen->ctx, screen->lock);
|
nn_lock(screen->ctx, screen->lock);
|
||||||
|
stat->usageCounter = screen->usage;
|
||||||
stat->screen.conf = &screen->conf;
|
stat->screen.conf = &screen->conf;
|
||||||
stat->screen.depth = screen->depth;
|
stat->screen.depth = screen->depth;
|
||||||
stat->screen.flags = screen->flags;
|
stat->screen.flags = screen->flags;
|
||||||
|
|||||||
@@ -1074,6 +1074,7 @@ struct nn_Computer {
|
|||||||
nn_Universe *universe;
|
nn_Universe *universe;
|
||||||
nn_Environment env;
|
nn_Environment env;
|
||||||
nn_Lock *lock;
|
nn_Lock *lock;
|
||||||
|
nn_refc_t refc;
|
||||||
void *userdata;
|
void *userdata;
|
||||||
char *address;
|
char *address;
|
||||||
char *tmpaddress;
|
char *tmpaddress;
|
||||||
@@ -1176,6 +1177,7 @@ nn_Computer *nn_createComputer(nn_Universe *universe, void *userdata, const char
|
|||||||
c->state = NN_BOOTUP;
|
c->state = NN_BOOTUP;
|
||||||
c->universe = universe;
|
c->universe = universe;
|
||||||
c->userdata = userdata;
|
c->userdata = userdata;
|
||||||
|
c->refc = 1;
|
||||||
|
|
||||||
c->lock = nn_createLock(ctx);
|
c->lock = nn_createLock(ctx);
|
||||||
if(c->lock == NULL) {
|
if(c->lock == NULL) {
|
||||||
@@ -1233,6 +1235,14 @@ nn_Computer *nn_createComputer(nn_Universe *universe, void *userdata, const char
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nn_retainComputer(nn_Computer *computer) {
|
||||||
|
nn_retainComputerN(computer, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nn_retainComputerN(nn_Computer *computer, size_t n) {
|
||||||
|
nn_incRef(&computer->refc, n);
|
||||||
|
}
|
||||||
|
|
||||||
void nn_lockComputer(nn_Computer *computer) {
|
void nn_lockComputer(nn_Computer *computer) {
|
||||||
nn_lock(&computer->universe->ctx, computer->lock);
|
nn_lock(&computer->universe->ctx, computer->lock);
|
||||||
}
|
}
|
||||||
@@ -1430,7 +1440,22 @@ void nn_beepComputer(nn_Computer *computer, nn_Beep beep) {
|
|||||||
computer->env.handler(&req);
|
computer->env.handler(&req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nn_beepComputerMorse(nn_Computer *computer, nn_MorseBeep beep) {
|
||||||
|
if(beep.beepDuration < 0) beep.beepDuration = 0;
|
||||||
|
nn_EnvironmentRequest req;
|
||||||
|
req.userdata = computer->env.userdata;
|
||||||
|
req.computer = computer;
|
||||||
|
req.action = NN_ENV_BEEPMORSE;
|
||||||
|
req.morseBeep = beep;
|
||||||
|
computer->env.handler(&req);
|
||||||
|
}
|
||||||
|
|
||||||
void nn_destroyComputer(nn_Computer *computer) {
|
void nn_destroyComputer(nn_Computer *computer) {
|
||||||
|
nn_destroyComputerN(computer, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nn_destroyComputerN(nn_Computer *computer, size_t n) {
|
||||||
|
if(!nn_decRef(&computer->refc, n)) return;
|
||||||
nn_Context *ctx = &computer->universe->ctx;
|
nn_Context *ctx = &computer->universe->ctx;
|
||||||
nn_stopComputer(computer);
|
nn_stopComputer(computer);
|
||||||
|
|
||||||
|
|||||||
@@ -393,17 +393,36 @@ typedef struct nn_Architecture {
|
|||||||
extern size_t nn_ramSizes[8];
|
extern size_t nn_ramSizes[8];
|
||||||
|
|
||||||
typedef struct nn_Beep {
|
typedef struct nn_Beep {
|
||||||
|
// frequenc, in Hz
|
||||||
double frequency;
|
double frequency;
|
||||||
|
// duration, in seconds
|
||||||
double duration;
|
double duration;
|
||||||
|
// 0 is mute, 1 is 100%
|
||||||
double volume;
|
double volume;
|
||||||
} nn_Beep;
|
} nn_Beep;
|
||||||
|
|
||||||
|
// Morse beep, like a normal beep except it follows a morse code pattern.
|
||||||
|
// . is a short sound
|
||||||
|
// - is a long sound, 2x in length
|
||||||
|
// a space is a pause, 2x in length and dead silent.
|
||||||
|
// Every sound, including actual pauses, have a 50ms pause between them.
|
||||||
|
typedef struct nn_MorseBeep {
|
||||||
|
const char *pattern;
|
||||||
|
// frequency, in Hz
|
||||||
|
double frequency;
|
||||||
|
// duration of a ., in seconds
|
||||||
|
double beepDuration;
|
||||||
|
// 0 is mute, 1 is 100%
|
||||||
|
double volume;
|
||||||
|
} nn_MorseBeep;
|
||||||
|
|
||||||
typedef enum nn_EnvironmentAction {
|
typedef enum nn_EnvironmentAction {
|
||||||
NN_ENV_DRAWENERGY,
|
NN_ENV_DRAWENERGY,
|
||||||
NN_ENV_POWERON,
|
NN_ENV_POWERON,
|
||||||
NN_ENV_POWEROFF,
|
NN_ENV_POWEROFF,
|
||||||
NN_ENV_CRASHED,
|
NN_ENV_CRASHED,
|
||||||
NN_ENV_BEEP,
|
NN_ENV_BEEP,
|
||||||
|
NN_ENV_BEEPMORSE,
|
||||||
} nn_EnvironmentAction;
|
} nn_EnvironmentAction;
|
||||||
|
|
||||||
typedef struct nn_EnvironmentRequest {
|
typedef struct nn_EnvironmentRequest {
|
||||||
@@ -415,6 +434,8 @@ typedef struct nn_EnvironmentRequest {
|
|||||||
double energy;
|
double energy;
|
||||||
// for BEEP, information about the beep
|
// for BEEP, information about the beep
|
||||||
nn_Beep beep;
|
nn_Beep beep;
|
||||||
|
// for BEEPMORSE, information about the beep
|
||||||
|
nn_MorseBeep morseBeep;
|
||||||
};
|
};
|
||||||
} nn_EnvironmentRequest;
|
} nn_EnvironmentRequest;
|
||||||
|
|
||||||
@@ -435,8 +456,11 @@ typedef struct nn_Environment {
|
|||||||
// maxComponents and maxDevices determine how many components can be connected simultaneously to this computer, and how much device info can be
|
// maxComponents and maxDevices determine how many components can be connected simultaneously to this computer, and how much device info can be
|
||||||
// registered on this computer.
|
// registered on this computer.
|
||||||
nn_Computer *nn_createComputer(nn_Universe *universe, void *userdata, const char *address, size_t totalMemory, size_t maxComponents, size_t maxDevices);
|
nn_Computer *nn_createComputer(nn_Universe *universe, void *userdata, const char *address, size_t totalMemory, size_t maxComponents, size_t maxDevices);
|
||||||
|
void nn_retainComputer(nn_Computer *computer);
|
||||||
|
void nn_retainComputerN(nn_Computer *computer, size_t n);
|
||||||
// Destroys the state, effectively shutting down the computer.
|
// Destroys the state, effectively shutting down the computer.
|
||||||
void nn_destroyComputer(nn_Computer *computer);
|
void nn_destroyComputer(nn_Computer *computer);
|
||||||
|
void nn_destroyComputerN(nn_Computer *computer, size_t n);
|
||||||
void nn_lockComputer(nn_Computer *computer);
|
void nn_lockComputer(nn_Computer *computer);
|
||||||
void nn_unlockComputer(nn_Computer *computer);
|
void nn_unlockComputer(nn_Computer *computer);
|
||||||
// stops the computer if an architecture state is already present,
|
// stops the computer if an architecture state is already present,
|
||||||
@@ -517,6 +541,7 @@ void nn_clearCommonDeviceInfo(nn_CommonDeviceInfo *info);
|
|||||||
bool nn_removeDeviceInfo(nn_Computer *computer, const char *addr);
|
bool nn_removeDeviceInfo(nn_Computer *computer, const char *addr);
|
||||||
|
|
||||||
void nn_beepComputer(nn_Computer *computer, nn_Beep beep);
|
void nn_beepComputer(nn_Computer *computer, nn_Beep beep);
|
||||||
|
void nn_beepComputerMorse(nn_Computer *computer, nn_MorseBeep beep);
|
||||||
|
|
||||||
// get the userdata pointer
|
// get the userdata pointer
|
||||||
void *nn_getComputerUserdata(nn_Computer *computer);
|
void *nn_getComputerUserdata(nn_Computer *computer);
|
||||||
|
|||||||
Reference in New Issue
Block a user