Last active
August 17, 2021 09:30
-
-
Save CAFxX/0b0f378f3c0f15019a171141b4ad5404 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// fast file list (linux) | |
// adapted from https://man7.org/linux/man-pages/man2/getdents.2.html | |
// gcc -O3 -march=native -o fls fls.c | |
#define _GNU_SOURCE | |
#include <dirent.h> /* Defines DT_* constants */ | |
#include <fcntl.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <unistd.h> | |
#include <stdlib.h> | |
#include <sys/stat.h> | |
#include <sys/syscall.h> | |
#include <malloc.h> | |
#include <argp.h> | |
#include <stdbool.h> | |
#include <string.h> | |
const char *argp_program_version = "fls 0.1"; | |
const char *argp_program_bug_address = "<[email protected]>"; | |
static char doc[] = "Fast file listing"; | |
static char args_doc[] = "[DIR]"; | |
static struct argp_option options[] = { | |
{ "noprinttype", 'n', 0, 0, "Do not print the file type"}, | |
{ "filtertype", 'f', "TYPE", 0, "Filter file type (rdfslbcu*, default *)"}, | |
{ "rsep", 's', "SEP", 0, "Row separator (default '\\n')"}, | |
{ "csep", 'c', "SEP", 0, "Column separator (default ' ')"}, | |
{ 0 } | |
}; | |
struct arguments { | |
char *dir; | |
bool printtype; | |
char filtertype; | |
char rsep; | |
char csep; | |
}; | |
#define handle_error(msg) \ | |
do { perror(msg); exit(EXIT_FAILURE); } while (0) | |
static error_t parse_opt(int key, char *arg, struct argp_state *state) { | |
struct arguments *arguments = state->input; | |
switch (key) { | |
case 'n': | |
arguments->printtype = false; | |
break; | |
case 'f': | |
if (strlen(arg) != 1) argp_error(state, "illegal file type"); | |
if (!strchr("rdfslbcu*", arg[0])) argp_error(state, "illegal file type"); | |
arguments->filtertype = arg[0]; | |
arguments->printtype = false; | |
break; | |
case 's': | |
if (strlen(arg) != 1) argp_error(state, "illegal row separator"); | |
arguments->rsep = arg[0]; | |
break; | |
case 'c': | |
if (strlen(arg) != 1) argp_error(state, "illegal column separator"); | |
arguments->csep = arg[0]; | |
break; | |
case ARGP_KEY_ARG: | |
if (state->arg_num > 0) | |
argp_usage (state); // Too many arguments. | |
arguments->dir = arg; | |
break; | |
default: | |
return ARGP_ERR_UNKNOWN; | |
} | |
return 0; | |
} | |
static struct argp argp = { options, parse_opt, args_doc, doc, 0, 0, 0 }; | |
struct linux_dirent64 { | |
ino64_t d_ino; /* 64-bit inode number */ | |
off64_t d_off; /* 64-bit offset to next structure */ | |
unsigned short d_reclen; /* Size of this dirent */ | |
unsigned char d_type; /* File type */ | |
char d_name[]; /* Filename (null-terminated) */ | |
}; | |
struct dirqueued { | |
char *dirname; | |
struct dirqueued *next; | |
}; | |
#define BUF_SIZE 64*1024*1024 | |
#define PIPE_SIZE 1*1024*1024 | |
#define OUTBUF_SIZE 512*1024 | |
int | |
main(int argc, char *argv[]) | |
{ | |
struct arguments arguments; | |
arguments.rsep = '\n'; | |
arguments.csep = ' '; | |
arguments.filtertype = '*'; | |
arguments.printtype = true; | |
arguments.dir = "."; | |
argp_parse(&argp, argc, argv, 0, 0, &arguments); | |
int fd = open(arguments.dir, O_RDONLY | O_DIRECTORY); | |
if (fd == -1) | |
handle_error("open"); | |
posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED | POSIX_FADV_SEQUENTIAL); | |
fcntl(fileno(stdout), F_SETPIPE_SZ, PIPE_SIZE); | |
char *outbuf = pvalloc(OUTBUF_SIZE); | |
if (outbuf) | |
setvbuf(stdout, outbuf, _IOFBF, OUTBUF_SIZE); | |
char *buf = pvalloc(BUF_SIZE); | |
if (!buf) | |
handle_error("malloc"); | |
for (;;) { | |
long nread = syscall(SYS_getdents64, fd, buf, BUF_SIZE); | |
if (nread == -1) | |
handle_error("getdents"); | |
if (nread == 0) | |
break; | |
for (long bpos = 0; bpos < nread;) { | |
struct linux_dirent64 *d = (struct linux_dirent64 *) (buf + bpos); | |
bpos += d->d_reclen; | |
char ft; | |
switch (d->d_type) { | |
case DT_REG: ft = 'r'; break; | |
case DT_DIR: ft = 'd'; break; | |
case DT_FIFO: ft = 'f'; break; | |
case DT_SOCK: ft = 's'; break; | |
case DT_LNK: ft = 'l'; break; | |
case DT_BLK: ft = 'b'; break; | |
case DT_CHR: ft = 'c'; break; | |
default: ft = 'u'; break; | |
} | |
if (arguments.filtertype != '*' && arguments.filtertype != ft) | |
continue; | |
if (arguments.printtype) { | |
fputc_unlocked(ft, stdout); | |
fputc_unlocked(arguments.csep, stdout); | |
} | |
fputs_unlocked(arguments.dir, stdout); | |
fputc_unlocked('/', stdout); | |
fputs_unlocked(d->d_name, stdout); | |
fputc_unlocked(arguments.rsep, stdout); | |
} | |
} | |
exit(EXIT_SUCCESS); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment