* represent often used comments as members in track_info
* replace string sort keys array with integer array
speedup: 21% cpu time for initial indexing (~30k files)
---
cache.c | 6 ++--
editable.c | 31 +++-----------------
editable.h | 4 +-
options.c | 89 +++++++++++++++++++++++++++++++++++++++++-----------------
player.c | 2 +-
track.c | 3 +-
track.h | 3 +-
track_info.c | 85 ++++++++++++++++++++++++++-----------------------------
track_info.h | 32 ++++++++++++++++++++-
tree.c | 26 ++++++++---------
ui_curses.c | 21 ++++++--------
utils.h | 2 +
12 files changed, 171 insertions(+), 133 deletions(-)
diff --git a/cache.c b/cache.c
index ffe5f1c..f999036 100644
--- a/cache.c
+++ b/cache.c
@@ -112,8 +112,7 @@ static struct track_info *cache_entry_to_ti(struct cache_entry *e)
// NOTE: filename already copied by track_info_new()
pos = strlen(strings) + 1;
- ti->comments = xnew(struct keyval, count + 1);
- kv = ti->comments;
+ kv = xnew(struct keyval, count + 1);
for (i = 0; i < count; i++) {
int size;
@@ -127,6 +126,7 @@ static struct track_info *cache_entry_to_ti(struct cache_entry *e)
}
kv[i].key = NULL;
kv[i].val = NULL;
+ track_info_set_comments(ti, kv);
return ti;
}
@@ -365,7 +365,7 @@ static struct track_info *ip_get_ti(const char *filename)
rc = ip_read_comments(ip, &comments);
if (!rc) {
ti = track_info_new(filename);
- ti->comments = comments;
+ track_info_set_comments(ti, comments);
ti->duration = ip_duration(ip);
ti->mtime = ip_is_remote(ip) ? -1 : file_get_mtime(filename);
}
diff --git a/editable.c b/editable.c
index 9ff5e6d..e09f8ff 100644
--- a/editable.c
+++ b/editable.c
@@ -39,8 +39,8 @@ void editable_init(struct editable *e, void (*free_track)(struct list_head *item
e->nr_tracks = 0;
e->nr_marked = 0;
e->total_time = 0;
- e->sort_keys = xnew(const char *, 1);
- e->sort_keys[0] = NULL;
+ e->sort_keys = xnew(sort_key_t, 1);
+ e->sort_keys[0] = SORT_INVALID;
e->sort_str[0] = 0;
e->free_track = free_track;
@@ -121,32 +121,11 @@ void editable_sort(struct editable *e)
window_goto_top(e->win);
}
-static void keys_to_str(const char **keys, char *buf, size_t bufsize)
-{
- int i, pos = 0;
-
- for (i = 0; keys[i]; i++) {
- const char *key = keys[i];
- int len = strlen(key);
-
- if ((int)bufsize - pos - len - 2 < 0)
- break;
-
- memcpy(buf + pos, key, len);
- pos += len;
- buf[pos++] = ' ';
- }
- if (pos > 0)
- pos--;
- buf[pos] = 0;
-}
-
-void editable_set_sort_keys(struct editable *e, const char **keys)
+void editable_set_sort_keys(struct editable *e, sort_key_t *keys)
{
free(e->sort_keys);
e->sort_keys = keys;
editable_sort(e);
- keys_to_str(keys, e->sort_str, sizeof(e->sort_str));
}
void editable_toggle_mark(struct editable *e)
@@ -261,7 +240,7 @@ void editable_move_after(struct editable *e)
{
struct simple_track *sel;
- if (e->nr_tracks <= 1 || e->sort_keys[0])
+ if (e->nr_tracks <= 1 || e->sort_keys[0] != SORT_INVALID)
return;
sel = get_selected(e);
@@ -273,7 +252,7 @@ void editable_move_before(struct editable *e)
{
struct simple_track *sel;
- if (e->nr_tracks <= 1 || e->sort_keys[0])
+ if (e->nr_tracks <= 1 || e->sort_keys[0] != SORT_INVALID)
return;
sel = get_selected(e);
diff --git a/editable.h b/editable.h
index bff11b7..84e8413 100644
--- a/editable.h
+++ b/editable.h
@@ -18,7 +18,7 @@ struct editable {
unsigned int nr_tracks;
unsigned int nr_marked;
unsigned int total_time;
- const char **sort_keys;
+ sort_key_t *sort_keys;
char sort_str[128];
struct searchable *searchable;
@@ -32,7 +32,7 @@ void editable_add(struct editable *e, struct simple_track *track);
void editable_remove_track(struct editable *e, struct simple_track *track);
void editable_remove_sel(struct editable *e);
void editable_sort(struct editable *e);
-void editable_set_sort_keys(struct editable *e, const char **keys);
+void editable_set_sort_keys(struct editable *e, sort_key_t *keys);
void editable_toggle_mark(struct editable *e);
void editable_move_after(struct editable *e);
void editable_move_before(struct editable *e);
diff --git a/options.c b/options.c
index 0ee8e8d..43ab196 100644
--- a/options.c
+++ b/options.c
@@ -22,6 +22,7 @@
#include "prog.h"
#include "output.h"
#include "config/datadir.h"
+#include "track_info.h"
#include <stdio.h>
#include <errno.h>
@@ -184,30 +185,33 @@ static void set_id3_default_charset(unsigned int id, const char *buf)
id3_default_charset = xstrdup(buf);
}
-static const char * const valid_sort_keys[] = {
- "artist",
- "album",
- "title",
- "tracknumber",
- "discnumber",
- "date",
- "genre",
- "comment",
- "filename",
- "albumartist",
- "filemtime",
- NULL
+static const struct {
+ const char *str;
+ sort_key_t key;
+} sort_key_map[] = {
+ { "artist", SORT_ARTIST },
+ { "album", SORT_ALBUM },
+ { "title", SORT_TITLE },
+ { "tracknumber", SORT_TRACKNUMBER },
+ { "discnumber", SORT_DISCNUMBER },
+ { "date", SORT_DATE },
+ { "genre", SORT_GENRE },
+ { "comment", SORT_COMMENT },
+ { "albumartist", SORT_ALBUMARTIST },
+ { "filename", SORT_FILENAME },
+ { "filemtime", SORT_FILEMTIME },
+ { NULL, SORT_INVALID }
};
-static const char **parse_sort_keys(const char *value)
+static sort_key_t *parse_sort_keys(const char *value)
{
- const char **keys;
+ sort_key_t *keys;
const char *s, *e;
int size = 4;
int pos = 0;
size = 4;
- keys = xnew(const char *, size);
+ keys = xnew(sort_key_t, size);
s = value;
while (1) {
@@ -232,26 +236,55 @@ static const char **parse_sort_keys(const char *value)
s = e;
for (i = 0; ; i++) {
- if (valid_sort_keys[i] == NULL) {
+ if (sort_key_map[i].str == NULL) {
error_msg("invalid sort key '%s'", buf);
free(keys);
return NULL;
}
- if (strcmp(buf, valid_sort_keys[i]) == 0)
+ if (strcmp(buf, sort_key_map[i].str) == 0)
break;
}
-
if (pos == size - 1) {
size *= 2;
- keys = xrenew(const char *, keys, size);
+ keys = xrenew(sort_key_t, keys, size);
}
- keys[pos++] = valid_sort_keys[i];
+ keys[pos++] = sort_key_map[i].key;
}
- keys[pos] = NULL;
+ keys[pos] = SORT_INVALID;
return keys;
}
+static const char *sort_key_to_str(sort_key_t key)
+{
+ int i;
+ for (i = 0; sort_key_map[i].str; i++) {
+ if (sort_key_map[i].key == key)
+ return sort_key_map[i].str;
+ }
+ return NULL;
+}
+
+static void sort_keys_to_str(const sort_key_t *keys, char *buf, size_t bufsize)
+{
+ int i, pos = 0;
+
+ for (i = 0; keys[i] != SORT_INVALID; i++) {
+ const char *key = sort_key_to_str(keys[i]);
+ int len = strlen(key);
+
+ if ((int)bufsize - pos - len - 2 < 0)
+ break;
+
+ memcpy(buf + pos, key, len);
+ pos += len;
+ buf[pos++] = ' ';
+ }
+ if (pos > 0)
+ pos--;
+ buf[pos] = 0;
+}
+
static void get_lib_sort(unsigned int id, char *buf)
{
strcpy(buf, lib_editable.sort_str);
@@ -259,10 +292,12 @@ static void get_lib_sort(unsigned int id, char *buf)
static void set_lib_sort(unsigned int id, const char *buf)
{
- const char **keys = parse_sort_keys(buf);
+ sort_key_t *keys = parse_sort_keys(buf);
- if (keys)
+ if (keys) {
editable_set_sort_keys(&lib_editable, keys);
+ sort_keys_to_str(keys, lib_editable.sort_str, sizeof(lib_editable.sort_str));
+ }
}
static void get_pl_sort(unsigned int id, char *buf)
@@ -272,10 +307,12 @@ static void get_pl_sort(unsigned int id, char *buf)
static void set_pl_sort(unsigned int id, const char *buf)
{
- const char **keys = parse_sort_keys(buf);
+ sort_key_t *keys = parse_sort_keys(buf);
- if (keys)
+ if (keys) {
editable_set_sort_keys(&pl_editable, keys);
+ sort_keys_to_str(keys, pl_editable.sort_str, sizeof(pl_editable.sort_str));
+ }
}
static void get_output_plugin(unsigned int id, char *buf)
diff --git a/player.c b/player.c
index fc1ec4f..9b97969 100644
--- a/player.c
+++ b/player.c
@@ -355,7 +355,7 @@ static inline void metadata_changed(void)
if (!rc) {
if (player_info.ti->comments)
keyvals_free(player_info.ti->comments);
- player_info.ti->comments = comments;
+ track_info_set_comments(player_info.ti, comments);
}
player_info.metadata_changed = 1;
diff --git a/track.c b/track.c
index ed199d8..8c63319 100644
--- a/track.c
+++ b/track.c
@@ -4,7 +4,6 @@
#include "track.h"
#include "iter.h"
-#include "track_info.h"
#include "search_mode.h"
#include "window.h"
#include "options.h"
@@ -151,7 +150,7 @@ again:
return NULL;
}
-void sorted_list_add_track(struct list_head *head, struct rb_root *tree_root, struct simple_track *track, const char * const *keys)
+void sorted_list_add_track(struct list_head *head, struct rb_root *tree_root, struct simple_track *track, const sort_key_t *keys)
{
struct rb_node **new = &(tree_root->rb_node), *parent = NULL, *curr, *prev, *next;
diff --git a/track.h b/track.h
index 6735a53..e6eeec9 100644
--- a/track.h
+++ b/track.h
@@ -8,6 +8,7 @@
#include "list.h"
#include "rbtree.h"
#include "iter.h"
+#include "track_info.h"
struct simple_track {
struct list_head node;
@@ -72,7 +73,7 @@ struct simple_track *simple_list_get_next(struct list_head *head, struct simple_
struct simple_track *simple_list_get_prev(struct list_head *head, struct simple_track *cur,
int (*filter)(const struct simple_track *));
-void sorted_list_add_track(struct list_head *head, struct rb_root *tree_root, struct simple_track *track, const char * const *keys);
+void sorted_list_add_track(struct list_head *head, struct rb_root *tree_root, struct simple_track *track, const sort_key_t *keys);
void list_add_rand(struct list_head *head, struct list_head *node, int nr);
diff --git a/track_info.c b/track_info.c
index 334ac9e..b211faf 100644
--- a/track_info.c
+++ b/track_info.c
@@ -41,9 +41,25 @@ struct track_info *track_info_new(const char *filename)
ti = xnew(struct track_info, 1);
ti->filename = xstrdup(filename);
ti->ref = 1;
+ ti->comments = NULL;
return ti;
}
+void track_info_set_comments(struct track_info *ti, struct keyval *comments) {
+ ti->comments = comments;
+ ti->artist = keyvals_get_val(comments, "artist");
+ ti->album = keyvals_get_val(comments, "album");
+ ti->title = keyvals_get_val(comments, "title");
+ ti->tracknumber = comments_get_int(comments, "tracknumber");
+ ti->discnumber = comments_get_int(comments, "discnumber");
+ ti->date = comments_get_date(comments, "date");
+ ti->genre = keyvals_get_val(comments, "genre");
+ ti->comment = keyvals_get_val(comments, "comment");
+ ti->albumartist = comments_get_albumartist(comments);
+ ti->artistsort = comments_get_artistsort(comments);
+ ti->is_va_compilation = track_is_va_compilation(comments);
+}
+
void track_info_ref(struct track_info *ti)
{
BUG_ON(ti->ref < 1);
@@ -60,17 +76,15 @@ void track_info_unref(struct track_info *ti)
int track_info_has_tag(const struct track_info *ti)
{
- return keyvals_get_val(ti->comments, "artist") ||
- keyvals_get_val(ti->comments, "album") ||
- keyvals_get_val(ti->comments, "title");
+ return ti->artist || ti->album || ti->title;
}
int track_info_matches(struct track_info *ti, const char *text, unsigned int flags)
{
- const char *artist = keyvals_get_val(ti->comments, "artist");
- const char *album = keyvals_get_val(ti->comments, "album");
- const char *title = keyvals_get_val(ti->comments, "title");
- const char *albumartist = keyvals_get_val(ti->comments, "albumartist");
+ const char *artist = ti->artist;
+ const char *album = ti->album;
+ const char *title = ti->title;
+ const char *albumartist = ti->albumartist;
char **words;
int i, matched = 1;
@@ -111,54 +125,35 @@ int track_info_matches(struct track_info *ti, const char *text, unsigned int fla
return matched;
}
-int track_info_cmp(const struct track_info *a, const struct track_info *b, const char * const *keys)
+/* this function gets called *alot*, it must be very fast */
+int track_info_cmp(const struct track_info *a, const struct track_info *b, const sort_key_t *keys)
{
int i, res = 0;
- for (i = 0; keys[i]; i++) {
- const char *key = keys[i];
+ for (i = 0; keys[i] != SORT_INVALID; i++) {
+ sort_key_t key = keys[i];
const char *av, *bv;
- /* numeric compare for tracknumber and discnumber */
- if (strcmp(key, "tracknumber") == 0) {
- res = comments_get_int(a->comments, key) -
- comments_get_int(b->comments, key);
- if (res)
- break;
- continue;
- }
- if (strcmp(key, "discnumber") == 0) {
- res = comments_get_int(a->comments, key) -
- comments_get_int(b->comments, key);
- if (res)
- break;
- continue;
- }
- if (strcmp(key, "filename") == 0) {
+ switch (key) {
+ case SORT_TRACKNUMBER:
+ case SORT_DISCNUMBER:
+ case SORT_DATE:
+ res = getentry(a, key, int) - getentry(b, key, int);
+ break;
+ case SORT_FILEMTIME:
+ res = a->mtime - b->mtime;
+ break;
+ case SORT_FILENAME:
/* NOTE: filenames are not necessarily UTF-8 */
res = strcoll(a->filename, b->filename);
- if (res)
- break;
- continue;
- }
- if (strcmp(key, "albumartist") == 0) {
- av = comments_get_albumartist(a->comments);
- bv = comments_get_albumartist(b->comments);
+ break;
+ default:
+ av = getentry(a, key, const char *);
+ bv = getentry(b, key, const char *);
res = u_strcasecoll0(av, bv);
- if (res)
- break;
- continue;
- }
- if (strcmp(key, "filemtime") == 0) {
- res = a->mtime - b->mtime;
- if (res)
- break;
- continue;
+ break;
}
- av = keyvals_get_val(a->comments, key);
- bv = keyvals_get_val(b->comments, key);
- res = u_strcasecoll0(av, bv);
if (res)
break;
}
diff --git a/track_info.h b/track_info.h
index 8353940..586948a 100644
--- a/track_info.h
+++ b/track_info.h
@@ -21,6 +21,7 @@
#define _TRACK_INFO_H
#include <time.h>
+#include <stddef.h>
struct track_info {
struct keyval *comments;
@@ -32,8 +33,36 @@ struct track_info {
int duration;
int ref;
char *filename;
+
+ int tracknumber;
+ int discnumber;
+ int date;
+ const char *artist;
+ const char *album;
+ const char *title;
+ const char *genre;
+ const char *comment;
+ const char *albumartist;
+ const char *artistsort;
+
+ int is_va_compilation : 1;
};
+typedef size_t sort_key_t;
+
+#define SORT_ARTIST offsetof(struct track_info, artist)
+#define SORT_ALBUM offsetof(struct track_info, album)
+#define SORT_TITLE offsetof(struct track_info, title)
+#define SORT_TRACKNUMBER offsetof(struct track_info, tracknumber)
+#define SORT_DISCNUMBER offsetof(struct track_info, discnumber)
+#define SORT_DATE offsetof(struct track_info, date)
+#define SORT_GENRE offsetof(struct track_info, genre)
+#define SORT_COMMENT offsetof(struct track_info, comment)
+#define SORT_ALBUMARTIST offsetof(struct track_info, albumartist)
+#define SORT_FILENAME offsetof(struct track_info, filename)
+#define SORT_FILEMTIME offsetof(struct track_info, mtime)
+#define SORT_INVALID ((sort_key_t) (-1))
+
#define TI_MATCH_ARTIST (1 << 0)
#define TI_MATCH_ALBUM (1 << 1)
#define TI_MATCH_TITLE (1 << 2)
@@ -41,6 +70,7 @@ struct track_info {
/* initializes only filename and ref */
struct track_info *track_info_new(const char *filename);
+void track_info_set_comments(struct track_info *ti, struct keyval *comments);
void track_info_ref(struct track_info *ti);
void track_info_unref(struct track_info *ti);
@@ -59,6 +89,6 @@ int track_info_has_tag(const struct track_info *ti);
*/
int track_info_matches(struct track_info *ti, const char *text, unsigned int flags);
-int track_info_cmp(const struct track_info *a, const struct track_info *b, const char * const *keys);
+int track_info_cmp(const struct track_info *a, const struct track_info *b, const sort_key_t *keys);
#endif
diff --git a/tree.c b/tree.c
index 95676bc..a4de3ee 100644
--- a/tree.c
+++ b/tree.c
@@ -586,8 +586,8 @@ static void album_add_track(struct album *album, struct tree_track *track)
* have all track numbers set or all unset (within one album
* of course).
*/
- static const char * const album_track_sort_keys[] = {
- "discnumber", "tracknumber", "filename", NULL
+ static const sort_key_t album_track_sort_keys[] = {
+ SORT_DISCNUMBER, SORT_TRACKNUMBER, SORT_FILENAME, SORT_INVALID
};
struct rb_node **new = &(album->track_root.rb_node), *parent = NULL;
@@ -611,23 +611,21 @@ static void album_add_track(struct album *album, struct tree_track *track)
rb_insert_color(&track->tree_node, &album->track_root);
}
-static const char *tree_artist_name(const struct keyval *comments)
+static const char *tree_artist_name(const struct track_info* ti)
{
- const char *val = keyvals_get_val(comments, "albumartist");
+ const char *val = ti->albumartist;
- if (track_is_va_compilation(comments))
+ if (ti->is_va_compilation)
val = "<Various Artists>";
if (!val || strcmp(val, "") == 0)
- val = keyvals_get_val(comments, "artist");
- if (!val || strcmp(val, "") == 0)
val = "<No Name>";
return val;
}
-static const char *tree_album_name(const struct keyval *comments)
+static const char *tree_album_name(const struct track_info* ti)
{
- const char *val = keyvals_get_val(comments, "album");
+ const char *val = ti->album;
if (!val || strcmp(val, "") == 0)
val = "<No Name>";
@@ -644,17 +642,17 @@ void tree_add_track(struct tree_track *track)
int date;
int is_va_compilation = 0;
- date = comments_get_date(ti->comments, "date");
+ date = ti->date;
if (is_url(ti->filename)) {
artist_name = "<Stream>";
album_name = "<Stream>";
} else {
- album_name = tree_album_name(ti->comments);
- artist_name = tree_artist_name(ti->comments);
- artistsort_name = comments_get_artistsort(ti->comments);
+ album_name = tree_album_name(ti);
+ artist_name = tree_artist_name(ti);
+ artistsort_name = ti->artistsort;
- is_va_compilation = track_is_va_compilation(ti->comments);
+ is_va_compilation = ti->is_va_compilation;
}
new_artist = artist_new(artist_name, artistsort_name);
diff --git a/ui_curses.c b/ui_curses.c
index d217d7d..e986a60 100644
--- a/ui_curses.c
+++ b/ui_curses.c
@@ -490,7 +490,6 @@ static inline void fopt_set_time(struct format_option *fopt, int value, int empt
static void fill_track_fopts_track_info(struct track_info *info)
{
char *filename;
- int num, disc;
if (using_utf8) {
filename = info->filename;
@@ -498,18 +497,16 @@ static void fill_track_fopts_track_info(struct track_info *info)
utf8_encode(info->filename);
filename = conv_buffer;
}
- disc = comments_get_int(info->comments, "discnumber");
- num = comments_get_int(info->comments, "tracknumber");
- fopt_set_str(&track_fopts[TF_ALBUMARTIST], comments_get_albumartist(info->comments));
- fopt_set_str(&track_fopts[TF_ARTIST], keyvals_get_val(info->comments, "artist"));
- fopt_set_str(&track_fopts[TF_ALBUM], keyvals_get_val(info->comments, "album"));
- fopt_set_int(&track_fopts[TF_DISC], disc, disc == -1);
- fopt_set_int(&track_fopts[TF_TRACK], num, num == -1);
- fopt_set_str(&track_fopts[TF_TITLE], keyvals_get_val(info->comments, "title"));
+ fopt_set_str(&track_fopts[TF_ALBUMARTIST], info->albumartist);
+ fopt_set_str(&track_fopts[TF_ARTIST], info->artist);
+ fopt_set_str(&track_fopts[TF_ALBUM], info->album);
+ fopt_set_int(&track_fopts[TF_DISC], info->discnumber, info->discnumber == -1);
+ fopt_set_int(&track_fopts[TF_TRACK], info->tracknumber, info->tracknumber == -1);
+ fopt_set_str(&track_fopts[TF_TITLE], info->title);
fopt_set_str(&track_fopts[TF_YEAR], keyvals_get_val(info->comments, "date"));
- fopt_set_str(&track_fopts[TF_GENRE], keyvals_get_val(info->comments, "genre"));
- fopt_set_str(&track_fopts[TF_COMMENT], keyvals_get_val(info->comments, "comment"));
+ fopt_set_str(&track_fopts[TF_GENRE], info->genre);
+ fopt_set_str(&track_fopts[TF_COMMENT], info->comment);
fopt_set_time(&track_fopts[TF_DURATION], info->duration, info->duration == -1);
fopt_set_str(&track_fopts[TF_PATHFILE], filename);
if (is_url(info->filename)) {
diff --git a/utils.h b/utils.h
index 87d5925..4f28ef5 100644
--- a/utils.h
+++ b/utils.h
@@ -30,6 +30,8 @@
#define N_ELEMENTS(array) (sizeof(array) / sizeof((array)[0]))
+#define getentry(ptr, offset, type) (*((type *) ((char *) (ptr) + (offset))))
+
static inline int min(int a, int b)
{
return a < b ? a : b;
--
1.7.2.3