summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Kling <anton@kling.gg>2024-10-02 15:56:24 +0200
committerAnton Kling <anton@kling.gg>2024-10-02 16:12:29 +0200
commit5a7640af235a2068c233ce47a56d74defd4c2f0e (patch)
tree5afbb84519a46dc1129d4de7d93f65b56e4cb7e2
parent44848d566066dbea008eecf4c2b9916f051bad06 (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.
-rw-r--r--include/dirent.h11
-rw-r--r--userland/libc/dirent/scandir.c60
-rw-r--r--userland/libc/include/stdlib.h2
-rw-r--r--userland/minibox/utilities/ls.c1
4 files changed, 72 insertions, 2 deletions
diff --git a/include/dirent.h b/include/dirent.h
index 53b3f70..b986ddb 100644
--- a/include/dirent.h
+++ b/include/dirent.h
@@ -7,7 +7,7 @@
#ifndef KERNEL
#include <fcntl.h>
#include <unistd.h>
-#endif
+#endif // KERNEL
struct dirent {
ino_t d_ino; // File serial number.
@@ -20,6 +20,7 @@ typedef struct {
int dir_num;
} DIR;
+#ifndef KERNEL
DIR *opendir(const char *dirname);
struct dirent *readdir(DIR *dir);
int closedir(DIR *dirp);
@@ -27,4 +28,10 @@ int alphasort(const struct dirent **d1, const struct dirent **d2);
int scandir(const char *dir, struct dirent ***namelist,
int (*sel)(const struct dirent *),
int (*compar)(const struct dirent **, const struct dirent **));
-#endif
+int scandir_sane(const char *dir, struct dirent ***namelist,
+ int (*sel)(const struct dirent *),
+ int (*compar)(const struct dirent **,
+ const struct dirent **));
+void scandir_sane_free(struct dirent **namelist);
+#endif // KERNEL
+#endif // DIRENT_H
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;
}