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/libc/dirent/scandir.c | |
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/libc/dirent/scandir.c')
-rw-r--r-- | userland/libc/dirent/scandir.c | 60 |
1 files changed, 60 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 **)) { |