Skip to content

Instantly share code, notes, and snippets.

@CAFxX
Last active August 17, 2021 09:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CAFxX/0b0f378f3c0f15019a171141b4ad5404 to your computer and use it in GitHub Desktop.
Save CAFxX/0b0f378f3c0f15019a171141b4ad5404 to your computer and use it in GitHub Desktop.
// 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 = "<cafxx+fls@strayorange.com>";
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