tagtree.c optimize get_tag()

store tag length to shortcut strcmp

this is nearly as fast as making a hash table using gperf

its not the hottest path but its
even slightly faster to shortcut
based on string length

    if (tagstr_len > match->len)
        continue;
    else if (tagstr_len < match->len)
        break;

but I found no measurable difference
I don't think its worth the extra constraint of keeping tags sorted

Change-Id: I4bb47cc6c5b8266d5f13c4ac78ae11d55ecb2d67
This commit is contained in:
William Wilgus 2023-02-08 07:41:22 -05:00 committed by William Wilgus
parent 23cb6139bd
commit 3bb75e0039

View file

@ -189,7 +189,8 @@ struct menu_root {
struct match struct match
{ {
const char* str; const char* str;
int symbol; uint16_t len;
uint16_t symbol;
}; };
/* Statusbar text of the current view. */ /* Statusbar text of the current view. */
@ -328,79 +329,93 @@ static int get_token_str(char *buf, int size)
static int get_tag(int *tag) static int get_tag(int *tag)
{ {
#define TAG_MATCH(str, tag) {str, sizeof(str) - 1, tag}
static const struct match get_tag_match[] = static const struct match get_tag_match[] =
{ {
{"album", tag_album}, TAG_MATCH("Lm", tag_virt_length_min),
{"artist", tag_artist}, TAG_MATCH("Ls", tag_virt_length_sec),
{"bitrate", tag_bitrate}, TAG_MATCH("Pm", tag_virt_playtime_min),
{"composer", tag_composer}, TAG_MATCH("Ps", tag_virt_playtime_sec),
{"comment", tag_comment}, TAG_MATCH("->", menu_next),
{"albumartist", tag_albumartist},
{"ensemble", tag_albumartist}, TAG_MATCH("==>", menu_load),
{"grouping", tag_grouping},
{"genre", tag_genre}, TAG_MATCH("year", tag_year),
{"length", tag_length},
{"Lm", tag_virt_length_min}, TAG_MATCH("album", tag_album),
{"Ls", tag_virt_length_sec}, TAG_MATCH("genre", tag_genre),
{"Pm", tag_virt_playtime_min}, TAG_MATCH("title", tag_title),
{"Ps", tag_virt_playtime_sec}, TAG_MATCH("%sort", var_sorttype),
{"title", tag_title},
{"filename", tag_filename}, TAG_MATCH("artist", tag_artist),
{"basename", tag_virt_basename}, TAG_MATCH("length", tag_length),
{"tracknum", tag_tracknumber}, TAG_MATCH("rating", tag_rating),
{"canonicalartist", tag_virt_canonicalartist}, TAG_MATCH("%limit", var_limit),
{"discnum", tag_discnumber}, TAG_MATCH("%strip", var_strip),
{"year", tag_year},
{"playcount", tag_playcount}, TAG_MATCH("bitrate", tag_bitrate),
{"rating", tag_rating}, TAG_MATCH("comment", tag_comment),
{"lastplayed", tag_lastplayed}, TAG_MATCH("discnum", tag_discnumber),
{"lastelapsed", tag_lastelapsed}, TAG_MATCH("%format", var_format),
{"lastoffset", tag_lastoffset}, TAG_MATCH("%reload", menu_reload),
{"commitid", tag_commitid},
{"entryage", tag_virt_entryage}, TAG_MATCH("filename", tag_filename),
{"autoscore", tag_virt_autoscore}, TAG_MATCH("basename", tag_virt_basename),
{"%sort", var_sorttype}, TAG_MATCH("tracknum", tag_tracknumber),
{"%limit", var_limit}, TAG_MATCH("composer", tag_composer),
{"%strip", var_strip}, TAG_MATCH("ensemble", tag_albumartist),
{"%menu_start", var_menu_start}, TAG_MATCH("grouping", tag_grouping),
{"%include", var_include}, TAG_MATCH("entryage", tag_virt_entryage),
{"%root_menu", var_rootmenu}, TAG_MATCH("commitid", tag_commitid),
{"%format", var_format}, TAG_MATCH("%include", var_include),
{"->", menu_next},
{"==>", menu_load}, TAG_MATCH("playcount", tag_playcount),
{"%reload", menu_reload} TAG_MATCH("autoscore", tag_virt_autoscore),
TAG_MATCH("lastplayed", tag_lastplayed),
TAG_MATCH("lastoffset", tag_lastoffset),
TAG_MATCH("%root_menu", var_rootmenu),
TAG_MATCH("albumartist", tag_albumartist),
TAG_MATCH("lastelapsed", tag_lastelapsed),
TAG_MATCH("%menu_start", var_menu_start),
TAG_MATCH("canonicalartist", tag_virt_canonicalartist),
TAG_MATCH("", 0) /* sentinel */
}; };
char buf[128]; #undef TAG_MATCH
unsigned int i; const size_t max_cmd_sz = 32; /* needs to be >= to len of longest tagstr */
const char *tagstr;
unsigned int tagstr_len;
const struct match *match;
/* Find the start. */ /* Find the start. */
while ((*strp == ' ' || *strp == '>') && *strp != '\0') while (*strp == ' ' || *strp == '>')
strp++; strp++;
if (*strp == '\0' || *strp == '?') if (*strp == '\0' || *strp == '?')
return 0; return 0;
for (i = 0; i < sizeof(buf)-1; i++) tagstr = strp;
for (tagstr_len = 0; tagstr_len < max_cmd_sz; tagstr_len++)
{ {
if (*strp == '\0' || *strp == ' ') if (*strp == '\0' || *strp == ' ')
break ; break ;
buf[i] = *strp;
strp++; strp++;
} }
buf[i] = '\0';
for (i = 0; i < ARRAYLEN(get_tag_match); i++) for (match = get_tag_match; match->len != 0; match++)
{ {
if (!strcasecmp(buf, get_tag_match[i].str)) if (tagstr_len != match->len)
continue;
else if (strncasecmp(tagstr, match->str, match->len) == 0)
{ {
*tag = get_tag_match[i].symbol; *tag = match->symbol;
return 1; return 1;
} }
} }
logf("NO MATCH: %s\n", buf); logf("NO MATCH: %.*s\n", tagstr_len, tagstr);
if (buf[0] == '?')
return 0;
return -1; return -1;
} }
@ -412,6 +427,7 @@ static int get_clause(int *condition)
#define CLAUSE(op1, op2, symbol) {OPS2VAL(op1, op2), symbol } #define CLAUSE(op1, op2, symbol) {OPS2VAL(op1, op2), symbol }
struct clause_symbol {int value;int symbol;}; struct clause_symbol {int value;int symbol;};
const struct clause_symbol *match;
static const struct clause_symbol get_clause_match[] = static const struct clause_symbol get_clause_match[] =
{ {
CLAUSE('=', ' ', clause_is), CLAUSE('=', ' ', clause_is),
@ -429,7 +445,8 @@ static int get_clause(int *condition)
CLAUSE('!', '$', clause_not_ends_with), CLAUSE('!', '$', clause_not_ends_with),
CLAUSE('@', '^', clause_begins_oneof), CLAUSE('@', '^', clause_begins_oneof),
CLAUSE('@', '$', clause_ends_oneof), CLAUSE('@', '$', clause_ends_oneof),
CLAUSE('@', ' ', clause_oneof) CLAUSE('@', ' ', clause_oneof),
CLAUSE(0, 0, 0) /* sentinel */
}; };
/* Find the start. */ /* Find the start. */
@ -446,11 +463,11 @@ static int get_clause(int *condition)
int value = OPS2VAL(op1, op2); int value = OPS2VAL(op1, op2);
for (unsigned int i = 0; i < ARRAYLEN(get_clause_match); i++) for (match = get_clause_match; match->value != 0; match++)
{ {
if (value == get_clause_match[i].value) if (value == match->value)
{ {
*condition = get_clause_match[i].symbol; *condition = match->symbol;
return 1; return 1;
} }
} }
@ -1321,7 +1338,7 @@ static bool show_search_progress(bool init, int count)
static int format_str(struct tagcache_search *tcs, struct display_format *fmt, static int format_str(struct tagcache_search *tcs, struct display_format *fmt,
char *buf, int buf_size) char *buf, int buf_size)
{ {
char fmtbuf[20]; static char fmtbuf[20];
bool read_format = false; bool read_format = false;
unsigned fmtbuf_pos = 0; unsigned fmtbuf_pos = 0;
int parpos = 0; int parpos = 0;
@ -1583,7 +1600,6 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
fmt = NULL; fmt = NULL;
/* Check the format */ /* Check the format */
core_pin(tagtree_handle);
for (i = 0; i < format_count; i++) for (i = 0; i < format_count; i++)
{ {
if (formats[i]->group_id != csi->format_id[level]) if (formats[i]->group_id != csi->format_id[level])
@ -1596,7 +1612,6 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
break; break;
} }
} }
core_unpin(tagtree_handle);
if (strcmp(tcs.result, UNTAGGED) == 0) if (strcmp(tcs.result, UNTAGGED) == 0)
{ {