mirror of
https://github.com/NeoFlock/neonucleus.git
synced 2025-09-24 09:03:32 +02:00
tmpfs is almost fully done
This commit is contained in:
parent
361c1b29e3
commit
bb61776b44
3
TODO.md
3
TODO.md
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
# Parity with Vanilla OC (only the stuff that makes sense for an emulator)
|
# Parity with Vanilla OC (only the stuff that makes sense for an emulator)
|
||||||
|
|
||||||
- in-memory version of `filesystem`
|
- support `rename` in tmpfs
|
||||||
- complete the GPU implementation (screen buffers and missing methods)
|
- complete the GPU implementation (screen buffers and missing methods)
|
||||||
- complete the screen implementation (bunch of missing methods)
|
- complete the screen implementation (bunch of missing methods)
|
||||||
- `hologram` component
|
- `hologram` component
|
||||||
@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
- Do a huge audit at some point
|
- Do a huge audit at some point
|
||||||
- `nn_unicode_charWidth` appears to be bugged, look into that.
|
- `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
|
# The extra components
|
||||||
|
|
||||||
|
@ -307,8 +307,20 @@ void nn_fs_rename(nn_filesystem *fs, nn_componentMethod *_, nn_component *compon
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: validate against cases such as a/b -> a or a -> a/b
|
||||||
|
|
||||||
nn_errorbuf_t err = "";
|
nn_errorbuf_t err = "";
|
||||||
nn_lock(&fs->ctx, fs->lock);
|
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_size_t movedCount = fs->table.rename(fs->table.userdata, canonicalFrom, canonicalTo, err);
|
||||||
nn_unlock(&fs->ctx, fs->lock);
|
nn_unlock(&fs->ctx, fs->lock);
|
||||||
if(!nn_error_isEmpty(err)) {
|
if(!nn_error_isEmpty(err)) {
|
||||||
@ -454,7 +466,7 @@ void nn_fs_open(nn_filesystem *fs, nn_componentMethod *_, nn_component *componen
|
|||||||
nn_errorbuf_t err = "";
|
nn_errorbuf_t err = "";
|
||||||
nn_lock(&fs->ctx, fs->lock);
|
nn_lock(&fs->ctx, fs->lock);
|
||||||
// technically wrongfully
|
// technically wrongfully
|
||||||
if(!fs->table.exists(fs->table.userdata, path, err)) {
|
if(!fs->table.exists(fs->table.userdata, canonical, err)) {
|
||||||
nn_fs_createCost(fs, 1, component);
|
nn_fs_createCost(fs, 1, component);
|
||||||
}
|
}
|
||||||
if(!nn_error_isEmpty(err)) {
|
if(!nn_error_isEmpty(err)) {
|
||||||
|
@ -83,7 +83,7 @@ nn_vfnode *nn_vf_allocDirectory(nn_vfilesystem *fs, const char *name) {
|
|||||||
.fs = fs,
|
.fs = fs,
|
||||||
.lastModified = nn_vf_now(fs),
|
.lastModified = nn_vf_now(fs),
|
||||||
.parent = NULL,
|
.parent = NULL,
|
||||||
.isDirectory = false,
|
.isDirectory = true,
|
||||||
.entries = buffer,
|
.entries = buffer,
|
||||||
.len = 0,
|
.len = 0,
|
||||||
.cap = fs->opts.maxDirEntries,
|
.cap = fs->opts.maxDirEntries,
|
||||||
@ -122,7 +122,7 @@ nn_size_t nn_vf_spaceUsedByNode(nn_vfnode *node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nn_vfnode *nn_vf_find(nn_vfnode *parent, const char *name) {
|
nn_vfnode *nn_vf_find(nn_vfnode *parent, const char *name) {
|
||||||
if(parent->isDirectory) return NULL;
|
if(!parent->isDirectory) return NULL;
|
||||||
for(nn_size_t i = 0; i < parent->len; i++) {
|
for(nn_size_t i = 0; i < parent->len; i++) {
|
||||||
nn_vfnode *entry = parent->entries[i];
|
nn_vfnode *entry = parent->entries[i];
|
||||||
|
|
||||||
@ -161,12 +161,18 @@ nn_size_t nn_vf_getIdealCapacity(nn_vfnode *file, nn_size_t spaceNeeded) {
|
|||||||
|
|
||||||
void nn_vf_clampHandlePosition(nn_vfhandle *handle) {
|
void nn_vf_clampHandlePosition(nn_vfhandle *handle) {
|
||||||
nn_size_t len = handle->node->len;
|
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 < 0) handle->position = 0;
|
||||||
if(handle->position > len) handle->position = len;
|
if(handle->position > len) handle->position = len;
|
||||||
}
|
}
|
||||||
|
|
||||||
nn_vfnode *nn_vf_resolvePathFromNode(nn_vfnode *node, const char *path) {
|
nn_vfnode *nn_vf_resolvePathFromNode(nn_vfnode *node, const char *path) {
|
||||||
if(path[0] == 0) return node;
|
if(path[0] == 0) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
char firstDirectory[NN_MAX_PATH];
|
char firstDirectory[NN_MAX_PATH];
|
||||||
char subpath[NN_MAX_PATH];
|
char subpath[NN_MAX_PATH];
|
||||||
if(nn_path_firstName(path, firstDirectory, subpath)) {
|
if(nn_path_firstName(path, firstDirectory, subpath)) {
|
||||||
@ -174,6 +180,8 @@ nn_vfnode *nn_vf_resolvePathFromNode(nn_vfnode *node, const char *path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nn_vfnode *dir = 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);
|
return nn_vf_resolvePathFromNode(dir, subpath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,6 +198,14 @@ nn_size_t nn_vf_countTree(nn_vfnode *node) {
|
|||||||
return n;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
// methods
|
// methods
|
||||||
|
|
||||||
void nn_vfs_deinit(nn_vfilesystem *fs) {
|
void nn_vfs_deinit(nn_vfilesystem *fs) {
|
||||||
@ -239,6 +255,10 @@ nn_size_t nn_vfs_remove(nn_vfilesystem *fs, const char *path, nn_errorbuf_t err)
|
|||||||
nn_error_write(err, "Unable to delete root");
|
nn_error_write(err, "Unable to delete root");
|
||||||
return 0;
|
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);
|
nn_size_t removed = nn_vf_countTree(node);
|
||||||
// it is super easy to delete a tree
|
// it is super easy to delete a tree
|
||||||
nn_size_t j = 0;
|
nn_size_t j = 0;
|
||||||
@ -283,17 +303,47 @@ nn_bool_t nn_vfs_isDirectory(nn_vfilesystem *fs, const char *path, nn_errorbuf_t
|
|||||||
return node->isDirectory;
|
return node->isDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
nn_bool_t nn_vfs_makeDirectory(nn_vfilesystem *fs, const char *path, nn_errorbuf_t err) {
|
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);
|
nn_vfnode *node = nn_vf_resolvePath(fs, path);
|
||||||
if(node != NULL) {
|
if(node != NULL) {
|
||||||
nn_error_write(err, "File already exists");
|
if(node->isDirectory) {
|
||||||
return 0;
|
return true;
|
||||||
|
}
|
||||||
|
nn_error_write(err, "Is a file");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
char name[NN_MAX_PATH];
|
char name[NN_MAX_PATH];
|
||||||
char parentPath[NN_MAX_PATH];
|
char parentPath[NN_MAX_PATH];
|
||||||
nn_path_lastName(path, name, parentPath);
|
nn_bool_t isRootDir = nn_path_lastName(path, name, parentPath);
|
||||||
nn_vfnode *parent = nn_vf_resolvePath(fs, path);
|
|
||||||
return false;
|
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) {
|
char **nn_vfs_list(nn_Alloc *alloc, nn_vfilesystem *fs, const char *path, nn_size_t *len, nn_errorbuf_t err) {
|
||||||
@ -321,8 +371,8 @@ char **nn_vfs_list(nn_Alloc *alloc, nn_vfilesystem *fs, const char *path, nn_siz
|
|||||||
s = nn_alloc(alloc, l + 2);
|
s = nn_alloc(alloc, l + 2);
|
||||||
if(s != NULL) {
|
if(s != NULL) {
|
||||||
nn_memcpy(s, entry->name, l);
|
nn_memcpy(s, entry->name, l);
|
||||||
entry->name[l] = '/';
|
s[l] = '/';
|
||||||
entry->name[l+1] = 0;
|
s[l+1] = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
s = nn_strdup(alloc, entry->name);
|
s = nn_strdup(alloc, entry->name);
|
||||||
@ -339,13 +389,117 @@ char **nn_vfs_list(nn_Alloc *alloc, nn_vfilesystem *fs, const char *path, nn_siz
|
|||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *nn_vfs_open(nn_vfilesystem *fs, const char *path, const char *mode, nn_errorbuf_t err) {
|
nn_vfhandle *nn_vfs_open(nn_vfilesystem *fs, const char *path, const char *mode, nn_errorbuf_t err) {
|
||||||
// TODO: complete
|
// TODO: complete
|
||||||
char m = mode[0];
|
char m = mode[0];
|
||||||
nn_vfmode fmode = NN_VFMODE_READ;
|
nn_vfmode fmode = NN_VFMODE_READ;
|
||||||
if(m == 'w') fmode = NN_VFMODE_WRITE;
|
if(m == 'w') fmode = NN_VFMODE_WRITE;
|
||||||
if(m == 'a') fmode = NN_VFMODE_APPEND;
|
if(m == 'a') fmode = NN_VFMODE_APPEND;
|
||||||
return NULL;
|
|
||||||
|
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_intptr_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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// main funciton
|
// main funciton
|
||||||
@ -353,9 +507,9 @@ void *nn_vfs_open(nn_vfilesystem *fs, const char *path, const char *mode, nn_err
|
|||||||
nn_filesystem *nn_volatileFilesystem(nn_Context *context, nn_vfilesystemOptions opts, nn_filesystemControl control) {
|
nn_filesystem *nn_volatileFilesystem(nn_Context *context, nn_vfilesystemOptions opts, nn_filesystemControl control) {
|
||||||
// TODO: handle OOM
|
// TODO: handle OOM
|
||||||
nn_vfilesystem *fs = nn_alloc(&context->allocator, sizeof(nn_vfilesystem));
|
nn_vfilesystem *fs = nn_alloc(&context->allocator, sizeof(nn_vfilesystem));
|
||||||
|
fs->ctx = *context;
|
||||||
nn_Clock c = fs->ctx.clock;
|
nn_Clock c = fs->ctx.clock;
|
||||||
double time = c.proc(c.userdata);
|
double time = c.proc(c.userdata);
|
||||||
fs->ctx = *context;
|
|
||||||
fs->birthday = time;
|
fs->birthday = time;
|
||||||
fs->opts = opts;
|
fs->opts = opts;
|
||||||
fs->root = nn_vf_allocDirectory(fs, "/");
|
fs->root = nn_vf_allocDirectory(fs, "/");
|
||||||
@ -369,10 +523,17 @@ nn_filesystem *nn_volatileFilesystem(nn_Context *context, nn_vfilesystemOptions
|
|||||||
.isReadOnly = (void *)nn_vfs_isReadOnly,
|
.isReadOnly = (void *)nn_vfs_isReadOnly,
|
||||||
.size = (void *)nn_vfs_size,
|
.size = (void *)nn_vfs_size,
|
||||||
.remove = (void *)nn_vfs_remove,
|
.remove = (void *)nn_vfs_remove,
|
||||||
|
.lastModified = (void *)nn_vfs_lastModified,
|
||||||
|
.rename = (void *)nn_vfs_rename,
|
||||||
.exists = (void *)nn_vfs_exists,
|
.exists = (void *)nn_vfs_exists,
|
||||||
.isDirectory = (void *)nn_vfs_isDirectory,
|
.isDirectory = (void *)nn_vfs_isDirectory,
|
||||||
.makeDirectory = (void *)nn_vfs_makeDirectory,
|
.makeDirectory = (void *)nn_vfs_makeDirectory,
|
||||||
.list = (void *)nn_vfs_list,
|
.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);
|
return nn_newFilesystem(context, table, control);
|
||||||
}
|
}
|
||||||
|
@ -673,6 +673,20 @@ int main() {
|
|||||||
nn_filesystem *genericFS = nn_newFilesystem(&ctx, genericFSTable, ne_fs_ctrl);
|
nn_filesystem *genericFS = nn_newFilesystem(&ctx, genericFSTable, ne_fs_ctrl);
|
||||||
nn_addFileSystem(computer, NULL, 1, genericFS);
|
nn_addFileSystem(computer, NULL, 1, genericFS);
|
||||||
|
|
||||||
|
nn_vfilesystemOptions tmpfsOpts = {
|
||||||
|
.isReadOnly = false,
|
||||||
|
.capacity = 64*1024,
|
||||||
|
.label = "tmpfs",
|
||||||
|
.labelLen = 5,
|
||||||
|
.creationTime = 0, // we are at the start of time
|
||||||
|
.maxDirEntries = 64,
|
||||||
|
};
|
||||||
|
|
||||||
|
nn_filesystem *tmpFS = nn_volatileFilesystem(&ctx, tmpfsOpts, ne_fs_ctrl);
|
||||||
|
nn_component *tmpfsComp = nn_addFileSystem(computer, NULL, 2, tmpFS);
|
||||||
|
|
||||||
|
nn_setTmpAddress(computer, nn_getComponentAddress(tmpfsComp));
|
||||||
|
|
||||||
nn_vdriveOptions vdriveOpts = {
|
nn_vdriveOptions vdriveOpts = {
|
||||||
.sectorSize = 512,
|
.sectorSize = 512,
|
||||||
.capacity = 1*1024*1024,
|
.capacity = 1*1024*1024,
|
||||||
|
@ -213,6 +213,7 @@ char *nn_strcpy(char *dest, const char *src);
|
|||||||
const char *nn_strchr(const char *str, int ch);
|
const char *nn_strchr(const char *str, int ch);
|
||||||
int nn_strcmp(const char *a, const char *b);
|
int nn_strcmp(const char *a, const char *b);
|
||||||
nn_size_t nn_strlen(const char *a);
|
nn_size_t nn_strlen(const char *a);
|
||||||
|
nn_bool_t nn_strbegin(const char *s, const char *prefix);
|
||||||
|
|
||||||
#ifndef NN_BAREMETAL
|
#ifndef NN_BAREMETAL
|
||||||
nn_Alloc nn_libcAllocator(void);
|
nn_Alloc nn_libcAllocator(void);
|
||||||
|
21
src/utils.c
21
src/utils.c
@ -176,7 +176,7 @@ static double nni_realTime(void) {
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static double nni_realTimeClock(void *_) {
|
double nni_realTimeClock(void *_) {
|
||||||
return nni_realTime();
|
return nni_realTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,6 +246,7 @@ void nn_memset(void *buf, unsigned char byte, nn_size_t len) {
|
|||||||
void nn_memcpy(void *dest, const void *src, nn_size_t len) {
|
void nn_memcpy(void *dest, const void *src, nn_size_t len) {
|
||||||
if(dest == NULL) return;
|
if(dest == NULL) return;
|
||||||
if(src == NULL) return;
|
if(src == NULL) return;
|
||||||
|
if(len == 0) return;
|
||||||
|
|
||||||
char *destBytes = dest;
|
char *destBytes = dest;
|
||||||
const char *srcBytes = src;
|
const char *srcBytes = src;
|
||||||
@ -301,6 +302,16 @@ nn_size_t nn_strlen(const char *a) {
|
|||||||
return 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) {
|
nn_bool_t nn_error_isEmpty(nn_errorbuf_t buf) {
|
||||||
if(buf == NULL) return true;
|
if(buf == NULL) return true;
|
||||||
return buf[0] == 0;
|
return buf[0] == 0;
|
||||||
@ -350,7 +361,7 @@ nn_bool_t nn_path_firstName(const char *path, char firstDirectory[NN_MAX_PATH],
|
|||||||
if(!nn_path_hasSlash(path)) {
|
if(!nn_path_hasSlash(path)) {
|
||||||
nn_strcpy(firstDirectory, path);
|
nn_strcpy(firstDirectory, path);
|
||||||
nn_strcpy(subpath, "");
|
nn_strcpy(subpath, "");
|
||||||
return false; // end
|
return true; // end
|
||||||
}
|
}
|
||||||
nn_size_t slash = nn_path_firstSlash(path);
|
nn_size_t slash = nn_path_firstSlash(path);
|
||||||
|
|
||||||
@ -358,7 +369,7 @@ nn_bool_t nn_path_firstName(const char *path, char firstDirectory[NN_MAX_PATH],
|
|||||||
firstDirectory[slash] = 0;
|
firstDirectory[slash] = 0;
|
||||||
|
|
||||||
nn_strcpy(subpath, path + slash + 1);
|
nn_strcpy(subpath, path + slash + 1);
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns whether it is the only name
|
// returns whether it is the only name
|
||||||
@ -366,7 +377,7 @@ nn_bool_t nn_path_lastName(const char *path, char name[NN_MAX_PATH], char parent
|
|||||||
if(!nn_path_hasSlash(path)) {
|
if(!nn_path_hasSlash(path)) {
|
||||||
nn_strcpy(name, path);
|
nn_strcpy(name, path);
|
||||||
nn_strcpy(parent, "");
|
nn_strcpy(parent, "");
|
||||||
return false; // end
|
return true; // end
|
||||||
}
|
}
|
||||||
|
|
||||||
nn_size_t slash = nn_path_lastSlash(path);
|
nn_size_t slash = nn_path_lastSlash(path);
|
||||||
@ -374,7 +385,7 @@ nn_bool_t nn_path_lastName(const char *path, char name[NN_MAX_PATH], char parent
|
|||||||
|
|
||||||
nn_memcpy(parent, path, slash);
|
nn_memcpy(parent, path, slash);
|
||||||
parent[slash] = 0;
|
parent[slash] = 0;
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *nn_path_illegal = "\"\\:*?<>|";
|
const char *nn_path_illegal = "\"\\:*?<>|";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user