commit fe9c1bbb3a51543b83eab9df08030fb66c2e5553
parent 63a254951da0feeb5b11c1da2de4cf37c31f4450
Author: NRK <nrk@disroot.org>
Date: Sun, 24 Jul 2022 16:10:28 +0600
icons: use a compact array
a lot of the extension use the same icon. this can be exploited via
having an array with all the unique icons and then storing a single byte
index into the unique array.
when using `O_EMOJI` this results in around ~1.7KiB drop in the total
table size. `O_NERD` and `O_ICONS` get roughly ~0.5KiB savings.
Diffstat:
3 files changed, 53 insertions(+), 23 deletions(-)
diff --git a/src/icons-hash.c b/src/icons-hash.c
@@ -152,6 +152,28 @@ main(void)
icon_max = MAX(icon_max, strlen(exec_icon.icon) + 1);
icon_max = MAX(icon_max, strlen(file_icon.icon) + 1);
+ const char *uniq[ARRLEN(icons_ext)] = {0};
+ size_t uniq_head = 0;
+ for (size_t i = 0; i < ARRLEN(icons_ext); ++i) {
+ if (icons_ext[i].icon[0] == 0) continue;
+ int isuniq = 1;
+ for (size_t k = 0; k < uniq_head; ++k) {
+ if (strcmp(uniq[k], icons_ext[i].icon) == 0) {
+ isuniq = 0;
+ break;
+ }
+ }
+ if (isuniq) {
+ assert(uniq_head < ARRLEN(uniq));
+ uniq[uniq_head++] = icons_ext[i].icon;
+ }
+ }
+ assert(uniq_head < (unsigned char)-1);
+
+ log("uniq icons: %6zu\n", uniq_head);
+ log("no-compact: %6zu bytes\n", ARRLEN(table) * icon_max);
+ log("compaction: %6zu bytes\n", uniq_head * icon_max + ARRLEN(table));
+
printf("#ifndef INCLUDE_ICONS_GENERATED\n");
printf("#define INCLUDE_ICONS_GENERATED\n\n");
@@ -167,12 +189,22 @@ main(void)
printf("struct icon_pair { const char match[%zu]; const char icon[%zu]; unsigned char color; };\n\n",
match_max, icon_max);
- printf("static const struct icon_pair icons_ext[%zu] = {\n", ARRLEN(table));
+ printf("static const char icons_ext_uniq[%zu][%zu] = {\n", uniq_head, icon_max);
+ for (size_t i = 0; i < uniq_head; ++i)
+ printf("\t\"%s\",\n", uniq[i]);
+ printf("};\n\n");
+
+ printf("static const struct {\n\tconst char match[%zu];"
+ "\n\tunsigned char idx;\n\tunsigned char color;\n} icons_ext[%zu] = {\n",
+ match_max, ARRLEN(table));
for (size_t i = 0; i < ARRLEN(table); ++i) {
if (table[i].icon == NULL || table[i].icon[0] == '\0') /* skip empty entries */
continue;
- printf("\t[%zu] = {\"%s\", \"%s\", %hhu },\n", i,
- table[i].match, table[i].icon, table[i].color);
+ int k;
+ for (k = 0; k < uniq_head; ++k) if (strcmp(table[i].icon, uniq[k]) == 0) break;
+ assert(k < uniq_head);
+ printf("\t[%3zu] = {\"%s\", %d, %hhu },\n",
+ i, table[i].match, k, table[i].color);
}
printf("};\n\n");
diff --git a/src/icons.h b/src/icons.h
@@ -167,9 +167,11 @@ static const unsigned char init_colors[] = { COLOR_LIST };
struct icon_pair { const char *match; const char *icon; unsigned char color; };
#endif
-static const struct icon_pair dir_icon = {"", ICON_DIRECTORY, 0};
-static const struct icon_pair file_icon = {"", ICON_FILE, 0};
-static const struct icon_pair exec_icon = {"", ICON_EXEC, 0};
+struct icon { const char *icon; unsigned char color; };
+
+static const struct icon dir_icon = {ICON_DIRECTORY, 0};
+static const struct icon file_icon = {ICON_FILE, 0};
+static const struct icon exec_icon = {ICON_EXEC, 0};
static const struct icon_pair icons_name[] = {
{".git", ICON_GIT, 0},
diff --git a/src/nnn.c b/src/nnn.c
@@ -3961,16 +3961,14 @@ static char *get_lsperms(mode_t mode)
}
#ifdef ICONS_ENABLED
-static const struct icon_pair *get_icon(const struct entry *ent)
+static struct icon get_icon(const struct entry *ent)
{
- ushort_t i = 0;
-
- for (; i < ELEMENTS(icons_name); ++i)
+ for (size_t i = 0; i < ELEMENTS(icons_name); ++i)
if (strcasecmp(ent->name, icons_name[i].match) == 0)
- return &icons_name[i];
+ return (struct icon){ icons_name[i].icon, icons_name[i].color };
if (ent->flags & DIR_OR_DIRLNK)
- return &dir_icon;
+ return dir_icon;
char *tmp = xextension(ent->name, ent->nlen);
@@ -3979,29 +3977,27 @@ static const struct icon_pair *get_icon(const struct entry *ent)
for (k = 0; k < ICONS_PROBE_MAX; ++k) {
z = (h + k) % ELEMENTS(icons_ext);
if (strcasecmp(tmp, icons_ext[z].match) == 0)
- return &icons_ext[z];
+ return (struct icon){ icons_ext_uniq[icons_ext[z].idx], icons_ext[z].color };
}
}
/* If there's no match and the file is executable, icon that */
if (ent->mode & 0100)
- return &exec_icon;
-
- return &file_icon;
+ return exec_icon;
+ return file_icon;
}
static void print_icon(const struct entry *ent, const int attrs)
{
- const struct icon_pair *picon = get_icon(ent);
-
+ const struct icon icon = get_icon(ent);
addstr(ICON_PADDING_LEFT);
- if (picon->color)
- attron(COLOR_PAIR(C_UND + 1 + picon->color));
+ if (icon.color)
+ attron(COLOR_PAIR(C_UND + 1 + icon.color));
else if (attrs)
attron(attrs);
- addstr(picon->icon);
- if (picon->color)
- attroff(COLOR_PAIR(C_UND + 1 + picon->color));
+ addstr(icon.icon);
+ if (icon.color)
+ attroff(COLOR_PAIR(C_UND + 1 + icon.color));
else if (attrs)
attroff(attrs);
addstr(ICON_PADDING_RIGHT);