diff options
author | Anton Kling <anton@kling.gg> | 2024-10-02 15:56:24 +0200 |
---|---|---|
committer | Anton Kling <anton@kling.gg> | 2024-10-02 16:12:29 +0200 |
commit | 5a7640af235a2068c233ce47a56d74defd4c2f0e (patch) | |
tree | 5afbb84519a46dc1129d4de7d93f65b56e4cb7e2 /userland | |
parent | 44848d566066dbea008eecf4c2b9916f051bad06 (diff) |
libc: Add faster scandir function(scandir_sane).
This function is significantly faster due to not requiring memory
allocations for each directory entry. Now they are instead just
allocated as a large chunk. This function is not compatible with scandir
since the cleanup of both function will be different.
With this a new function scandir_sane_free has also been added.
Besides having a function(like scandir) that forces the programmer to
implement their own cleanup procedure that is anymore complicated than a
simple free() call is absurd.
Diffstat (limited to 'userland')
-rw-r--r-- | userland/libc/dirent/scandir.c | 60 | ||||
-rw-r--r-- | userland/libc/include/stdlib.h | 2 | ||||
-rw-r--r-- | userland/minibox/utilities/ls.c | 1 |
3 files changed, 63 insertions, 0 deletions
diff --git a/userland/libc/dirent/scandir.c b/userland/libc/dirent/scandir.c index 564cbc8..3ac040a 100644 --- a/userland/libc/dirent/scandir.c +++ b/userland/libc/dirent/scandir.c @@ -1,12 +1,72 @@ #include <dirent.h> +#include <fcntl.h> #include <stdlib.h> #include <string.h> +#include <unistd.h> int nop_compar(const struct dirent **d1, const struct dirent **d2) { *d2 = *d1; return 0; } +// Differs from scandir in the sense that the namelist has its own way +// of allocating its entries(in this case it allocates it as a large +// chunk). As a result the usual free() operation can not be ran on +// the elements. +// +// Instead scandir_sane_free() should be used to deallocate the +// namelist. +int scandir_sane(const char *dir, struct dirent ***namelist, + int (*sel)(const struct dirent *), + int (*compar)(const struct dirent **, + const struct dirent **)) { + if (!compar) { + compar = nop_compar; + } + + int fd = open(dir, O_RDONLY); + if (-1 == fd) { + // TODO: Figure out what to set errno to + return -1; + } + + size_t capacity = 128; + size_t num_entries; + struct dirent *chunk = NULL; + for (;;) { + chunk = realloc(chunk, sizeof(struct dirent) * capacity); + int rc = pread(fd, chunk, sizeof(struct dirent) * capacity, 0); + if (-1 == rc) { + free(chunk); + // TODO: Figure out what to set errno to + return -1; + } + if (sizeof(struct dirent) * capacity == (size_t)rc) { + // The directory **may** contain more entries. + // Redo + capacity *= 2; + continue; + } + num_entries = rc / (sizeof(struct dirent)); + break; + } + + *namelist = allocarray(num_entries, sizeof(struct dirent *)); + + for (size_t i = 0; i < num_entries; i++) { + (*namelist)[i] = &chunk[i]; + } + + close(fd); + return num_entries; +} + +void scandir_sane_free(struct dirent **namelist) { + free(namelist[0]); // First pointer is the start of the chunk in the + // current implementation + free(namelist); +} + int scandir(const char *dir, struct dirent ***namelist, int (*sel)(const struct dirent *), int (*compar)(const struct dirent **, const struct dirent **)) { diff --git a/userland/libc/include/stdlib.h b/userland/libc/include/stdlib.h index 7e23035..a389e8c 100644 --- a/userland/libc/include/stdlib.h +++ b/userland/libc/include/stdlib.h @@ -10,6 +10,8 @@ typedef size_t size_t; // only for 32 bit void *malloc(size_t s); void *calloc(size_t nelem, size_t elsize); +void *allocarray(size_t nmemb, size_t size); +void *reallocarray(void *ptr, size_t nmemb, size_t size); void *realloc(void *ptr, size_t size); void free(void *p); char *getenv(const char *name); diff --git a/userland/minibox/utilities/ls.c b/userland/minibox/utilities/ls.c index bedc3a3..0ce70f8 100644 --- a/userland/minibox/utilities/ls.c +++ b/userland/minibox/utilities/ls.c @@ -40,5 +40,6 @@ int ls_main(int argc, char **argv) { printf("%s", namelist[i]->d_name); } putchar('\n'); + scandir_sane_free(namelist); return 0; } |