mirror of
https://github.com/jarun/nnn.git
synced 2024-11-28 05:41:31 +00:00
An even faster nnn - no more copying file names!
nnn has been using `struct entry` to hold both file name as well as file info. The design forces file names to be copied in the following cases: - swaps during file sort (nnn uses quicksort) applied after all the matching files are read (in no particular order; good old readdir()) into memory. - swaps during manual filtering. nnn moves non-matching entries below so they are not encountered twice resulting in fast filtering. There were scopes for _massive_ improvements in this area. So I did what had to be done - decouple the file names from `struct entry` and use a separate struct to hold the names with the indices set when the files are added by opendir(). There was a hidden problem to this approach - nnn uses realloc() to allocate memory for file information and realloc() may move the original pointer in mem when it can't fit the new memory to the earlier pointer. To handle that the new algorithm tracks the change in memory location and re-adjusts the existing dir entry names when that happens; this too, without any copying! Though the results seem pretty clean from a theoretical point of view and early tests, we may uncover some bugs. However, the speed is just mind-blowing! And the binary size remains the same too!!!
This commit is contained in:
parent
683fa65578
commit
7a16440e1f
54
nnn.c
54
nnn.c
|
@ -188,13 +188,17 @@ typedef unsigned char uchar;
|
|||
|
||||
/* Directory entry */
|
||||
typedef struct entry {
|
||||
char name[NAME_MAX + 1];
|
||||
char *name;
|
||||
time_t t;
|
||||
off_t size;
|
||||
blkcnt_t blocks; /* number of 512B blocks allocated */
|
||||
mode_t mode;
|
||||
} *pEntry;
|
||||
|
||||
typedef struct {
|
||||
char pname[NAME_MAX + 1];
|
||||
} namebuf;
|
||||
|
||||
/* Bookmark */
|
||||
typedef struct {
|
||||
char *key;
|
||||
|
@ -221,6 +225,7 @@ typedef struct {
|
|||
static settings cfg = {0, 0, 0, 0, 0, 1, 1, 0, 0, 4};
|
||||
|
||||
static struct entry *dents;
|
||||
static namebuf *pnamebuf;
|
||||
static int ndents, cur, total_dents;
|
||||
static uint idle;
|
||||
static uint idletimeout;
|
||||
|
@ -881,27 +886,9 @@ fill(struct entry **dents, int (*filter)(regex_t *, char *), regex_t *re)
|
|||
dentp1 = &(*dents)[count];
|
||||
dentp2 = &(*dents)[ndents];
|
||||
|
||||
/* Copy count to tmp */
|
||||
xstrlcpy(_dent.name, dentp1->name, NAME_MAX);
|
||||
_dent.mode = dentp1->mode;
|
||||
_dent.t = dentp1->t;
|
||||
_dent.size = dentp1->size;
|
||||
_dent.blocks = dentp1->blocks;
|
||||
|
||||
/* Copy ndents - 1 to count */
|
||||
xstrlcpy(dentp1->name, dentp2->name, NAME_MAX);
|
||||
dentp1->mode = dentp2->mode;
|
||||
dentp1->t = dentp2->t;
|
||||
dentp1->size = dentp2->size;
|
||||
dentp1->blocks = dentp2->blocks;
|
||||
|
||||
/* Copy tmp to ndents - 1 */
|
||||
xstrlcpy(dentp2->name, _dent.name, NAME_MAX);
|
||||
dentp2->mode = _dent.mode;
|
||||
dentp2->t = _dent.t;
|
||||
dentp2->size = _dent.size;
|
||||
dentp2->blocks = _dent.blocks;
|
||||
|
||||
memcpy(&_dent, dentp1, sizeof(struct entry));
|
||||
memcpy(dentp1, dentp2, sizeof(struct entry));
|
||||
memcpy(dentp2, &_dent, sizeof(struct entry));
|
||||
--count;
|
||||
}
|
||||
|
||||
|
@ -1779,10 +1766,11 @@ dentfill(char *path, struct entry **dents,
|
|||
static DIR *dirp;
|
||||
static struct dirent *dp;
|
||||
static struct stat sb_path, sb;
|
||||
static int fd, n;
|
||||
static int fd, n, count;
|
||||
static char *namep;
|
||||
static ulong num_saved;
|
||||
static struct entry *dentp;
|
||||
static namebuf *pnb;
|
||||
|
||||
dirp = opendir(path);
|
||||
if (dirp == NULL)
|
||||
|
@ -1852,10 +1840,23 @@ dentfill(char *path, struct entry **dents,
|
|||
*dents = realloc(*dents, total_dents * sizeof(**dents));
|
||||
if (*dents == NULL)
|
||||
errexit();
|
||||
|
||||
pnb = pnamebuf;
|
||||
pnamebuf = (namebuf *)realloc(pnamebuf, total_dents * sizeof(namebuf));
|
||||
if (pnamebuf == NULL) {
|
||||
free(*dents);
|
||||
errexit();
|
||||
}
|
||||
|
||||
/* realloc() may result in memory move, we must re-adjust if that happens */
|
||||
if (pnb && pnb != pnamebuf)
|
||||
for (count = 0; count < n; ++count)
|
||||
(&(*dents)[count])->name = pnamebuf[count].pname;
|
||||
}
|
||||
|
||||
dentp = &(*dents)[n];
|
||||
xstrlcpy(dentp->name, namep, NAME_MAX);
|
||||
xstrlcpy(pnamebuf[n].pname, namep, NAME_MAX);
|
||||
dentp->name = pnamebuf[n].pname;
|
||||
|
||||
dentp->mode = sb.st_mode;
|
||||
dentp->t = sb.st_mtime;
|
||||
|
@ -1889,8 +1890,10 @@ dentfill(char *path, struct entry **dents,
|
|||
|
||||
/* Should never be null */
|
||||
if (closedir(dirp) == -1) {
|
||||
if (*dents)
|
||||
if (*dents) {
|
||||
free(pnamebuf);
|
||||
free(*dents);
|
||||
}
|
||||
errexit();
|
||||
}
|
||||
|
||||
|
@ -1900,6 +1903,7 @@ dentfill(char *path, struct entry **dents,
|
|||
static void
|
||||
dentfree(struct entry *dents)
|
||||
{
|
||||
free(pnamebuf);
|
||||
free(dents);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue